Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sctp: add the sctp_diag.c file

This one will implement all the interface of inet_diag, inet_diag_handler.
which includes sctp_diag_dump, sctp_diag_dump_one and sctp_diag_get_info.

It will work as a module, and register inet_diag_handler when loading.

v2->v3:
- fix the mistake in inet_assoc_attr_size().

- change inet_diag_msg_laddrs_fill() name to inet_diag_msg_sctpladdrs_fill.

- change inet_diag_msg_paddrs_fill() name to inet_diag_msg_sctpaddrs_fill.

- add inet_diag_msg_sctpinfo_fill() to make asoc/ep fill code clearer.

- add inet_diag_msg_sctpasoc_fill() to make asoc fill code clearer.

- merge inet_asoc_diag_fill() and inet_ep_diag_fill() to
inet_sctp_diag_fill().

- call sctp_diag_get_info() directly, instead by handler, cause the caller
is in the same file with it.

- call lock_sock in sctp_tsp_dump_one() to make sure we call get sctp info
safely.

- after lock_sock(sk), we should check sk != assoc->base.sk.

- change mem[SK_MEMINFO_WMEM_ALLOC] to asoc->sndbuf_used for asoc dump when
asoc->ep->sndbuf_policy is set. don't use INET_DIAG_MEMINFO attr any more.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Xin Long and committed by
David S. Miller
8f840e47 cb2050a7

