diff options
Diffstat (limited to 'net/ipv4/inet_diag.c')
| -rw-r--r-- | net/ipv4/inet_diag.c | 158 |
1 files changed, 111 insertions, 47 deletions
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 4c4ae4aaf8f6..985c5730e4fb 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c | |||
| @@ -70,20 +70,22 @@ static int inet_diag_fill(struct sk_buff *skb, struct sock *sk, | |||
| 70 | nlh->nlmsg_flags = nlmsg_flags; | 70 | nlh->nlmsg_flags = nlmsg_flags; |
| 71 | 71 | ||
| 72 | r = NLMSG_DATA(nlh); | 72 | r = NLMSG_DATA(nlh); |
| 73 | if (sk->sk_state != TCP_TIME_WAIT) { | 73 | BUG_ON(sk->sk_state == TCP_TIME_WAIT); |
| 74 | if (ext & (1 << (INET_DIAG_MEMINFO - 1))) | 74 | |
| 75 | minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO, | 75 | if (ext & (1 << (INET_DIAG_MEMINFO - 1))) |
| 76 | sizeof(*minfo)); | 76 | minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO, sizeof(*minfo)); |
| 77 | if (ext & (1 << (INET_DIAG_INFO - 1))) | 77 | |
| 78 | info = INET_DIAG_PUT(skb, INET_DIAG_INFO, | 78 | if (ext & (1 << (INET_DIAG_INFO - 1))) |
| 79 | handler->idiag_info_size); | 79 | info = INET_DIAG_PUT(skb, INET_DIAG_INFO, |
| 80 | 80 | handler->idiag_info_size); | |
| 81 | if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) { | 81 | |
| 82 | size_t len = strlen(icsk->icsk_ca_ops->name); | 82 | if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) { |
| 83 | strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1), | 83 | const size_t len = strlen(icsk->icsk_ca_ops->name); |
| 84 | icsk->icsk_ca_ops->name); | 84 | |
| 85 | } | 85 | strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1), |
| 86 | icsk->icsk_ca_ops->name); | ||
| 86 | } | 87 | } |
| 88 | |||
| 87 | r->idiag_family = sk->sk_family; | 89 | r->idiag_family = sk->sk_family; |
| 88 | r->idiag_state = sk->sk_state; | 90 | r->idiag_state = sk->sk_state; |
| 89 | r->idiag_timer = 0; | 91 | r->idiag_timer = 0; |
| @@ -93,37 +95,6 @@ static int inet_diag_fill(struct sk_buff *skb, struct sock *sk, | |||
| 93 | r->id.idiag_cookie[0] = (u32)(unsigned long)sk; | 95 | r->id.idiag_cookie[0] = (u32)(unsigned long)sk; |
| 94 | r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1); | 96 | r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1); |
| 95 | 97 | ||
| 96 | if (r->idiag_state == TCP_TIME_WAIT) { | ||
| 97 | const struct inet_timewait_sock *tw = inet_twsk(sk); | ||
| 98 | long tmo = tw->tw_ttd - jiffies; | ||
| 99 | if (tmo < 0) | ||
| 100 | tmo = 0; | ||
| 101 | |||
| 102 | r->id.idiag_sport = tw->tw_sport; | ||
| 103 | r->id.idiag_dport = tw->tw_dport; | ||
| 104 | r->id.idiag_src[0] = tw->tw_rcv_saddr; | ||
| 105 | r->id.idiag_dst[0] = tw->tw_daddr; | ||
| 106 | r->idiag_state = tw->tw_substate; | ||
| 107 | r->idiag_timer = 3; | ||
| 108 | r->idiag_expires = (tmo * 1000 + HZ - 1) / HZ; | ||
| 109 | r->idiag_rqueue = 0; | ||
| 110 | r->idiag_wqueue = 0; | ||
| 111 | r->idiag_uid = 0; | ||
| 112 | r->idiag_inode = 0; | ||
| 113 | #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | ||
| 114 | if (r->idiag_family == AF_INET6) { | ||
| 115 | const struct inet6_timewait_sock *tw6 = inet6_twsk(sk); | ||
| 116 | |||
| 117 | ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, | ||
| 118 | &tw6->tw_v6_rcv_saddr); | ||
| 119 | ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, | ||
| 120 | &tw6->tw_v6_daddr); | ||
| 121 | } | ||
| 122 | #endif | ||
| 123 | nlh->nlmsg_len = skb->tail - b; | ||
| 124 | return skb->len; | ||
| 125 | } | ||
| 126 | |||
| 127 | r->id.idiag_sport = inet->sport; | 98 | r->id.idiag_sport = inet->sport; |
| 128 | r->id.idiag_dport = inet->dport; | 99 | r->id.idiag_dport = inet->dport; |
| 129 | r->id.idiag_src[0] = inet->rcv_saddr; | 100 | r->id.idiag_src[0] = inet->rcv_saddr; |
| @@ -185,6 +156,62 @@ nlmsg_failure: | |||
| 185 | return -1; | 156 | return -1; |
| 186 | } | 157 | } |
| 187 | 158 | ||
| 159 | static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, | ||
| 160 | struct sk_buff *skb, int ext, u32 pid, | ||
| 161 | u32 seq, u16 nlmsg_flags, | ||
| 162 | const struct nlmsghdr *unlh) | ||
| 163 | { | ||
| 164 | long tmo; | ||
| 165 | struct inet_diag_msg *r; | ||
| 166 | const unsigned char *previous_tail = skb->tail; | ||
| 167 | struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq, | ||
| 168 | unlh->nlmsg_type, sizeof(*r)); | ||
| 169 | |||
| 170 | r = NLMSG_DATA(nlh); | ||
| 171 | BUG_ON(tw->tw_state != TCP_TIME_WAIT); | ||
| 172 | |||
| 173 | nlh->nlmsg_flags = nlmsg_flags; | ||
| 174 | |||
| 175 | tmo = tw->tw_ttd - jiffies; | ||
| 176 | if (tmo < 0) | ||
| 177 | tmo = 0; | ||
| 178 | |||
| 179 | r->idiag_family = tw->tw_family; | ||
| 180 | r->idiag_state = tw->tw_state; | ||
| 181 | r->idiag_timer = 0; | ||
| 182 | r->idiag_retrans = 0; | ||
| 183 | r->id.idiag_if = tw->tw_bound_dev_if; | ||
| 184 | r->id.idiag_cookie[0] = (u32)(unsigned long)tw; | ||
| 185 | r->id.idiag_cookie[1] = (u32)(((unsigned long)tw >> 31) >> 1); | ||
| 186 | r->id.idiag_sport = tw->tw_sport; | ||
| 187 | r->id.idiag_dport = tw->tw_dport; | ||
| 188 | r->id.idiag_src[0] = tw->tw_rcv_saddr; | ||
| 189 | r->id.idiag_dst[0] = tw->tw_daddr; | ||
| 190 | r->idiag_state = tw->tw_substate; | ||
| 191 | r->idiag_timer = 3; | ||
| 192 | r->idiag_expires = (tmo * 1000 + HZ - 1) / HZ; | ||
| 193 | r->idiag_rqueue = 0; | ||
| 194 | r->idiag_wqueue = 0; | ||
| 195 | r->idiag_uid = 0; | ||
| 196 | r->idiag_inode = 0; | ||
| 197 | #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | ||
| 198 | if (tw->tw_family == AF_INET6) { | ||
| 199 | const struct inet6_timewait_sock *tw6 = | ||
| 200 | inet6_twsk((struct sock *)tw); | ||
| 201 | |||
| 202 | ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, | ||
| 203 | &tw6->tw_v6_rcv_saddr); | ||
| 204 | ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, | ||
| 205 | &tw6->tw_v6_daddr); | ||
| 206 | } | ||
| 207 | #endif | ||
| 208 | nlh->nlmsg_len = skb->tail - previous_tail; | ||
| 209 | return skb->len; | ||
| 210 | nlmsg_failure: | ||
| 211 | skb_trim(skb, previous_tail - skb->data); | ||
| 212 | return -1; | ||
| 213 | } | ||
| 214 | |||
| 188 | static int inet_diag_get_exact(struct sk_buff *in_skb, | 215 | static int inet_diag_get_exact(struct sk_buff *in_skb, |
| 189 | const struct nlmsghdr *nlh) | 216 | const struct nlmsghdr *nlh) |
| 190 | { | 217 | { |
| @@ -450,6 +477,42 @@ static int inet_diag_dump_sock(struct sk_buff *skb, struct sock *sk, | |||
| 450 | cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); | 477 | cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); |
| 451 | } | 478 | } |
| 452 | 479 | ||
| 480 | static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, | ||
| 481 | struct sk_buff *skb, | ||
| 482 | struct netlink_callback *cb) | ||
| 483 | { | ||
| 484 | struct inet_diag_req *r = NLMSG_DATA(cb->nlh); | ||
| 485 | |||
| 486 | if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { | ||
| 487 | struct inet_diag_entry entry; | ||
| 488 | struct rtattr *bc = (struct rtattr *)(r + 1); | ||
| 489 | |||
| 490 | entry.family = tw->tw_family; | ||
| 491 | #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | ||
| 492 | if (tw->tw_family == AF_INET6) { | ||
| 493 | struct inet6_timewait_sock *tw6 = | ||
| 494 | inet6_twsk((struct sock *)tw); | ||
| 495 | entry.saddr = tw6->tw_v6_rcv_saddr.s6_addr32; | ||
| 496 | entry.daddr = tw6->tw_v6_daddr.s6_addr32; | ||
| 497 | } else | ||
| 498 | #endif | ||
| 499 | { | ||
| 500 | entry.saddr = &tw->tw_rcv_saddr; | ||
| 501 | entry.daddr = &tw->tw_daddr; | ||
| 502 | } | ||
| 503 | entry.sport = tw->tw_num; | ||
| 504 | entry.dport = ntohs(tw->tw_dport); | ||
| 505 | entry.userlocks = 0; | ||
| 506 | |||
| 507 | if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) | ||
| 508 | return 0; | ||
| 509 | } | ||
| 510 | |||
| 511 | return inet_twsk_diag_fill(tw, skb, r->idiag_ext, | ||
| 512 | NETLINK_CB(cb->skb).pid, | ||
| 513 | cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); | ||
| 514 | } | ||
| 515 | |||
| 453 | static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, | 516 | static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, |
| 454 | struct request_sock *req, u32 pid, u32 seq, | 517 | struct request_sock *req, u32 pid, u32 seq, |
| 455 | const struct nlmsghdr *unlh) | 518 | const struct nlmsghdr *unlh) |
| @@ -696,9 +759,10 @@ next_normal: | |||
| 696 | } | 759 | } |
| 697 | 760 | ||
| 698 | if (r->idiag_states & TCPF_TIME_WAIT) { | 761 | if (r->idiag_states & TCPF_TIME_WAIT) { |
| 699 | sk_for_each(sk, node, | 762 | struct inet_timewait_sock *tw; |
| 763 | |||
| 764 | inet_twsk_for_each(tw, node, | ||
| 700 | &hashinfo->ehash[i + hashinfo->ehash_size].chain) { | 765 | &hashinfo->ehash[i + hashinfo->ehash_size].chain) { |
| 701 | const struct inet_timewait_sock *tw = inet_twsk(sk); | ||
| 702 | 766 | ||
| 703 | if (num < s_num) | 767 | if (num < s_num) |
| 704 | goto next_dying; | 768 | goto next_dying; |
| @@ -708,7 +772,7 @@ next_normal: | |||
| 708 | if (r->id.idiag_dport != tw->tw_dport && | 772 | if (r->id.idiag_dport != tw->tw_dport && |
| 709 | r->id.idiag_dport) | 773 | r->id.idiag_dport) |
| 710 | goto next_dying; | 774 | goto next_dying; |
| 711 | if (inet_diag_dump_sock(skb, sk, cb) < 0) { | 775 | if (inet_twsk_diag_dump(tw, skb, cb) < 0) { |
| 712 | read_unlock_bh(&head->lock); | 776 | read_unlock_bh(&head->lock); |
| 713 | goto done; | 777 | goto done; |
| 714 | } | 778 | } |
