1 #ifndef _NF_TPROXY_CORE_H
2 #define _NF_TPROXY_CORE_H
4 #include <linux/types.h>
6 #include <linux/skbuff.h>
8 #include <net/inet_hashtables.h>
9 #include <net/inet6_hashtables.h>
12 #define NFT_LOOKUP_ANY 0
13 #define NFT_LOOKUP_LISTENER 1
14 #define NFT_LOOKUP_ESTABLISHED 2
16 /* look up and get a reference to a matching socket */
19 /* This function is used by the 'TPROXY' target and the 'socket'
20 * match. The following lookups are supported:
22 * Explicit TProxy target rule
23 * ===========================
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:
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
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).
42 * "socket" match based redirection (no specific rule)
43 * ===================================================
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:
53 * - match: if there's a fully established connection matching the
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
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.
66 static inline struct sock
*
67 nf_tproxy_get_sock_v4(struct net
*net
, const u8 protocol
,
68 const __be32 saddr
, const __be32 daddr
,
69 const __be16 sport
, const __be16 dport
,
70 const struct net_device
*in
, int lookup_type
)
77 switch (lookup_type
) {
79 sk
= __inet_lookup(net
, &tcp_hashinfo
,
80 saddr
, sport
, daddr
, dport
,
83 case NFT_LOOKUP_LISTENER
:
84 sk
= inet_lookup_listener(net
, &tcp_hashinfo
,
89 /* NOTE: we return listeners even if bound to
90 * 0.0.0.0, those are filtered out in
91 * xt_socket, since xt_TPROXY needs 0 bound
95 case NFT_LOOKUP_ESTABLISHED
:
96 sk
= inet_lookup_established(net
, &tcp_hashinfo
,
97 saddr
, sport
, daddr
, dport
,
107 sk
= udp4_lib_lookup(net
, saddr
, sport
, daddr
, dport
,
109 if (sk
&& lookup_type
!= NFT_LOOKUP_ANY
) {
110 int connected
= (sk
->sk_state
== TCP_ESTABLISHED
);
111 int wildcard
= (inet_sk(sk
)->inet_rcv_saddr
== 0);
113 /* NOTE: we return listeners even if bound to
114 * 0.0.0.0, those are filtered out in
115 * xt_socket, since xt_TPROXY needs 0 bound
117 if ((lookup_type
== NFT_LOOKUP_ESTABLISHED
&& (!connected
|| wildcard
)) ||
118 (lookup_type
== NFT_LOOKUP_LISTENER
&& connected
)) {
129 pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, lookup type: %d, sock %p\n",
130 protocol
, ntohl(saddr
), ntohs(sport
), ntohl(daddr
), ntohs(dport
), lookup_type
, sk
);
135 #if IS_ENABLED(CONFIG_IPV6)
136 static inline struct sock
*
137 nf_tproxy_get_sock_v6(struct net
*net
, const u8 protocol
,
138 const struct in6_addr
*saddr
, const struct in6_addr
*daddr
,
139 const __be16 sport
, const __be16 dport
,
140 const struct net_device
*in
, int lookup_type
)
147 switch (lookup_type
) {
149 sk
= inet6_lookup(net
, &tcp_hashinfo
,
150 saddr
, sport
, daddr
, dport
,
153 case NFT_LOOKUP_LISTENER
:
154 sk
= inet6_lookup_listener(net
, &tcp_hashinfo
,
159 /* NOTE: we return listeners even if bound to
160 * 0.0.0.0, those are filtered out in
161 * xt_socket, since xt_TPROXY needs 0 bound
165 case NFT_LOOKUP_ESTABLISHED
:
166 sk
= __inet6_lookup_established(net
, &tcp_hashinfo
,
167 saddr
, sport
, daddr
, ntohs(dport
),
177 sk
= udp6_lib_lookup(net
, saddr
, sport
, daddr
, dport
,
179 if (sk
&& lookup_type
!= NFT_LOOKUP_ANY
) {
180 int connected
= (sk
->sk_state
== TCP_ESTABLISHED
);
181 int wildcard
= ipv6_addr_any(&inet6_sk(sk
)->rcv_saddr
);
183 /* NOTE: we return listeners even if bound to
184 * 0.0.0.0, those are filtered out in
185 * xt_socket, since xt_TPROXY needs 0 bound
187 if ((lookup_type
== NFT_LOOKUP_ESTABLISHED
&& (!connected
|| wildcard
)) ||
188 (lookup_type
== NFT_LOOKUP_LISTENER
&& connected
)) {
199 pr_debug("tproxy socket lookup: proto %u %pI6:%u -> %pI6:%u, lookup type: %d, sock %p\n",
200 protocol
, saddr
, ntohs(sport
), daddr
, ntohs(dport
), lookup_type
, sk
);
206 /* assign a socket to the skb -- consumes sk */
208 nf_tproxy_assign_sock(struct sk_buff
*skb
, struct sock
*sk
);