+504
+2
include/uapi/linux/inet_diag.h
··· 113 113 INET_DIAG_DCTCPINFO, 114 114 INET_DIAG_PROTOCOL, /* response attribute only */ 115 115 INET_DIAG_SKV6ONLY, 116 + INET_DIAG_LOCALS, 117 + INET_DIAG_PEERS, 116 118 }; 117 119 118 120 #define INET_DIAG_MAX INET_DIAG_SKV6ONLY
+4
net/sctp/Kconfig
··· 99 99 select CRYPTO_HMAC if SCTP_COOKIE_HMAC_SHA1 100 100 select CRYPTO_SHA1 if SCTP_COOKIE_HMAC_SHA1 101 101 102 + config INET_SCTP_DIAG 103 + depends on INET_DIAG 104 + def_tristate INET_DIAG 105 + 102 106 103 107 endif # IP_SCTP
+1
net/sctp/Makefile
··· 4 4 5 5 obj-$(CONFIG_IP_SCTP) += sctp.o 6 6 obj-$(CONFIG_NET_SCTPPROBE) += sctp_probe.o 7 + obj-$(CONFIG_INET_SCTP_DIAG) += sctp_diag.o 7 8 8 9 sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \ 9 10 protocol.o endpointola.o associola.o \
+497
net/sctp/sctp_diag.c
··· 1 + #include <linux/module.h> 2 + #include <linux/inet_diag.h> 3 + #include <linux/sock_diag.h> 4 + #include <net/sctp/sctp.h> 5 + 6 + extern void inet_diag_msg_common_fill(struct inet_diag_msg *r, 7 + struct sock *sk); 8 + extern int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, 9 + struct inet_diag_msg *r, int ext, 10 + struct user_namespace *user_ns); 11 + 12 + static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, 13 + void *info); 14 + 15 + /* define some functions to make asoc/ep fill look clean */ 16 + static void inet_diag_msg_sctpasoc_fill(struct inet_diag_msg *r, 17 + struct sock *sk, 18 + struct sctp_association *asoc) 19 + { 20 + union sctp_addr laddr, paddr; 21 + struct dst_entry *dst; 22 + 23 + laddr = list_entry(asoc->base.bind_addr.address_list.next, 24 + struct sctp_sockaddr_entry, list)->a; 25 + paddr = asoc->peer.primary_path->ipaddr; 26 + dst = asoc->peer.primary_path->dst; 27 + 28 + r->idiag_family = sk->sk_family; 29 + r->id.idiag_sport = htons(asoc->base.bind_addr.port); 30 + r->id.idiag_dport = htons(asoc->peer.port); 31 + r->id.idiag_if = dst ? dst->dev->ifindex : 0; 32 + sock_diag_save_cookie(sk, r->id.idiag_cookie); 33 + 34 + #if IS_ENABLED(CONFIG_IPV6) 35 + if (sk->sk_family == AF_INET6) { 36 + *(struct in6_addr *)r->id.idiag_src = laddr.v6.sin6_addr; 37 + *(struct in6_addr *)r->id.idiag_dst = paddr.v6.sin6_addr; 38 + } else 39 + #endif 40 + { 41 + memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src)); 42 + memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst)); 43 + 44 + r->id.idiag_src[0] = laddr.v4.sin_addr.s_addr; 45 + r->id.idiag_dst[0] = paddr.v4.sin_addr.s_addr; 46 + } 47 + 48 + r->idiag_state = asoc->state; 49 + r->idiag_timer = SCTP_EVENT_TIMEOUT_T3_RTX; 50 + r->idiag_retrans = asoc->rtx_data_chunks; 51 + #define EXPIRES_IN_MS(tmo) DIV_ROUND_UP((tmo - jiffies) * 1000, HZ) 52 + r->idiag_expires = 53 + EXPIRES_IN_MS(asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX]); 54 + #undef EXPIRES_IN_MS 55 + } 56 + 57 + static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb, 58 + struct list_head *address_list) 59 + { 60 + struct sctp_sockaddr_entry *laddr; 61 + int addrlen = sizeof(struct sockaddr_storage); 62 + int addrcnt = 0; 63 + struct nlattr *attr; 64 + void *info = NULL; 65 + 66 + list_for_each_entry_rcu(laddr, address_list, list) 67 + addrcnt++; 68 + 69 + attr = nla_reserve(skb, INET_DIAG_LOCALS, addrlen * addrcnt); 70 + if (!attr) 71 + return -EMSGSIZE; 72 + 73 + info = nla_data(attr); 74 + list_for_each_entry_rcu(laddr, address_list, list) { 75 + memcpy(info, &laddr->a, addrlen); 76 + info += addrlen; 77 + } 78 + 79 + return 0; 80 + } 81 + 82 + static int inet_diag_msg_sctpaddrs_fill(struct sk_buff *skb, 83 + struct sctp_association *asoc) 84 + { 85 + int addrlen = sizeof(struct sockaddr_storage); 86 + struct sctp_transport *from; 87 + struct nlattr *attr; 88 + void *info = NULL; 89 + 90 + attr = nla_reserve(skb, INET_DIAG_PEERS, 91 + addrlen * asoc->peer.transport_count); 92 + if (!attr) 93 + return -EMSGSIZE; 94 + 95 + info = nla_data(attr); 96 + list_for_each_entry(from, &asoc->peer.transport_addr_list, 97 + transports) { 98 + memcpy(info, &from->ipaddr, addrlen); 99 + info += addrlen; 100 + } 101 + 102 + return 0; 103 + } 104 + 105 + /* sctp asoc/ep fill*/ 106 + static int inet_sctp_diag_fill(struct sock *sk, struct sctp_association *asoc, 107 + struct sk_buff *skb, 108 + const struct inet_diag_req_v2 *req, 109 + struct user_namespace *user_ns, 110 + int portid, u32 seq, u16 nlmsg_flags, 111 + const struct nlmsghdr *unlh) 112 + { 113 + struct sctp_endpoint *ep = sctp_sk(sk)->ep; 114 + struct list_head *addr_list; 115 + struct inet_diag_msg *r; 116 + struct nlmsghdr *nlh; 117 + int ext = req->idiag_ext; 118 + struct sctp_infox infox; 119 + void *info = NULL; 120 + 121 + nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), 122 + nlmsg_flags); 123 + if (!nlh) 124 + return -EMSGSIZE; 125 + 126 + r = nlmsg_data(nlh); 127 + BUG_ON(!sk_fullsock(sk)); 128 + 129 + if (asoc) { 130 + inet_diag_msg_sctpasoc_fill(r, sk, asoc); 131 + } else { 132 + inet_diag_msg_common_fill(r, sk); 133 + r->idiag_state = sk->sk_state; 134 + r->idiag_timer = 0; 135 + r->idiag_retrans = 0; 136 + } 137 + 138 + if (inet_diag_msg_attrs_fill(sk, skb, r, ext, user_ns)) 139 + goto errout; 140 + 141 + if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) { 142 + u32 mem[SK_MEMINFO_VARS]; 143 + int amt; 144 + 145 + if (asoc && asoc->ep->sndbuf_policy) 146 + amt = asoc->sndbuf_used; 147 + else 148 + amt = sk_wmem_alloc_get(sk); 149 + mem[SK_MEMINFO_WMEM_ALLOC] = amt; 150 + mem[SK_MEMINFO_RMEM_ALLOC] = sk_rmem_alloc_get(sk); 151 + mem[SK_MEMINFO_RCVBUF] = sk->sk_rcvbuf; 152 + mem[SK_MEMINFO_SNDBUF] = sk->sk_sndbuf; 153 + mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc; 154 + mem[SK_MEMINFO_WMEM_QUEUED] = sk->sk_wmem_queued; 155 + mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc); 156 + mem[SK_MEMINFO_BACKLOG] = sk->sk_backlog.len; 157 + mem[SK_MEMINFO_DROPS] = atomic_read(&sk->sk_drops); 158 + 159 + if (nla_put(skb, INET_DIAG_SKMEMINFO, sizeof(mem), &mem) < 0) 160 + goto errout; 161 + } 162 + 163 + if (ext & (1 << (INET_DIAG_INFO - 1))) { 164 + struct nlattr *attr; 165 + 166 + attr = nla_reserve(skb, INET_DIAG_INFO, 167 + sizeof(struct sctp_info)); 168 + if (!attr) 169 + goto errout; 170 + 171 + info = nla_data(attr); 172 + } 173 + infox.sctpinfo = (struct sctp_info *)info; 174 + infox.asoc = asoc; 175 + sctp_diag_get_info(sk, r, &infox); 176 + 177 + addr_list = asoc ? &asoc->base.bind_addr.address_list 178 + : &ep->base.bind_addr.address_list; 179 + if (inet_diag_msg_sctpladdrs_fill(skb, addr_list)) 180 + goto errout; 181 + 182 + if (asoc && (ext & (1 << (INET_DIAG_CONG - 1)))) 183 + if (nla_put_string(skb, INET_DIAG_CONG, "reno") < 0) 184 + goto errout; 185 + 186 + if (asoc && inet_diag_msg_sctpaddrs_fill(skb, asoc)) 187 + goto errout; 188 + 189 + nlmsg_end(skb, nlh); 190 + return 0; 191 + 192 + errout: 193 + nlmsg_cancel(skb, nlh); 194 + return -EMSGSIZE; 195 + } 196 + 197 + /* callback and param */ 198 + struct sctp_comm_param { 199 + struct sk_buff *skb; 200 + struct netlink_callback *cb; 201 + const struct inet_diag_req_v2 *r; 202 + const struct nlmsghdr *nlh; 203 + }; 204 + 205 + static size_t inet_assoc_attr_size(struct sctp_association *asoc) 206 + { 207 + int addrlen = sizeof(struct sockaddr_storage); 208 + int addrcnt = 0; 209 + struct sctp_sockaddr_entry *laddr; 210 + 211 + list_for_each_entry_rcu(laddr, &asoc->base.bind_addr.address_list, 212 + list) 213 + addrcnt++; 214 + 215 + return nla_total_size(sizeof(struct sctp_info)) 216 + + nla_total_size(1) /* INET_DIAG_SHUTDOWN */ 217 + + nla_total_size(1) /* INET_DIAG_TOS */ 218 + + nla_total_size(1) /* INET_DIAG_TCLASS */ 219 + + nla_total_size(addrlen * asoc->peer.transport_count) 220 + + nla_total_size(addrlen * addrcnt) 221 + + nla_total_size(sizeof(struct inet_diag_meminfo)) 222 + + nla_total_size(sizeof(struct inet_diag_msg)) 223 + + 64; 224 + } 225 + 226 + static int sctp_tsp_dump_one(struct sctp_transport *tsp, void *p) 227 + { 228 + struct sctp_association *assoc = tsp->asoc; 229 + struct sock *sk = tsp->asoc->base.sk; 230 + struct sctp_comm_param *commp = p; 231 + struct sk_buff *in_skb = commp->skb; 232 + const struct inet_diag_req_v2 *req = commp->r; 233 + const struct nlmsghdr *nlh = commp->nlh; 234 + struct net *net = sock_net(in_skb->sk); 235 + struct sk_buff *rep; 236 + int err; 237 + 238 + err = sock_diag_check_cookie(sk, req->id.idiag_cookie); 239 + if (err) 240 + goto out; 241 + 242 + err = -ENOMEM; 243 + rep = nlmsg_new(inet_assoc_attr_size(assoc), GFP_KERNEL); 244 + if (!rep) 245 + goto out; 246 + 247 + lock_sock(sk); 248 + if (sk != assoc->base.sk) { 249 + release_sock(sk); 250 + sk = assoc->base.sk; 251 + lock_sock(sk); 252 + } 253 + err = inet_sctp_diag_fill(sk, assoc, rep, req, 254 + sk_user_ns(NETLINK_CB(in_skb).sk), 255 + NETLINK_CB(in_skb).portid, 256 + nlh->nlmsg_seq, 0, nlh); 257 + release_sock(sk); 258 + if (err < 0) { 259 + WARN_ON(err == -EMSGSIZE); 260 + kfree_skb(rep); 261 + goto out; 262 + } 263 + 264 + err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, 265 + MSG_DONTWAIT); 266 + if (err > 0) 267 + err = 0; 268 + out: 269 + return err; 270 + } 271 + 272 + static int sctp_tsp_dump(struct sctp_transport *tsp, void *p) 273 + { 274 + struct sctp_endpoint *ep = tsp->asoc->ep; 275 + struct sctp_comm_param *commp = p; 276 + struct sock *sk = ep->base.sk; 277 + struct sk_buff *skb = commp->skb; 278 + struct netlink_callback *cb = commp->cb; 279 + const struct inet_diag_req_v2 *r = commp->r; 280 + struct sctp_association *assoc = 281 + list_entry(ep->asocs.next, struct sctp_association, asocs); 282 + int err = 0; 283 + 284 + /* find the ep only once through the transports by this condition */ 285 + if (tsp->asoc != assoc) 286 + goto out; 287 + 288 + if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family) 289 + goto out; 290 + 291 + lock_sock(sk); 292 + if (sk != assoc->base.sk) 293 + goto release; 294 + list_for_each_entry(assoc, &ep->asocs, asocs) { 295 + if (cb->args[4] < cb->args[1]) 296 + goto next; 297 + 298 + if (r->id.idiag_sport != htons(assoc->base.bind_addr.port) && 299 + r->id.idiag_sport) 300 + goto next; 301 + if (r->id.idiag_dport != htons(assoc->peer.port) && 302 + r->id.idiag_dport) 303 + goto next; 304 + 305 + if (!cb->args[3] && 306 + inet_sctp_diag_fill(sk, NULL, skb, r, 307 + sk_user_ns(NETLINK_CB(cb->skb).sk), 308 + NETLINK_CB(cb->skb).portid, 309 + cb->nlh->nlmsg_seq, 310 + NLM_F_MULTI, cb->nlh) < 0) { 311 + cb->args[3] = 1; 312 + err = 2; 313 + goto release; 314 + } 315 + cb->args[3] = 1; 316 + 317 + if (inet_sctp_diag_fill(sk, assoc, skb, r, 318 + sk_user_ns(NETLINK_CB(cb->skb).sk), 319 + NETLINK_CB(cb->skb).portid, 320 + cb->nlh->nlmsg_seq, 0, cb->nlh) < 0) { 321 + err = 2; 322 + goto release; 323 + } 324 + next: 325 + cb->args[4]++; 326 + } 327 + cb->args[1] = 0; 328 + cb->args[2]++; 329 + cb->args[3] = 0; 330 + cb->args[4] = 0; 331 + release: 332 + release_sock(sk); 333 + return err; 334 + out: 335 + cb->args[2]++; 336 + return err; 337 + } 338 + 339 + static int sctp_ep_dump(struct sctp_endpoint *ep, void *p) 340 + { 341 + struct sctp_comm_param *commp = p; 342 + struct sock *sk = ep->base.sk; 343 + struct sk_buff *skb = commp->skb; 344 + struct netlink_callback *cb = commp->cb; 345 + const struct inet_diag_req_v2 *r = commp->r; 346 + struct net *net = sock_net(skb->sk); 347 + struct inet_sock *inet = inet_sk(sk); 348 + int err = 0; 349 + 350 + if (!net_eq(sock_net(sk), net)) 351 + goto out; 352 + 353 + if (cb->args[4] < cb->args[1]) 354 + goto next; 355 + 356 + if (r->sdiag_family != AF_UNSPEC && 357 + sk->sk_family != r->sdiag_family) 358 + goto next; 359 + 360 + if (r->id.idiag_sport != inet->inet_sport && 361 + r->id.idiag_sport) 362 + goto next; 363 + 364 + if (r->id.idiag_dport != inet->inet_dport && 365 + r->id.idiag_dport) 366 + goto next; 367 + 368 + if (inet_sctp_diag_fill(sk, NULL, skb, r, 369 + sk_user_ns(NETLINK_CB(cb->skb).sk), 370 + NETLINK_CB(cb->skb).portid, 371 + cb->nlh->nlmsg_seq, NLM_F_MULTI, 372 + cb->nlh) < 0) { 373 + err = 2; 374 + goto out; 375 + } 376 + next: 377 + cb->args[4]++; 378 + out: 379 + return err; 380 + } 381 + 382 + /* define the functions for sctp_diag_handler*/ 383 + static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, 384 + void *info) 385 + { 386 + struct sctp_infox *infox = (struct sctp_infox *)info; 387 + 388 + if (infox->asoc) { 389 + r->idiag_rqueue = atomic_read(&infox->asoc->rmem_alloc); 390 + r->idiag_wqueue = infox->asoc->sndbuf_used; 391 + } else { 392 + r->idiag_rqueue = sk->sk_ack_backlog; 393 + r->idiag_wqueue = sk->sk_max_ack_backlog; 394 + } 395 + if (infox->sctpinfo) 396 + sctp_get_sctp_info(sk, infox->asoc, infox->sctpinfo); 397 + } 398 + 399 + static int sctp_diag_dump_one(struct sk_buff *in_skb, 400 + const struct nlmsghdr *nlh, 401 + const struct inet_diag_req_v2 *req) 402 + { 403 + struct net *net = sock_net(in_skb->sk); 404 + union sctp_addr laddr, paddr; 405 + struct sctp_comm_param commp = { 406 + .skb = in_skb, 407 + .r = req, 408 + .nlh = nlh, 409 + }; 410 + 411 + if (req->sdiag_family == AF_INET) { 412 + laddr.v4.sin_port = req->id.idiag_sport; 413 + laddr.v4.sin_addr.s_addr = req->id.idiag_src[0]; 414 + laddr.v4.sin_family = AF_INET; 415 + 416 + paddr.v4.sin_port = req->id.idiag_dport; 417 + paddr.v4.sin_addr.s_addr = req->id.idiag_dst[0]; 418 + paddr.v4.sin_family = AF_INET; 419 + } else { 420 + laddr.v6.sin6_port = req->id.idiag_sport; 421 + memcpy(&laddr.v6.sin6_addr, req->id.idiag_src, 64); 422 + laddr.v6.sin6_family = AF_INET6; 423 + 424 + paddr.v6.sin6_port = req->id.idiag_dport; 425 + memcpy(&paddr.v6.sin6_addr, req->id.idiag_dst, 64); 426 + paddr.v6.sin6_family = AF_INET6; 427 + } 428 + 429 + return sctp_transport_lookup_process(sctp_tsp_dump_one, 430 + net, &laddr, &paddr, &commp); 431 + } 432 + 433 + static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 434 + const struct inet_diag_req_v2 *r, struct nlattr *bc) 435 + { 436 + u32 idiag_states = r->idiag_states; 437 + struct net *net = sock_net(skb->sk); 438 + struct sctp_comm_param commp = { 439 + .skb = skb, 440 + .cb = cb, 441 + .r = r, 442 + }; 443 + 444 + /* eps hashtable dumps 445 + * args: 446 + * 0 : if it will traversal listen sock 447 + * 1 : to record the sock pos of this time's traversal 448 + * 4 : to work as a temporary variable to traversal list 449 + */ 450 + if (cb->args[0] == 0) { 451 + if (!(idiag_states & TCPF_LISTEN)) 452 + goto skip; 453 + if (sctp_for_each_endpoint(sctp_ep_dump, &commp)) 454 + goto done; 455 + skip: 456 + cb->args[0] = 1; 457 + cb->args[1] = 0; 458 + cb->args[4] = 0; 459 + } 460 + 461 + /* asocs by transport hashtable dump 462 + * args: 463 + * 1 : to record the assoc pos of this time's traversal 464 + * 2 : to record the transport pos of this time's traversal 465 + * 3 : to mark if we have dumped the ep info of the current asoc 466 + * 4 : to work as a temporary variable to traversal list 467 + */ 468 + if (!(idiag_states & ~TCPF_LISTEN)) 469 + goto done; 470 + sctp_for_each_transport(sctp_tsp_dump, net, cb->args[2], &commp); 471 + done: 472 + cb->args[1] = cb->args[4]; 473 + cb->args[4] = 0; 474 + } 475 + 476 + static const struct inet_diag_handler sctp_diag_handler = { 477 + .dump = sctp_diag_dump, 478 + .dump_one = sctp_diag_dump_one, 479 + .idiag_get_info = sctp_diag_get_info, 480 + .idiag_type = IPPROTO_SCTP, 481 + .idiag_info_size = sizeof(struct sctp_info), 482 + }; 483 + 484 + static int __init sctp_diag_init(void) 485 + { 486 + return inet_diag_register(&sctp_diag_handler); 487 + } 488 + 489 + static void __exit sctp_diag_exit(void) 490 + { 491 + inet_diag_unregister(&sctp_diag_handler); 492 + } 493 + 494 + module_init(sctp_diag_init); 495 + module_exit(sctp_diag_exit); 496 + MODULE_LICENSE("GPL"); 497 + MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-132);