xref: /linux/net/netfilter/nft_tproxy.c (revision 021bc4b9)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <linux/module.h>
3 #include <linux/netfilter/nf_tables.h>
4 #include <net/netfilter/nf_tables.h>
5 #include <net/netfilter/nf_tables_core.h>
6 #include <net/netfilter/nf_tproxy.h>
7 #include <net/inet_sock.h>
8 #include <net/tcp.h>
9 #include <linux/if_ether.h>
10 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
11 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
12 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
13 #endif
14 
15 struct nft_tproxy {
16 	u8	sreg_addr;
17 	u8	sreg_port;
18 	u8	family;
19 };
20 
21 static void nft_tproxy_eval_v4(const struct nft_expr *expr,
22 			       struct nft_regs *regs,
23 			       const struct nft_pktinfo *pkt)
24 {
25 	const struct nft_tproxy *priv = nft_expr_priv(expr);
26 	struct sk_buff *skb = pkt->skb;
27 	const struct iphdr *iph = ip_hdr(skb);
28 	struct udphdr _hdr, *hp;
29 	__be32 taddr = 0;
30 	__be16 tport = 0;
31 	struct sock *sk;
32 
33 	if (pkt->tprot != IPPROTO_TCP &&
34 	    pkt->tprot != IPPROTO_UDP) {
35 		regs->verdict.code = NFT_BREAK;
36 		return;
37 	}
38 
39 	hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
40 	if (!hp) {
41 		regs->verdict.code = NFT_BREAK;
42 		return;
43 	}
44 
45 	/* check if there's an ongoing connection on the packet addresses, this
46 	 * happens if the redirect already happened and the current packet
47 	 * belongs to an already established connection
48 	 */
49 	sk = nf_tproxy_get_sock_v4(nft_net(pkt), skb, iph->protocol,
50 				   iph->saddr, iph->daddr,
51 				   hp->source, hp->dest,
52 				   skb->dev, NF_TPROXY_LOOKUP_ESTABLISHED);
53 
54 	if (priv->sreg_addr)
55 		taddr = nft_reg_load_be32(&regs->data[priv->sreg_addr]);
56 	taddr = nf_tproxy_laddr4(skb, taddr, iph->daddr);
57 
58 	if (priv->sreg_port)
59 		tport = nft_reg_load_be16(&regs->data[priv->sreg_port]);
60 	if (!tport)
61 		tport = hp->dest;
62 
63 	/* UDP has no TCP_TIME_WAIT state, so we never enter here */
64 	if (sk && sk->sk_state == TCP_TIME_WAIT) {
65 		/* reopening a TIME_WAIT connection needs special handling */
66 		sk = nf_tproxy_handle_time_wait4(nft_net(pkt), skb, taddr, tport, sk);
67 	} else if (!sk) {
68 		/* no, there's no established connection, check if
69 		 * there's a listener on the redirected addr/port
70 		 */
71 		sk = nf_tproxy_get_sock_v4(nft_net(pkt), skb, iph->protocol,
72 					   iph->saddr, taddr,
73 					   hp->source, tport,
74 					   skb->dev, NF_TPROXY_LOOKUP_LISTENER);
75 	}
76 
77 	if (sk && nf_tproxy_sk_is_transparent(sk))
78 		nf_tproxy_assign_sock(skb, sk);
79 	else
80 		regs->verdict.code = NFT_BREAK;
81 }
82 
83 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
84 static void nft_tproxy_eval_v6(const struct nft_expr *expr,
85 			       struct nft_regs *regs,
86 			       const struct nft_pktinfo *pkt)
87 {
88 	const struct nft_tproxy *priv = nft_expr_priv(expr);
89 	struct sk_buff *skb = pkt->skb;
90 	const struct ipv6hdr *iph = ipv6_hdr(skb);
91 	int thoff = nft_thoff(pkt);
92 	struct udphdr _hdr, *hp;
93 	struct in6_addr taddr;
94 	__be16 tport = 0;
95 	struct sock *sk;
96 	int l4proto;
97 
98 	memset(&taddr, 0, sizeof(taddr));
99 
100 	if (pkt->tprot != IPPROTO_TCP &&
101 	    pkt->tprot != IPPROTO_UDP) {
102 		regs->verdict.code = NFT_BREAK;
103 		return;
104 	}
105 	l4proto = pkt->tprot;
106 
107 	hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
108 	if (hp == NULL) {
109 		regs->verdict.code = NFT_BREAK;
110 		return;
111 	}
112 
113 	/* check if there's an ongoing connection on the packet addresses, this
114 	 * happens if the redirect already happened and the current packet
115 	 * belongs to an already established connection
116 	 */
117 	sk = nf_tproxy_get_sock_v6(nft_net(pkt), skb, thoff, l4proto,
118 				   &iph->saddr, &iph->daddr,
119 				   hp->source, hp->dest,
120 				   nft_in(pkt), NF_TPROXY_LOOKUP_ESTABLISHED);
121 
122 	if (priv->sreg_addr)
123 		memcpy(&taddr, &regs->data[priv->sreg_addr], sizeof(taddr));
124 	taddr = *nf_tproxy_laddr6(skb, &taddr, &iph->daddr);
125 
126 	if (priv->sreg_port)
127 		tport = nft_reg_load_be16(&regs->data[priv->sreg_port]);
128 	if (!tport)
129 		tport = hp->dest;
130 
131 	/* UDP has no TCP_TIME_WAIT state, so we never enter here */
132 	if (sk && sk->sk_state == TCP_TIME_WAIT) {
133 		/* reopening a TIME_WAIT connection needs special handling */
134 		sk = nf_tproxy_handle_time_wait6(skb, l4proto, thoff,
135 						 nft_net(pkt),
136 						 &taddr,
137 						 tport,
138 						 sk);
139 	} else if (!sk) {
140 		/* no there's no established connection, check if
141 		 * there's a listener on the redirected addr/port
142 		 */
143 		sk = nf_tproxy_get_sock_v6(nft_net(pkt), skb, thoff,
144 					   l4proto, &iph->saddr, &taddr,
145 					   hp->source, tport,
146 					   nft_in(pkt), NF_TPROXY_LOOKUP_LISTENER);
147 	}
148 
149 	/* NOTE: assign_sock consumes our sk reference */
150 	if (sk && nf_tproxy_sk_is_transparent(sk))
151 		nf_tproxy_assign_sock(skb, sk);
152 	else
153 		regs->verdict.code = NFT_BREAK;
154 }
155 #endif
156 
157 static void nft_tproxy_eval(const struct nft_expr *expr,
158 			    struct nft_regs *regs,
159 			    const struct nft_pktinfo *pkt)
160 {
161 	const struct nft_tproxy *priv = nft_expr_priv(expr);
162 
163 	switch (nft_pf(pkt)) {
164 	case NFPROTO_IPV4:
165 		switch (priv->family) {
166 		case NFPROTO_IPV4:
167 		case NFPROTO_UNSPEC:
168 			nft_tproxy_eval_v4(expr, regs, pkt);
169 			return;
170 		}
171 		break;
172 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
173 	case NFPROTO_IPV6:
174 		switch (priv->family) {
175 		case NFPROTO_IPV6:
176 		case NFPROTO_UNSPEC:
177 			nft_tproxy_eval_v6(expr, regs, pkt);
178 			return;
179 		}
180 #endif
181 	}
182 	regs->verdict.code = NFT_BREAK;
183 }
184 
185 static const struct nla_policy nft_tproxy_policy[NFTA_TPROXY_MAX + 1] = {
186 	[NFTA_TPROXY_FAMILY]   = NLA_POLICY_MAX(NLA_BE32, 255),
187 	[NFTA_TPROXY_REG_ADDR] = { .type = NLA_U32 },
188 	[NFTA_TPROXY_REG_PORT] = { .type = NLA_U32 },
189 };
190 
191 static int nft_tproxy_init(const struct nft_ctx *ctx,
192 			   const struct nft_expr *expr,
193 			   const struct nlattr * const tb[])
194 {
195 	struct nft_tproxy *priv = nft_expr_priv(expr);
196 	unsigned int alen = 0;
197 	int err;
198 
199 	if (!tb[NFTA_TPROXY_FAMILY] ||
200 	    (!tb[NFTA_TPROXY_REG_ADDR] && !tb[NFTA_TPROXY_REG_PORT]))
201 		return -EINVAL;
202 
203 	priv->family = ntohl(nla_get_be32(tb[NFTA_TPROXY_FAMILY]));
204 
205 	switch (ctx->family) {
206 	case NFPROTO_IPV4:
207 		if (priv->family != NFPROTO_IPV4)
208 			return -EINVAL;
209 		break;
210 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
211 	case NFPROTO_IPV6:
212 		if (priv->family != NFPROTO_IPV6)
213 			return -EINVAL;
214 		break;
215 #endif
216 	case NFPROTO_INET:
217 		break;
218 	default:
219 		return -EOPNOTSUPP;
220 	}
221 
222 	/* Address is specified but the rule family is not set accordingly */
223 	if (priv->family == NFPROTO_UNSPEC && tb[NFTA_TPROXY_REG_ADDR])
224 		return -EINVAL;
225 
226 	switch (priv->family) {
227 	case NFPROTO_IPV4:
228 		alen = sizeof_field(union nf_inet_addr, in);
229 		err = nf_defrag_ipv4_enable(ctx->net);
230 		if (err)
231 			return err;
232 		break;
233 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
234 	case NFPROTO_IPV6:
235 		alen = sizeof_field(union nf_inet_addr, in6);
236 		err = nf_defrag_ipv6_enable(ctx->net);
237 		if (err)
238 			return err;
239 		break;
240 #endif
241 	case NFPROTO_UNSPEC:
242 		/* No address is specified here */
243 		err = nf_defrag_ipv4_enable(ctx->net);
244 		if (err)
245 			return err;
246 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
247 		err = nf_defrag_ipv6_enable(ctx->net);
248 		if (err)
249 			return err;
250 #endif
251 		break;
252 	default:
253 		return -EOPNOTSUPP;
254 	}
255 
256 	if (tb[NFTA_TPROXY_REG_ADDR]) {
257 		err = nft_parse_register_load(tb[NFTA_TPROXY_REG_ADDR],
258 					      &priv->sreg_addr, alen);
259 		if (err < 0)
260 			return err;
261 	}
262 
263 	if (tb[NFTA_TPROXY_REG_PORT]) {
264 		err = nft_parse_register_load(tb[NFTA_TPROXY_REG_PORT],
265 					      &priv->sreg_port, sizeof(u16));
266 		if (err < 0)
267 			return err;
268 	}
269 
270 	return 0;
271 }
272 
273 static void nft_tproxy_destroy(const struct nft_ctx *ctx,
274 			       const struct nft_expr *expr)
275 {
276 	const struct nft_tproxy *priv = nft_expr_priv(expr);
277 
278 	switch (priv->family) {
279 	case NFPROTO_IPV4:
280 		nf_defrag_ipv4_disable(ctx->net);
281 		break;
282 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
283 	case NFPROTO_IPV6:
284 		nf_defrag_ipv6_disable(ctx->net);
285 		break;
286 #endif
287 	case NFPROTO_UNSPEC:
288 		nf_defrag_ipv4_disable(ctx->net);
289 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
290 		nf_defrag_ipv6_disable(ctx->net);
291 #endif
292 		break;
293 	}
294 }
295 
296 static int nft_tproxy_dump(struct sk_buff *skb,
297 			   const struct nft_expr *expr, bool reset)
298 {
299 	const struct nft_tproxy *priv = nft_expr_priv(expr);
300 
301 	if (nla_put_be32(skb, NFTA_TPROXY_FAMILY, htonl(priv->family)))
302 		return -1;
303 
304 	if (priv->sreg_addr &&
305 	    nft_dump_register(skb, NFTA_TPROXY_REG_ADDR, priv->sreg_addr))
306 		return -1;
307 
308 	if (priv->sreg_port &&
309 	    nft_dump_register(skb, NFTA_TPROXY_REG_PORT, priv->sreg_port))
310 			return -1;
311 
312 	return 0;
313 }
314 
315 static int nft_tproxy_validate(const struct nft_ctx *ctx,
316 			       const struct nft_expr *expr,
317 			       const struct nft_data **data)
318 {
319 	if (ctx->family != NFPROTO_IPV4 &&
320 	    ctx->family != NFPROTO_IPV6 &&
321 	    ctx->family != NFPROTO_INET)
322 		return -EOPNOTSUPP;
323 
324 	return nft_chain_validate_hooks(ctx->chain, 1 << NF_INET_PRE_ROUTING);
325 }
326 
327 static struct nft_expr_type nft_tproxy_type;
328 static const struct nft_expr_ops nft_tproxy_ops = {
329 	.type		= &nft_tproxy_type,
330 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_tproxy)),
331 	.eval		= nft_tproxy_eval,
332 	.init		= nft_tproxy_init,
333 	.destroy	= nft_tproxy_destroy,
334 	.dump		= nft_tproxy_dump,
335 	.reduce		= NFT_REDUCE_READONLY,
336 	.validate	= nft_tproxy_validate,
337 };
338 
339 static struct nft_expr_type nft_tproxy_type __read_mostly = {
340 	.name		= "tproxy",
341 	.ops		= &nft_tproxy_ops,
342 	.policy		= nft_tproxy_policy,
343 	.maxattr	= NFTA_TPROXY_MAX,
344 	.owner		= THIS_MODULE,
345 };
346 
347 static int __init nft_tproxy_module_init(void)
348 {
349 	return nft_register_expr(&nft_tproxy_type);
350 }
351 
352 static void __exit nft_tproxy_module_exit(void)
353 {
354 	nft_unregister_expr(&nft_tproxy_type);
355 }
356 
357 module_init(nft_tproxy_module_init);
358 module_exit(nft_tproxy_module_exit);
359 
360 MODULE_LICENSE("GPL");
361 MODULE_AUTHOR("Máté Eckl");
362 MODULE_DESCRIPTION("nf_tables tproxy support module");
363 MODULE_ALIAS_NFT_EXPR("tproxy");
364