diff options
Diffstat (limited to 'net/ipv4/tcp_diag.c')
-rw-r--r-- | net/ipv4/tcp_diag.c | 86 |
1 files changed, 61 insertions, 25 deletions
diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 4288ecfec9a7..f5fc84aaa9b4 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c | |||
@@ -45,11 +45,15 @@ static struct sock *tcpnl; | |||
45 | #define TCPDIAG_PUT(skb, attrtype, attrlen) \ | 45 | #define TCPDIAG_PUT(skb, attrtype, attrlen) \ |
46 | RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) | 46 | RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) |
47 | 47 | ||
48 | #if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) | ||
49 | extern struct inet_hashinfo dccp_hashinfo; | ||
50 | #endif | ||
51 | |||
48 | static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, | 52 | static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, |
49 | int ext, u32 pid, u32 seq, u16 nlmsg_flags) | 53 | int ext, u32 pid, u32 seq, u16 nlmsg_flags, |
54 | const struct nlmsghdr *unlh) | ||
50 | { | 55 | { |
51 | const struct inet_sock *inet = inet_sk(sk); | 56 | const struct inet_sock *inet = inet_sk(sk); |
52 | struct tcp_sock *tp = tcp_sk(sk); | ||
53 | const struct inet_connection_sock *icsk = inet_csk(sk); | 57 | const struct inet_connection_sock *icsk = inet_csk(sk); |
54 | struct tcpdiagmsg *r; | 58 | struct tcpdiagmsg *r; |
55 | struct nlmsghdr *nlh; | 59 | struct nlmsghdr *nlh; |
@@ -57,7 +61,7 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, | |||
57 | struct tcpdiag_meminfo *minfo = NULL; | 61 | struct tcpdiag_meminfo *minfo = NULL; |
58 | unsigned char *b = skb->tail; | 62 | unsigned char *b = skb->tail; |
59 | 63 | ||
60 | nlh = NLMSG_PUT(skb, pid, seq, TCPDIAG_GETSOCK, sizeof(*r)); | 64 | nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); |
61 | nlh->nlmsg_flags = nlmsg_flags; | 65 | nlh->nlmsg_flags = nlmsg_flags; |
62 | r = NLMSG_DATA(nlh); | 66 | r = NLMSG_DATA(nlh); |
63 | if (sk->sk_state != TCP_TIME_WAIT) { | 67 | if (sk->sk_state != TCP_TIME_WAIT) { |
@@ -147,8 +151,20 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, | |||
147 | r->tcpdiag_expires = 0; | 151 | r->tcpdiag_expires = 0; |
148 | } | 152 | } |
149 | #undef EXPIRES_IN_MS | 153 | #undef EXPIRES_IN_MS |
150 | r->tcpdiag_rqueue = tp->rcv_nxt - tp->copied_seq; | 154 | /* |
151 | r->tcpdiag_wqueue = tp->write_seq - tp->snd_una; | 155 | * Ahem... for now we'll have some knowledge about TCP -acme |
156 | * But this is just one of two small exceptions, both in this | ||
157 | * function, so lets close our eyes for some 15 lines or so... 8) | ||
158 | * -acme | ||
159 | */ | ||
160 | if (sk->sk_protocol == IPPROTO_TCP) { | ||
161 | const struct tcp_sock *tp = tcp_sk(sk); | ||
162 | |||
163 | r->tcpdiag_rqueue = tp->rcv_nxt - tp->copied_seq; | ||
164 | r->tcpdiag_wqueue = tp->write_seq - tp->snd_una; | ||
165 | } else | ||
166 | r->tcpdiag_rqueue = r->tcpdiag_wqueue = 0; | ||
167 | |||
152 | r->tcpdiag_uid = sock_i_uid(sk); | 168 | r->tcpdiag_uid = sock_i_uid(sk); |
153 | r->tcpdiag_inode = sock_i_ino(sk); | 169 | r->tcpdiag_inode = sock_i_ino(sk); |
154 | 170 | ||
@@ -159,8 +175,13 @@ static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk, | |||
159 | minfo->tcpdiag_tmem = atomic_read(&sk->sk_wmem_alloc); | 175 | minfo->tcpdiag_tmem = atomic_read(&sk->sk_wmem_alloc); |
160 | } | 176 | } |
161 | 177 | ||
162 | if (info) | 178 | /* Ahem... for now we'll have some knowledge about TCP -acme */ |
163 | tcp_get_info(sk, info); | 179 | if (info) { |
180 | if (sk->sk_protocol == IPPROTO_TCP) | ||
181 | tcp_get_info(sk, info); | ||
182 | else | ||
183 | memset(info, 0, sizeof(*info)); | ||
184 | } | ||
164 | 185 | ||
165 | if (sk->sk_state < TCP_TIME_WAIT && | 186 | if (sk->sk_state < TCP_TIME_WAIT && |
166 | icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) | 187 | icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) |
@@ -194,9 +215,13 @@ static int tcpdiag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh) | |||
194 | struct sock *sk; | 215 | struct sock *sk; |
195 | struct tcpdiagreq *req = NLMSG_DATA(nlh); | 216 | struct tcpdiagreq *req = NLMSG_DATA(nlh); |
196 | struct sk_buff *rep; | 217 | struct sk_buff *rep; |
197 | 218 | struct inet_hashinfo *hashinfo = &tcp_hashinfo; | |
219 | #if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) | ||
220 | if (nlh->nlmsg_type == DCCPDIAG_GETSOCK) | ||
221 | hashinfo = &dccp_hashinfo; | ||
222 | #endif | ||
198 | if (req->tcpdiag_family == AF_INET) { | 223 | if (req->tcpdiag_family == AF_INET) { |
199 | sk = inet_lookup(&tcp_hashinfo, req->id.tcpdiag_dst[0], | 224 | sk = inet_lookup(hashinfo, req->id.tcpdiag_dst[0], |
200 | req->id.tcpdiag_dport, req->id.tcpdiag_src[0], | 225 | req->id.tcpdiag_dport, req->id.tcpdiag_src[0], |
201 | req->id.tcpdiag_sport, req->id.tcpdiag_if); | 226 | req->id.tcpdiag_sport, req->id.tcpdiag_if); |
202 | } | 227 | } |
@@ -230,7 +255,7 @@ static int tcpdiag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh) | |||
230 | 255 | ||
231 | if (tcpdiag_fill(rep, sk, req->tcpdiag_ext, | 256 | if (tcpdiag_fill(rep, sk, req->tcpdiag_ext, |
232 | NETLINK_CB(in_skb).pid, | 257 | NETLINK_CB(in_skb).pid, |
233 | nlh->nlmsg_seq, 0) <= 0) | 258 | nlh->nlmsg_seq, 0, nlh) <= 0) |
234 | BUG(); | 259 | BUG(); |
235 | 260 | ||
236 | err = netlink_unicast(tcpnl, rep, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); | 261 | err = netlink_unicast(tcpnl, rep, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); |
@@ -436,12 +461,13 @@ static int tcpdiag_dump_sock(struct sk_buff *skb, struct sock *sk, | |||
436 | } | 461 | } |
437 | 462 | ||
438 | return tcpdiag_fill(skb, sk, r->tcpdiag_ext, NETLINK_CB(cb->skb).pid, | 463 | return tcpdiag_fill(skb, sk, r->tcpdiag_ext, NETLINK_CB(cb->skb).pid, |
439 | cb->nlh->nlmsg_seq, NLM_F_MULTI); | 464 | cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); |
440 | } | 465 | } |
441 | 466 | ||
442 | static int tcpdiag_fill_req(struct sk_buff *skb, struct sock *sk, | 467 | static int tcpdiag_fill_req(struct sk_buff *skb, struct sock *sk, |
443 | struct request_sock *req, | 468 | struct request_sock *req, |
444 | u32 pid, u32 seq) | 469 | u32 pid, u32 seq, |
470 | const struct nlmsghdr *unlh) | ||
445 | { | 471 | { |
446 | const struct inet_request_sock *ireq = inet_rsk(req); | 472 | const struct inet_request_sock *ireq = inet_rsk(req); |
447 | struct inet_sock *inet = inet_sk(sk); | 473 | struct inet_sock *inet = inet_sk(sk); |
@@ -450,7 +476,7 @@ static int tcpdiag_fill_req(struct sk_buff *skb, struct sock *sk, | |||
450 | struct nlmsghdr *nlh; | 476 | struct nlmsghdr *nlh; |
451 | long tmo; | 477 | long tmo; |
452 | 478 | ||
453 | nlh = NLMSG_PUT(skb, pid, seq, TCPDIAG_GETSOCK, sizeof(*r)); | 479 | nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); |
454 | nlh->nlmsg_flags = NLM_F_MULTI; | 480 | nlh->nlmsg_flags = NLM_F_MULTI; |
455 | r = NLMSG_DATA(nlh); | 481 | r = NLMSG_DATA(nlh); |
456 | 482 | ||
@@ -526,7 +552,7 @@ static int tcpdiag_dump_reqs(struct sk_buff *skb, struct sock *sk, | |||
526 | entry.userlocks = sk->sk_userlocks; | 552 | entry.userlocks = sk->sk_userlocks; |
527 | } | 553 | } |
528 | 554 | ||
529 | for (j = s_j; j < TCP_SYNQ_HSIZE; j++) { | 555 | for (j = s_j; j < lopt->nr_table_entries; j++) { |
530 | struct request_sock *req, *head = lopt->syn_table[j]; | 556 | struct request_sock *req, *head = lopt->syn_table[j]; |
531 | 557 | ||
532 | reqnum = 0; | 558 | reqnum = 0; |
@@ -561,7 +587,7 @@ static int tcpdiag_dump_reqs(struct sk_buff *skb, struct sock *sk, | |||
561 | 587 | ||
562 | err = tcpdiag_fill_req(skb, sk, req, | 588 | err = tcpdiag_fill_req(skb, sk, req, |
563 | NETLINK_CB(cb->skb).pid, | 589 | NETLINK_CB(cb->skb).pid, |
564 | cb->nlh->nlmsg_seq); | 590 | cb->nlh->nlmsg_seq, cb->nlh); |
565 | if (err < 0) { | 591 | if (err < 0) { |
566 | cb->args[3] = j + 1; | 592 | cb->args[3] = j + 1; |
567 | cb->args[4] = reqnum; | 593 | cb->args[4] = reqnum; |
@@ -583,20 +609,26 @@ static int tcpdiag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
583 | int i, num; | 609 | int i, num; |
584 | int s_i, s_num; | 610 | int s_i, s_num; |
585 | struct tcpdiagreq *r = NLMSG_DATA(cb->nlh); | 611 | struct tcpdiagreq *r = NLMSG_DATA(cb->nlh); |
612 | struct inet_hashinfo *hashinfo; | ||
586 | 613 | ||
587 | s_i = cb->args[1]; | 614 | s_i = cb->args[1]; |
588 | s_num = num = cb->args[2]; | 615 | s_num = num = cb->args[2]; |
589 | 616 | hashinfo = &tcp_hashinfo; | |
617 | #if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) | ||
618 | if (cb->nlh->nlmsg_type == DCCPDIAG_GETSOCK) | ||
619 | hashinfo = &dccp_hashinfo; | ||
620 | #endif | ||
590 | if (cb->args[0] == 0) { | 621 | if (cb->args[0] == 0) { |
591 | if (!(r->tcpdiag_states&(TCPF_LISTEN|TCPF_SYN_RECV))) | 622 | if (!(r->tcpdiag_states&(TCPF_LISTEN|TCPF_SYN_RECV))) |
592 | goto skip_listen_ht; | 623 | goto skip_listen_ht; |
593 | inet_listen_lock(&tcp_hashinfo); | 624 | |
625 | inet_listen_lock(hashinfo); | ||
594 | for (i = s_i; i < INET_LHTABLE_SIZE; i++) { | 626 | for (i = s_i; i < INET_LHTABLE_SIZE; i++) { |
595 | struct sock *sk; | 627 | struct sock *sk; |
596 | struct hlist_node *node; | 628 | struct hlist_node *node; |
597 | 629 | ||
598 | num = 0; | 630 | num = 0; |
599 | sk_for_each(sk, node, &tcp_hashinfo.listening_hash[i]) { | 631 | sk_for_each(sk, node, &hashinfo->listening_hash[i]) { |
600 | struct inet_sock *inet = inet_sk(sk); | 632 | struct inet_sock *inet = inet_sk(sk); |
601 | 633 | ||
602 | if (num < s_num) { | 634 | if (num < s_num) { |
@@ -614,7 +646,7 @@ static int tcpdiag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
614 | goto syn_recv; | 646 | goto syn_recv; |
615 | 647 | ||
616 | if (tcpdiag_dump_sock(skb, sk, cb) < 0) { | 648 | if (tcpdiag_dump_sock(skb, sk, cb) < 0) { |
617 | inet_listen_unlock(&tcp_hashinfo); | 649 | inet_listen_unlock(hashinfo); |
618 | goto done; | 650 | goto done; |
619 | } | 651 | } |
620 | 652 | ||
@@ -623,7 +655,7 @@ syn_recv: | |||
623 | goto next_listen; | 655 | goto next_listen; |
624 | 656 | ||
625 | if (tcpdiag_dump_reqs(skb, sk, cb) < 0) { | 657 | if (tcpdiag_dump_reqs(skb, sk, cb) < 0) { |
626 | inet_listen_unlock(&tcp_hashinfo); | 658 | inet_listen_unlock(hashinfo); |
627 | goto done; | 659 | goto done; |
628 | } | 660 | } |
629 | 661 | ||
@@ -637,7 +669,7 @@ next_listen: | |||
637 | cb->args[3] = 0; | 669 | cb->args[3] = 0; |
638 | cb->args[4] = 0; | 670 | cb->args[4] = 0; |
639 | } | 671 | } |
640 | inet_listen_unlock(&tcp_hashinfo); | 672 | inet_listen_unlock(hashinfo); |
641 | skip_listen_ht: | 673 | skip_listen_ht: |
642 | cb->args[0] = 1; | 674 | cb->args[0] = 1; |
643 | s_i = num = s_num = 0; | 675 | s_i = num = s_num = 0; |
@@ -646,8 +678,8 @@ skip_listen_ht: | |||
646 | if (!(r->tcpdiag_states&~(TCPF_LISTEN|TCPF_SYN_RECV))) | 678 | if (!(r->tcpdiag_states&~(TCPF_LISTEN|TCPF_SYN_RECV))) |
647 | return skb->len; | 679 | return skb->len; |
648 | 680 | ||
649 | for (i = s_i; i < tcp_hashinfo.ehash_size; i++) { | 681 | for (i = s_i; i < hashinfo->ehash_size; i++) { |
650 | struct inet_ehash_bucket *head = &tcp_hashinfo.ehash[i]; | 682 | struct inet_ehash_bucket *head = &hashinfo->ehash[i]; |
651 | struct sock *sk; | 683 | struct sock *sk; |
652 | struct hlist_node *node; | 684 | struct hlist_node *node; |
653 | 685 | ||
@@ -679,7 +711,7 @@ next_normal: | |||
679 | 711 | ||
680 | if (r->tcpdiag_states&TCPF_TIME_WAIT) { | 712 | if (r->tcpdiag_states&TCPF_TIME_WAIT) { |
681 | sk_for_each(sk, node, | 713 | sk_for_each(sk, node, |
682 | &tcp_hashinfo.ehash[i + tcp_hashinfo.ehash_size].chain) { | 714 | &hashinfo->ehash[i + hashinfo->ehash_size].chain) { |
683 | struct inet_sock *inet = inet_sk(sk); | 715 | struct inet_sock *inet = inet_sk(sk); |
684 | 716 | ||
685 | if (num < s_num) | 717 | if (num < s_num) |
@@ -719,7 +751,11 @@ tcpdiag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
719 | if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) | 751 | if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) |
720 | return 0; | 752 | return 0; |
721 | 753 | ||
722 | if (nlh->nlmsg_type != TCPDIAG_GETSOCK) | 754 | if (nlh->nlmsg_type != TCPDIAG_GETSOCK |
755 | #if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) | ||
756 | && nlh->nlmsg_type != DCCPDIAG_GETSOCK | ||
757 | #endif | ||
758 | ) | ||
723 | goto err_inval; | 759 | goto err_inval; |
724 | 760 | ||
725 | if (NLMSG_LENGTH(sizeof(struct tcpdiagreq)) > skb->len) | 761 | if (NLMSG_LENGTH(sizeof(struct tcpdiagreq)) > skb->len) |