diff options
-rw-r--r-- | include/linux/in6.h | 4 | ||||
-rw-r--r-- | include/linux/ipv6.h | 4 | ||||
-rw-r--r-- | include/linux/netfilter/xt_TPROXY.h | 13 | ||||
-rw-r--r-- | include/net/inet_hashtables.h | 2 | ||||
-rw-r--r-- | include/net/netfilter/ipv6/nf_defrag_ipv6.h | 6 | ||||
-rw-r--r-- | include/net/netfilter/nf_tproxy_core.h | 192 | ||||
-rw-r--r-- | include/net/udp.h | 3 | ||||
-rw-r--r-- | net/dccp/ipv4.c | 10 | ||||
-rw-r--r-- | net/dccp/ipv6.c | 10 | ||||
-rw-r--r-- | net/ipv4/inet_hashtables.c | 28 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 10 | ||||
-rw-r--r-- | net/ipv6/af_inet6.c | 3 | ||||
-rw-r--r-- | net/ipv6/datagram.c | 19 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 23 | ||||
-rw-r--r-- | net/ipv6/netfilter/Makefile | 5 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 78 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_reasm.c | 14 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 131 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 12 | ||||
-rw-r--r-- | net/ipv6/udp.c | 16 | ||||
-rw-r--r-- | net/netfilter/nf_tproxy_core.c | 35 | ||||
-rw-r--r-- | net/netfilter/xt_TPROXY.c | 366 | ||||
-rw-r--r-- | net/netfilter/xt_socket.c | 167 |
23 files changed, 969 insertions, 182 deletions
diff --git a/include/linux/in6.h b/include/linux/in6.h index c4bf46f764bf..097a34b55560 100644 --- a/include/linux/in6.h +++ b/include/linux/in6.h | |||
@@ -268,6 +268,10 @@ struct in6_flowlabel_req { | |||
268 | /* RFC5082: Generalized Ttl Security Mechanism */ | 268 | /* RFC5082: Generalized Ttl Security Mechanism */ |
269 | #define IPV6_MINHOPCOUNT 73 | 269 | #define IPV6_MINHOPCOUNT 73 |
270 | 270 | ||
271 | #define IPV6_ORIGDSTADDR 74 | ||
272 | #define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR | ||
273 | #define IPV6_TRANSPARENT 75 | ||
274 | |||
271 | /* | 275 | /* |
272 | * Multicast Routing: | 276 | * Multicast Routing: |
273 | * see include/linux/mroute6.h. | 277 | * see include/linux/mroute6.h. |
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index e62683ba88e6..8e429d0e0405 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h | |||
@@ -341,7 +341,9 @@ struct ipv6_pinfo { | |||
341 | odstopts:1, | 341 | odstopts:1, |
342 | rxflow:1, | 342 | rxflow:1, |
343 | rxtclass:1, | 343 | rxtclass:1, |
344 | rxpmtu:1; | 344 | rxpmtu:1, |
345 | rxorigdstaddr:1; | ||
346 | /* 2 bits hole */ | ||
345 | } bits; | 347 | } bits; |
346 | __u16 all; | 348 | __u16 all; |
347 | } rxopt; | 349 | } rxopt; |
diff --git a/include/linux/netfilter/xt_TPROXY.h b/include/linux/netfilter/xt_TPROXY.h index 152e8f97132b..3f3d69361289 100644 --- a/include/linux/netfilter/xt_TPROXY.h +++ b/include/linux/netfilter/xt_TPROXY.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _XT_TPROXY_H_target | 1 | #ifndef _XT_TPROXY_H |
2 | #define _XT_TPROXY_H_target | 2 | #define _XT_TPROXY_H |
3 | 3 | ||
4 | /* TPROXY target is capable of marking the packet to perform | 4 | /* TPROXY target is capable of marking the packet to perform |
5 | * redirection. We can get rid of that whenever we get support for | 5 | * redirection. We can get rid of that whenever we get support for |
@@ -11,4 +11,11 @@ struct xt_tproxy_target_info { | |||
11 | __be16 lport; | 11 | __be16 lport; |
12 | }; | 12 | }; |
13 | 13 | ||
14 | #endif /* _XT_TPROXY_H_target */ | 14 | struct xt_tproxy_target_info_v1 { |
15 | u_int32_t mark_mask; | ||
16 | u_int32_t mark_value; | ||
17 | union nf_inet_addr laddr; | ||
18 | __be16 lport; | ||
19 | }; | ||
20 | |||
21 | #endif /* _XT_TPROXY_H */ | ||
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 74358d1b3f43..e9c2ed8af864 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h | |||
@@ -245,7 +245,7 @@ static inline int inet_sk_listen_hashfn(const struct sock *sk) | |||
245 | } | 245 | } |
246 | 246 | ||
247 | /* Caller must disable local BH processing. */ | 247 | /* Caller must disable local BH processing. */ |
248 | extern void __inet_inherit_port(struct sock *sk, struct sock *child); | 248 | extern int __inet_inherit_port(struct sock *sk, struct sock *child); |
249 | 249 | ||
250 | extern void inet_put_port(struct sock *sk); | 250 | extern void inet_put_port(struct sock *sk); |
251 | 251 | ||
diff --git a/include/net/netfilter/ipv6/nf_defrag_ipv6.h b/include/net/netfilter/ipv6/nf_defrag_ipv6.h new file mode 100644 index 000000000000..94dd54d76b48 --- /dev/null +++ b/include/net/netfilter/ipv6/nf_defrag_ipv6.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef _NF_DEFRAG_IPV6_H | ||
2 | #define _NF_DEFRAG_IPV6_H | ||
3 | |||
4 | extern void nf_defrag_ipv6_enable(void); | ||
5 | |||
6 | #endif /* _NF_DEFRAG_IPV6_H */ | ||
diff --git a/include/net/netfilter/nf_tproxy_core.h b/include/net/netfilter/nf_tproxy_core.h index 208b46f4d6d2..cd85b3bc8327 100644 --- a/include/net/netfilter/nf_tproxy_core.h +++ b/include/net/netfilter/nf_tproxy_core.h | |||
@@ -5,15 +5,201 @@ | |||
5 | #include <linux/in.h> | 5 | #include <linux/in.h> |
6 | #include <linux/skbuff.h> | 6 | #include <linux/skbuff.h> |
7 | #include <net/sock.h> | 7 | #include <net/sock.h> |
8 | #include <net/inet_sock.h> | 8 | #include <net/inet_hashtables.h> |
9 | #include <net/inet6_hashtables.h> | ||
9 | #include <net/tcp.h> | 10 | #include <net/tcp.h> |
10 | 11 | ||
12 | #define NFT_LOOKUP_ANY 0 | ||
13 | #define NFT_LOOKUP_LISTENER 1 | ||
14 | #define NFT_LOOKUP_ESTABLISHED 2 | ||
15 | |||
11 | /* look up and get a reference to a matching socket */ | 16 | /* look up and get a reference to a matching socket */ |
12 | extern struct sock * | 17 | |
18 | |||
19 | /* This function is used by the 'TPROXY' target and the 'socket' | ||
20 | * match. The following lookups are supported: | ||
21 | * | ||
22 | * Explicit TProxy target rule | ||
23 | * =========================== | ||
24 | * | ||
25 | * This is used when the user wants to intercept a connection matching | ||
26 | * an explicit iptables rule. In this case the sockets are assumed | ||
27 | * matching in preference order: | ||
28 | * | ||
29 | * - match: if there's a fully established connection matching the | ||
30 | * _packet_ tuple, it is returned, assuming the redirection | ||
31 | * already took place and we process a packet belonging to an | ||
32 | * established connection | ||
33 | * | ||
34 | * - match: if there's a listening socket matching the redirection | ||
35 | * (e.g. on-port & on-ip of the connection), it is returned, | ||
36 | * regardless if it was bound to 0.0.0.0 or an explicit | ||
37 | * address. The reasoning is that if there's an explicit rule, it | ||
38 | * does not really matter if the listener is bound to an interface | ||
39 | * or to 0. The user already stated that he wants redirection | ||
40 | * (since he added the rule). | ||
41 | * | ||
42 | * "socket" match based redirection (no specific rule) | ||
43 | * =================================================== | ||
44 | * | ||
45 | * There are connections with dynamic endpoints (e.g. FTP data | ||
46 | * connection) that the user is unable to add explicit rules | ||
47 | * for. These are taken care of by a generic "socket" rule. It is | ||
48 | * assumed that the proxy application is trusted to open such | ||
49 | * connections without explicit iptables rule (except of course the | ||
50 | * generic 'socket' rule). In this case the following sockets are | ||
51 | * matched in preference order: | ||
52 | * | ||
53 | * - match: if there's a fully established connection matching the | ||
54 | * _packet_ tuple | ||
55 | * | ||
56 | * - match: if there's a non-zero bound listener (possibly with a | ||
57 | * non-local address) We don't accept zero-bound listeners, since | ||
58 | * then local services could intercept traffic going through the | ||
59 | * box. | ||
60 | * | ||
61 | * Please note that there's an overlap between what a TPROXY target | ||
62 | * and a socket match will match. Normally if you have both rules the | ||
63 | * "socket" match will be the first one, effectively all packets | ||
64 | * belonging to established connections going through that one. | ||
65 | */ | ||
66 | static inline struct sock * | ||
13 | nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, | 67 | nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, |
14 | const __be32 saddr, const __be32 daddr, | 68 | const __be32 saddr, const __be32 daddr, |
15 | const __be16 sport, const __be16 dport, | 69 | const __be16 sport, const __be16 dport, |
16 | const struct net_device *in, bool listening); | 70 | const struct net_device *in, int lookup_type) |
71 | { | ||
72 | struct sock *sk; | ||
73 | |||
74 | /* look up socket */ | ||
75 | switch (protocol) { | ||
76 | case IPPROTO_TCP: | ||
77 | switch (lookup_type) { | ||
78 | case NFT_LOOKUP_ANY: | ||
79 | sk = __inet_lookup(net, &tcp_hashinfo, | ||
80 | saddr, sport, daddr, dport, | ||
81 | in->ifindex); | ||
82 | break; | ||
83 | case NFT_LOOKUP_LISTENER: | ||
84 | sk = inet_lookup_listener(net, &tcp_hashinfo, | ||
85 | daddr, dport, | ||
86 | in->ifindex); | ||
87 | |||
88 | /* NOTE: we return listeners even if bound to | ||
89 | * 0.0.0.0, those are filtered out in | ||
90 | * xt_socket, since xt_TPROXY needs 0 bound | ||
91 | * listeners too */ | ||
92 | |||
93 | break; | ||
94 | case NFT_LOOKUP_ESTABLISHED: | ||
95 | sk = inet_lookup_established(net, &tcp_hashinfo, | ||
96 | saddr, sport, daddr, dport, | ||
97 | in->ifindex); | ||
98 | break; | ||
99 | default: | ||
100 | WARN_ON(1); | ||
101 | sk = NULL; | ||
102 | break; | ||
103 | } | ||
104 | break; | ||
105 | case IPPROTO_UDP: | ||
106 | sk = udp4_lib_lookup(net, saddr, sport, daddr, dport, | ||
107 | in->ifindex); | ||
108 | if (sk && lookup_type != NFT_LOOKUP_ANY) { | ||
109 | int connected = (sk->sk_state == TCP_ESTABLISHED); | ||
110 | int wildcard = (inet_sk(sk)->inet_rcv_saddr == 0); | ||
111 | |||
112 | /* NOTE: we return listeners even if bound to | ||
113 | * 0.0.0.0, those are filtered out in | ||
114 | * xt_socket, since xt_TPROXY needs 0 bound | ||
115 | * listeners too */ | ||
116 | if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || | ||
117 | (lookup_type == NFT_LOOKUP_LISTENER && connected)) { | ||
118 | sock_put(sk); | ||
119 | sk = NULL; | ||
120 | } | ||
121 | } | ||
122 | break; | ||
123 | default: | ||
124 | WARN_ON(1); | ||
125 | sk = NULL; | ||
126 | } | ||
127 | |||
128 | pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, lookup type: %d, sock %p\n", | ||
129 | protocol, ntohl(saddr), ntohs(sport), ntohl(daddr), ntohs(dport), lookup_type, sk); | ||
130 | |||
131 | return sk; | ||
132 | } | ||
133 | |||
134 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
135 | static inline struct sock * | ||
136 | nf_tproxy_get_sock_v6(struct net *net, const u8 protocol, | ||
137 | const struct in6_addr *saddr, const struct in6_addr *daddr, | ||
138 | const __be16 sport, const __be16 dport, | ||
139 | const struct net_device *in, int lookup_type) | ||
140 | { | ||
141 | struct sock *sk; | ||
142 | |||
143 | /* look up socket */ | ||
144 | switch (protocol) { | ||
145 | case IPPROTO_TCP: | ||
146 | switch (lookup_type) { | ||
147 | case NFT_LOOKUP_ANY: | ||
148 | sk = inet6_lookup(net, &tcp_hashinfo, | ||
149 | saddr, sport, daddr, dport, | ||
150 | in->ifindex); | ||
151 | break; | ||
152 | case NFT_LOOKUP_LISTENER: | ||
153 | sk = inet6_lookup_listener(net, &tcp_hashinfo, | ||
154 | daddr, ntohs(dport), | ||
155 | in->ifindex); | ||
156 | |||
157 | /* NOTE: we return listeners even if bound to | ||
158 | * 0.0.0.0, those are filtered out in | ||
159 | * xt_socket, since xt_TPROXY needs 0 bound | ||
160 | * listeners too */ | ||
161 | |||
162 | break; | ||
163 | case NFT_LOOKUP_ESTABLISHED: | ||
164 | sk = __inet6_lookup_established(net, &tcp_hashinfo, | ||
165 | saddr, sport, daddr, ntohs(dport), | ||
166 | in->ifindex); | ||
167 | break; | ||
168 | default: | ||
169 | WARN_ON(1); | ||
170 | sk = NULL; | ||
171 | break; | ||
172 | } | ||
173 | break; | ||
174 | case IPPROTO_UDP: | ||
175 | sk = udp6_lib_lookup(net, saddr, sport, daddr, dport, | ||
176 | in->ifindex); | ||
177 | if (sk && lookup_type != NFT_LOOKUP_ANY) { | ||
178 | int connected = (sk->sk_state == TCP_ESTABLISHED); | ||
179 | int wildcard = ipv6_addr_any(&inet6_sk(sk)->rcv_saddr); | ||
180 | |||
181 | /* NOTE: we return listeners even if bound to | ||
182 | * 0.0.0.0, those are filtered out in | ||
183 | * xt_socket, since xt_TPROXY needs 0 bound | ||
184 | * listeners too */ | ||
185 | if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || | ||
186 | (lookup_type == NFT_LOOKUP_LISTENER && connected)) { | ||
187 | sock_put(sk); | ||
188 | sk = NULL; | ||
189 | } | ||
190 | } | ||
191 | break; | ||
192 | default: | ||
193 | WARN_ON(1); | ||
194 | sk = NULL; | ||
195 | } | ||
196 | |||
197 | pr_debug("tproxy socket lookup: proto %u %pI6:%u -> %pI6:%u, lookup type: %d, sock %p\n", | ||
198 | protocol, saddr, ntohs(sport), daddr, ntohs(dport), lookup_type, sk); | ||
199 | |||
200 | return sk; | ||
201 | } | ||
202 | #endif | ||
17 | 203 | ||
18 | static inline void | 204 | static inline void |
19 | nf_tproxy_put_sock(struct sock *sk) | 205 | nf_tproxy_put_sock(struct sock *sk) |
diff --git a/include/net/udp.h b/include/net/udp.h index a184d3496b13..200b82848c9a 100644 --- a/include/net/udp.h +++ b/include/net/udp.h | |||
@@ -183,6 +183,9 @@ extern int udp_lib_setsockopt(struct sock *sk, int level, int optname, | |||
183 | extern struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, | 183 | extern struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, |
184 | __be32 daddr, __be16 dport, | 184 | __be32 daddr, __be16 dport, |
185 | int dif); | 185 | int dif); |
186 | extern struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport, | ||
187 | const struct in6_addr *daddr, __be16 dport, | ||
188 | int dif); | ||
186 | 189 | ||
187 | /* | 190 | /* |
188 | * SNMP statistics for UDP and UDP-Lite | 191 | * SNMP statistics for UDP and UDP-Lite |
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index d4a166f0f391..3f69ea114829 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c | |||
@@ -392,7 +392,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
392 | 392 | ||
393 | newsk = dccp_create_openreq_child(sk, req, skb); | 393 | newsk = dccp_create_openreq_child(sk, req, skb); |
394 | if (newsk == NULL) | 394 | if (newsk == NULL) |
395 | goto exit; | 395 | goto exit_nonewsk; |
396 | 396 | ||
397 | sk_setup_caps(newsk, dst); | 397 | sk_setup_caps(newsk, dst); |
398 | 398 | ||
@@ -409,16 +409,20 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
409 | 409 | ||
410 | dccp_sync_mss(newsk, dst_mtu(dst)); | 410 | dccp_sync_mss(newsk, dst_mtu(dst)); |
411 | 411 | ||
412 | if (__inet_inherit_port(sk, newsk) < 0) { | ||
413 | sock_put(newsk); | ||
414 | goto exit; | ||
415 | } | ||
412 | __inet_hash_nolisten(newsk, NULL); | 416 | __inet_hash_nolisten(newsk, NULL); |
413 | __inet_inherit_port(sk, newsk); | ||
414 | 417 | ||
415 | return newsk; | 418 | return newsk; |
416 | 419 | ||
417 | exit_overflow: | 420 | exit_overflow: |
418 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); | 421 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); |
422 | exit_nonewsk: | ||
423 | dst_release(dst); | ||
419 | exit: | 424 | exit: |
420 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); | 425 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); |
421 | dst_release(dst); | ||
422 | return NULL; | 426 | return NULL; |
423 | } | 427 | } |
424 | 428 | ||
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6e3f32575df7..dca711df9b60 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c | |||
@@ -564,7 +564,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, | |||
564 | 564 | ||
565 | newsk = dccp_create_openreq_child(sk, req, skb); | 565 | newsk = dccp_create_openreq_child(sk, req, skb); |
566 | if (newsk == NULL) | 566 | if (newsk == NULL) |
567 | goto out; | 567 | goto out_nonewsk; |
568 | 568 | ||
569 | /* | 569 | /* |
570 | * No need to charge this sock to the relevant IPv6 refcnt debug socks | 570 | * No need to charge this sock to the relevant IPv6 refcnt debug socks |
@@ -632,18 +632,22 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, | |||
632 | newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; | 632 | newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; |
633 | newinet->inet_rcv_saddr = LOOPBACK4_IPV6; | 633 | newinet->inet_rcv_saddr = LOOPBACK4_IPV6; |
634 | 634 | ||
635 | if (__inet_inherit_port(sk, newsk) < 0) { | ||
636 | sock_put(newsk); | ||
637 | goto out; | ||
638 | } | ||
635 | __inet6_hash(newsk, NULL); | 639 | __inet6_hash(newsk, NULL); |
636 | __inet_inherit_port(sk, newsk); | ||
637 | 640 | ||
638 | return newsk; | 641 | return newsk; |
639 | 642 | ||
640 | out_overflow: | 643 | out_overflow: |
641 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); | 644 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); |
645 | out_nonewsk: | ||
646 | dst_release(dst); | ||
642 | out: | 647 | out: |
643 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); | 648 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); |
644 | if (opt != NULL && opt != np->opt) | 649 | if (opt != NULL && opt != np->opt) |
645 | sock_kfree_s(sk, opt, opt->tot_len); | 650 | sock_kfree_s(sk, opt, opt->tot_len); |
646 | dst_release(dst); | ||
647 | return NULL; | 651 | return NULL; |
648 | } | 652 | } |
649 | 653 | ||
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index fb7ad5a21ff3..1b344f30b463 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c | |||
@@ -101,19 +101,43 @@ void inet_put_port(struct sock *sk) | |||
101 | } | 101 | } |
102 | EXPORT_SYMBOL(inet_put_port); | 102 | EXPORT_SYMBOL(inet_put_port); |
103 | 103 | ||
104 | void __inet_inherit_port(struct sock *sk, struct sock *child) | 104 | int __inet_inherit_port(struct sock *sk, struct sock *child) |
105 | { | 105 | { |
106 | struct inet_hashinfo *table = sk->sk_prot->h.hashinfo; | 106 | struct inet_hashinfo *table = sk->sk_prot->h.hashinfo; |
107 | const int bhash = inet_bhashfn(sock_net(sk), inet_sk(child)->inet_num, | 107 | unsigned short port = inet_sk(child)->inet_num; |
108 | const int bhash = inet_bhashfn(sock_net(sk), port, | ||
108 | table->bhash_size); | 109 | table->bhash_size); |
109 | struct inet_bind_hashbucket *head = &table->bhash[bhash]; | 110 | struct inet_bind_hashbucket *head = &table->bhash[bhash]; |
110 | struct inet_bind_bucket *tb; | 111 | struct inet_bind_bucket *tb; |
111 | 112 | ||
112 | spin_lock(&head->lock); | 113 | spin_lock(&head->lock); |
113 | tb = inet_csk(sk)->icsk_bind_hash; | 114 | tb = inet_csk(sk)->icsk_bind_hash; |
115 | if (tb->port != port) { | ||
116 | /* NOTE: using tproxy and redirecting skbs to a proxy | ||
117 | * on a different listener port breaks the assumption | ||
118 | * that the listener socket's icsk_bind_hash is the same | ||
119 | * as that of the child socket. We have to look up or | ||
120 | * create a new bind bucket for the child here. */ | ||
121 | struct hlist_node *node; | ||
122 | inet_bind_bucket_for_each(tb, node, &head->chain) { | ||
123 | if (net_eq(ib_net(tb), sock_net(sk)) && | ||
124 | tb->port == port) | ||
125 | break; | ||
126 | } | ||
127 | if (!node) { | ||
128 | tb = inet_bind_bucket_create(table->bind_bucket_cachep, | ||
129 | sock_net(sk), head, port); | ||
130 | if (!tb) { | ||
131 | spin_unlock(&head->lock); | ||
132 | return -ENOMEM; | ||
133 | } | ||
134 | } | ||
135 | } | ||
114 | sk_add_bind_node(child, &tb->owners); | 136 | sk_add_bind_node(child, &tb->owners); |
115 | inet_csk(child)->icsk_bind_hash = tb; | 137 | inet_csk(child)->icsk_bind_hash = tb; |
116 | spin_unlock(&head->lock); | 138 | spin_unlock(&head->lock); |
139 | |||
140 | return 0; | ||
117 | } | 141 | } |
118 | EXPORT_SYMBOL_GPL(__inet_inherit_port); | 142 | EXPORT_SYMBOL_GPL(__inet_inherit_port); |
119 | 143 | ||
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a0232f3a358b..8f8527d41682 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -1422,7 +1422,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
1422 | 1422 | ||
1423 | newsk = tcp_create_openreq_child(sk, req, skb); | 1423 | newsk = tcp_create_openreq_child(sk, req, skb); |
1424 | if (!newsk) | 1424 | if (!newsk) |
1425 | goto exit; | 1425 | goto exit_nonewsk; |
1426 | 1426 | ||
1427 | newsk->sk_gso_type = SKB_GSO_TCPV4; | 1427 | newsk->sk_gso_type = SKB_GSO_TCPV4; |
1428 | sk_setup_caps(newsk, dst); | 1428 | sk_setup_caps(newsk, dst); |
@@ -1469,16 +1469,20 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
1469 | } | 1469 | } |
1470 | #endif | 1470 | #endif |
1471 | 1471 | ||
1472 | if (__inet_inherit_port(sk, newsk) < 0) { | ||
1473 | sock_put(newsk); | ||
1474 | goto exit; | ||
1475 | } | ||
1472 | __inet_hash_nolisten(newsk, NULL); | 1476 | __inet_hash_nolisten(newsk, NULL); |
1473 | __inet_inherit_port(sk, newsk); | ||
1474 | 1477 | ||
1475 | return newsk; | 1478 | return newsk; |
1476 | 1479 | ||
1477 | exit_overflow: | 1480 | exit_overflow: |
1478 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); | 1481 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); |
1482 | exit_nonewsk: | ||
1483 | dst_release(dst); | ||
1479 | exit: | 1484 | exit: |
1480 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); | 1485 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); |
1481 | dst_release(dst); | ||
1482 | return NULL; | 1486 | return NULL; |
1483 | } | 1487 | } |
1484 | EXPORT_SYMBOL(tcp_v4_syn_recv_sock); | 1488 | EXPORT_SYMBOL(tcp_v4_syn_recv_sock); |
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 56b9bf2516f4..4869797c1afa 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c | |||
@@ -343,7 +343,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
343 | */ | 343 | */ |
344 | v4addr = LOOPBACK4_IPV6; | 344 | v4addr = LOOPBACK4_IPV6; |
345 | if (!(addr_type & IPV6_ADDR_MULTICAST)) { | 345 | if (!(addr_type & IPV6_ADDR_MULTICAST)) { |
346 | if (!ipv6_chk_addr(net, &addr->sin6_addr, | 346 | if (!inet->transparent && |
347 | !ipv6_chk_addr(net, &addr->sin6_addr, | ||
347 | dev, 0)) { | 348 | dev, 0)) { |
348 | err = -EADDRNOTAVAIL; | 349 | err = -EADDRNOTAVAIL; |
349 | goto out_unlock; | 350 | goto out_unlock; |
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index ef371aa01ac5..320bdb877eed 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c | |||
@@ -577,6 +577,25 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) | |||
577 | u8 *ptr = nh + opt->dst1; | 577 | u8 *ptr = nh + opt->dst1; |
578 | put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr); | 578 | put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr); |
579 | } | 579 | } |
580 | if (np->rxopt.bits.rxorigdstaddr) { | ||
581 | struct sockaddr_in6 sin6; | ||
582 | u16 *ports = (u16 *) skb_transport_header(skb); | ||
583 | |||
584 | if (skb_transport_offset(skb) + 4 <= skb->len) { | ||
585 | /* All current transport protocols have the port numbers in the | ||
586 | * first four bytes of the transport header and this function is | ||
587 | * written with this assumption in mind. | ||
588 | */ | ||
589 | |||
590 | sin6.sin6_family = AF_INET6; | ||
591 | ipv6_addr_copy(&sin6.sin6_addr, &ipv6_hdr(skb)->daddr); | ||
592 | sin6.sin6_port = ports[1]; | ||
593 | sin6.sin6_flowinfo = 0; | ||
594 | sin6.sin6_scope_id = 0; | ||
595 | |||
596 | put_cmsg(msg, SOL_IPV6, IPV6_ORIGDSTADDR, sizeof(sin6), &sin6); | ||
597 | } | ||
598 | } | ||
580 | return 0; | 599 | return 0; |
581 | } | 600 | } |
582 | 601 | ||
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index a7f66bc8f0b0..0553867a317f 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -342,6 +342,21 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
342 | retv = 0; | 342 | retv = 0; |
343 | break; | 343 | break; |
344 | 344 | ||
345 | case IPV6_TRANSPARENT: | ||
346 | if (optlen < sizeof(int)) | ||
347 | goto e_inval; | ||
348 | /* we don't have a separate transparent bit for IPV6 we use the one in the IPv4 socket */ | ||
349 | inet_sk(sk)->transparent = valbool; | ||
350 | retv = 0; | ||
351 | break; | ||
352 | |||
353 | case IPV6_RECVORIGDSTADDR: | ||
354 | if (optlen < sizeof(int)) | ||
355 | goto e_inval; | ||
356 | np->rxopt.bits.rxorigdstaddr = valbool; | ||
357 | retv = 0; | ||
358 | break; | ||
359 | |||
345 | case IPV6_HOPOPTS: | 360 | case IPV6_HOPOPTS: |
346 | case IPV6_RTHDRDSTOPTS: | 361 | case IPV6_RTHDRDSTOPTS: |
347 | case IPV6_RTHDR: | 362 | case IPV6_RTHDR: |
@@ -1104,6 +1119,14 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1104 | break; | 1119 | break; |
1105 | } | 1120 | } |
1106 | 1121 | ||
1122 | case IPV6_TRANSPARENT: | ||
1123 | val = inet_sk(sk)->transparent; | ||
1124 | break; | ||
1125 | |||
1126 | case IPV6_RECVORIGDSTADDR: | ||
1127 | val = np->rxopt.bits.rxorigdstaddr; | ||
1128 | break; | ||
1129 | |||
1107 | case IPV6_UNICAST_HOPS: | 1130 | case IPV6_UNICAST_HOPS: |
1108 | case IPV6_MULTICAST_HOPS: | 1131 | case IPV6_MULTICAST_HOPS: |
1109 | { | 1132 | { |
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index aafbba30c899..3f8e4a3d83ce 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
@@ -11,10 +11,11 @@ obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o | |||
11 | obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o | 11 | obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o |
12 | 12 | ||
13 | # objects for l3 independent conntrack | 13 | # objects for l3 independent conntrack |
14 | nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o | 14 | nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o |
15 | nf_defrag_ipv6-objs := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o | ||
15 | 16 | ||
16 | # l3 independent conntrack | 17 | # l3 independent conntrack |
17 | obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o | 18 | obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o nf_defrag_ipv6.o |
18 | 19 | ||
19 | # matches | 20 | # matches |
20 | obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o | 21 | obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o |
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index ff43461704be..c8af58b22562 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/skbuff.h> | 17 | #include <linux/skbuff.h> |
18 | #include <linux/icmp.h> | 18 | #include <linux/icmp.h> |
19 | #include <linux/sysctl.h> | ||
20 | #include <net/ipv6.h> | 19 | #include <net/ipv6.h> |
21 | #include <net/inet_frag.h> | 20 | #include <net/inet_frag.h> |
22 | 21 | ||
@@ -29,6 +28,7 @@ | |||
29 | #include <net/netfilter/nf_conntrack_core.h> | 28 | #include <net/netfilter/nf_conntrack_core.h> |
30 | #include <net/netfilter/nf_conntrack_zones.h> | 29 | #include <net/netfilter/nf_conntrack_zones.h> |
31 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> | 30 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> |
31 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | ||
32 | #include <net/netfilter/nf_log.h> | 32 | #include <net/netfilter/nf_log.h> |
33 | 33 | ||
34 | static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, | 34 | static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, |
@@ -189,53 +189,6 @@ out: | |||
189 | return nf_conntrack_confirm(skb); | 189 | return nf_conntrack_confirm(skb); |
190 | } | 190 | } |
191 | 191 | ||
192 | static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, | ||
193 | struct sk_buff *skb) | ||
194 | { | ||
195 | u16 zone = NF_CT_DEFAULT_ZONE; | ||
196 | |||
197 | if (skb->nfct) | ||
198 | zone = nf_ct_zone((struct nf_conn *)skb->nfct); | ||
199 | |||
200 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
201 | if (skb->nf_bridge && | ||
202 | skb->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING) | ||
203 | return IP6_DEFRAG_CONNTRACK_BRIDGE_IN + zone; | ||
204 | #endif | ||
205 | if (hooknum == NF_INET_PRE_ROUTING) | ||
206 | return IP6_DEFRAG_CONNTRACK_IN + zone; | ||
207 | else | ||
208 | return IP6_DEFRAG_CONNTRACK_OUT + zone; | ||
209 | |||
210 | } | ||
211 | |||
212 | static unsigned int ipv6_defrag(unsigned int hooknum, | ||
213 | struct sk_buff *skb, | ||
214 | const struct net_device *in, | ||
215 | const struct net_device *out, | ||
216 | int (*okfn)(struct sk_buff *)) | ||
217 | { | ||
218 | struct sk_buff *reasm; | ||
219 | |||
220 | /* Previously seen (loopback)? */ | ||
221 | if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct)) | ||
222 | return NF_ACCEPT; | ||
223 | |||
224 | reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb)); | ||
225 | /* queued */ | ||
226 | if (reasm == NULL) | ||
227 | return NF_STOLEN; | ||
228 | |||
229 | /* error occured or not fragmented */ | ||
230 | if (reasm == skb) | ||
231 | return NF_ACCEPT; | ||
232 | |||
233 | nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in, | ||
234 | (struct net_device *)out, okfn); | ||
235 | |||
236 | return NF_STOLEN; | ||
237 | } | ||
238 | |||
239 | static unsigned int __ipv6_conntrack_in(struct net *net, | 192 | static unsigned int __ipv6_conntrack_in(struct net *net, |
240 | unsigned int hooknum, | 193 | unsigned int hooknum, |
241 | struct sk_buff *skb, | 194 | struct sk_buff *skb, |
@@ -288,13 +241,6 @@ static unsigned int ipv6_conntrack_local(unsigned int hooknum, | |||
288 | 241 | ||
289 | static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { | 242 | static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { |
290 | { | 243 | { |
291 | .hook = ipv6_defrag, | ||
292 | .owner = THIS_MODULE, | ||
293 | .pf = NFPROTO_IPV6, | ||
294 | .hooknum = NF_INET_PRE_ROUTING, | ||
295 | .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, | ||
296 | }, | ||
297 | { | ||
298 | .hook = ipv6_conntrack_in, | 244 | .hook = ipv6_conntrack_in, |
299 | .owner = THIS_MODULE, | 245 | .owner = THIS_MODULE, |
300 | .pf = NFPROTO_IPV6, | 246 | .pf = NFPROTO_IPV6, |
@@ -309,13 +255,6 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { | |||
309 | .priority = NF_IP6_PRI_CONNTRACK, | 255 | .priority = NF_IP6_PRI_CONNTRACK, |
310 | }, | 256 | }, |
311 | { | 257 | { |
312 | .hook = ipv6_defrag, | ||
313 | .owner = THIS_MODULE, | ||
314 | .pf = NFPROTO_IPV6, | ||
315 | .hooknum = NF_INET_LOCAL_OUT, | ||
316 | .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, | ||
317 | }, | ||
318 | { | ||
319 | .hook = ipv6_confirm, | 258 | .hook = ipv6_confirm, |
320 | .owner = THIS_MODULE, | 259 | .owner = THIS_MODULE, |
321 | .pf = NFPROTO_IPV6, | 260 | .pf = NFPROTO_IPV6, |
@@ -387,10 +326,6 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { | |||
387 | .nlattr_to_tuple = ipv6_nlattr_to_tuple, | 326 | .nlattr_to_tuple = ipv6_nlattr_to_tuple, |
388 | .nla_policy = ipv6_nla_policy, | 327 | .nla_policy = ipv6_nla_policy, |
389 | #endif | 328 | #endif |
390 | #ifdef CONFIG_SYSCTL | ||
391 | .ctl_table_path = nf_net_netfilter_sysctl_path, | ||
392 | .ctl_table = nf_ct_ipv6_sysctl_table, | ||
393 | #endif | ||
394 | .me = THIS_MODULE, | 329 | .me = THIS_MODULE, |
395 | }; | 330 | }; |
396 | 331 | ||
@@ -403,16 +338,12 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) | |||
403 | int ret = 0; | 338 | int ret = 0; |
404 | 339 | ||
405 | need_conntrack(); | 340 | need_conntrack(); |
341 | nf_defrag_ipv6_enable(); | ||
406 | 342 | ||
407 | ret = nf_ct_frag6_init(); | ||
408 | if (ret < 0) { | ||
409 | pr_err("nf_conntrack_ipv6: can't initialize frag6.\n"); | ||
410 | return ret; | ||
411 | } | ||
412 | ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp6); | 343 | ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp6); |
413 | if (ret < 0) { | 344 | if (ret < 0) { |
414 | pr_err("nf_conntrack_ipv6: can't register tcp.\n"); | 345 | pr_err("nf_conntrack_ipv6: can't register tcp.\n"); |
415 | goto cleanup_frag6; | 346 | return ret; |
416 | } | 347 | } |
417 | 348 | ||
418 | ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp6); | 349 | ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp6); |
@@ -450,8 +381,6 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) | |||
450 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6); | 381 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6); |
451 | cleanup_tcp: | 382 | cleanup_tcp: |
452 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6); | 383 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6); |
453 | cleanup_frag6: | ||
454 | nf_ct_frag6_cleanup(); | ||
455 | return ret; | 384 | return ret; |
456 | } | 385 | } |
457 | 386 | ||
@@ -463,7 +392,6 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) | |||
463 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); | 392 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); |
464 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6); | 393 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6); |
465 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6); | 394 | nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6); |
466 | nf_ct_frag6_cleanup(); | ||
467 | } | 395 | } |
468 | 396 | ||
469 | module_init(nf_conntrack_l3proto_ipv6_init); | 397 | module_init(nf_conntrack_l3proto_ipv6_init); |
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 138a8b362706..489d71b844ac 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c | |||
@@ -73,7 +73,7 @@ static struct inet_frags nf_frags; | |||
73 | static struct netns_frags nf_init_frags; | 73 | static struct netns_frags nf_init_frags; |
74 | 74 | ||
75 | #ifdef CONFIG_SYSCTL | 75 | #ifdef CONFIG_SYSCTL |
76 | struct ctl_table nf_ct_ipv6_sysctl_table[] = { | 76 | struct ctl_table nf_ct_frag6_sysctl_table[] = { |
77 | { | 77 | { |
78 | .procname = "nf_conntrack_frag6_timeout", | 78 | .procname = "nf_conntrack_frag6_timeout", |
79 | .data = &nf_init_frags.timeout, | 79 | .data = &nf_init_frags.timeout, |
@@ -97,6 +97,8 @@ struct ctl_table nf_ct_ipv6_sysctl_table[] = { | |||
97 | }, | 97 | }, |
98 | { } | 98 | { } |
99 | }; | 99 | }; |
100 | |||
101 | static struct ctl_table_header *nf_ct_frag6_sysctl_header; | ||
100 | #endif | 102 | #endif |
101 | 103 | ||
102 | static unsigned int nf_hashfn(struct inet_frag_queue *q) | 104 | static unsigned int nf_hashfn(struct inet_frag_queue *q) |
@@ -623,11 +625,21 @@ int nf_ct_frag6_init(void) | |||
623 | inet_frags_init_net(&nf_init_frags); | 625 | inet_frags_init_net(&nf_init_frags); |
624 | inet_frags_init(&nf_frags); | 626 | inet_frags_init(&nf_frags); |
625 | 627 | ||
628 | nf_ct_frag6_sysctl_header = register_sysctl_paths(nf_net_netfilter_sysctl_path, | ||
629 | nf_ct_frag6_sysctl_table); | ||
630 | if (!nf_ct_frag6_sysctl_header) { | ||
631 | inet_frags_fini(&nf_frags); | ||
632 | return -ENOMEM; | ||
633 | } | ||
634 | |||
626 | return 0; | 635 | return 0; |
627 | } | 636 | } |
628 | 637 | ||
629 | void nf_ct_frag6_cleanup(void) | 638 | void nf_ct_frag6_cleanup(void) |
630 | { | 639 | { |
640 | unregister_sysctl_table(nf_ct_frag6_sysctl_header); | ||
641 | nf_ct_frag6_sysctl_header = NULL; | ||
642 | |||
631 | inet_frags_fini(&nf_frags); | 643 | inet_frags_fini(&nf_frags); |
632 | 644 | ||
633 | nf_init_frags.low_thresh = 0; | 645 | nf_init_frags.low_thresh = 0; |
diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c new file mode 100644 index 000000000000..99abfb53bab9 --- /dev/null +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* (C) 1999-2001 Paul `Rusty' Russell | ||
2 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/ipv6.h> | ||
11 | #include <linux/in6.h> | ||
12 | #include <linux/netfilter.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/icmp.h> | ||
16 | #include <linux/sysctl.h> | ||
17 | #include <net/ipv6.h> | ||
18 | #include <net/inet_frag.h> | ||
19 | |||
20 | #include <linux/netfilter_ipv6.h> | ||
21 | #include <linux/netfilter_bridge.h> | ||
22 | #include <net/netfilter/nf_conntrack.h> | ||
23 | #include <net/netfilter/nf_conntrack_helper.h> | ||
24 | #include <net/netfilter/nf_conntrack_l4proto.h> | ||
25 | #include <net/netfilter/nf_conntrack_l3proto.h> | ||
26 | #include <net/netfilter/nf_conntrack_core.h> | ||
27 | #include <net/netfilter/nf_conntrack_zones.h> | ||
28 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> | ||
29 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | ||
30 | |||
31 | static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, | ||
32 | struct sk_buff *skb) | ||
33 | { | ||
34 | u16 zone = NF_CT_DEFAULT_ZONE; | ||
35 | |||
36 | if (skb->nfct) | ||
37 | zone = nf_ct_zone((struct nf_conn *)skb->nfct); | ||
38 | |||
39 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
40 | if (skb->nf_bridge && | ||
41 | skb->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING) | ||
42 | return IP6_DEFRAG_CONNTRACK_BRIDGE_IN + zone; | ||
43 | #endif | ||
44 | if (hooknum == NF_INET_PRE_ROUTING) | ||
45 | return IP6_DEFRAG_CONNTRACK_IN + zone; | ||
46 | else | ||
47 | return IP6_DEFRAG_CONNTRACK_OUT + zone; | ||
48 | |||
49 | } | ||
50 | |||
51 | static unsigned int ipv6_defrag(unsigned int hooknum, | ||
52 | struct sk_buff *skb, | ||
53 | const struct net_device *in, | ||
54 | const struct net_device *out, | ||
55 | int (*okfn)(struct sk_buff *)) | ||
56 | { | ||
57 | struct sk_buff *reasm; | ||
58 | |||
59 | /* Previously seen (loopback)? */ | ||
60 | if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct)) | ||
61 | return NF_ACCEPT; | ||
62 | |||
63 | reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb)); | ||
64 | /* queued */ | ||
65 | if (reasm == NULL) | ||
66 | return NF_STOLEN; | ||
67 | |||
68 | /* error occured or not fragmented */ | ||
69 | if (reasm == skb) | ||
70 | return NF_ACCEPT; | ||
71 | |||
72 | nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in, | ||
73 | (struct net_device *)out, okfn); | ||
74 | |||
75 | return NF_STOLEN; | ||
76 | } | ||
77 | |||
78 | static struct nf_hook_ops ipv6_defrag_ops[] = { | ||
79 | { | ||
80 | .hook = ipv6_defrag, | ||
81 | .owner = THIS_MODULE, | ||
82 | .pf = NFPROTO_IPV6, | ||
83 | .hooknum = NF_INET_PRE_ROUTING, | ||
84 | .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, | ||
85 | }, | ||
86 | { | ||
87 | .hook = ipv6_defrag, | ||
88 | .owner = THIS_MODULE, | ||
89 | .pf = NFPROTO_IPV6, | ||
90 | .hooknum = NF_INET_LOCAL_OUT, | ||
91 | .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, | ||
92 | }, | ||
93 | }; | ||
94 | |||
95 | static int __init nf_defrag_init(void) | ||
96 | { | ||
97 | int ret = 0; | ||
98 | |||
99 | ret = nf_ct_frag6_init(); | ||
100 | if (ret < 0) { | ||
101 | pr_err("nf_defrag_ipv6: can't initialize frag6.\n"); | ||
102 | return ret; | ||
103 | } | ||
104 | ret = nf_register_hooks(ipv6_defrag_ops, ARRAY_SIZE(ipv6_defrag_ops)); | ||
105 | if (ret < 0) { | ||
106 | pr_err("nf_defrag_ipv6: can't register hooks\n"); | ||
107 | goto cleanup_frag6; | ||
108 | } | ||
109 | return ret; | ||
110 | |||
111 | cleanup_frag6: | ||
112 | nf_ct_frag6_cleanup(); | ||
113 | return ret; | ||
114 | |||
115 | } | ||
116 | |||
117 | static void __exit nf_defrag_fini(void) | ||
118 | { | ||
119 | nf_unregister_hooks(ipv6_defrag_ops, ARRAY_SIZE(ipv6_defrag_ops)); | ||
120 | nf_ct_frag6_cleanup(); | ||
121 | } | ||
122 | |||
123 | void nf_defrag_ipv6_enable(void) | ||
124 | { | ||
125 | } | ||
126 | EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable); | ||
127 | |||
128 | module_init(nf_defrag_init); | ||
129 | module_exit(nf_defrag_fini); | ||
130 | |||
131 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index fe6d40418c0b..ba5258ef1c57 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -1409,7 +1409,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
1409 | 1409 | ||
1410 | newsk = tcp_create_openreq_child(sk, req, skb); | 1410 | newsk = tcp_create_openreq_child(sk, req, skb); |
1411 | if (newsk == NULL) | 1411 | if (newsk == NULL) |
1412 | goto out; | 1412 | goto out_nonewsk; |
1413 | 1413 | ||
1414 | /* | 1414 | /* |
1415 | * No need to charge this sock to the relevant IPv6 refcnt debug socks | 1415 | * No need to charge this sock to the relevant IPv6 refcnt debug socks |
@@ -1497,18 +1497,22 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, | |||
1497 | } | 1497 | } |
1498 | #endif | 1498 | #endif |
1499 | 1499 | ||
1500 | if (__inet_inherit_port(sk, newsk) < 0) { | ||
1501 | sock_put(newsk); | ||
1502 | goto out; | ||
1503 | } | ||
1500 | __inet6_hash(newsk, NULL); | 1504 | __inet6_hash(newsk, NULL); |
1501 | __inet_inherit_port(sk, newsk); | ||
1502 | 1505 | ||
1503 | return newsk; | 1506 | return newsk; |
1504 | 1507 | ||
1505 | out_overflow: | 1508 | out_overflow: |
1506 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); | 1509 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); |
1507 | out: | 1510 | out_nonewsk: |
1508 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); | ||
1509 | if (opt && opt != np->opt) | 1511 | if (opt && opt != np->opt) |
1510 | sock_kfree_s(sk, opt, opt->tot_len); | 1512 | sock_kfree_s(sk, opt, opt->tot_len); |
1511 | dst_release(dst); | 1513 | dst_release(dst); |
1514 | out: | ||
1515 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); | ||
1512 | return NULL; | 1516 | return NULL; |
1513 | } | 1517 | } |
1514 | 1518 | ||
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 5acb3560ff15..c84dad432114 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -122,8 +122,8 @@ static void udp_v6_rehash(struct sock *sk) | |||
122 | 122 | ||
123 | static inline int compute_score(struct sock *sk, struct net *net, | 123 | static inline int compute_score(struct sock *sk, struct net *net, |
124 | unsigned short hnum, | 124 | unsigned short hnum, |
125 | struct in6_addr *saddr, __be16 sport, | 125 | const struct in6_addr *saddr, __be16 sport, |
126 | struct in6_addr *daddr, __be16 dport, | 126 | const struct in6_addr *daddr, __be16 dport, |
127 | int dif) | 127 | int dif) |
128 | { | 128 | { |
129 | int score = -1; | 129 | int score = -1; |
@@ -239,8 +239,8 @@ exact_match: | |||
239 | } | 239 | } |
240 | 240 | ||
241 | static struct sock *__udp6_lib_lookup(struct net *net, | 241 | static struct sock *__udp6_lib_lookup(struct net *net, |
242 | struct in6_addr *saddr, __be16 sport, | 242 | const struct in6_addr *saddr, __be16 sport, |
243 | struct in6_addr *daddr, __be16 dport, | 243 | const struct in6_addr *daddr, __be16 dport, |
244 | int dif, struct udp_table *udptable) | 244 | int dif, struct udp_table *udptable) |
245 | { | 245 | { |
246 | struct sock *sk, *result; | 246 | struct sock *sk, *result; |
@@ -320,6 +320,14 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, | |||
320 | udptable); | 320 | udptable); |
321 | } | 321 | } |
322 | 322 | ||
323 | struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport, | ||
324 | const struct in6_addr *daddr, __be16 dport, int dif) | ||
325 | { | ||
326 | return __udp6_lib_lookup(net, saddr, sport, daddr, dport, dif, &udp_table); | ||
327 | } | ||
328 | EXPORT_SYMBOL_GPL(udp6_lib_lookup); | ||
329 | |||
330 | |||
323 | /* | 331 | /* |
324 | * This should be easy, if there is something there we | 332 | * This should be easy, if there is something there we |
325 | * return it, otherwise we block. | 333 | * return it, otherwise we block. |
diff --git a/net/netfilter/nf_tproxy_core.c b/net/netfilter/nf_tproxy_core.c index 5490fc37c92d..db655638d76d 100644 --- a/net/netfilter/nf_tproxy_core.c +++ b/net/netfilter/nf_tproxy_core.c | |||
@@ -18,41 +18,6 @@ | |||
18 | #include <net/udp.h> | 18 | #include <net/udp.h> |
19 | #include <net/netfilter/nf_tproxy_core.h> | 19 | #include <net/netfilter/nf_tproxy_core.h> |
20 | 20 | ||
21 | struct sock * | ||
22 | nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, | ||
23 | const __be32 saddr, const __be32 daddr, | ||
24 | const __be16 sport, const __be16 dport, | ||
25 | const struct net_device *in, bool listening_only) | ||
26 | { | ||
27 | struct sock *sk; | ||
28 | |||
29 | /* look up socket */ | ||
30 | switch (protocol) { | ||
31 | case IPPROTO_TCP: | ||
32 | if (listening_only) | ||
33 | sk = __inet_lookup_listener(net, &tcp_hashinfo, | ||
34 | daddr, ntohs(dport), | ||
35 | in->ifindex); | ||
36 | else | ||
37 | sk = __inet_lookup(net, &tcp_hashinfo, | ||
38 | saddr, sport, daddr, dport, | ||
39 | in->ifindex); | ||
40 | break; | ||
41 | case IPPROTO_UDP: | ||
42 | sk = udp4_lib_lookup(net, saddr, sport, daddr, dport, | ||
43 | in->ifindex); | ||
44 | break; | ||
45 | default: | ||
46 | WARN_ON(1); | ||
47 | sk = NULL; | ||
48 | } | ||
49 | |||
50 | pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, listener only: %d, sock %p\n", | ||
51 | protocol, ntohl(saddr), ntohs(sport), ntohl(daddr), ntohs(dport), listening_only, sk); | ||
52 | |||
53 | return sk; | ||
54 | } | ||
55 | EXPORT_SYMBOL_GPL(nf_tproxy_get_sock_v4); | ||
56 | 21 | ||
57 | static void | 22 | static void |
58 | nf_tproxy_destructor(struct sk_buff *skb) | 23 | nf_tproxy_destructor(struct sk_buff *skb) |
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 21bb2aff6b8f..19c482caf30b 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Transparent proxy support for Linux/iptables | 2 | * Transparent proxy support for Linux/iptables |
3 | * | 3 | * |
4 | * Copyright (c) 2006-2007 BalaBit IT Ltd. | 4 | * Copyright (c) 2006-2010 BalaBit IT Ltd. |
5 | * Author: Balazs Scheidler, Krisztian Kovacs | 5 | * Author: Balazs Scheidler, Krisztian Kovacs |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
@@ -16,19 +16,96 @@ | |||
16 | #include <net/checksum.h> | 16 | #include <net/checksum.h> |
17 | #include <net/udp.h> | 17 | #include <net/udp.h> |
18 | #include <net/inet_sock.h> | 18 | #include <net/inet_sock.h> |
19 | 19 | #include <linux/inetdevice.h> | |
20 | #include <linux/netfilter/x_tables.h> | 20 | #include <linux/netfilter/x_tables.h> |
21 | #include <linux/netfilter_ipv4/ip_tables.h> | 21 | #include <linux/netfilter_ipv4/ip_tables.h> |
22 | #include <linux/netfilter/xt_TPROXY.h> | ||
23 | 22 | ||
24 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | 23 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> |
24 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
25 | #include <net/if_inet6.h> | ||
26 | #include <net/addrconf.h> | ||
27 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
28 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | ||
29 | #endif | ||
30 | |||
25 | #include <net/netfilter/nf_tproxy_core.h> | 31 | #include <net/netfilter/nf_tproxy_core.h> |
32 | #include <linux/netfilter/xt_TPROXY.h> | ||
33 | |||
34 | static inline __be32 | ||
35 | tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) | ||
36 | { | ||
37 | struct in_device *indev; | ||
38 | __be32 laddr; | ||
39 | |||
40 | if (user_laddr) | ||
41 | return user_laddr; | ||
42 | |||
43 | laddr = 0; | ||
44 | rcu_read_lock(); | ||
45 | indev = __in_dev_get_rcu(skb->dev); | ||
46 | for_primary_ifa(indev) { | ||
47 | laddr = ifa->ifa_local; | ||
48 | break; | ||
49 | } endfor_ifa(indev); | ||
50 | rcu_read_unlock(); | ||
51 | |||
52 | return laddr ? laddr : daddr; | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * tproxy_handle_time_wait4() - handle IPv4 TCP TIME_WAIT reopen redirections | ||
57 | * @skb: The skb being processed. | ||
58 | * @laddr: IPv4 address to redirect to or zero. | ||
59 | * @lport: TCP port to redirect to or zero. | ||
60 | * @sk: The TIME_WAIT TCP socket found by the lookup. | ||
61 | * | ||
62 | * We have to handle SYN packets arriving to TIME_WAIT sockets | ||
63 | * differently: instead of reopening the connection we should rather | ||
64 | * redirect the new connection to the proxy if there's a listener | ||
65 | * socket present. | ||
66 | * | ||
67 | * tproxy_handle_time_wait4() consumes the socket reference passed in. | ||
68 | * | ||
69 | * Returns the listener socket if there's one, the TIME_WAIT socket if | ||
70 | * no such listener is found, or NULL if the TCP header is incomplete. | ||
71 | */ | ||
72 | static struct sock * | ||
73 | tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport, | ||
74 | struct sock *sk) | ||
75 | { | ||
76 | const struct iphdr *iph = ip_hdr(skb); | ||
77 | struct tcphdr _hdr, *hp; | ||
78 | |||
79 | hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); | ||
80 | if (hp == NULL) { | ||
81 | inet_twsk_put(inet_twsk(sk)); | ||
82 | return NULL; | ||
83 | } | ||
84 | |||
85 | if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { | ||
86 | /* SYN to a TIME_WAIT socket, we'd rather redirect it | ||
87 | * to a listener socket if there's one */ | ||
88 | struct sock *sk2; | ||
89 | |||
90 | sk2 = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, | ||
91 | iph->saddr, laddr ? laddr : iph->daddr, | ||
92 | hp->source, lport ? lport : hp->dest, | ||
93 | skb->dev, NFT_LOOKUP_LISTENER); | ||
94 | if (sk2) { | ||
95 | inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); | ||
96 | inet_twsk_put(inet_twsk(sk)); | ||
97 | sk = sk2; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | return sk; | ||
102 | } | ||
26 | 103 | ||
27 | static unsigned int | 104 | static unsigned int |
28 | tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) | 105 | tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, |
106 | u_int32_t mark_mask, u_int32_t mark_value) | ||
29 | { | 107 | { |
30 | const struct iphdr *iph = ip_hdr(skb); | 108 | const struct iphdr *iph = ip_hdr(skb); |
31 | const struct xt_tproxy_target_info *tgi = par->targinfo; | ||
32 | struct udphdr _hdr, *hp; | 109 | struct udphdr _hdr, *hp; |
33 | struct sock *sk; | 110 | struct sock *sk; |
34 | 111 | ||
@@ -36,12 +113,195 @@ tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) | |||
36 | if (hp == NULL) | 113 | if (hp == NULL) |
37 | return NF_DROP; | 114 | return NF_DROP; |
38 | 115 | ||
116 | /* check if there's an ongoing connection on the packet | ||
117 | * addresses, this happens if the redirect already happened | ||
118 | * and the current packet belongs to an already established | ||
119 | * connection */ | ||
39 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, | 120 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, |
40 | iph->saddr, | 121 | iph->saddr, iph->daddr, |
41 | tgi->laddr ? tgi->laddr : iph->daddr, | 122 | hp->source, hp->dest, |
42 | hp->source, | 123 | skb->dev, NFT_LOOKUP_ESTABLISHED); |
43 | tgi->lport ? tgi->lport : hp->dest, | 124 | |
44 | par->in, true); | 125 | laddr = tproxy_laddr4(skb, laddr, iph->daddr); |
126 | if (!lport) | ||
127 | lport = hp->dest; | ||
128 | |||
129 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ | ||
130 | if (sk && sk->sk_state == TCP_TIME_WAIT) | ||
131 | /* reopening a TIME_WAIT connection needs special handling */ | ||
132 | sk = tproxy_handle_time_wait4(skb, laddr, lport, sk); | ||
133 | else if (!sk) | ||
134 | /* no, there's no established connection, check if | ||
135 | * there's a listener on the redirected addr/port */ | ||
136 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), iph->protocol, | ||
137 | iph->saddr, laddr, | ||
138 | hp->source, lport, | ||
139 | skb->dev, NFT_LOOKUP_LISTENER); | ||
140 | |||
141 | /* NOTE: assign_sock consumes our sk reference */ | ||
142 | if (sk && nf_tproxy_assign_sock(skb, sk)) { | ||
143 | /* This should be in a separate target, but we don't do multiple | ||
144 | targets on the same rule yet */ | ||
145 | skb->mark = (skb->mark & ~mark_mask) ^ mark_value; | ||
146 | |||
147 | pr_debug("redirecting: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n", | ||
148 | iph->protocol, &iph->daddr, ntohs(hp->dest), | ||
149 | &laddr, ntohs(lport), skb->mark); | ||
150 | return NF_ACCEPT; | ||
151 | } | ||
152 | |||
153 | pr_debug("no socket, dropping: proto %hhu %pI4:%hu -> %pI4:%hu, mark: %x\n", | ||
154 | iph->protocol, &iph->saddr, ntohs(hp->source), | ||
155 | &iph->daddr, ntohs(hp->dest), skb->mark); | ||
156 | return NF_DROP; | ||
157 | } | ||
158 | |||
159 | static unsigned int | ||
160 | tproxy_tg4_v0(struct sk_buff *skb, const struct xt_action_param *par) | ||
161 | { | ||
162 | const struct xt_tproxy_target_info *tgi = par->targinfo; | ||
163 | |||
164 | return tproxy_tg4(skb, tgi->laddr, tgi->lport, tgi->mark_mask, tgi->mark_value); | ||
165 | } | ||
166 | |||
167 | static unsigned int | ||
168 | tproxy_tg4_v1(struct sk_buff *skb, const struct xt_action_param *par) | ||
169 | { | ||
170 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | ||
171 | |||
172 | return tproxy_tg4(skb, tgi->laddr.ip, tgi->lport, tgi->mark_mask, tgi->mark_value); | ||
173 | } | ||
174 | |||
175 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
176 | |||
177 | static inline const struct in6_addr * | ||
178 | tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr, | ||
179 | const struct in6_addr *daddr) | ||
180 | { | ||
181 | struct inet6_dev *indev; | ||
182 | struct inet6_ifaddr *ifa; | ||
183 | struct in6_addr *laddr; | ||
184 | |||
185 | if (!ipv6_addr_any(user_laddr)) | ||
186 | return user_laddr; | ||
187 | laddr = NULL; | ||
188 | |||
189 | rcu_read_lock(); | ||
190 | indev = __in6_dev_get(skb->dev); | ||
191 | if (indev) | ||
192 | list_for_each_entry(ifa, &indev->addr_list, if_list) { | ||
193 | if (ifa->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)) | ||
194 | continue; | ||
195 | |||
196 | laddr = &ifa->addr; | ||
197 | break; | ||
198 | } | ||
199 | rcu_read_unlock(); | ||
200 | |||
201 | return laddr ? laddr : daddr; | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * tproxy_handle_time_wait6() - handle IPv6 TCP TIME_WAIT reopen redirections | ||
206 | * @skb: The skb being processed. | ||
207 | * @tproto: Transport protocol. | ||
208 | * @thoff: Transport protocol header offset. | ||
209 | * @par: Iptables target parameters. | ||
210 | * @sk: The TIME_WAIT TCP socket found by the lookup. | ||
211 | * | ||
212 | * We have to handle SYN packets arriving to TIME_WAIT sockets | ||
213 | * differently: instead of reopening the connection we should rather | ||
214 | * redirect the new connection to the proxy if there's a listener | ||
215 | * socket present. | ||
216 | * | ||
217 | * tproxy_handle_time_wait6() consumes the socket reference passed in. | ||
218 | * | ||
219 | * Returns the listener socket if there's one, the TIME_WAIT socket if | ||
220 | * no such listener is found, or NULL if the TCP header is incomplete. | ||
221 | */ | ||
222 | static struct sock * | ||
223 | tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff, | ||
224 | const struct xt_action_param *par, | ||
225 | struct sock *sk) | ||
226 | { | ||
227 | const struct ipv6hdr *iph = ipv6_hdr(skb); | ||
228 | struct tcphdr _hdr, *hp; | ||
229 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | ||
230 | |||
231 | hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); | ||
232 | if (hp == NULL) { | ||
233 | inet_twsk_put(inet_twsk(sk)); | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | if (hp->syn && !hp->rst && !hp->ack && !hp->fin) { | ||
238 | /* SYN to a TIME_WAIT socket, we'd rather redirect it | ||
239 | * to a listener socket if there's one */ | ||
240 | struct sock *sk2; | ||
241 | |||
242 | sk2 = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | ||
243 | &iph->saddr, | ||
244 | tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr), | ||
245 | hp->source, | ||
246 | tgi->lport ? tgi->lport : hp->dest, | ||
247 | skb->dev, NFT_LOOKUP_LISTENER); | ||
248 | if (sk2) { | ||
249 | inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row); | ||
250 | inet_twsk_put(inet_twsk(sk)); | ||
251 | sk = sk2; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | return sk; | ||
256 | } | ||
257 | |||
258 | static unsigned int | ||
259 | tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | ||
260 | { | ||
261 | const struct ipv6hdr *iph = ipv6_hdr(skb); | ||
262 | const struct xt_tproxy_target_info_v1 *tgi = par->targinfo; | ||
263 | struct udphdr _hdr, *hp; | ||
264 | struct sock *sk; | ||
265 | const struct in6_addr *laddr; | ||
266 | __be16 lport; | ||
267 | int thoff; | ||
268 | int tproto; | ||
269 | |||
270 | tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); | ||
271 | if (tproto < 0) { | ||
272 | pr_debug("unable to find transport header in IPv6 packet, dropping\n"); | ||
273 | return NF_DROP; | ||
274 | } | ||
275 | |||
276 | hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); | ||
277 | if (hp == NULL) { | ||
278 | pr_debug("unable to grab transport header contents in IPv6 packet, dropping\n"); | ||
279 | return NF_DROP; | ||
280 | } | ||
281 | |||
282 | /* check if there's an ongoing connection on the packet | ||
283 | * addresses, this happens if the redirect already happened | ||
284 | * and the current packet belongs to an already established | ||
285 | * connection */ | ||
286 | sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | ||
287 | &iph->saddr, &iph->daddr, | ||
288 | hp->source, hp->dest, | ||
289 | par->in, NFT_LOOKUP_ESTABLISHED); | ||
290 | |||
291 | laddr = tproxy_laddr6(skb, &tgi->laddr.in6, &iph->daddr); | ||
292 | lport = tgi->lport ? tgi->lport : hp->dest; | ||
293 | |||
294 | /* UDP has no TCP_TIME_WAIT state, so we never enter here */ | ||
295 | if (sk && sk->sk_state == TCP_TIME_WAIT) | ||
296 | /* reopening a TIME_WAIT connection needs special handling */ | ||
297 | sk = tproxy_handle_time_wait6(skb, tproto, thoff, par, sk); | ||
298 | else if (!sk) | ||
299 | /* no there's no established connection, check if | ||
300 | * there's a listener on the redirected addr/port */ | ||
301 | sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | ||
302 | &iph->saddr, laddr, | ||
303 | hp->source, lport, | ||
304 | par->in, NFT_LOOKUP_LISTENER); | ||
45 | 305 | ||
46 | /* NOTE: assign_sock consumes our sk reference */ | 306 | /* NOTE: assign_sock consumes our sk reference */ |
47 | if (sk && nf_tproxy_assign_sock(skb, sk)) { | 307 | if (sk && nf_tproxy_assign_sock(skb, sk)) { |
@@ -49,19 +309,34 @@ tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) | |||
49 | targets on the same rule yet */ | 309 | targets on the same rule yet */ |
50 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; | 310 | skb->mark = (skb->mark & ~tgi->mark_mask) ^ tgi->mark_value; |
51 | 311 | ||
52 | pr_debug("redirecting: proto %u %08x:%u -> %08x:%u, mark: %x\n", | 312 | pr_debug("redirecting: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
53 | iph->protocol, ntohl(iph->daddr), ntohs(hp->dest), | 313 | tproto, &iph->saddr, ntohs(hp->source), |
54 | ntohl(tgi->laddr), ntohs(tgi->lport), skb->mark); | 314 | laddr, ntohs(lport), skb->mark); |
55 | return NF_ACCEPT; | 315 | return NF_ACCEPT; |
56 | } | 316 | } |
57 | 317 | ||
58 | pr_debug("no socket, dropping: proto %u %08x:%u -> %08x:%u, mark: %x\n", | 318 | pr_debug("no socket, dropping: proto %hhu %pI6:%hu -> %pI6:%hu, mark: %x\n", |
59 | iph->protocol, ntohl(iph->daddr), ntohs(hp->dest), | 319 | tproto, &iph->saddr, ntohs(hp->source), |
60 | ntohl(tgi->laddr), ntohs(tgi->lport), skb->mark); | 320 | &iph->daddr, ntohs(hp->dest), skb->mark); |
321 | |||
61 | return NF_DROP; | 322 | return NF_DROP; |
62 | } | 323 | } |
63 | 324 | ||
64 | static int tproxy_tg_check(const struct xt_tgchk_param *par) | 325 | static int tproxy_tg6_check(const struct xt_tgchk_param *par) |
326 | { | ||
327 | const struct ip6t_ip6 *i = par->entryinfo; | ||
328 | |||
329 | if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) | ||
330 | && !(i->flags & IP6T_INV_PROTO)) | ||
331 | return 0; | ||
332 | |||
333 | pr_info("Can be used only in combination with " | ||
334 | "either -p tcp or -p udp\n"); | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | #endif | ||
338 | |||
339 | static int tproxy_tg4_check(const struct xt_tgchk_param *par) | ||
65 | { | 340 | { |
66 | const struct ipt_ip *i = par->entryinfo; | 341 | const struct ipt_ip *i = par->entryinfo; |
67 | 342 | ||
@@ -74,31 +349,64 @@ static int tproxy_tg_check(const struct xt_tgchk_param *par) | |||
74 | return -EINVAL; | 349 | return -EINVAL; |
75 | } | 350 | } |
76 | 351 | ||
77 | static struct xt_target tproxy_tg_reg __read_mostly = { | 352 | static struct xt_target tproxy_tg_reg[] __read_mostly = { |
78 | .name = "TPROXY", | 353 | { |
79 | .family = NFPROTO_IPV4, | 354 | .name = "TPROXY", |
80 | .table = "mangle", | 355 | .family = NFPROTO_IPV4, |
81 | .target = tproxy_tg, | 356 | .table = "mangle", |
82 | .targetsize = sizeof(struct xt_tproxy_target_info), | 357 | .target = tproxy_tg4_v0, |
83 | .checkentry = tproxy_tg_check, | 358 | .revision = 0, |
84 | .hooks = 1 << NF_INET_PRE_ROUTING, | 359 | .targetsize = sizeof(struct xt_tproxy_target_info), |
85 | .me = THIS_MODULE, | 360 | .checkentry = tproxy_tg4_check, |
361 | .hooks = 1 << NF_INET_PRE_ROUTING, | ||
362 | .me = THIS_MODULE, | ||
363 | }, | ||
364 | { | ||
365 | .name = "TPROXY", | ||
366 | .family = NFPROTO_IPV4, | ||
367 | .table = "mangle", | ||
368 | .target = tproxy_tg4_v1, | ||
369 | .revision = 1, | ||
370 | .targetsize = sizeof(struct xt_tproxy_target_info_v1), | ||
371 | .checkentry = tproxy_tg4_check, | ||
372 | .hooks = 1 << NF_INET_PRE_ROUTING, | ||
373 | .me = THIS_MODULE, | ||
374 | }, | ||
375 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
376 | { | ||
377 | .name = "TPROXY", | ||
378 | .family = NFPROTO_IPV6, | ||
379 | .table = "mangle", | ||
380 | .target = tproxy_tg6_v1, | ||
381 | .revision = 1, | ||
382 | .targetsize = sizeof(struct xt_tproxy_target_info_v1), | ||
383 | .checkentry = tproxy_tg6_check, | ||
384 | .hooks = 1 << NF_INET_PRE_ROUTING, | ||
385 | .me = THIS_MODULE, | ||
386 | }, | ||
387 | #endif | ||
388 | |||
86 | }; | 389 | }; |
87 | 390 | ||
88 | static int __init tproxy_tg_init(void) | 391 | static int __init tproxy_tg_init(void) |
89 | { | 392 | { |
90 | nf_defrag_ipv4_enable(); | 393 | nf_defrag_ipv4_enable(); |
91 | return xt_register_target(&tproxy_tg_reg); | 394 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
395 | nf_defrag_ipv6_enable(); | ||
396 | #endif | ||
397 | |||
398 | return xt_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg)); | ||
92 | } | 399 | } |
93 | 400 | ||
94 | static void __exit tproxy_tg_exit(void) | 401 | static void __exit tproxy_tg_exit(void) |
95 | { | 402 | { |
96 | xt_unregister_target(&tproxy_tg_reg); | 403 | xt_unregister_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg)); |
97 | } | 404 | } |
98 | 405 | ||
99 | module_init(tproxy_tg_init); | 406 | module_init(tproxy_tg_init); |
100 | module_exit(tproxy_tg_exit); | 407 | module_exit(tproxy_tg_exit); |
101 | MODULE_LICENSE("GPL"); | 408 | MODULE_LICENSE("GPL"); |
102 | MODULE_AUTHOR("Krisztian Kovacs"); | 409 | MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs"); |
103 | MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module."); | 410 | MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module."); |
104 | MODULE_ALIAS("ipt_TPROXY"); | 411 | MODULE_ALIAS("ipt_TPROXY"); |
412 | MODULE_ALIAS("ip6t_TPROXY"); | ||
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 1ca89908cbad..2dbd4c857735 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/skbuff.h> | 14 | #include <linux/skbuff.h> |
15 | #include <linux/netfilter/x_tables.h> | 15 | #include <linux/netfilter/x_tables.h> |
16 | #include <linux/netfilter_ipv4/ip_tables.h> | 16 | #include <linux/netfilter_ipv4/ip_tables.h> |
17 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
17 | #include <net/tcp.h> | 18 | #include <net/tcp.h> |
18 | #include <net/udp.h> | 19 | #include <net/udp.h> |
19 | #include <net/icmp.h> | 20 | #include <net/icmp.h> |
@@ -21,6 +22,7 @@ | |||
21 | #include <net/inet_sock.h> | 22 | #include <net/inet_sock.h> |
22 | #include <net/netfilter/nf_tproxy_core.h> | 23 | #include <net/netfilter/nf_tproxy_core.h> |
23 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | 24 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> |
25 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | ||
24 | 26 | ||
25 | #include <linux/netfilter/xt_socket.h> | 27 | #include <linux/netfilter/xt_socket.h> |
26 | 28 | ||
@@ -30,7 +32,7 @@ | |||
30 | #endif | 32 | #endif |
31 | 33 | ||
32 | static int | 34 | static int |
33 | extract_icmp_fields(const struct sk_buff *skb, | 35 | extract_icmp4_fields(const struct sk_buff *skb, |
34 | u8 *protocol, | 36 | u8 *protocol, |
35 | __be32 *raddr, | 37 | __be32 *raddr, |
36 | __be32 *laddr, | 38 | __be32 *laddr, |
@@ -86,7 +88,6 @@ extract_icmp_fields(const struct sk_buff *skb, | |||
86 | return 0; | 88 | return 0; |
87 | } | 89 | } |
88 | 90 | ||
89 | |||
90 | static bool | 91 | static bool |
91 | socket_match(const struct sk_buff *skb, struct xt_action_param *par, | 92 | socket_match(const struct sk_buff *skb, struct xt_action_param *par, |
92 | const struct xt_socket_mtinfo1 *info) | 93 | const struct xt_socket_mtinfo1 *info) |
@@ -115,7 +116,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, | |||
115 | dport = hp->dest; | 116 | dport = hp->dest; |
116 | 117 | ||
117 | } else if (iph->protocol == IPPROTO_ICMP) { | 118 | } else if (iph->protocol == IPPROTO_ICMP) { |
118 | if (extract_icmp_fields(skb, &protocol, &saddr, &daddr, | 119 | if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, |
119 | &sport, &dport)) | 120 | &sport, &dport)) |
120 | return false; | 121 | return false; |
121 | } else { | 122 | } else { |
@@ -142,7 +143,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, | |||
142 | #endif | 143 | #endif |
143 | 144 | ||
144 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, | 145 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, |
145 | saddr, daddr, sport, dport, par->in, false); | 146 | saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); |
146 | if (sk != NULL) { | 147 | if (sk != NULL) { |
147 | bool wildcard; | 148 | bool wildcard; |
148 | bool transparent = true; | 149 | bool transparent = true; |
@@ -165,32 +166,157 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, | |||
165 | sk = NULL; | 166 | sk = NULL; |
166 | } | 167 | } |
167 | 168 | ||
168 | pr_debug("proto %u %08x:%u -> %08x:%u (orig %08x:%u) sock %p\n", | 169 | pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", |
169 | protocol, ntohl(saddr), ntohs(sport), | 170 | protocol, &saddr, ntohs(sport), |
170 | ntohl(daddr), ntohs(dport), | 171 | &daddr, ntohs(dport), |
171 | ntohl(iph->daddr), hp ? ntohs(hp->dest) : 0, sk); | 172 | &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); |
172 | 173 | ||
173 | return (sk != NULL); | 174 | return (sk != NULL); |
174 | } | 175 | } |
175 | 176 | ||
176 | static bool | 177 | static bool |
177 | socket_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) | 178 | socket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par) |
178 | { | 179 | { |
179 | return socket_match(skb, par, NULL); | 180 | return socket_match(skb, par, NULL); |
180 | } | 181 | } |
181 | 182 | ||
182 | static bool | 183 | static bool |
183 | socket_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) | 184 | socket_mt4_v1(const struct sk_buff *skb, struct xt_action_param *par) |
184 | { | 185 | { |
185 | return socket_match(skb, par, par->matchinfo); | 186 | return socket_match(skb, par, par->matchinfo); |
186 | } | 187 | } |
187 | 188 | ||
189 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
190 | |||
191 | static int | ||
192 | extract_icmp6_fields(const struct sk_buff *skb, | ||
193 | unsigned int outside_hdrlen, | ||
194 | u8 *protocol, | ||
195 | struct in6_addr **raddr, | ||
196 | struct in6_addr **laddr, | ||
197 | __be16 *rport, | ||
198 | __be16 *lport) | ||
199 | { | ||
200 | struct ipv6hdr *inside_iph, _inside_iph; | ||
201 | struct icmp6hdr *icmph, _icmph; | ||
202 | __be16 *ports, _ports[2]; | ||
203 | u8 inside_nexthdr; | ||
204 | int inside_hdrlen; | ||
205 | |||
206 | icmph = skb_header_pointer(skb, outside_hdrlen, | ||
207 | sizeof(_icmph), &_icmph); | ||
208 | if (icmph == NULL) | ||
209 | return 1; | ||
210 | |||
211 | if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK) | ||
212 | return 1; | ||
213 | |||
214 | inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), sizeof(_inside_iph), &_inside_iph); | ||
215 | if (inside_iph == NULL) | ||
216 | return 1; | ||
217 | inside_nexthdr = inside_iph->nexthdr; | ||
218 | |||
219 | inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph), &inside_nexthdr); | ||
220 | if (inside_hdrlen < 0) | ||
221 | return 1; /* hjm: Packet has no/incomplete transport layer headers. */ | ||
222 | |||
223 | if (inside_nexthdr != IPPROTO_TCP && | ||
224 | inside_nexthdr != IPPROTO_UDP) | ||
225 | return 1; | ||
226 | |||
227 | ports = skb_header_pointer(skb, inside_hdrlen, | ||
228 | sizeof(_ports), &_ports); | ||
229 | if (ports == NULL) | ||
230 | return 1; | ||
231 | |||
232 | /* the inside IP packet is the one quoted from our side, thus | ||
233 | * its saddr is the local address */ | ||
234 | *protocol = inside_nexthdr; | ||
235 | *laddr = &inside_iph->saddr; | ||
236 | *lport = ports[0]; | ||
237 | *raddr = &inside_iph->daddr; | ||
238 | *rport = ports[1]; | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static bool | ||
244 | socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) | ||
245 | { | ||
246 | struct ipv6hdr *iph = ipv6_hdr(skb); | ||
247 | struct udphdr _hdr, *hp = NULL; | ||
248 | struct sock *sk; | ||
249 | struct in6_addr *daddr, *saddr; | ||
250 | __be16 dport, sport; | ||
251 | int thoff; | ||
252 | u8 tproto; | ||
253 | const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; | ||
254 | |||
255 | tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); | ||
256 | if (tproto < 0) { | ||
257 | pr_debug("unable to find transport header in IPv6 packet, dropping\n"); | ||
258 | return NF_DROP; | ||
259 | } | ||
260 | |||
261 | if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) { | ||
262 | hp = skb_header_pointer(skb, thoff, | ||
263 | sizeof(_hdr), &_hdr); | ||
264 | if (hp == NULL) | ||
265 | return false; | ||
266 | |||
267 | saddr = &iph->saddr; | ||
268 | sport = hp->source; | ||
269 | daddr = &iph->daddr; | ||
270 | dport = hp->dest; | ||
271 | |||
272 | } else if (tproto == IPPROTO_ICMPV6) { | ||
273 | if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, | ||
274 | &sport, &dport)) | ||
275 | return false; | ||
276 | } else { | ||
277 | return false; | ||
278 | } | ||
279 | |||
280 | sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, | ||
281 | saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); | ||
282 | if (sk != NULL) { | ||
283 | bool wildcard; | ||
284 | bool transparent = true; | ||
285 | |||
286 | /* Ignore sockets listening on INADDR_ANY */ | ||
287 | wildcard = (sk->sk_state != TCP_TIME_WAIT && | ||
288 | ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)); | ||
289 | |||
290 | /* Ignore non-transparent sockets, | ||
291 | if XT_SOCKET_TRANSPARENT is used */ | ||
292 | if (info && info->flags & XT_SOCKET_TRANSPARENT) | ||
293 | transparent = ((sk->sk_state != TCP_TIME_WAIT && | ||
294 | inet_sk(sk)->transparent) || | ||
295 | (sk->sk_state == TCP_TIME_WAIT && | ||
296 | inet_twsk(sk)->tw_transparent)); | ||
297 | |||
298 | nf_tproxy_put_sock(sk); | ||
299 | |||
300 | if (wildcard || !transparent) | ||
301 | sk = NULL; | ||
302 | } | ||
303 | |||
304 | pr_debug("proto %hhu %pI6:%hu -> %pI6:%hu " | ||
305 | "(orig %pI6:%hu) sock %p\n", | ||
306 | tproto, saddr, ntohs(sport), | ||
307 | daddr, ntohs(dport), | ||
308 | &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); | ||
309 | |||
310 | return (sk != NULL); | ||
311 | } | ||
312 | #endif | ||
313 | |||
188 | static struct xt_match socket_mt_reg[] __read_mostly = { | 314 | static struct xt_match socket_mt_reg[] __read_mostly = { |
189 | { | 315 | { |
190 | .name = "socket", | 316 | .name = "socket", |
191 | .revision = 0, | 317 | .revision = 0, |
192 | .family = NFPROTO_IPV4, | 318 | .family = NFPROTO_IPV4, |
193 | .match = socket_mt_v0, | 319 | .match = socket_mt4_v0, |
194 | .hooks = (1 << NF_INET_PRE_ROUTING) | | 320 | .hooks = (1 << NF_INET_PRE_ROUTING) | |
195 | (1 << NF_INET_LOCAL_IN), | 321 | (1 << NF_INET_LOCAL_IN), |
196 | .me = THIS_MODULE, | 322 | .me = THIS_MODULE, |
@@ -199,17 +325,33 @@ static struct xt_match socket_mt_reg[] __read_mostly = { | |||
199 | .name = "socket", | 325 | .name = "socket", |
200 | .revision = 1, | 326 | .revision = 1, |
201 | .family = NFPROTO_IPV4, | 327 | .family = NFPROTO_IPV4, |
202 | .match = socket_mt_v1, | 328 | .match = socket_mt4_v1, |
203 | .matchsize = sizeof(struct xt_socket_mtinfo1), | 329 | .matchsize = sizeof(struct xt_socket_mtinfo1), |
204 | .hooks = (1 << NF_INET_PRE_ROUTING) | | 330 | .hooks = (1 << NF_INET_PRE_ROUTING) | |
205 | (1 << NF_INET_LOCAL_IN), | 331 | (1 << NF_INET_LOCAL_IN), |
206 | .me = THIS_MODULE, | 332 | .me = THIS_MODULE, |
207 | }, | 333 | }, |
334 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
335 | { | ||
336 | .name = "socket", | ||
337 | .revision = 1, | ||
338 | .family = NFPROTO_IPV6, | ||
339 | .match = socket_mt6_v1, | ||
340 | .matchsize = sizeof(struct xt_socket_mtinfo1), | ||
341 | .hooks = (1 << NF_INET_PRE_ROUTING) | | ||
342 | (1 << NF_INET_LOCAL_IN), | ||
343 | .me = THIS_MODULE, | ||
344 | }, | ||
345 | #endif | ||
208 | }; | 346 | }; |
209 | 347 | ||
210 | static int __init socket_mt_init(void) | 348 | static int __init socket_mt_init(void) |
211 | { | 349 | { |
212 | nf_defrag_ipv4_enable(); | 350 | nf_defrag_ipv4_enable(); |
351 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
352 | nf_defrag_ipv6_enable(); | ||
353 | #endif | ||
354 | |||
213 | return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); | 355 | return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); |
214 | } | 356 | } |
215 | 357 | ||
@@ -225,3 +367,4 @@ MODULE_LICENSE("GPL"); | |||
225 | MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); | 367 | MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); |
226 | MODULE_DESCRIPTION("x_tables socket match module"); | 368 | MODULE_DESCRIPTION("x_tables socket match module"); |
227 | MODULE_ALIAS("ipt_socket"); | 369 | MODULE_ALIAS("ipt_socket"); |
370 | MODULE_ALIAS("ip6t_socket"); | ||