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 | } |