Commit | Line | Data |
---|---|---|
9ad2d745 KK |
1 | #ifndef _NF_TPROXY_CORE_H |
2 | #define _NF_TPROXY_CORE_H | |
3 | ||
4 | #include <linux/types.h> | |
5 | #include <linux/in.h> | |
6 | #include <linux/skbuff.h> | |
7 | #include <net/sock.h> | |
3b9afb29 BS |
8 | #include <net/inet_hashtables.h> |
9 | #include <net/inet6_hashtables.h> | |
9ad2d745 KK |
10 | #include <net/tcp.h> |
11 | ||
106e4c26 BS |
12 | #define NFT_LOOKUP_ANY 0 |
13 | #define NFT_LOOKUP_LISTENER 1 | |
14 | #define NFT_LOOKUP_ESTABLISHED 2 | |
15 | ||
9ad2d745 | 16 | /* look up and get a reference to a matching socket */ |
6006db84 BS |
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 * | |
9ad2d745 KK |
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, | |
6006db84 BS |
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, | |
da5e3630 | 85 | saddr, sport, |
6006db84 BS |
86 | daddr, dport, |
87 | in->ifindex); | |
88 | ||
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 | |
92 | * listeners too */ | |
93 | ||
94 | break; | |
95 | case NFT_LOOKUP_ESTABLISHED: | |
96 | sk = inet_lookup_established(net, &tcp_hashinfo, | |
97 | saddr, sport, daddr, dport, | |
98 | in->ifindex); | |
99 | break; | |
100 | default: | |
101 | WARN_ON(1); | |
102 | sk = NULL; | |
103 | break; | |
104 | } | |
105 | break; | |
106 | case IPPROTO_UDP: | |
107 | sk = udp4_lib_lookup(net, saddr, sport, daddr, dport, | |
108 | in->ifindex); | |
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); | |
112 | ||
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 | |
116 | * listeners too */ | |
117 | if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || | |
118 | (lookup_type == NFT_LOOKUP_LISTENER && connected)) { | |
119 | sock_put(sk); | |
120 | sk = NULL; | |
121 | } | |
122 | } | |
123 | break; | |
124 | default: | |
125 | WARN_ON(1); | |
126 | sk = NULL; | |
127 | } | |
128 | ||
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); | |
131 | ||
132 | return sk; | |
133 | } | |
134 | ||
dfd56b8b | 135 | #if IS_ENABLED(CONFIG_IPV6) |
3b9afb29 BS |
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) | |
141 | { | |
142 | struct sock *sk; | |
143 | ||
144 | /* look up socket */ | |
145 | switch (protocol) { | |
146 | case IPPROTO_TCP: | |
147 | switch (lookup_type) { | |
148 | case NFT_LOOKUP_ANY: | |
149 | sk = inet6_lookup(net, &tcp_hashinfo, | |
150 | saddr, sport, daddr, dport, | |
151 | in->ifindex); | |
152 | break; | |
153 | case NFT_LOOKUP_LISTENER: | |
154 | sk = inet6_lookup_listener(net, &tcp_hashinfo, | |
5ba24953 | 155 | saddr, sport, |
3b9afb29 BS |
156 | daddr, ntohs(dport), |
157 | in->ifindex); | |
158 | ||
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 | |
162 | * listeners too */ | |
163 | ||
164 | break; | |
165 | case NFT_LOOKUP_ESTABLISHED: | |
166 | sk = __inet6_lookup_established(net, &tcp_hashinfo, | |
167 | saddr, sport, daddr, ntohs(dport), | |
168 | in->ifindex); | |
169 | break; | |
170 | default: | |
171 | WARN_ON(1); | |
172 | sk = NULL; | |
173 | break; | |
174 | } | |
175 | break; | |
176 | case IPPROTO_UDP: | |
177 | sk = udp6_lib_lookup(net, saddr, sport, daddr, dport, | |
178 | in->ifindex); | |
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); | |
182 | ||
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 | |
186 | * listeners too */ | |
187 | if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || | |
188 | (lookup_type == NFT_LOOKUP_LISTENER && connected)) { | |
189 | sock_put(sk); | |
190 | sk = NULL; | |
191 | } | |
192 | } | |
193 | break; | |
194 | default: | |
195 | WARN_ON(1); | |
196 | sk = NULL; | |
197 | } | |
198 | ||
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); | |
201 | ||
202 | return sk; | |
203 | } | |
204 | #endif | |
9ad2d745 | 205 | |
9ad2d745 | 206 | /* assign a socket to the skb -- consumes sk */ |
d503b30b | 207 | void |
9ad2d745 KK |
208 | nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk); |
209 | ||
210 | #endif |