Commit a8c2190ee7da1a1dc68ff1a6b5f03feb61e523a5
Committed by
David S. Miller
1 parent
73c1f4a033
Exists in
master
and in
39 other branches
[INET_DIAG]: Rename tcp_diag.[ch] to inet_diag.[ch]
Next changeset will introduce net/ipv4/tcp_diag.c, moving the code that was put transitioanlly in inet_diag.c. Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 8 changed files with 1035 additions and 1034 deletions Side-by-side Diff
include/linux/inet_diag.h
1 | +#ifndef _INET_DIAG_H_ | |
2 | +#define _INET_DIAG_H_ 1 | |
3 | + | |
4 | +/* Just some random number */ | |
5 | +#define TCPDIAG_GETSOCK 18 | |
6 | +#define DCCPDIAG_GETSOCK 19 | |
7 | + | |
8 | +#define INET_DIAG_GETSOCK_MAX 24 | |
9 | + | |
10 | +/* Socket identity */ | |
11 | +struct inet_diag_sockid { | |
12 | + __u16 idiag_sport; | |
13 | + __u16 idiag_dport; | |
14 | + __u32 idiag_src[4]; | |
15 | + __u32 idiag_dst[4]; | |
16 | + __u32 idiag_if; | |
17 | + __u32 idiag_cookie[2]; | |
18 | +#define INET_DIAG_NOCOOKIE (~0U) | |
19 | +}; | |
20 | + | |
21 | +/* Request structure */ | |
22 | + | |
23 | +struct inet_diag_req { | |
24 | + __u8 idiag_family; /* Family of addresses. */ | |
25 | + __u8 idiag_src_len; | |
26 | + __u8 idiag_dst_len; | |
27 | + __u8 idiag_ext; /* Query extended information */ | |
28 | + | |
29 | + struct inet_diag_sockid id; | |
30 | + | |
31 | + __u32 idiag_states; /* States to dump */ | |
32 | + __u32 idiag_dbs; /* Tables to dump (NI) */ | |
33 | +}; | |
34 | + | |
35 | +enum { | |
36 | + INET_DIAG_REQ_NONE, | |
37 | + INET_DIAG_REQ_BYTECODE, | |
38 | +}; | |
39 | + | |
40 | +#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE | |
41 | + | |
42 | +/* Bytecode is sequence of 4 byte commands followed by variable arguments. | |
43 | + * All the commands identified by "code" are conditional jumps forward: | |
44 | + * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be | |
45 | + * length of the command and its arguments. | |
46 | + */ | |
47 | + | |
48 | +struct inet_diag_bc_op { | |
49 | + unsigned char code; | |
50 | + unsigned char yes; | |
51 | + unsigned short no; | |
52 | +}; | |
53 | + | |
54 | +enum { | |
55 | + INET_DIAG_BC_NOP, | |
56 | + INET_DIAG_BC_JMP, | |
57 | + INET_DIAG_BC_S_GE, | |
58 | + INET_DIAG_BC_S_LE, | |
59 | + INET_DIAG_BC_D_GE, | |
60 | + INET_DIAG_BC_D_LE, | |
61 | + INET_DIAG_BC_AUTO, | |
62 | + INET_DIAG_BC_S_COND, | |
63 | + INET_DIAG_BC_D_COND, | |
64 | +}; | |
65 | + | |
66 | +struct inet_diag_hostcond { | |
67 | + __u8 family; | |
68 | + __u8 prefix_len; | |
69 | + int port; | |
70 | + __u32 addr[0]; | |
71 | +}; | |
72 | + | |
73 | +/* Base info structure. It contains socket identity (addrs/ports/cookie) | |
74 | + * and, alas, the information shown by netstat. */ | |
75 | +struct inet_diag_msg { | |
76 | + __u8 idiag_family; | |
77 | + __u8 idiag_state; | |
78 | + __u8 idiag_timer; | |
79 | + __u8 idiag_retrans; | |
80 | + | |
81 | + struct inet_diag_sockid id; | |
82 | + | |
83 | + __u32 idiag_expires; | |
84 | + __u32 idiag_rqueue; | |
85 | + __u32 idiag_wqueue; | |
86 | + __u32 idiag_uid; | |
87 | + __u32 idiag_inode; | |
88 | +}; | |
89 | + | |
90 | +/* Extensions */ | |
91 | + | |
92 | +enum { | |
93 | + INET_DIAG_NONE, | |
94 | + INET_DIAG_MEMINFO, | |
95 | + INET_DIAG_INFO, | |
96 | + INET_DIAG_VEGASINFO, | |
97 | + INET_DIAG_CONG, | |
98 | +}; | |
99 | + | |
100 | +#define INET_DIAG_MAX INET_DIAG_CONG | |
101 | + | |
102 | + | |
103 | +/* INET_DIAG_MEM */ | |
104 | + | |
105 | +struct inet_diag_meminfo { | |
106 | + __u32 idiag_rmem; | |
107 | + __u32 idiag_wmem; | |
108 | + __u32 idiag_fmem; | |
109 | + __u32 idiag_tmem; | |
110 | +}; | |
111 | + | |
112 | +/* INET_DIAG_VEGASINFO */ | |
113 | + | |
114 | +struct tcpvegas_info { | |
115 | + __u32 tcpv_enabled; | |
116 | + __u32 tcpv_rttcnt; | |
117 | + __u32 tcpv_rtt; | |
118 | + __u32 tcpv_minrtt; | |
119 | +}; | |
120 | + | |
121 | +#ifdef __KERNEL__ | |
122 | +struct sock; | |
123 | +struct inet_hashinfo; | |
124 | + | |
125 | +struct inet_diag_handler { | |
126 | + struct inet_hashinfo *idiag_hashinfo; | |
127 | + void (*idiag_get_info)(struct sock *sk, | |
128 | + struct inet_diag_msg *r, | |
129 | + void *info); | |
130 | + __u16 idiag_info_size; | |
131 | + __u16 idiag_type; | |
132 | +}; | |
133 | + | |
134 | +extern int inet_diag_register(const struct inet_diag_handler *handler); | |
135 | +extern void inet_diag_unregister(const struct inet_diag_handler *handler); | |
136 | +#endif /* __KERNEL__ */ | |
137 | + | |
138 | +#endif /* _INET_DIAG_H_ */ |
include/linux/tcp_diag.h
1 | -#ifndef _INET_DIAG_H_ | |
2 | -#define _INET_DIAG_H_ 1 | |
3 | - | |
4 | -/* Just some random number */ | |
5 | -#define TCPDIAG_GETSOCK 18 | |
6 | -#define DCCPDIAG_GETSOCK 19 | |
7 | - | |
8 | -#define INET_DIAG_GETSOCK_MAX 24 | |
9 | - | |
10 | -/* Socket identity */ | |
11 | -struct inet_diag_sockid { | |
12 | - __u16 idiag_sport; | |
13 | - __u16 idiag_dport; | |
14 | - __u32 idiag_src[4]; | |
15 | - __u32 idiag_dst[4]; | |
16 | - __u32 idiag_if; | |
17 | - __u32 idiag_cookie[2]; | |
18 | -#define INET_DIAG_NOCOOKIE (~0U) | |
19 | -}; | |
20 | - | |
21 | -/* Request structure */ | |
22 | - | |
23 | -struct inet_diag_req { | |
24 | - __u8 idiag_family; /* Family of addresses. */ | |
25 | - __u8 idiag_src_len; | |
26 | - __u8 idiag_dst_len; | |
27 | - __u8 idiag_ext; /* Query extended information */ | |
28 | - | |
29 | - struct inet_diag_sockid id; | |
30 | - | |
31 | - __u32 idiag_states; /* States to dump */ | |
32 | - __u32 idiag_dbs; /* Tables to dump (NI) */ | |
33 | -}; | |
34 | - | |
35 | -enum { | |
36 | - INET_DIAG_REQ_NONE, | |
37 | - INET_DIAG_REQ_BYTECODE, | |
38 | -}; | |
39 | - | |
40 | -#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE | |
41 | - | |
42 | -/* Bytecode is sequence of 4 byte commands followed by variable arguments. | |
43 | - * All the commands identified by "code" are conditional jumps forward: | |
44 | - * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be | |
45 | - * length of the command and its arguments. | |
46 | - */ | |
47 | - | |
48 | -struct inet_diag_bc_op { | |
49 | - unsigned char code; | |
50 | - unsigned char yes; | |
51 | - unsigned short no; | |
52 | -}; | |
53 | - | |
54 | -enum { | |
55 | - INET_DIAG_BC_NOP, | |
56 | - INET_DIAG_BC_JMP, | |
57 | - INET_DIAG_BC_S_GE, | |
58 | - INET_DIAG_BC_S_LE, | |
59 | - INET_DIAG_BC_D_GE, | |
60 | - INET_DIAG_BC_D_LE, | |
61 | - INET_DIAG_BC_AUTO, | |
62 | - INET_DIAG_BC_S_COND, | |
63 | - INET_DIAG_BC_D_COND, | |
64 | -}; | |
65 | - | |
66 | -struct inet_diag_hostcond { | |
67 | - __u8 family; | |
68 | - __u8 prefix_len; | |
69 | - int port; | |
70 | - __u32 addr[0]; | |
71 | -}; | |
72 | - | |
73 | -/* Base info structure. It contains socket identity (addrs/ports/cookie) | |
74 | - * and, alas, the information shown by netstat. */ | |
75 | -struct inet_diag_msg { | |
76 | - __u8 idiag_family; | |
77 | - __u8 idiag_state; | |
78 | - __u8 idiag_timer; | |
79 | - __u8 idiag_retrans; | |
80 | - | |
81 | - struct inet_diag_sockid id; | |
82 | - | |
83 | - __u32 idiag_expires; | |
84 | - __u32 idiag_rqueue; | |
85 | - __u32 idiag_wqueue; | |
86 | - __u32 idiag_uid; | |
87 | - __u32 idiag_inode; | |
88 | -}; | |
89 | - | |
90 | -/* Extensions */ | |
91 | - | |
92 | -enum { | |
93 | - INET_DIAG_NONE, | |
94 | - INET_DIAG_MEMINFO, | |
95 | - INET_DIAG_INFO, | |
96 | - INET_DIAG_VEGASINFO, | |
97 | - INET_DIAG_CONG, | |
98 | -}; | |
99 | - | |
100 | -#define INET_DIAG_MAX INET_DIAG_CONG | |
101 | - | |
102 | - | |
103 | -/* INET_DIAG_MEM */ | |
104 | - | |
105 | -struct inet_diag_meminfo { | |
106 | - __u32 idiag_rmem; | |
107 | - __u32 idiag_wmem; | |
108 | - __u32 idiag_fmem; | |
109 | - __u32 idiag_tmem; | |
110 | -}; | |
111 | - | |
112 | -/* INET_DIAG_VEGASINFO */ | |
113 | - | |
114 | -struct tcpvegas_info { | |
115 | - __u32 tcpv_enabled; | |
116 | - __u32 tcpv_rttcnt; | |
117 | - __u32 tcpv_rtt; | |
118 | - __u32 tcpv_minrtt; | |
119 | -}; | |
120 | - | |
121 | -#ifdef __KERNEL__ | |
122 | -struct sock; | |
123 | -struct inet_hashinfo; | |
124 | - | |
125 | -struct inet_diag_handler { | |
126 | - struct inet_hashinfo *idiag_hashinfo; | |
127 | - void (*idiag_get_info)(struct sock *sk, | |
128 | - struct inet_diag_msg *r, | |
129 | - void *info); | |
130 | - __u16 idiag_info_size; | |
131 | - __u16 idiag_type; | |
132 | -}; | |
133 | - | |
134 | -extern int inet_diag_register(const struct inet_diag_handler *handler); | |
135 | -extern void inet_diag_unregister(const struct inet_diag_handler *handler); | |
136 | -#endif /* __KERNEL__ */ | |
137 | - | |
138 | -#endif /* _INET_DIAG_H_ */ |
net/dccp/diag.c
net/ipv4/Makefile
... | ... | @@ -30,7 +30,7 @@ |
30 | 30 | obj-$(CONFIG_IP_ROUTE_MULTIPATH_DRR) += multipath_drr.o |
31 | 31 | obj-$(CONFIG_NETFILTER) += netfilter/ |
32 | 32 | obj-$(CONFIG_IP_VS) += ipvs/ |
33 | -obj-$(CONFIG_IP_INET_DIAG) += tcp_diag.o | |
33 | +obj-$(CONFIG_IP_INET_DIAG) += inet_diag.o | |
34 | 34 | obj-$(CONFIG_IP_ROUTE_MULTIPATH_CACHED) += multipath.o |
35 | 35 | obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o |
36 | 36 | obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o |
net/ipv4/inet_diag.c
1 | +/* | |
2 | + * inet_diag.c Module for monitoring INET transport protocols sockets. | |
3 | + * | |
4 | + * Version: $Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $ | |
5 | + * | |
6 | + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or | |
9 | + * modify it under the terms of the GNU General Public License | |
10 | + * as published by the Free Software Foundation; either version | |
11 | + * 2 of the License, or (at your option) any later version. | |
12 | + */ | |
13 | + | |
14 | +#include <linux/config.h> | |
15 | +#include <linux/module.h> | |
16 | +#include <linux/types.h> | |
17 | +#include <linux/fcntl.h> | |
18 | +#include <linux/random.h> | |
19 | +#include <linux/cache.h> | |
20 | +#include <linux/init.h> | |
21 | +#include <linux/time.h> | |
22 | + | |
23 | +#include <net/icmp.h> | |
24 | +#include <net/tcp.h> | |
25 | +#include <net/ipv6.h> | |
26 | +#include <net/inet_common.h> | |
27 | +#include <net/inet_connection_sock.h> | |
28 | +#include <net/inet_hashtables.h> | |
29 | +#include <net/inet_timewait_sock.h> | |
30 | +#include <net/inet6_hashtables.h> | |
31 | + | |
32 | +#include <linux/inet.h> | |
33 | +#include <linux/stddef.h> | |
34 | + | |
35 | +#include <linux/inet_diag.h> | |
36 | + | |
37 | +static const struct inet_diag_handler **inet_diag_table; | |
38 | + | |
39 | +struct inet_diag_entry { | |
40 | + u32 *saddr; | |
41 | + u32 *daddr; | |
42 | + u16 sport; | |
43 | + u16 dport; | |
44 | + u16 family; | |
45 | + u16 userlocks; | |
46 | +}; | |
47 | + | |
48 | +static struct sock *idiagnl; | |
49 | + | |
50 | +#define INET_DIAG_PUT(skb, attrtype, attrlen) \ | |
51 | + RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) | |
52 | + | |
53 | +static int inet_diag_fill(struct sk_buff *skb, struct sock *sk, | |
54 | + int ext, u32 pid, u32 seq, u16 nlmsg_flags, | |
55 | + const struct nlmsghdr *unlh) | |
56 | +{ | |
57 | + const struct inet_sock *inet = inet_sk(sk); | |
58 | + const struct inet_connection_sock *icsk = inet_csk(sk); | |
59 | + struct inet_diag_msg *r; | |
60 | + struct nlmsghdr *nlh; | |
61 | + void *info = NULL; | |
62 | + struct inet_diag_meminfo *minfo = NULL; | |
63 | + unsigned char *b = skb->tail; | |
64 | + const struct inet_diag_handler *handler; | |
65 | + | |
66 | + handler = inet_diag_table[unlh->nlmsg_type]; | |
67 | + BUG_ON(handler == NULL); | |
68 | + | |
69 | + nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); | |
70 | + nlh->nlmsg_flags = nlmsg_flags; | |
71 | + | |
72 | + r = NLMSG_DATA(nlh); | |
73 | + if (sk->sk_state != TCP_TIME_WAIT) { | |
74 | + if (ext & (1 << (INET_DIAG_MEMINFO - 1))) | |
75 | + minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO, | |
76 | + sizeof(*minfo)); | |
77 | + if (ext & (1 << (INET_DIAG_INFO - 1))) | |
78 | + info = INET_DIAG_PUT(skb, INET_DIAG_INFO, | |
79 | + handler->idiag_info_size); | |
80 | + | |
81 | + if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) { | |
82 | + size_t len = strlen(icsk->icsk_ca_ops->name); | |
83 | + strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1), | |
84 | + icsk->icsk_ca_ops->name); | |
85 | + } | |
86 | + } | |
87 | + r->idiag_family = sk->sk_family; | |
88 | + r->idiag_state = sk->sk_state; | |
89 | + r->idiag_timer = 0; | |
90 | + r->idiag_retrans = 0; | |
91 | + | |
92 | + r->id.idiag_if = sk->sk_bound_dev_if; | |
93 | + r->id.idiag_cookie[0] = (u32)(unsigned long)sk; | |
94 | + r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1); | |
95 | + | |
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 tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk); | |
116 | + | |
117 | + ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, | |
118 | + &tcp6tw->tw_v6_rcv_saddr); | |
119 | + ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, | |
120 | + &tcp6tw->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; | |
128 | + r->id.idiag_dport = inet->dport; | |
129 | + r->id.idiag_src[0] = inet->rcv_saddr; | |
130 | + r->id.idiag_dst[0] = inet->daddr; | |
131 | + | |
132 | +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
133 | + if (r->idiag_family == AF_INET6) { | |
134 | + struct ipv6_pinfo *np = inet6_sk(sk); | |
135 | + | |
136 | + ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, | |
137 | + &np->rcv_saddr); | |
138 | + ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, | |
139 | + &np->daddr); | |
140 | + } | |
141 | +#endif | |
142 | + | |
143 | +#define EXPIRES_IN_MS(tmo) ((tmo - jiffies) * 1000 + HZ - 1) / HZ | |
144 | + | |
145 | + if (icsk->icsk_pending == ICSK_TIME_RETRANS) { | |
146 | + r->idiag_timer = 1; | |
147 | + r->idiag_retrans = icsk->icsk_retransmits; | |
148 | + r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); | |
149 | + } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { | |
150 | + r->idiag_timer = 4; | |
151 | + r->idiag_retrans = icsk->icsk_probes_out; | |
152 | + r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); | |
153 | + } else if (timer_pending(&sk->sk_timer)) { | |
154 | + r->idiag_timer = 2; | |
155 | + r->idiag_retrans = icsk->icsk_probes_out; | |
156 | + r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires); | |
157 | + } else { | |
158 | + r->idiag_timer = 0; | |
159 | + r->idiag_expires = 0; | |
160 | + } | |
161 | +#undef EXPIRES_IN_MS | |
162 | + | |
163 | + r->idiag_uid = sock_i_uid(sk); | |
164 | + r->idiag_inode = sock_i_ino(sk); | |
165 | + | |
166 | + if (minfo) { | |
167 | + minfo->idiag_rmem = atomic_read(&sk->sk_rmem_alloc); | |
168 | + minfo->idiag_wmem = sk->sk_wmem_queued; | |
169 | + minfo->idiag_fmem = sk->sk_forward_alloc; | |
170 | + minfo->idiag_tmem = atomic_read(&sk->sk_wmem_alloc); | |
171 | + } | |
172 | + | |
173 | + handler->idiag_get_info(sk, r, info); | |
174 | + | |
175 | + if (sk->sk_state < TCP_TIME_WAIT && | |
176 | + icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) | |
177 | + icsk->icsk_ca_ops->get_info(sk, ext, skb); | |
178 | + | |
179 | + nlh->nlmsg_len = skb->tail - b; | |
180 | + return skb->len; | |
181 | + | |
182 | +rtattr_failure: | |
183 | +nlmsg_failure: | |
184 | + skb_trim(skb, b - skb->data); | |
185 | + return -1; | |
186 | +} | |
187 | + | |
188 | +static int inet_diag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh) | |
189 | +{ | |
190 | + int err; | |
191 | + struct sock *sk; | |
192 | + struct inet_diag_req *req = NLMSG_DATA(nlh); | |
193 | + struct sk_buff *rep; | |
194 | + struct inet_hashinfo *hashinfo; | |
195 | + const struct inet_diag_handler *handler; | |
196 | + | |
197 | + handler = inet_diag_table[nlh->nlmsg_type]; | |
198 | + BUG_ON(handler == NULL); | |
199 | + hashinfo = handler->idiag_hashinfo; | |
200 | + | |
201 | + if (req->idiag_family == AF_INET) { | |
202 | + sk = inet_lookup(hashinfo, req->id.idiag_dst[0], | |
203 | + req->id.idiag_dport, req->id.idiag_src[0], | |
204 | + req->id.idiag_sport, req->id.idiag_if); | |
205 | + } | |
206 | +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
207 | + else if (req->idiag_family == AF_INET6) { | |
208 | + sk = inet6_lookup(hashinfo, | |
209 | + (struct in6_addr *)req->id.idiag_dst, | |
210 | + req->id.idiag_dport, | |
211 | + (struct in6_addr *)req->id.idiag_src, | |
212 | + req->id.idiag_sport, | |
213 | + req->id.idiag_if); | |
214 | + } | |
215 | +#endif | |
216 | + else { | |
217 | + return -EINVAL; | |
218 | + } | |
219 | + | |
220 | + if (sk == NULL) | |
221 | + return -ENOENT; | |
222 | + | |
223 | + err = -ESTALE; | |
224 | + if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE || | |
225 | + req->id.idiag_cookie[1] != INET_DIAG_NOCOOKIE) && | |
226 | + ((u32)(unsigned long)sk != req->id.idiag_cookie[0] || | |
227 | + (u32)((((unsigned long)sk) >> 31) >> 1) != req->id.idiag_cookie[1])) | |
228 | + goto out; | |
229 | + | |
230 | + err = -ENOMEM; | |
231 | + rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) + | |
232 | + sizeof(struct inet_diag_meminfo) + | |
233 | + handler->idiag_info_size + 64)), | |
234 | + GFP_KERNEL); | |
235 | + if (!rep) | |
236 | + goto out; | |
237 | + | |
238 | + if (inet_diag_fill(rep, sk, req->idiag_ext, | |
239 | + NETLINK_CB(in_skb).pid, | |
240 | + nlh->nlmsg_seq, 0, nlh) <= 0) | |
241 | + BUG(); | |
242 | + | |
243 | + err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid, | |
244 | + MSG_DONTWAIT); | |
245 | + if (err > 0) | |
246 | + err = 0; | |
247 | + | |
248 | +out: | |
249 | + if (sk) { | |
250 | + if (sk->sk_state == TCP_TIME_WAIT) | |
251 | + inet_twsk_put((struct inet_timewait_sock *)sk); | |
252 | + else | |
253 | + sock_put(sk); | |
254 | + } | |
255 | + return err; | |
256 | +} | |
257 | + | |
258 | +static int bitstring_match(const u32 *a1, const u32 *a2, int bits) | |
259 | +{ | |
260 | + int words = bits >> 5; | |
261 | + | |
262 | + bits &= 0x1f; | |
263 | + | |
264 | + if (words) { | |
265 | + if (memcmp(a1, a2, words << 2)) | |
266 | + return 0; | |
267 | + } | |
268 | + if (bits) { | |
269 | + __u32 w1, w2; | |
270 | + __u32 mask; | |
271 | + | |
272 | + w1 = a1[words]; | |
273 | + w2 = a2[words]; | |
274 | + | |
275 | + mask = htonl((0xffffffff) << (32 - bits)); | |
276 | + | |
277 | + if ((w1 ^ w2) & mask) | |
278 | + return 0; | |
279 | + } | |
280 | + | |
281 | + return 1; | |
282 | +} | |
283 | + | |
284 | + | |
285 | +static int inet_diag_bc_run(const void *bc, int len, | |
286 | + const struct inet_diag_entry *entry) | |
287 | +{ | |
288 | + while (len > 0) { | |
289 | + int yes = 1; | |
290 | + const struct inet_diag_bc_op *op = bc; | |
291 | + | |
292 | + switch (op->code) { | |
293 | + case INET_DIAG_BC_NOP: | |
294 | + break; | |
295 | + case INET_DIAG_BC_JMP: | |
296 | + yes = 0; | |
297 | + break; | |
298 | + case INET_DIAG_BC_S_GE: | |
299 | + yes = entry->sport >= op[1].no; | |
300 | + break; | |
301 | + case INET_DIAG_BC_S_LE: | |
302 | + yes = entry->dport <= op[1].no; | |
303 | + break; | |
304 | + case INET_DIAG_BC_D_GE: | |
305 | + yes = entry->dport >= op[1].no; | |
306 | + break; | |
307 | + case INET_DIAG_BC_D_LE: | |
308 | + yes = entry->dport <= op[1].no; | |
309 | + break; | |
310 | + case INET_DIAG_BC_AUTO: | |
311 | + yes = !(entry->userlocks & SOCK_BINDPORT_LOCK); | |
312 | + break; | |
313 | + case INET_DIAG_BC_S_COND: | |
314 | + case INET_DIAG_BC_D_COND: { | |
315 | + struct inet_diag_hostcond *cond; | |
316 | + u32 *addr; | |
317 | + | |
318 | + cond = (struct inet_diag_hostcond *)(op + 1); | |
319 | + if (cond->port != -1 && | |
320 | + cond->port != (op->code == INET_DIAG_BC_S_COND ? | |
321 | + entry->sport : entry->dport)) { | |
322 | + yes = 0; | |
323 | + break; | |
324 | + } | |
325 | + | |
326 | + if (cond->prefix_len == 0) | |
327 | + break; | |
328 | + | |
329 | + if (op->code == INET_DIAG_BC_S_COND) | |
330 | + addr = entry->saddr; | |
331 | + else | |
332 | + addr = entry->daddr; | |
333 | + | |
334 | + if (bitstring_match(addr, cond->addr, cond->prefix_len)) | |
335 | + break; | |
336 | + if (entry->family == AF_INET6 && | |
337 | + cond->family == AF_INET) { | |
338 | + if (addr[0] == 0 && addr[1] == 0 && | |
339 | + addr[2] == htonl(0xffff) && | |
340 | + bitstring_match(addr + 3, cond->addr, | |
341 | + cond->prefix_len)) | |
342 | + break; | |
343 | + } | |
344 | + yes = 0; | |
345 | + break; | |
346 | + } | |
347 | + } | |
348 | + | |
349 | + if (yes) { | |
350 | + len -= op->yes; | |
351 | + bc += op->yes; | |
352 | + } else { | |
353 | + len -= op->no; | |
354 | + bc += op->no; | |
355 | + } | |
356 | + } | |
357 | + return (len == 0); | |
358 | +} | |
359 | + | |
360 | +static int valid_cc(const void *bc, int len, int cc) | |
361 | +{ | |
362 | + while (len >= 0) { | |
363 | + const struct inet_diag_bc_op *op = bc; | |
364 | + | |
365 | + if (cc > len) | |
366 | + return 0; | |
367 | + if (cc == len) | |
368 | + return 1; | |
369 | + if (op->yes < 4) | |
370 | + return 0; | |
371 | + len -= op->yes; | |
372 | + bc += op->yes; | |
373 | + } | |
374 | + return 0; | |
375 | +} | |
376 | + | |
377 | +static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) | |
378 | +{ | |
379 | + const unsigned char *bc = bytecode; | |
380 | + int len = bytecode_len; | |
381 | + | |
382 | + while (len > 0) { | |
383 | + struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)bc; | |
384 | + | |
385 | +//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len); | |
386 | + switch (op->code) { | |
387 | + case INET_DIAG_BC_AUTO: | |
388 | + case INET_DIAG_BC_S_COND: | |
389 | + case INET_DIAG_BC_D_COND: | |
390 | + case INET_DIAG_BC_S_GE: | |
391 | + case INET_DIAG_BC_S_LE: | |
392 | + case INET_DIAG_BC_D_GE: | |
393 | + case INET_DIAG_BC_D_LE: | |
394 | + if (op->yes < 4 || op->yes > len + 4) | |
395 | + return -EINVAL; | |
396 | + case INET_DIAG_BC_JMP: | |
397 | + if (op->no < 4 || op->no > len + 4) | |
398 | + return -EINVAL; | |
399 | + if (op->no < len && | |
400 | + !valid_cc(bytecode, bytecode_len, len - op->no)) | |
401 | + return -EINVAL; | |
402 | + break; | |
403 | + case INET_DIAG_BC_NOP: | |
404 | + if (op->yes < 4 || op->yes > len + 4) | |
405 | + return -EINVAL; | |
406 | + break; | |
407 | + default: | |
408 | + return -EINVAL; | |
409 | + } | |
410 | + bc += op->yes; | |
411 | + len -= op->yes; | |
412 | + } | |
413 | + return len == 0 ? 0 : -EINVAL; | |
414 | +} | |
415 | + | |
416 | +static int inet_diag_dump_sock(struct sk_buff *skb, struct sock *sk, | |
417 | + struct netlink_callback *cb) | |
418 | +{ | |
419 | + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); | |
420 | + | |
421 | + if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { | |
422 | + struct inet_diag_entry entry; | |
423 | + struct rtattr *bc = (struct rtattr *)(r + 1); | |
424 | + struct inet_sock *inet = inet_sk(sk); | |
425 | + | |
426 | + entry.family = sk->sk_family; | |
427 | +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
428 | + if (entry.family == AF_INET6) { | |
429 | + struct ipv6_pinfo *np = inet6_sk(sk); | |
430 | + | |
431 | + entry.saddr = np->rcv_saddr.s6_addr32; | |
432 | + entry.daddr = np->daddr.s6_addr32; | |
433 | + } else | |
434 | +#endif | |
435 | + { | |
436 | + entry.saddr = &inet->rcv_saddr; | |
437 | + entry.daddr = &inet->daddr; | |
438 | + } | |
439 | + entry.sport = inet->num; | |
440 | + entry.dport = ntohs(inet->dport); | |
441 | + entry.userlocks = sk->sk_userlocks; | |
442 | + | |
443 | + if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) | |
444 | + return 0; | |
445 | + } | |
446 | + | |
447 | + return inet_diag_fill(skb, sk, r->idiag_ext, NETLINK_CB(cb->skb).pid, | |
448 | + cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); | |
449 | +} | |
450 | + | |
451 | +static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, | |
452 | + struct request_sock *req, | |
453 | + u32 pid, u32 seq, | |
454 | + const struct nlmsghdr *unlh) | |
455 | +{ | |
456 | + const struct inet_request_sock *ireq = inet_rsk(req); | |
457 | + struct inet_sock *inet = inet_sk(sk); | |
458 | + unsigned char *b = skb->tail; | |
459 | + struct inet_diag_msg *r; | |
460 | + struct nlmsghdr *nlh; | |
461 | + long tmo; | |
462 | + | |
463 | + nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); | |
464 | + nlh->nlmsg_flags = NLM_F_MULTI; | |
465 | + r = NLMSG_DATA(nlh); | |
466 | + | |
467 | + r->idiag_family = sk->sk_family; | |
468 | + r->idiag_state = TCP_SYN_RECV; | |
469 | + r->idiag_timer = 1; | |
470 | + r->idiag_retrans = req->retrans; | |
471 | + | |
472 | + r->id.idiag_if = sk->sk_bound_dev_if; | |
473 | + r->id.idiag_cookie[0] = (u32)(unsigned long)req; | |
474 | + r->id.idiag_cookie[1] = (u32)(((unsigned long)req >> 31) >> 1); | |
475 | + | |
476 | + tmo = req->expires - jiffies; | |
477 | + if (tmo < 0) | |
478 | + tmo = 0; | |
479 | + | |
480 | + r->id.idiag_sport = inet->sport; | |
481 | + r->id.idiag_dport = ireq->rmt_port; | |
482 | + r->id.idiag_src[0] = ireq->loc_addr; | |
483 | + r->id.idiag_dst[0] = ireq->rmt_addr; | |
484 | + r->idiag_expires = jiffies_to_msecs(tmo); | |
485 | + r->idiag_rqueue = 0; | |
486 | + r->idiag_wqueue = 0; | |
487 | + r->idiag_uid = sock_i_uid(sk); | |
488 | + r->idiag_inode = 0; | |
489 | +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
490 | + if (r->idiag_family == AF_INET6) { | |
491 | + ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, | |
492 | + &tcp6_rsk(req)->loc_addr); | |
493 | + ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, | |
494 | + &tcp6_rsk(req)->rmt_addr); | |
495 | + } | |
496 | +#endif | |
497 | + nlh->nlmsg_len = skb->tail - b; | |
498 | + | |
499 | + return skb->len; | |
500 | + | |
501 | +nlmsg_failure: | |
502 | + skb_trim(skb, b - skb->data); | |
503 | + return -1; | |
504 | +} | |
505 | + | |
506 | +static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, | |
507 | + struct netlink_callback *cb) | |
508 | +{ | |
509 | + struct inet_diag_entry entry; | |
510 | + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); | |
511 | + struct inet_connection_sock *icsk = inet_csk(sk); | |
512 | + struct listen_sock *lopt; | |
513 | + struct rtattr *bc = NULL; | |
514 | + struct inet_sock *inet = inet_sk(sk); | |
515 | + int j, s_j; | |
516 | + int reqnum, s_reqnum; | |
517 | + int err = 0; | |
518 | + | |
519 | + s_j = cb->args[3]; | |
520 | + s_reqnum = cb->args[4]; | |
521 | + | |
522 | + if (s_j > 0) | |
523 | + s_j--; | |
524 | + | |
525 | + entry.family = sk->sk_family; | |
526 | + | |
527 | + read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); | |
528 | + | |
529 | + lopt = icsk->icsk_accept_queue.listen_opt; | |
530 | + if (!lopt || !lopt->qlen) | |
531 | + goto out; | |
532 | + | |
533 | + if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { | |
534 | + bc = (struct rtattr *)(r + 1); | |
535 | + entry.sport = inet->num; | |
536 | + entry.userlocks = sk->sk_userlocks; | |
537 | + } | |
538 | + | |
539 | + for (j = s_j; j < lopt->nr_table_entries; j++) { | |
540 | + struct request_sock *req, *head = lopt->syn_table[j]; | |
541 | + | |
542 | + reqnum = 0; | |
543 | + for (req = head; req; reqnum++, req = req->dl_next) { | |
544 | + struct inet_request_sock *ireq = inet_rsk(req); | |
545 | + | |
546 | + if (reqnum < s_reqnum) | |
547 | + continue; | |
548 | + if (r->id.idiag_dport != ireq->rmt_port && | |
549 | + r->id.idiag_dport) | |
550 | + continue; | |
551 | + | |
552 | + if (bc) { | |
553 | + entry.saddr = | |
554 | +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
555 | + (entry.family == AF_INET6) ? | |
556 | + tcp6_rsk(req)->loc_addr.s6_addr32 : | |
557 | +#endif | |
558 | + &ireq->loc_addr; | |
559 | + entry.daddr = | |
560 | +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
561 | + (entry.family == AF_INET6) ? | |
562 | + tcp6_rsk(req)->rmt_addr.s6_addr32 : | |
563 | +#endif | |
564 | + &ireq->rmt_addr; | |
565 | + entry.dport = ntohs(ireq->rmt_port); | |
566 | + | |
567 | + if (!inet_diag_bc_run(RTA_DATA(bc), | |
568 | + RTA_PAYLOAD(bc), &entry)) | |
569 | + continue; | |
570 | + } | |
571 | + | |
572 | + err = inet_diag_fill_req(skb, sk, req, | |
573 | + NETLINK_CB(cb->skb).pid, | |
574 | + cb->nlh->nlmsg_seq, cb->nlh); | |
575 | + if (err < 0) { | |
576 | + cb->args[3] = j + 1; | |
577 | + cb->args[4] = reqnum; | |
578 | + goto out; | |
579 | + } | |
580 | + } | |
581 | + | |
582 | + s_reqnum = 0; | |
583 | + } | |
584 | + | |
585 | +out: | |
586 | + read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); | |
587 | + | |
588 | + return err; | |
589 | +} | |
590 | + | |
591 | +static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |
592 | +{ | |
593 | + int i, num; | |
594 | + int s_i, s_num; | |
595 | + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); | |
596 | + const struct inet_diag_handler *handler; | |
597 | + struct inet_hashinfo *hashinfo; | |
598 | + | |
599 | + handler = inet_diag_table[cb->nlh->nlmsg_type]; | |
600 | + BUG_ON(handler == NULL); | |
601 | + hashinfo = handler->idiag_hashinfo; | |
602 | + | |
603 | + s_i = cb->args[1]; | |
604 | + s_num = num = cb->args[2]; | |
605 | + | |
606 | + if (cb->args[0] == 0) { | |
607 | + if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV))) | |
608 | + goto skip_listen_ht; | |
609 | + | |
610 | + inet_listen_lock(hashinfo); | |
611 | + for (i = s_i; i < INET_LHTABLE_SIZE; i++) { | |
612 | + struct sock *sk; | |
613 | + struct hlist_node *node; | |
614 | + | |
615 | + num = 0; | |
616 | + sk_for_each(sk, node, &hashinfo->listening_hash[i]) { | |
617 | + struct inet_sock *inet = inet_sk(sk); | |
618 | + | |
619 | + if (num < s_num) { | |
620 | + num++; | |
621 | + continue; | |
622 | + } | |
623 | + | |
624 | + if (r->id.idiag_sport != inet->sport && | |
625 | + r->id.idiag_sport) | |
626 | + goto next_listen; | |
627 | + | |
628 | + if (!(r->idiag_states & TCPF_LISTEN) || | |
629 | + r->id.idiag_dport || | |
630 | + cb->args[3] > 0) | |
631 | + goto syn_recv; | |
632 | + | |
633 | + if (inet_diag_dump_sock(skb, sk, cb) < 0) { | |
634 | + inet_listen_unlock(hashinfo); | |
635 | + goto done; | |
636 | + } | |
637 | + | |
638 | +syn_recv: | |
639 | + if (!(r->idiag_states & TCPF_SYN_RECV)) | |
640 | + goto next_listen; | |
641 | + | |
642 | + if (inet_diag_dump_reqs(skb, sk, cb) < 0) { | |
643 | + inet_listen_unlock(hashinfo); | |
644 | + goto done; | |
645 | + } | |
646 | + | |
647 | +next_listen: | |
648 | + cb->args[3] = 0; | |
649 | + cb->args[4] = 0; | |
650 | + ++num; | |
651 | + } | |
652 | + | |
653 | + s_num = 0; | |
654 | + cb->args[3] = 0; | |
655 | + cb->args[4] = 0; | |
656 | + } | |
657 | + inet_listen_unlock(hashinfo); | |
658 | +skip_listen_ht: | |
659 | + cb->args[0] = 1; | |
660 | + s_i = num = s_num = 0; | |
661 | + } | |
662 | + | |
663 | + if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV))) | |
664 | + return skb->len; | |
665 | + | |
666 | + for (i = s_i; i < hashinfo->ehash_size; i++) { | |
667 | + struct inet_ehash_bucket *head = &hashinfo->ehash[i]; | |
668 | + struct sock *sk; | |
669 | + struct hlist_node *node; | |
670 | + | |
671 | + if (i > s_i) | |
672 | + s_num = 0; | |
673 | + | |
674 | + read_lock_bh(&head->lock); | |
675 | + | |
676 | + num = 0; | |
677 | + sk_for_each(sk, node, &head->chain) { | |
678 | + struct inet_sock *inet = inet_sk(sk); | |
679 | + | |
680 | + if (num < s_num) | |
681 | + goto next_normal; | |
682 | + if (!(r->idiag_states & (1 << sk->sk_state))) | |
683 | + goto next_normal; | |
684 | + if (r->id.idiag_sport != inet->sport && | |
685 | + r->id.idiag_sport) | |
686 | + goto next_normal; | |
687 | + if (r->id.idiag_dport != inet->dport && r->id.idiag_dport) | |
688 | + goto next_normal; | |
689 | + if (inet_diag_dump_sock(skb, sk, cb) < 0) { | |
690 | + read_unlock_bh(&head->lock); | |
691 | + goto done; | |
692 | + } | |
693 | +next_normal: | |
694 | + ++num; | |
695 | + } | |
696 | + | |
697 | + if (r->idiag_states & TCPF_TIME_WAIT) { | |
698 | + sk_for_each(sk, node, | |
699 | + &hashinfo->ehash[i + hashinfo->ehash_size].chain) { | |
700 | + struct inet_sock *inet = inet_sk(sk); | |
701 | + | |
702 | + if (num < s_num) | |
703 | + goto next_dying; | |
704 | + if (r->id.idiag_sport != inet->sport && | |
705 | + r->id.idiag_sport) | |
706 | + goto next_dying; | |
707 | + if (r->id.idiag_dport != inet->dport && | |
708 | + r->id.idiag_dport) | |
709 | + goto next_dying; | |
710 | + if (inet_diag_dump_sock(skb, sk, cb) < 0) { | |
711 | + read_unlock_bh(&head->lock); | |
712 | + goto done; | |
713 | + } | |
714 | +next_dying: | |
715 | + ++num; | |
716 | + } | |
717 | + } | |
718 | + read_unlock_bh(&head->lock); | |
719 | + } | |
720 | + | |
721 | +done: | |
722 | + cb->args[1] = i; | |
723 | + cb->args[2] = num; | |
724 | + return skb->len; | |
725 | +} | |
726 | + | |
727 | +static int inet_diag_dump_done(struct netlink_callback *cb) | |
728 | +{ | |
729 | + return 0; | |
730 | +} | |
731 | + | |
732 | + | |
733 | +static __inline__ int | |
734 | +inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |
735 | +{ | |
736 | + if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) | |
737 | + return 0; | |
738 | + | |
739 | + if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) | |
740 | + goto err_inval; | |
741 | + | |
742 | + if (inet_diag_table[nlh->nlmsg_type] == NULL) | |
743 | + return -ENOENT; | |
744 | + | |
745 | + if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len) | |
746 | + goto err_inval; | |
747 | + | |
748 | + if (nlh->nlmsg_flags&NLM_F_DUMP) { | |
749 | + if (nlh->nlmsg_len > | |
750 | + (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) { | |
751 | + struct rtattr *rta = (void *)(NLMSG_DATA(nlh) + | |
752 | + sizeof(struct inet_diag_req)); | |
753 | + if (rta->rta_type != INET_DIAG_REQ_BYTECODE || | |
754 | + rta->rta_len < 8 || | |
755 | + rta->rta_len > | |
756 | + (nlh->nlmsg_len - | |
757 | + NLMSG_SPACE(sizeof(struct inet_diag_req)))) | |
758 | + goto err_inval; | |
759 | + if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta))) | |
760 | + goto err_inval; | |
761 | + } | |
762 | + return netlink_dump_start(idiagnl, skb, nlh, | |
763 | + inet_diag_dump, | |
764 | + inet_diag_dump_done); | |
765 | + } else { | |
766 | + return inet_diag_get_exact(skb, nlh); | |
767 | + } | |
768 | + | |
769 | +err_inval: | |
770 | + return -EINVAL; | |
771 | +} | |
772 | + | |
773 | + | |
774 | +static inline void inet_diag_rcv_skb(struct sk_buff *skb) | |
775 | +{ | |
776 | + int err; | |
777 | + struct nlmsghdr * nlh; | |
778 | + | |
779 | + if (skb->len >= NLMSG_SPACE(0)) { | |
780 | + nlh = (struct nlmsghdr *)skb->data; | |
781 | + if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) | |
782 | + return; | |
783 | + err = inet_diag_rcv_msg(skb, nlh); | |
784 | + if (err || nlh->nlmsg_flags & NLM_F_ACK) | |
785 | + netlink_ack(skb, nlh, err); | |
786 | + } | |
787 | +} | |
788 | + | |
789 | +static void inet_diag_rcv(struct sock *sk, int len) | |
790 | +{ | |
791 | + struct sk_buff *skb; | |
792 | + unsigned int qlen = skb_queue_len(&sk->sk_receive_queue); | |
793 | + | |
794 | + while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) { | |
795 | + inet_diag_rcv_skb(skb); | |
796 | + kfree_skb(skb); | |
797 | + } | |
798 | +} | |
799 | + | |
800 | +static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, | |
801 | + void *_info) | |
802 | +{ | |
803 | + const struct tcp_sock *tp = tcp_sk(sk); | |
804 | + struct tcp_info *info = _info; | |
805 | + | |
806 | + r->idiag_rqueue = tp->rcv_nxt - tp->copied_seq; | |
807 | + r->idiag_wqueue = tp->write_seq - tp->snd_una; | |
808 | + if (info != NULL) | |
809 | + tcp_get_info(sk, info); | |
810 | +} | |
811 | + | |
812 | +static struct inet_diag_handler tcp_diag_handler = { | |
813 | + .idiag_hashinfo = &tcp_hashinfo, | |
814 | + .idiag_get_info = tcp_diag_get_info, | |
815 | + .idiag_type = TCPDIAG_GETSOCK, | |
816 | + .idiag_info_size = sizeof(struct tcp_info), | |
817 | +}; | |
818 | + | |
819 | +static DEFINE_SPINLOCK(inet_diag_register_lock); | |
820 | + | |
821 | +int inet_diag_register(const struct inet_diag_handler *h) | |
822 | +{ | |
823 | + const __u16 type = h->idiag_type; | |
824 | + int err = -EINVAL; | |
825 | + | |
826 | + if (type >= INET_DIAG_GETSOCK_MAX) | |
827 | + goto out; | |
828 | + | |
829 | + spin_lock(&inet_diag_register_lock); | |
830 | + err = -EEXIST; | |
831 | + if (inet_diag_table[type] == NULL) { | |
832 | + inet_diag_table[type] = h; | |
833 | + err = 0; | |
834 | + } | |
835 | + spin_unlock(&inet_diag_register_lock); | |
836 | +out: | |
837 | + return err; | |
838 | +} | |
839 | +EXPORT_SYMBOL_GPL(inet_diag_register); | |
840 | + | |
841 | +void inet_diag_unregister(const struct inet_diag_handler *h) | |
842 | +{ | |
843 | + const __u16 type = h->idiag_type; | |
844 | + | |
845 | + if (type >= INET_DIAG_GETSOCK_MAX) | |
846 | + return; | |
847 | + | |
848 | + spin_lock(&inet_diag_register_lock); | |
849 | + inet_diag_table[type] = NULL; | |
850 | + spin_unlock(&inet_diag_register_lock); | |
851 | + | |
852 | + synchronize_rcu(); | |
853 | +} | |
854 | +EXPORT_SYMBOL_GPL(inet_diag_unregister); | |
855 | + | |
856 | +static int __init inet_diag_init(void) | |
857 | +{ | |
858 | + const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX * | |
859 | + sizeof(struct inet_diag_handler *)); | |
860 | + int err = -ENOMEM; | |
861 | + | |
862 | + inet_diag_table = kmalloc(inet_diag_table_size, GFP_KERNEL); | |
863 | + if (!inet_diag_table) | |
864 | + goto out; | |
865 | + | |
866 | + memset(inet_diag_table, 0, inet_diag_table_size); | |
867 | + | |
868 | + idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, inet_diag_rcv, | |
869 | + THIS_MODULE); | |
870 | + if (idiagnl == NULL) | |
871 | + goto out_free_table; | |
872 | + | |
873 | + err = inet_diag_register(&tcp_diag_handler); | |
874 | + if (err) | |
875 | + goto out_sock_release; | |
876 | +out: | |
877 | + return err; | |
878 | +out_sock_release: | |
879 | + sock_release(idiagnl->sk_socket); | |
880 | +out_free_table: | |
881 | + kfree(inet_diag_table); | |
882 | + goto out; | |
883 | +} | |
884 | + | |
885 | +static void __exit inet_diag_exit(void) | |
886 | +{ | |
887 | + sock_release(idiagnl->sk_socket); | |
888 | + kfree(inet_diag_table); | |
889 | +} | |
890 | + | |
891 | +module_init(inet_diag_init); | |
892 | +module_exit(inet_diag_exit); | |
893 | +MODULE_LICENSE("GPL"); |
net/ipv4/tcp_diag.c
1 | -/* | |
2 | - * inet_diag.c Module for monitoring INET transport protocols sockets. | |
3 | - * | |
4 | - * Version: $Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $ | |
5 | - * | |
6 | - * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | |
7 | - * | |
8 | - * This program is free software; you can redistribute it and/or | |
9 | - * modify it under the terms of the GNU General Public License | |
10 | - * as published by the Free Software Foundation; either version | |
11 | - * 2 of the License, or (at your option) any later version. | |
12 | - */ | |
13 | - | |
14 | -#include <linux/config.h> | |
15 | -#include <linux/module.h> | |
16 | -#include <linux/types.h> | |
17 | -#include <linux/fcntl.h> | |
18 | -#include <linux/random.h> | |
19 | -#include <linux/cache.h> | |
20 | -#include <linux/init.h> | |
21 | -#include <linux/time.h> | |
22 | - | |
23 | -#include <net/icmp.h> | |
24 | -#include <net/tcp.h> | |
25 | -#include <net/ipv6.h> | |
26 | -#include <net/inet_common.h> | |
27 | -#include <net/inet_connection_sock.h> | |
28 | -#include <net/inet_hashtables.h> | |
29 | -#include <net/inet_timewait_sock.h> | |
30 | -#include <net/inet6_hashtables.h> | |
31 | - | |
32 | -#include <linux/inet.h> | |
33 | -#include <linux/stddef.h> | |
34 | - | |
35 | -#include <linux/tcp_diag.h> | |
36 | - | |
37 | -static const struct inet_diag_handler **inet_diag_table; | |
38 | - | |
39 | -struct inet_diag_entry { | |
40 | - u32 *saddr; | |
41 | - u32 *daddr; | |
42 | - u16 sport; | |
43 | - u16 dport; | |
44 | - u16 family; | |
45 | - u16 userlocks; | |
46 | -}; | |
47 | - | |
48 | -static struct sock *idiagnl; | |
49 | - | |
50 | -#define INET_DIAG_PUT(skb, attrtype, attrlen) \ | |
51 | - RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) | |
52 | - | |
53 | -static int inet_diag_fill(struct sk_buff *skb, struct sock *sk, | |
54 | - int ext, u32 pid, u32 seq, u16 nlmsg_flags, | |
55 | - const struct nlmsghdr *unlh) | |
56 | -{ | |
57 | - const struct inet_sock *inet = inet_sk(sk); | |
58 | - const struct inet_connection_sock *icsk = inet_csk(sk); | |
59 | - struct inet_diag_msg *r; | |
60 | - struct nlmsghdr *nlh; | |
61 | - void *info = NULL; | |
62 | - struct inet_diag_meminfo *minfo = NULL; | |
63 | - unsigned char *b = skb->tail; | |
64 | - const struct inet_diag_handler *handler; | |
65 | - | |
66 | - handler = inet_diag_table[unlh->nlmsg_type]; | |
67 | - BUG_ON(handler == NULL); | |
68 | - | |
69 | - nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); | |
70 | - nlh->nlmsg_flags = nlmsg_flags; | |
71 | - | |
72 | - r = NLMSG_DATA(nlh); | |
73 | - if (sk->sk_state != TCP_TIME_WAIT) { | |
74 | - if (ext & (1 << (INET_DIAG_MEMINFO - 1))) | |
75 | - minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO, | |
76 | - sizeof(*minfo)); | |
77 | - if (ext & (1 << (INET_DIAG_INFO - 1))) | |
78 | - info = INET_DIAG_PUT(skb, INET_DIAG_INFO, | |
79 | - handler->idiag_info_size); | |
80 | - | |
81 | - if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) { | |
82 | - size_t len = strlen(icsk->icsk_ca_ops->name); | |
83 | - strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1), | |
84 | - icsk->icsk_ca_ops->name); | |
85 | - } | |
86 | - } | |
87 | - r->idiag_family = sk->sk_family; | |
88 | - r->idiag_state = sk->sk_state; | |
89 | - r->idiag_timer = 0; | |
90 | - r->idiag_retrans = 0; | |
91 | - | |
92 | - r->id.idiag_if = sk->sk_bound_dev_if; | |
93 | - r->id.idiag_cookie[0] = (u32)(unsigned long)sk; | |
94 | - r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1); | |
95 | - | |
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 tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk); | |
116 | - | |
117 | - ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, | |
118 | - &tcp6tw->tw_v6_rcv_saddr); | |
119 | - ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, | |
120 | - &tcp6tw->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; | |
128 | - r->id.idiag_dport = inet->dport; | |
129 | - r->id.idiag_src[0] = inet->rcv_saddr; | |
130 | - r->id.idiag_dst[0] = inet->daddr; | |
131 | - | |
132 | -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
133 | - if (r->idiag_family == AF_INET6) { | |
134 | - struct ipv6_pinfo *np = inet6_sk(sk); | |
135 | - | |
136 | - ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, | |
137 | - &np->rcv_saddr); | |
138 | - ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, | |
139 | - &np->daddr); | |
140 | - } | |
141 | -#endif | |
142 | - | |
143 | -#define EXPIRES_IN_MS(tmo) ((tmo-jiffies)*1000+HZ-1)/HZ | |
144 | - | |
145 | - if (icsk->icsk_pending == ICSK_TIME_RETRANS) { | |
146 | - r->idiag_timer = 1; | |
147 | - r->idiag_retrans = icsk->icsk_retransmits; | |
148 | - r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); | |
149 | - } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { | |
150 | - r->idiag_timer = 4; | |
151 | - r->idiag_retrans = icsk->icsk_probes_out; | |
152 | - r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); | |
153 | - } else if (timer_pending(&sk->sk_timer)) { | |
154 | - r->idiag_timer = 2; | |
155 | - r->idiag_retrans = icsk->icsk_probes_out; | |
156 | - r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires); | |
157 | - } else { | |
158 | - r->idiag_timer = 0; | |
159 | - r->idiag_expires = 0; | |
160 | - } | |
161 | -#undef EXPIRES_IN_MS | |
162 | - | |
163 | - r->idiag_uid = sock_i_uid(sk); | |
164 | - r->idiag_inode = sock_i_ino(sk); | |
165 | - | |
166 | - if (minfo) { | |
167 | - minfo->idiag_rmem = atomic_read(&sk->sk_rmem_alloc); | |
168 | - minfo->idiag_wmem = sk->sk_wmem_queued; | |
169 | - minfo->idiag_fmem = sk->sk_forward_alloc; | |
170 | - minfo->idiag_tmem = atomic_read(&sk->sk_wmem_alloc); | |
171 | - } | |
172 | - | |
173 | - handler->idiag_get_info(sk, r, info); | |
174 | - | |
175 | - if (sk->sk_state < TCP_TIME_WAIT && | |
176 | - icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) | |
177 | - icsk->icsk_ca_ops->get_info(sk, ext, skb); | |
178 | - | |
179 | - nlh->nlmsg_len = skb->tail - b; | |
180 | - return skb->len; | |
181 | - | |
182 | -rtattr_failure: | |
183 | -nlmsg_failure: | |
184 | - skb_trim(skb, b - skb->data); | |
185 | - return -1; | |
186 | -} | |
187 | - | |
188 | -static int inet_diag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh) | |
189 | -{ | |
190 | - int err; | |
191 | - struct sock *sk; | |
192 | - struct inet_diag_req *req = NLMSG_DATA(nlh); | |
193 | - struct sk_buff *rep; | |
194 | - struct inet_hashinfo *hashinfo; | |
195 | - const struct inet_diag_handler *handler; | |
196 | - | |
197 | - handler = inet_diag_table[nlh->nlmsg_type]; | |
198 | - BUG_ON(handler == NULL); | |
199 | - hashinfo = handler->idiag_hashinfo; | |
200 | - | |
201 | - if (req->idiag_family == AF_INET) { | |
202 | - sk = inet_lookup(hashinfo, req->id.idiag_dst[0], | |
203 | - req->id.idiag_dport, req->id.idiag_src[0], | |
204 | - req->id.idiag_sport, req->id.idiag_if); | |
205 | - } | |
206 | -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
207 | - else if (req->idiag_family == AF_INET6) { | |
208 | - sk = inet6_lookup(hashinfo, | |
209 | - (struct in6_addr *)req->id.idiag_dst, | |
210 | - req->id.idiag_dport, | |
211 | - (struct in6_addr *)req->id.idiag_src, | |
212 | - req->id.idiag_sport, | |
213 | - req->id.idiag_if); | |
214 | - } | |
215 | -#endif | |
216 | - else { | |
217 | - return -EINVAL; | |
218 | - } | |
219 | - | |
220 | - if (sk == NULL) | |
221 | - return -ENOENT; | |
222 | - | |
223 | - err = -ESTALE; | |
224 | - if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE || | |
225 | - req->id.idiag_cookie[1] != INET_DIAG_NOCOOKIE) && | |
226 | - ((u32)(unsigned long)sk != req->id.idiag_cookie[0] || | |
227 | - (u32)((((unsigned long)sk) >> 31) >> 1) != req->id.idiag_cookie[1])) | |
228 | - goto out; | |
229 | - | |
230 | - err = -ENOMEM; | |
231 | - rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) + | |
232 | - sizeof(struct inet_diag_meminfo) + | |
233 | - handler->idiag_info_size + 64)), | |
234 | - GFP_KERNEL); | |
235 | - if (!rep) | |
236 | - goto out; | |
237 | - | |
238 | - if (inet_diag_fill(rep, sk, req->idiag_ext, | |
239 | - NETLINK_CB(in_skb).pid, | |
240 | - nlh->nlmsg_seq, 0, nlh) <= 0) | |
241 | - BUG(); | |
242 | - | |
243 | - err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid, | |
244 | - MSG_DONTWAIT); | |
245 | - if (err > 0) | |
246 | - err = 0; | |
247 | - | |
248 | -out: | |
249 | - if (sk) { | |
250 | - if (sk->sk_state == TCP_TIME_WAIT) | |
251 | - inet_twsk_put((struct inet_timewait_sock *)sk); | |
252 | - else | |
253 | - sock_put(sk); | |
254 | - } | |
255 | - return err; | |
256 | -} | |
257 | - | |
258 | -static int bitstring_match(const u32 *a1, const u32 *a2, int bits) | |
259 | -{ | |
260 | - int words = bits >> 5; | |
261 | - | |
262 | - bits &= 0x1f; | |
263 | - | |
264 | - if (words) { | |
265 | - if (memcmp(a1, a2, words << 2)) | |
266 | - return 0; | |
267 | - } | |
268 | - if (bits) { | |
269 | - __u32 w1, w2; | |
270 | - __u32 mask; | |
271 | - | |
272 | - w1 = a1[words]; | |
273 | - w2 = a2[words]; | |
274 | - | |
275 | - mask = htonl((0xffffffff) << (32 - bits)); | |
276 | - | |
277 | - if ((w1 ^ w2) & mask) | |
278 | - return 0; | |
279 | - } | |
280 | - | |
281 | - return 1; | |
282 | -} | |
283 | - | |
284 | - | |
285 | -static int inet_diag_bc_run(const void *bc, int len, | |
286 | - const struct inet_diag_entry *entry) | |
287 | -{ | |
288 | - while (len > 0) { | |
289 | - int yes = 1; | |
290 | - const struct inet_diag_bc_op *op = bc; | |
291 | - | |
292 | - switch (op->code) { | |
293 | - case INET_DIAG_BC_NOP: | |
294 | - break; | |
295 | - case INET_DIAG_BC_JMP: | |
296 | - yes = 0; | |
297 | - break; | |
298 | - case INET_DIAG_BC_S_GE: | |
299 | - yes = entry->sport >= op[1].no; | |
300 | - break; | |
301 | - case INET_DIAG_BC_S_LE: | |
302 | - yes = entry->dport <= op[1].no; | |
303 | - break; | |
304 | - case INET_DIAG_BC_D_GE: | |
305 | - yes = entry->dport >= op[1].no; | |
306 | - break; | |
307 | - case INET_DIAG_BC_D_LE: | |
308 | - yes = entry->dport <= op[1].no; | |
309 | - break; | |
310 | - case INET_DIAG_BC_AUTO: | |
311 | - yes = !(entry->userlocks & SOCK_BINDPORT_LOCK); | |
312 | - break; | |
313 | - case INET_DIAG_BC_S_COND: | |
314 | - case INET_DIAG_BC_D_COND: | |
315 | - { | |
316 | - struct inet_diag_hostcond *cond = (struct inet_diag_hostcond*)(op+1); | |
317 | - u32 *addr; | |
318 | - | |
319 | - if (cond->port != -1 && | |
320 | - cond->port != (op->code == INET_DIAG_BC_S_COND ? | |
321 | - entry->sport : entry->dport)) { | |
322 | - yes = 0; | |
323 | - break; | |
324 | - } | |
325 | - | |
326 | - if (cond->prefix_len == 0) | |
327 | - break; | |
328 | - | |
329 | - if (op->code == INET_DIAG_BC_S_COND) | |
330 | - addr = entry->saddr; | |
331 | - else | |
332 | - addr = entry->daddr; | |
333 | - | |
334 | - if (bitstring_match(addr, cond->addr, cond->prefix_len)) | |
335 | - break; | |
336 | - if (entry->family == AF_INET6 && | |
337 | - cond->family == AF_INET) { | |
338 | - if (addr[0] == 0 && addr[1] == 0 && | |
339 | - addr[2] == htonl(0xffff) && | |
340 | - bitstring_match(addr+3, cond->addr, cond->prefix_len)) | |
341 | - break; | |
342 | - } | |
343 | - yes = 0; | |
344 | - break; | |
345 | - } | |
346 | - } | |
347 | - | |
348 | - if (yes) { | |
349 | - len -= op->yes; | |
350 | - bc += op->yes; | |
351 | - } else { | |
352 | - len -= op->no; | |
353 | - bc += op->no; | |
354 | - } | |
355 | - } | |
356 | - return (len == 0); | |
357 | -} | |
358 | - | |
359 | -static int valid_cc(const void *bc, int len, int cc) | |
360 | -{ | |
361 | - while (len >= 0) { | |
362 | - const struct inet_diag_bc_op *op = bc; | |
363 | - | |
364 | - if (cc > len) | |
365 | - return 0; | |
366 | - if (cc == len) | |
367 | - return 1; | |
368 | - if (op->yes < 4) | |
369 | - return 0; | |
370 | - len -= op->yes; | |
371 | - bc += op->yes; | |
372 | - } | |
373 | - return 0; | |
374 | -} | |
375 | - | |
376 | -static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) | |
377 | -{ | |
378 | - const unsigned char *bc = bytecode; | |
379 | - int len = bytecode_len; | |
380 | - | |
381 | - while (len > 0) { | |
382 | - struct inet_diag_bc_op *op = (struct inet_diag_bc_op*)bc; | |
383 | - | |
384 | -//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len); | |
385 | - switch (op->code) { | |
386 | - case INET_DIAG_BC_AUTO: | |
387 | - case INET_DIAG_BC_S_COND: | |
388 | - case INET_DIAG_BC_D_COND: | |
389 | - case INET_DIAG_BC_S_GE: | |
390 | - case INET_DIAG_BC_S_LE: | |
391 | - case INET_DIAG_BC_D_GE: | |
392 | - case INET_DIAG_BC_D_LE: | |
393 | - if (op->yes < 4 || op->yes > len+4) | |
394 | - return -EINVAL; | |
395 | - case INET_DIAG_BC_JMP: | |
396 | - if (op->no < 4 || op->no > len+4) | |
397 | - return -EINVAL; | |
398 | - if (op->no < len && | |
399 | - !valid_cc(bytecode, bytecode_len, len-op->no)) | |
400 | - return -EINVAL; | |
401 | - break; | |
402 | - case INET_DIAG_BC_NOP: | |
403 | - if (op->yes < 4 || op->yes > len+4) | |
404 | - return -EINVAL; | |
405 | - break; | |
406 | - default: | |
407 | - return -EINVAL; | |
408 | - } | |
409 | - bc += op->yes; | |
410 | - len -= op->yes; | |
411 | - } | |
412 | - return len == 0 ? 0 : -EINVAL; | |
413 | -} | |
414 | - | |
415 | -static int inet_diag_dump_sock(struct sk_buff *skb, struct sock *sk, | |
416 | - struct netlink_callback *cb) | |
417 | -{ | |
418 | - struct inet_diag_req *r = NLMSG_DATA(cb->nlh); | |
419 | - | |
420 | - if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { | |
421 | - struct inet_diag_entry entry; | |
422 | - struct rtattr *bc = (struct rtattr *)(r + 1); | |
423 | - struct inet_sock *inet = inet_sk(sk); | |
424 | - | |
425 | - entry.family = sk->sk_family; | |
426 | -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
427 | - if (entry.family == AF_INET6) { | |
428 | - struct ipv6_pinfo *np = inet6_sk(sk); | |
429 | - | |
430 | - entry.saddr = np->rcv_saddr.s6_addr32; | |
431 | - entry.daddr = np->daddr.s6_addr32; | |
432 | - } else | |
433 | -#endif | |
434 | - { | |
435 | - entry.saddr = &inet->rcv_saddr; | |
436 | - entry.daddr = &inet->daddr; | |
437 | - } | |
438 | - entry.sport = inet->num; | |
439 | - entry.dport = ntohs(inet->dport); | |
440 | - entry.userlocks = sk->sk_userlocks; | |
441 | - | |
442 | - if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) | |
443 | - return 0; | |
444 | - } | |
445 | - | |
446 | - return inet_diag_fill(skb, sk, r->idiag_ext, NETLINK_CB(cb->skb).pid, | |
447 | - cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); | |
448 | -} | |
449 | - | |
450 | -static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, | |
451 | - struct request_sock *req, | |
452 | - u32 pid, u32 seq, | |
453 | - const struct nlmsghdr *unlh) | |
454 | -{ | |
455 | - const struct inet_request_sock *ireq = inet_rsk(req); | |
456 | - struct inet_sock *inet = inet_sk(sk); | |
457 | - unsigned char *b = skb->tail; | |
458 | - struct inet_diag_msg *r; | |
459 | - struct nlmsghdr *nlh; | |
460 | - long tmo; | |
461 | - | |
462 | - nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); | |
463 | - nlh->nlmsg_flags = NLM_F_MULTI; | |
464 | - r = NLMSG_DATA(nlh); | |
465 | - | |
466 | - r->idiag_family = sk->sk_family; | |
467 | - r->idiag_state = TCP_SYN_RECV; | |
468 | - r->idiag_timer = 1; | |
469 | - r->idiag_retrans = req->retrans; | |
470 | - | |
471 | - r->id.idiag_if = sk->sk_bound_dev_if; | |
472 | - r->id.idiag_cookie[0] = (u32)(unsigned long)req; | |
473 | - r->id.idiag_cookie[1] = (u32)(((unsigned long)req >> 31) >> 1); | |
474 | - | |
475 | - tmo = req->expires - jiffies; | |
476 | - if (tmo < 0) | |
477 | - tmo = 0; | |
478 | - | |
479 | - r->id.idiag_sport = inet->sport; | |
480 | - r->id.idiag_dport = ireq->rmt_port; | |
481 | - r->id.idiag_src[0] = ireq->loc_addr; | |
482 | - r->id.idiag_dst[0] = ireq->rmt_addr; | |
483 | - r->idiag_expires = jiffies_to_msecs(tmo); | |
484 | - r->idiag_rqueue = 0; | |
485 | - r->idiag_wqueue = 0; | |
486 | - r->idiag_uid = sock_i_uid(sk); | |
487 | - r->idiag_inode = 0; | |
488 | -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
489 | - if (r->idiag_family == AF_INET6) { | |
490 | - ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, | |
491 | - &tcp6_rsk(req)->loc_addr); | |
492 | - ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, | |
493 | - &tcp6_rsk(req)->rmt_addr); | |
494 | - } | |
495 | -#endif | |
496 | - nlh->nlmsg_len = skb->tail - b; | |
497 | - | |
498 | - return skb->len; | |
499 | - | |
500 | -nlmsg_failure: | |
501 | - skb_trim(skb, b - skb->data); | |
502 | - return -1; | |
503 | -} | |
504 | - | |
505 | -static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, | |
506 | - struct netlink_callback *cb) | |
507 | -{ | |
508 | - struct inet_diag_entry entry; | |
509 | - struct inet_diag_req *r = NLMSG_DATA(cb->nlh); | |
510 | - struct inet_connection_sock *icsk = inet_csk(sk); | |
511 | - struct listen_sock *lopt; | |
512 | - struct rtattr *bc = NULL; | |
513 | - struct inet_sock *inet = inet_sk(sk); | |
514 | - int j, s_j; | |
515 | - int reqnum, s_reqnum; | |
516 | - int err = 0; | |
517 | - | |
518 | - s_j = cb->args[3]; | |
519 | - s_reqnum = cb->args[4]; | |
520 | - | |
521 | - if (s_j > 0) | |
522 | - s_j--; | |
523 | - | |
524 | - entry.family = sk->sk_family; | |
525 | - | |
526 | - read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); | |
527 | - | |
528 | - lopt = icsk->icsk_accept_queue.listen_opt; | |
529 | - if (!lopt || !lopt->qlen) | |
530 | - goto out; | |
531 | - | |
532 | - if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { | |
533 | - bc = (struct rtattr *)(r + 1); | |
534 | - entry.sport = inet->num; | |
535 | - entry.userlocks = sk->sk_userlocks; | |
536 | - } | |
537 | - | |
538 | - for (j = s_j; j < lopt->nr_table_entries; j++) { | |
539 | - struct request_sock *req, *head = lopt->syn_table[j]; | |
540 | - | |
541 | - reqnum = 0; | |
542 | - for (req = head; req; reqnum++, req = req->dl_next) { | |
543 | - struct inet_request_sock *ireq = inet_rsk(req); | |
544 | - | |
545 | - if (reqnum < s_reqnum) | |
546 | - continue; | |
547 | - if (r->id.idiag_dport != ireq->rmt_port && | |
548 | - r->id.idiag_dport) | |
549 | - continue; | |
550 | - | |
551 | - if (bc) { | |
552 | - entry.saddr = | |
553 | -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
554 | - (entry.family == AF_INET6) ? | |
555 | - tcp6_rsk(req)->loc_addr.s6_addr32 : | |
556 | -#endif | |
557 | - &ireq->loc_addr; | |
558 | - entry.daddr = | |
559 | -#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) | |
560 | - (entry.family == AF_INET6) ? | |
561 | - tcp6_rsk(req)->rmt_addr.s6_addr32 : | |
562 | -#endif | |
563 | - &ireq->rmt_addr; | |
564 | - entry.dport = ntohs(ireq->rmt_port); | |
565 | - | |
566 | - if (!inet_diag_bc_run(RTA_DATA(bc), | |
567 | - RTA_PAYLOAD(bc), &entry)) | |
568 | - continue; | |
569 | - } | |
570 | - | |
571 | - err = inet_diag_fill_req(skb, sk, req, | |
572 | - NETLINK_CB(cb->skb).pid, | |
573 | - cb->nlh->nlmsg_seq, cb->nlh); | |
574 | - if (err < 0) { | |
575 | - cb->args[3] = j + 1; | |
576 | - cb->args[4] = reqnum; | |
577 | - goto out; | |
578 | - } | |
579 | - } | |
580 | - | |
581 | - s_reqnum = 0; | |
582 | - } | |
583 | - | |
584 | -out: | |
585 | - read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); | |
586 | - | |
587 | - return err; | |
588 | -} | |
589 | - | |
590 | -static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |
591 | -{ | |
592 | - int i, num; | |
593 | - int s_i, s_num; | |
594 | - struct inet_diag_req *r = NLMSG_DATA(cb->nlh); | |
595 | - const struct inet_diag_handler *handler; | |
596 | - struct inet_hashinfo *hashinfo; | |
597 | - | |
598 | - handler = inet_diag_table[cb->nlh->nlmsg_type]; | |
599 | - BUG_ON(handler == NULL); | |
600 | - hashinfo = handler->idiag_hashinfo; | |
601 | - | |
602 | - s_i = cb->args[1]; | |
603 | - s_num = num = cb->args[2]; | |
604 | - | |
605 | - if (cb->args[0] == 0) { | |
606 | - if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV))) | |
607 | - goto skip_listen_ht; | |
608 | - | |
609 | - inet_listen_lock(hashinfo); | |
610 | - for (i = s_i; i < INET_LHTABLE_SIZE; i++) { | |
611 | - struct sock *sk; | |
612 | - struct hlist_node *node; | |
613 | - | |
614 | - num = 0; | |
615 | - sk_for_each(sk, node, &hashinfo->listening_hash[i]) { | |
616 | - struct inet_sock *inet = inet_sk(sk); | |
617 | - | |
618 | - if (num < s_num) { | |
619 | - num++; | |
620 | - continue; | |
621 | - } | |
622 | - | |
623 | - if (r->id.idiag_sport != inet->sport && | |
624 | - r->id.idiag_sport) | |
625 | - goto next_listen; | |
626 | - | |
627 | - if (!(r->idiag_states & TCPF_LISTEN) || | |
628 | - r->id.idiag_dport || | |
629 | - cb->args[3] > 0) | |
630 | - goto syn_recv; | |
631 | - | |
632 | - if (inet_diag_dump_sock(skb, sk, cb) < 0) { | |
633 | - inet_listen_unlock(hashinfo); | |
634 | - goto done; | |
635 | - } | |
636 | - | |
637 | -syn_recv: | |
638 | - if (!(r->idiag_states & TCPF_SYN_RECV)) | |
639 | - goto next_listen; | |
640 | - | |
641 | - if (inet_diag_dump_reqs(skb, sk, cb) < 0) { | |
642 | - inet_listen_unlock(hashinfo); | |
643 | - goto done; | |
644 | - } | |
645 | - | |
646 | -next_listen: | |
647 | - cb->args[3] = 0; | |
648 | - cb->args[4] = 0; | |
649 | - ++num; | |
650 | - } | |
651 | - | |
652 | - s_num = 0; | |
653 | - cb->args[3] = 0; | |
654 | - cb->args[4] = 0; | |
655 | - } | |
656 | - inet_listen_unlock(hashinfo); | |
657 | -skip_listen_ht: | |
658 | - cb->args[0] = 1; | |
659 | - s_i = num = s_num = 0; | |
660 | - } | |
661 | - | |
662 | - if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV))) | |
663 | - return skb->len; | |
664 | - | |
665 | - for (i = s_i; i < hashinfo->ehash_size; i++) { | |
666 | - struct inet_ehash_bucket *head = &hashinfo->ehash[i]; | |
667 | - struct sock *sk; | |
668 | - struct hlist_node *node; | |
669 | - | |
670 | - if (i > s_i) | |
671 | - s_num = 0; | |
672 | - | |
673 | - read_lock_bh(&head->lock); | |
674 | - | |
675 | - num = 0; | |
676 | - sk_for_each(sk, node, &head->chain) { | |
677 | - struct inet_sock *inet = inet_sk(sk); | |
678 | - | |
679 | - if (num < s_num) | |
680 | - goto next_normal; | |
681 | - if (!(r->idiag_states & (1 << sk->sk_state))) | |
682 | - goto next_normal; | |
683 | - if (r->id.idiag_sport != inet->sport && | |
684 | - r->id.idiag_sport) | |
685 | - goto next_normal; | |
686 | - if (r->id.idiag_dport != inet->dport && r->id.idiag_dport) | |
687 | - goto next_normal; | |
688 | - if (inet_diag_dump_sock(skb, sk, cb) < 0) { | |
689 | - read_unlock_bh(&head->lock); | |
690 | - goto done; | |
691 | - } | |
692 | -next_normal: | |
693 | - ++num; | |
694 | - } | |
695 | - | |
696 | - if (r->idiag_states & TCPF_TIME_WAIT) { | |
697 | - sk_for_each(sk, node, | |
698 | - &hashinfo->ehash[i + hashinfo->ehash_size].chain) { | |
699 | - struct inet_sock *inet = inet_sk(sk); | |
700 | - | |
701 | - if (num < s_num) | |
702 | - goto next_dying; | |
703 | - if (r->id.idiag_sport != inet->sport && | |
704 | - r->id.idiag_sport) | |
705 | - goto next_dying; | |
706 | - if (r->id.idiag_dport != inet->dport && | |
707 | - r->id.idiag_dport) | |
708 | - goto next_dying; | |
709 | - if (inet_diag_dump_sock(skb, sk, cb) < 0) { | |
710 | - read_unlock_bh(&head->lock); | |
711 | - goto done; | |
712 | - } | |
713 | -next_dying: | |
714 | - ++num; | |
715 | - } | |
716 | - } | |
717 | - read_unlock_bh(&head->lock); | |
718 | - } | |
719 | - | |
720 | -done: | |
721 | - cb->args[1] = i; | |
722 | - cb->args[2] = num; | |
723 | - return skb->len; | |
724 | -} | |
725 | - | |
726 | -static int inet_diag_dump_done(struct netlink_callback *cb) | |
727 | -{ | |
728 | - return 0; | |
729 | -} | |
730 | - | |
731 | - | |
732 | -static __inline__ int | |
733 | -inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |
734 | -{ | |
735 | - if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) | |
736 | - return 0; | |
737 | - | |
738 | - if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) | |
739 | - goto err_inval; | |
740 | - | |
741 | - if (inet_diag_table[nlh->nlmsg_type] == NULL) | |
742 | - return -ENOENT; | |
743 | - | |
744 | - if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len) | |
745 | - goto err_inval; | |
746 | - | |
747 | - if (nlh->nlmsg_flags&NLM_F_DUMP) { | |
748 | - if (nlh->nlmsg_len > | |
749 | - (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) { | |
750 | - struct rtattr *rta = (void *)(NLMSG_DATA(nlh) + | |
751 | - sizeof(struct inet_diag_req)); | |
752 | - if (rta->rta_type != INET_DIAG_REQ_BYTECODE || | |
753 | - rta->rta_len < 8 || | |
754 | - rta->rta_len > | |
755 | - (nlh->nlmsg_len - | |
756 | - NLMSG_SPACE(sizeof(struct inet_diag_req)))) | |
757 | - goto err_inval; | |
758 | - if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta))) | |
759 | - goto err_inval; | |
760 | - } | |
761 | - return netlink_dump_start(idiagnl, skb, nlh, | |
762 | - inet_diag_dump, | |
763 | - inet_diag_dump_done); | |
764 | - } else { | |
765 | - return inet_diag_get_exact(skb, nlh); | |
766 | - } | |
767 | - | |
768 | -err_inval: | |
769 | - return -EINVAL; | |
770 | -} | |
771 | - | |
772 | - | |
773 | -static inline void inet_diag_rcv_skb(struct sk_buff *skb) | |
774 | -{ | |
775 | - int err; | |
776 | - struct nlmsghdr * nlh; | |
777 | - | |
778 | - if (skb->len >= NLMSG_SPACE(0)) { | |
779 | - nlh = (struct nlmsghdr *)skb->data; | |
780 | - if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) | |
781 | - return; | |
782 | - err = inet_diag_rcv_msg(skb, nlh); | |
783 | - if (err || nlh->nlmsg_flags & NLM_F_ACK) | |
784 | - netlink_ack(skb, nlh, err); | |
785 | - } | |
786 | -} | |
787 | - | |
788 | -static void inet_diag_rcv(struct sock *sk, int len) | |
789 | -{ | |
790 | - struct sk_buff *skb; | |
791 | - unsigned int qlen = skb_queue_len(&sk->sk_receive_queue); | |
792 | - | |
793 | - while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) { | |
794 | - inet_diag_rcv_skb(skb); | |
795 | - kfree_skb(skb); | |
796 | - } | |
797 | -} | |
798 | - | |
799 | -static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, | |
800 | - void *_info) | |
801 | -{ | |
802 | - const struct tcp_sock *tp = tcp_sk(sk); | |
803 | - struct tcp_info *info = _info; | |
804 | - | |
805 | - r->idiag_rqueue = tp->rcv_nxt - tp->copied_seq; | |
806 | - r->idiag_wqueue = tp->write_seq - tp->snd_una; | |
807 | - if (info != NULL) | |
808 | - tcp_get_info(sk, info); | |
809 | -} | |
810 | - | |
811 | -static struct inet_diag_handler tcp_diag_handler = { | |
812 | - .idiag_hashinfo = &tcp_hashinfo, | |
813 | - .idiag_get_info = tcp_diag_get_info, | |
814 | - .idiag_type = TCPDIAG_GETSOCK, | |
815 | - .idiag_info_size = sizeof(struct tcp_info), | |
816 | -}; | |
817 | - | |
818 | -static DEFINE_SPINLOCK(inet_diag_register_lock); | |
819 | - | |
820 | -int inet_diag_register(const struct inet_diag_handler *h) | |
821 | -{ | |
822 | - const __u16 type = h->idiag_type; | |
823 | - int err = -EINVAL; | |
824 | - | |
825 | - if (type >= INET_DIAG_GETSOCK_MAX) | |
826 | - goto out; | |
827 | - | |
828 | - spin_lock(&inet_diag_register_lock); | |
829 | - err = -EEXIST; | |
830 | - if (inet_diag_table[type] == NULL) { | |
831 | - inet_diag_table[type] = h; | |
832 | - err = 0; | |
833 | - } | |
834 | - spin_unlock(&inet_diag_register_lock); | |
835 | -out: | |
836 | - return err; | |
837 | -} | |
838 | -EXPORT_SYMBOL_GPL(inet_diag_register); | |
839 | - | |
840 | -void inet_diag_unregister(const struct inet_diag_handler *h) | |
841 | -{ | |
842 | - const __u16 type = h->idiag_type; | |
843 | - | |
844 | - if (type >= INET_DIAG_GETSOCK_MAX) | |
845 | - return; | |
846 | - | |
847 | - spin_lock(&inet_diag_register_lock); | |
848 | - inet_diag_table[type] = NULL; | |
849 | - spin_unlock(&inet_diag_register_lock); | |
850 | - | |
851 | - synchronize_rcu(); | |
852 | -} | |
853 | -EXPORT_SYMBOL_GPL(inet_diag_unregister); | |
854 | - | |
855 | -static int __init inet_diag_init(void) | |
856 | -{ | |
857 | - const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX * | |
858 | - sizeof(struct inet_diag_handler *)); | |
859 | - int err = -ENOMEM; | |
860 | - | |
861 | - inet_diag_table = kmalloc(inet_diag_table_size, GFP_KERNEL); | |
862 | - if (!inet_diag_table) | |
863 | - goto out; | |
864 | - | |
865 | - memset(inet_diag_table, 0, inet_diag_table_size); | |
866 | - | |
867 | - idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, inet_diag_rcv, | |
868 | - THIS_MODULE); | |
869 | - if (idiagnl == NULL) | |
870 | - goto out_free_table; | |
871 | - | |
872 | - err = inet_diag_register(&tcp_diag_handler); | |
873 | - if (err) | |
874 | - goto out_sock_release; | |
875 | -out: | |
876 | - return err; | |
877 | -out_sock_release: | |
878 | - sock_release(idiagnl->sk_socket); | |
879 | -out_free_table: | |
880 | - kfree(inet_diag_table); | |
881 | - goto out; | |
882 | -} | |
883 | - | |
884 | -static void __exit inet_diag_exit(void) | |
885 | -{ | |
886 | - sock_release(idiagnl->sk_socket); | |
887 | - kfree(inet_diag_table); | |
888 | -} | |
889 | - | |
890 | -module_init(inet_diag_init); | |
891 | -module_exit(inet_diag_exit); | |
892 | -MODULE_LICENSE("GPL"); |
net/ipv4/tcp_vegas.c
net/ipv4/tcp_westwood.c