aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/in6.h4
-rw-r--r--include/linux/ipv6.h4
-rw-r--r--include/linux/netfilter/xt_TPROXY.h13
-rw-r--r--include/net/inet_hashtables.h2
-rw-r--r--include/net/netfilter/ipv6/nf_defrag_ipv6.h6
-rw-r--r--include/net/netfilter/nf_tproxy_core.h192
-rw-r--r--include/net/udp.h3
-rw-r--r--net/dccp/ipv4.c10
-rw-r--r--net/dccp/ipv6.c10
-rw-r--r--net/ipv4/inet_hashtables.c28
-rw-r--r--net/ipv4/tcp_ipv4.c10
-rw-r--r--net/ipv6/af_inet6.c3
-rw-r--r--net/ipv6/datagram.c19
-rw-r--r--net/ipv6/ipv6_sockglue.c23
-rw-r--r--net/ipv6/netfilter/Makefile5
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c78
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c14
-rw-r--r--net/ipv6/netfilter/nf_defrag_ipv6_hooks.c131
-rw-r--r--net/ipv6/tcp_ipv6.c12
-rw-r--r--net/ipv6/udp.c16
-rw-r--r--net/netfilter/nf_tproxy_core.c35
-rw-r--r--net/netfilter/xt_TPROXY.c366
-rw-r--r--net/netfilter/xt_socket.c167
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 */ 14struct 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. */
248extern void __inet_inherit_port(struct sock *sk, struct sock *child); 248extern int __inet_inherit_port(struct sock *sk, struct sock *child);
249 249
250extern void inet_put_port(struct sock *sk); 250extern 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
4extern 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 */
12extern 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 */
66static inline struct sock *
13nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, 67nf_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)
135static inline struct sock *
136nf_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
18static inline void 204static inline void
19nf_tproxy_put_sock(struct sock *sk) 205nf_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,
183extern struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, 183extern 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);
186extern 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
417exit_overflow: 420exit_overflow:
418 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); 421 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
422exit_nonewsk:
423 dst_release(dst);
419exit: 424exit:
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
640out_overflow: 643out_overflow:
641 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); 644 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
645out_nonewsk:
646 dst_release(dst);
642out: 647out:
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}
102EXPORT_SYMBOL(inet_put_port); 102EXPORT_SYMBOL(inet_put_port);
103 103
104void __inet_inherit_port(struct sock *sk, struct sock *child) 104int __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}
118EXPORT_SYMBOL_GPL(__inet_inherit_port); 142EXPORT_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
1477exit_overflow: 1480exit_overflow:
1478 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); 1481 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
1482exit_nonewsk:
1483 dst_release(dst);
1479exit: 1484exit:
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}
1484EXPORT_SYMBOL(tcp_v4_syn_recv_sock); 1488EXPORT_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
11obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o 11obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o
12 12
13# objects for l3 independent conntrack 13# objects for l3 independent conntrack
14nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o 14nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o
15nf_defrag_ipv6-objs := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o
15 16
16# l3 independent conntrack 17# l3 independent conntrack
17obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o 18obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o nf_defrag_ipv6.o
18 19
19# matches 20# matches
20obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o 21obj-$(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
34static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, 34static 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
192static 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
212static 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
239static unsigned int __ipv6_conntrack_in(struct net *net, 192static 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
289static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { 242static 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
469module_init(nf_conntrack_l3proto_ipv6_init); 397module_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;
73static struct netns_frags nf_init_frags; 73static struct netns_frags nf_init_frags;
74 74
75#ifdef CONFIG_SYSCTL 75#ifdef CONFIG_SYSCTL
76struct ctl_table nf_ct_ipv6_sysctl_table[] = { 76struct 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
101static struct ctl_table_header *nf_ct_frag6_sysctl_header;
100#endif 102#endif
101 103
102static unsigned int nf_hashfn(struct inet_frag_queue *q) 104static 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
629void nf_ct_frag6_cleanup(void) 638void 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
31static 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
51static 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
78static 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
95static 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
111cleanup_frag6:
112 nf_ct_frag6_cleanup();
113 return ret;
114
115}
116
117static 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
123void nf_defrag_ipv6_enable(void)
124{
125}
126EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable);
127
128module_init(nf_defrag_init);
129module_exit(nf_defrag_fini);
130
131MODULE_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
1505out_overflow: 1508out_overflow:
1506 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); 1509 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
1507out: 1510out_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);
1514out:
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
123static inline int compute_score(struct sock *sk, struct net *net, 123static 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
241static struct sock *__udp6_lib_lookup(struct net *net, 241static 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
323struct 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}
328EXPORT_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
21struct sock *
22nf_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}
55EXPORT_SYMBOL_GPL(nf_tproxy_get_sock_v4);
56 21
57static void 22static void
58nf_tproxy_destructor(struct sk_buff *skb) 23nf_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
34static inline __be32
35tproxy_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 */
72static struct sock *
73tproxy_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
27static unsigned int 104static unsigned int
28tproxy_tg(struct sk_buff *skb, const struct xt_action_param *par) 105tproxy_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
159static unsigned int
160tproxy_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
167static unsigned int
168tproxy_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
177static inline const struct in6_addr *
178tproxy_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 */
222static struct sock *
223tproxy_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
258static unsigned int
259tproxy_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
64static int tproxy_tg_check(const struct xt_tgchk_param *par) 325static 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
339static 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
77static struct xt_target tproxy_tg_reg __read_mostly = { 352static 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
88static int __init tproxy_tg_init(void) 391static 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
94static void __exit tproxy_tg_exit(void) 401static 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
99module_init(tproxy_tg_init); 406module_init(tproxy_tg_init);
100module_exit(tproxy_tg_exit); 407module_exit(tproxy_tg_exit);
101MODULE_LICENSE("GPL"); 408MODULE_LICENSE("GPL");
102MODULE_AUTHOR("Krisztian Kovacs"); 409MODULE_AUTHOR("Balazs Scheidler, Krisztian Kovacs");
103MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module."); 410MODULE_DESCRIPTION("Netfilter transparent proxy (TPROXY) target module.");
104MODULE_ALIAS("ipt_TPROXY"); 411MODULE_ALIAS("ipt_TPROXY");
412MODULE_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
32static int 34static int
33extract_icmp_fields(const struct sk_buff *skb, 35extract_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
90static bool 91static bool
91socket_match(const struct sk_buff *skb, struct xt_action_param *par, 92socket_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
176static bool 177static bool
177socket_mt_v0(const struct sk_buff *skb, struct xt_action_param *par) 178socket_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
182static bool 183static bool
183socket_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) 184socket_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
191static int
192extract_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
243static bool
244socket_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
188static struct xt_match socket_mt_reg[] __read_mostly = { 314static 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
210static int __init socket_mt_init(void) 348static 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");
225MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); 367MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
226MODULE_DESCRIPTION("x_tables socket match module"); 368MODULE_DESCRIPTION("x_tables socket match module");
227MODULE_ALIAS("ipt_socket"); 369MODULE_ALIAS("ipt_socket");
370MODULE_ALIAS("ip6t_socket");