1147231Sglebius// SPDX-License-Identifier: GPL-2.0-only
2147231Sglebius/*
3147231Sglebius * Transparent proxy support for Linux/iptables
4147231Sglebius *
5147231Sglebius * Copyright (C) 2007-2008 BalaBit IT Ltd.
6147231Sglebius * Author: Krisztian Kovacs
7147231Sglebius */
8147231Sglebius#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9147231Sglebius#include <linux/module.h>
10147231Sglebius#include <linux/skbuff.h>
11147231Sglebius#include <linux/netfilter/x_tables.h>
12147231Sglebius#include <linux/netfilter_ipv4/ip_tables.h>
13147231Sglebius#include <net/tcp.h>
14147231Sglebius#include <net/udp.h>
15147231Sglebius#include <net/icmp.h>
16147231Sglebius#include <net/sock.h>
17147231Sglebius#include <net/inet_sock.h>
18147231Sglebius#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
19147231Sglebius
20147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
21147231Sglebius#include <linux/netfilter_ipv6/ip6_tables.h>
22147231Sglebius#include <net/inet6_hashtables.h>
23147231Sglebius#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
24147231Sglebius#endif
25147231Sglebius
26147231Sglebius#include <net/netfilter/nf_socket.h>
27147231Sglebius#include <linux/netfilter/xt_socket.h>
28147231Sglebius
29147231Sglebius/* "socket" match based redirection (no specific rule)
30147231Sglebius * ===================================================
31147231Sglebius *
32147231Sglebius * There are connections with dynamic endpoints (e.g. FTP data
33147231Sglebius * connection) that the user is unable to add explicit rules
34147231Sglebius * for. These are taken care of by a generic "socket" rule. It is
35147231Sglebius * assumed that the proxy application is trusted to open such
36147231Sglebius * connections without explicit iptables rule (except of course the
37147231Sglebius * generic 'socket' rule). In this case the following sockets are
38147231Sglebius * matched in preference order:
39147231Sglebius *
40147231Sglebius *   - match: if there's a fully established connection matching the
41147231Sglebius *     _packet_ tuple
42147231Sglebius *
43147231Sglebius *   - match: if there's a non-zero bound listener (possibly with a
44147231Sglebius *     non-local address) We don't accept zero-bound listeners, since
45147231Sglebius *     then local services could intercept traffic going through the
46147231Sglebius *     box.
47147231Sglebius */
48147231Sglebiusstatic bool
49147231Sglebiussocket_match(const struct sk_buff *skb, struct xt_action_param *par,
50206032Smav	     const struct xt_socket_mtinfo1 *info)
51147231Sglebius{
52147231Sglebius	struct sk_buff *pskb = (struct sk_buff *)skb;
53147231Sglebius	struct sock *sk = skb->sk;
54147231Sglebius
55147231Sglebius	if (sk && !net_eq(xt_net(par), sock_net(sk)))
56147231Sglebius		sk = NULL;
57147231Sglebius
58147231Sglebius	if (!sk)
59147231Sglebius		sk = nf_sk_lookup_slow_v4(xt_net(par), skb, xt_in(par));
60147231Sglebius
61147231Sglebius	if (sk) {
62147231Sglebius		bool wildcard;
63147231Sglebius		bool transparent = true;
64147231Sglebius
65147231Sglebius		/* Ignore sockets listening on INADDR_ANY,
66147231Sglebius		 * unless XT_SOCKET_NOWILDCARD is set
67147231Sglebius		 */
68147231Sglebius		wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
69147231Sglebius			    sk_fullsock(sk) &&
70147231Sglebius			    inet_sk(sk)->inet_rcv_saddr == 0);
71147231Sglebius
72147231Sglebius		/* Ignore non-transparent sockets,
73147231Sglebius		 * if XT_SOCKET_TRANSPARENT is used
74147231Sglebius		 */
75147231Sglebius		if (info->flags & XT_SOCKET_TRANSPARENT)
76147231Sglebius			transparent = inet_sk_transparent(sk);
77147231Sglebius
78147231Sglebius		if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard &&
79147231Sglebius		    transparent && sk_fullsock(sk))
80147231Sglebius			pskb->mark = READ_ONCE(sk->sk_mark);
81147231Sglebius
82147231Sglebius		if (sk != skb->sk)
83147231Sglebius			sock_gen_put(sk);
84147231Sglebius
85147231Sglebius		if (wildcard || !transparent)
86147231Sglebius			sk = NULL;
87147231Sglebius	}
88147231Sglebius
89147231Sglebius	return sk != NULL;
90147231Sglebius}
91147231Sglebius
92147231Sglebiusstatic bool
93147231Sglebiussocket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par)
94147231Sglebius{
95147231Sglebius	static struct xt_socket_mtinfo1 xt_info_v0 = {
96147231Sglebius		.flags = 0,
97147231Sglebius	};
98147231Sglebius
99147231Sglebius	return socket_match(skb, par, &xt_info_v0);
100147231Sglebius}
101147231Sglebius
102147231Sglebiusstatic bool
103147231Sglebiussocket_mt4_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par)
104147231Sglebius{
105147231Sglebius	return socket_match(skb, par, par->matchinfo);
106147231Sglebius}
107147231Sglebius
108147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
109147231Sglebiusstatic bool
110147231Sglebiussocket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par)
111147231Sglebius{
112147231Sglebius	const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
113147231Sglebius	struct sk_buff *pskb = (struct sk_buff *)skb;
114147231Sglebius	struct sock *sk = skb->sk;
115147231Sglebius
116147231Sglebius	if (sk && !net_eq(xt_net(par), sock_net(sk)))
117147231Sglebius		sk = NULL;
118147231Sglebius
119147231Sglebius	if (!sk)
120147231Sglebius		sk = nf_sk_lookup_slow_v6(xt_net(par), skb, xt_in(par));
121147231Sglebius
122147231Sglebius	if (sk) {
123147231Sglebius		bool wildcard;
124147231Sglebius		bool transparent = true;
125147231Sglebius
126147231Sglebius		/* Ignore sockets listening on INADDR_ANY
127147231Sglebius		 * unless XT_SOCKET_NOWILDCARD is set
128147231Sglebius		 */
129147231Sglebius		wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
130147231Sglebius			    sk_fullsock(sk) &&
131147231Sglebius			    ipv6_addr_any(&sk->sk_v6_rcv_saddr));
132147231Sglebius
133147231Sglebius		/* Ignore non-transparent sockets,
134147231Sglebius		 * if XT_SOCKET_TRANSPARENT is used
135147231Sglebius		 */
136147231Sglebius		if (info->flags & XT_SOCKET_TRANSPARENT)
137147231Sglebius			transparent = inet_sk_transparent(sk);
138147231Sglebius
139147231Sglebius		if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard &&
140147231Sglebius		    transparent && sk_fullsock(sk))
141147231Sglebius			pskb->mark = READ_ONCE(sk->sk_mark);
142147231Sglebius
143147231Sglebius		if (sk != skb->sk)
144147231Sglebius			sock_gen_put(sk);
145147231Sglebius
146147231Sglebius		if (wildcard || !transparent)
147147231Sglebius			sk = NULL;
148147231Sglebius	}
149147231Sglebius
150147231Sglebius	return sk != NULL;
151147231Sglebius}
152147231Sglebius#endif
153147231Sglebius
154147231Sglebiusstatic int socket_mt_enable_defrag(struct net *net, int family)
155147231Sglebius{
156147231Sglebius	switch (family) {
157147231Sglebius	case NFPROTO_IPV4:
158147231Sglebius		return nf_defrag_ipv4_enable(net);
159147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
160147231Sglebius	case NFPROTO_IPV6:
161147231Sglebius		return nf_defrag_ipv6_enable(net);
162147231Sglebius#endif
163184205Sdes	}
164147231Sglebius	WARN_ONCE(1, "Unknown family %d\n", family);
165147231Sglebius	return 0;
166147231Sglebius}
167147231Sglebius
168147231Sglebiusstatic int socket_mt_v1_check(const struct xt_mtchk_param *par)
169147231Sglebius{
170147231Sglebius	const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
171147231Sglebius	int err;
172147231Sglebius
173147231Sglebius	err = socket_mt_enable_defrag(par->net, par->family);
174147231Sglebius	if (err)
175147231Sglebius		return err;
176147231Sglebius
177147231Sglebius	if (info->flags & ~XT_SOCKET_FLAGS_V1) {
178147231Sglebius		pr_info_ratelimited("unknown flags 0x%x\n",
179147231Sglebius				    info->flags & ~XT_SOCKET_FLAGS_V1);
180147231Sglebius		return -EINVAL;
181147231Sglebius	}
182147231Sglebius	return 0;
183147231Sglebius}
184147231Sglebius
185147231Sglebiusstatic int socket_mt_v2_check(const struct xt_mtchk_param *par)
186147231Sglebius{
187147231Sglebius	const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo;
188147231Sglebius	int err;
189147231Sglebius
190147231Sglebius	err = socket_mt_enable_defrag(par->net, par->family);
191147231Sglebius	if (err)
192147231Sglebius		return err;
193147231Sglebius
194147231Sglebius	if (info->flags & ~XT_SOCKET_FLAGS_V2) {
195147231Sglebius		pr_info_ratelimited("unknown flags 0x%x\n",
196147231Sglebius				    info->flags & ~XT_SOCKET_FLAGS_V2);
197147231Sglebius		return -EINVAL;
198147231Sglebius	}
199147231Sglebius	return 0;
200147231Sglebius}
201147231Sglebius
202147231Sglebiusstatic int socket_mt_v3_check(const struct xt_mtchk_param *par)
203147231Sglebius{
204147231Sglebius	const struct xt_socket_mtinfo3 *info =
205147231Sglebius				    (struct xt_socket_mtinfo3 *)par->matchinfo;
206147231Sglebius	int err;
207147231Sglebius
208147231Sglebius	err = socket_mt_enable_defrag(par->net, par->family);
209147231Sglebius	if (err)
210147231Sglebius		return err;
211147231Sglebius	if (info->flags & ~XT_SOCKET_FLAGS_V3) {
212147231Sglebius		pr_info_ratelimited("unknown flags 0x%x\n",
213147231Sglebius				    info->flags & ~XT_SOCKET_FLAGS_V3);
214147231Sglebius		return -EINVAL;
215147231Sglebius	}
216147231Sglebius	return 0;
217147231Sglebius}
218147231Sglebius
219147231Sglebiusstatic void socket_mt_destroy(const struct xt_mtdtor_param *par)
220147231Sglebius{
221147231Sglebius	if (par->family == NFPROTO_IPV4)
222147231Sglebius		nf_defrag_ipv4_disable(par->net);
223147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
224147231Sglebius	else if (par->family == NFPROTO_IPV6)
225147231Sglebius		nf_defrag_ipv6_disable(par->net);
226147231Sglebius#endif
227147231Sglebius}
228147231Sglebius
229147231Sglebiusstatic struct xt_match socket_mt_reg[] __read_mostly = {
230147231Sglebius	{
231147231Sglebius		.name		= "socket",
232147231Sglebius		.revision	= 0,
233147231Sglebius		.family		= NFPROTO_IPV4,
234147231Sglebius		.match		= socket_mt4_v0,
235147231Sglebius		.hooks		= (1 << NF_INET_PRE_ROUTING) |
236147231Sglebius				  (1 << NF_INET_LOCAL_IN),
237147231Sglebius		.me		= THIS_MODULE,
238147231Sglebius	},
239147231Sglebius	{
240147231Sglebius		.name		= "socket",
241147231Sglebius		.revision	= 1,
242147231Sglebius		.family		= NFPROTO_IPV4,
243147231Sglebius		.match		= socket_mt4_v1_v2_v3,
244147231Sglebius		.destroy	= socket_mt_destroy,
245147231Sglebius		.checkentry	= socket_mt_v1_check,
246147231Sglebius		.matchsize	= sizeof(struct xt_socket_mtinfo1),
247147231Sglebius		.hooks		= (1 << NF_INET_PRE_ROUTING) |
248147231Sglebius				  (1 << NF_INET_LOCAL_IN),
249147231Sglebius		.me		= THIS_MODULE,
250147231Sglebius	},
251147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
252147231Sglebius	{
253147231Sglebius		.name		= "socket",
254147231Sglebius		.revision	= 1,
255147231Sglebius		.family		= NFPROTO_IPV6,
256147231Sglebius		.match		= socket_mt6_v1_v2_v3,
257147231Sglebius		.checkentry	= socket_mt_v1_check,
258147231Sglebius		.matchsize	= sizeof(struct xt_socket_mtinfo1),
259147231Sglebius		.destroy	= socket_mt_destroy,
260147231Sglebius		.hooks		= (1 << NF_INET_PRE_ROUTING) |
261147231Sglebius				  (1 << NF_INET_LOCAL_IN),
262147231Sglebius		.me		= THIS_MODULE,
263147231Sglebius	},
264147231Sglebius#endif
265147231Sglebius	{
266147231Sglebius		.name		= "socket",
267147231Sglebius		.revision	= 2,
268147231Sglebius		.family		= NFPROTO_IPV4,
269147231Sglebius		.match		= socket_mt4_v1_v2_v3,
270147231Sglebius		.checkentry	= socket_mt_v2_check,
271147231Sglebius		.destroy	= socket_mt_destroy,
272147231Sglebius		.matchsize	= sizeof(struct xt_socket_mtinfo1),
273147231Sglebius		.hooks		= (1 << NF_INET_PRE_ROUTING) |
274147231Sglebius				  (1 << NF_INET_LOCAL_IN),
275147231Sglebius		.me		= THIS_MODULE,
276147231Sglebius	},
277147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
278147231Sglebius	{
279147231Sglebius		.name		= "socket",
280147231Sglebius		.revision	= 2,
281147231Sglebius		.family		= NFPROTO_IPV6,
282147231Sglebius		.match		= socket_mt6_v1_v2_v3,
283147231Sglebius		.checkentry	= socket_mt_v2_check,
284147231Sglebius		.destroy	= socket_mt_destroy,
285147231Sglebius		.matchsize	= sizeof(struct xt_socket_mtinfo1),
286147231Sglebius		.hooks		= (1 << NF_INET_PRE_ROUTING) |
287147231Sglebius				  (1 << NF_INET_LOCAL_IN),
288147231Sglebius		.me		= THIS_MODULE,
289147231Sglebius	},
290147231Sglebius#endif
291147231Sglebius	{
292147231Sglebius		.name		= "socket",
293147248Sglebius		.revision	= 3,
294147231Sglebius		.family		= NFPROTO_IPV4,
295147248Sglebius		.match		= socket_mt4_v1_v2_v3,
296147248Sglebius		.checkentry	= socket_mt_v3_check,
297147231Sglebius		.destroy	= socket_mt_destroy,
298147231Sglebius		.matchsize	= sizeof(struct xt_socket_mtinfo1),
299147231Sglebius		.hooks		= (1 << NF_INET_PRE_ROUTING) |
300147231Sglebius				  (1 << NF_INET_LOCAL_IN),
301147231Sglebius		.me		= THIS_MODULE,
302147231Sglebius	},
303147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
304147231Sglebius	{
305147231Sglebius		.name		= "socket",
306147231Sglebius		.revision	= 3,
307147231Sglebius		.family		= NFPROTO_IPV6,
308147231Sglebius		.match		= socket_mt6_v1_v2_v3,
309147231Sglebius		.checkentry	= socket_mt_v3_check,
310147231Sglebius		.destroy	= socket_mt_destroy,
311147231Sglebius		.matchsize	= sizeof(struct xt_socket_mtinfo1),
312147231Sglebius		.hooks		= (1 << NF_INET_PRE_ROUTING) |
313147231Sglebius				  (1 << NF_INET_LOCAL_IN),
314147231Sglebius		.me		= THIS_MODULE,
315147231Sglebius	},
316147231Sglebius#endif
317147231Sglebius};
318147248Sglebius
319166018Sglebiusstatic int __init socket_mt_init(void)
320147231Sglebius{
321147231Sglebius	return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
322147231Sglebius}
323147231Sglebius
324147231Sglebiusstatic void __exit socket_mt_exit(void)
325147231Sglebius{
326147231Sglebius	xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
327147231Sglebius}
328147231Sglebius
329147231Sglebiusmodule_init(socket_mt_init);
330147231Sglebiusmodule_exit(socket_mt_exit);
331147231Sglebius
332147231SglebiusMODULE_LICENSE("GPL");
333147231SglebiusMODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
334147248SglebiusMODULE_DESCRIPTION("x_tables socket match module");
335166018SglebiusMODULE_ALIAS("ipt_socket");
336166018SglebiusMODULE_ALIAS("ip6t_socket");
337147231Sglebius