xref: /linux-6.15/net/netfilter/xt_CT.c (revision e8d4f892)
1 /*
2  * Copyright (c) 2010 Patrick McHardy <[email protected]>
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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 #include <linux/module.h>
10 #include <linux/gfp.h>
11 #include <linux/skbuff.h>
12 #include <linux/netfilter_ipv4/ip_tables.h>
13 #include <linux/netfilter_ipv6/ip6_tables.h>
14 #include <linux/netfilter/x_tables.h>
15 #include <linux/netfilter/xt_CT.h>
16 #include <net/netfilter/nf_conntrack.h>
17 #include <net/netfilter/nf_conntrack_l4proto.h>
18 #include <net/netfilter/nf_conntrack_helper.h>
19 #include <net/netfilter/nf_conntrack_ecache.h>
20 #include <net/netfilter/nf_conntrack_timeout.h>
21 #include <net/netfilter/nf_conntrack_zones.h>
22 
23 static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
24 {
25 	/* Previously seen (loopback)? Ignore. */
26 	if (skb->_nfct != 0)
27 		return XT_CONTINUE;
28 
29 	if (ct) {
30 		atomic_inc(&ct->ct_general.use);
31 		nf_ct_set(skb, ct, IP_CT_NEW);
32 	} else {
33 		nf_ct_set(skb, ct, IP_CT_UNTRACKED);
34 	}
35 
36 	return XT_CONTINUE;
37 }
38 
39 static unsigned int xt_ct_target_v0(struct sk_buff *skb,
40 				    const struct xt_action_param *par)
41 {
42 	const struct xt_ct_target_info *info = par->targinfo;
43 	struct nf_conn *ct = info->ct;
44 
45 	return xt_ct_target(skb, ct);
46 }
47 
48 static unsigned int xt_ct_target_v1(struct sk_buff *skb,
49 				    const struct xt_action_param *par)
50 {
51 	const struct xt_ct_target_info_v1 *info = par->targinfo;
52 	struct nf_conn *ct = info->ct;
53 
54 	return xt_ct_target(skb, ct);
55 }
56 
57 static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
58 {
59 	if (par->family == NFPROTO_IPV4) {
60 		const struct ipt_entry *e = par->entryinfo;
61 
62 		if (e->ip.invflags & IPT_INV_PROTO)
63 			return 0;
64 		return e->ip.proto;
65 	} else if (par->family == NFPROTO_IPV6) {
66 		const struct ip6t_entry *e = par->entryinfo;
67 
68 		if (e->ipv6.invflags & IP6T_INV_PROTO)
69 			return 0;
70 		return e->ipv6.proto;
71 	} else
72 		return 0;
73 }
74 
75 static int
76 xt_ct_set_helper(struct nf_conn *ct, const char *helper_name,
77 		 const struct xt_tgchk_param *par)
78 {
79 	struct nf_conntrack_helper *helper;
80 	struct nf_conn_help *help;
81 	u8 proto;
82 
83 	proto = xt_ct_find_proto(par);
84 	if (!proto) {
85 		pr_info_ratelimited("You must specify a L4 protocol and not use inversions on it\n");
86 		return -ENOENT;
87 	}
88 
89 	helper = nf_conntrack_helper_try_module_get(helper_name, par->family,
90 						    proto);
91 	if (helper == NULL) {
92 		pr_info_ratelimited("No such helper \"%s\"\n", helper_name);
93 		return -ENOENT;
94 	}
95 
96 	help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
97 	if (help == NULL) {
98 		nf_conntrack_helper_put(helper);
99 		return -ENOMEM;
100 	}
101 
102 	help->helper = helper;
103 	return 0;
104 }
105 
106 static int
107 xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
108 		  const char *timeout_name)
109 {
110 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
111 	const struct nf_conntrack_l4proto *l4proto;
112 	u8 proto;
113 
114 	proto = xt_ct_find_proto(par);
115 	if (!proto) {
116 		pr_info_ratelimited("You must specify a L4 protocol and not "
117 				    "use inversions on it");
118 		return -EINVAL;
119 	}
120 	l4proto = nf_ct_l4proto_find(proto);
121 	return nf_ct_set_timeout(par->net, ct, par->family, l4proto->l4proto,
122 				 timeout_name);
123 
124 #else
125 	return -EOPNOTSUPP;
126 #endif
127 }
128 
129 static u16 xt_ct_flags_to_dir(const struct xt_ct_target_info_v1 *info)
130 {
131 	switch (info->flags & (XT_CT_ZONE_DIR_ORIG |
132 			       XT_CT_ZONE_DIR_REPL)) {
133 	case XT_CT_ZONE_DIR_ORIG:
134 		return NF_CT_ZONE_DIR_ORIG;
135 	case XT_CT_ZONE_DIR_REPL:
136 		return NF_CT_ZONE_DIR_REPL;
137 	default:
138 		return NF_CT_DEFAULT_ZONE_DIR;
139 	}
140 }
141 
142 static int xt_ct_tg_check(const struct xt_tgchk_param *par,
143 			  struct xt_ct_target_info_v1 *info)
144 {
145 	struct nf_conntrack_zone zone;
146 	struct nf_conn_help *help;
147 	struct nf_conn *ct;
148 	int ret = -EOPNOTSUPP;
149 
150 	if (info->flags & XT_CT_NOTRACK) {
151 		ct = NULL;
152 		goto out;
153 	}
154 
155 #ifndef CONFIG_NF_CONNTRACK_ZONES
156 	if (info->zone || info->flags & (XT_CT_ZONE_DIR_ORIG |
157 					 XT_CT_ZONE_DIR_REPL |
158 					 XT_CT_ZONE_MARK))
159 		goto err1;
160 #endif
161 
162 	ret = nf_ct_netns_get(par->net, par->family);
163 	if (ret < 0)
164 		goto err1;
165 
166 	memset(&zone, 0, sizeof(zone));
167 	zone.id = info->zone;
168 	zone.dir = xt_ct_flags_to_dir(info);
169 	if (info->flags & XT_CT_ZONE_MARK)
170 		zone.flags |= NF_CT_FLAG_MARK;
171 
172 	ct = nf_ct_tmpl_alloc(par->net, &zone, GFP_KERNEL);
173 	if (!ct) {
174 		ret = -ENOMEM;
175 		goto err2;
176 	}
177 
178 	ret = 0;
179 	if ((info->ct_events || info->exp_events) &&
180 	    !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
181 				  GFP_KERNEL)) {
182 		ret = -EINVAL;
183 		goto err3;
184 	}
185 
186 	if (info->helper[0]) {
187 		if (strnlen(info->helper, sizeof(info->helper)) == sizeof(info->helper)) {
188 			ret = -ENAMETOOLONG;
189 			goto err3;
190 		}
191 
192 		ret = xt_ct_set_helper(ct, info->helper, par);
193 		if (ret < 0)
194 			goto err3;
195 	}
196 
197 	if (info->timeout[0]) {
198 		if (strnlen(info->timeout, sizeof(info->timeout)) == sizeof(info->timeout)) {
199 			ret = -ENAMETOOLONG;
200 			goto err4;
201 		}
202 
203 		ret = xt_ct_set_timeout(ct, par, info->timeout);
204 		if (ret < 0)
205 			goto err4;
206 	}
207 	__set_bit(IPS_CONFIRMED_BIT, &ct->status);
208 	nf_conntrack_get(&ct->ct_general);
209 out:
210 	info->ct = ct;
211 	return 0;
212 
213 err4:
214 	help = nfct_help(ct);
215 	if (help)
216 		nf_conntrack_helper_put(help->helper);
217 err3:
218 	nf_ct_tmpl_free(ct);
219 err2:
220 	nf_ct_netns_put(par->net, par->family);
221 err1:
222 	return ret;
223 }
224 
225 static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
226 {
227 	struct xt_ct_target_info *info = par->targinfo;
228 	struct xt_ct_target_info_v1 info_v1 = {
229 		.flags 		= info->flags,
230 		.zone		= info->zone,
231 		.ct_events	= info->ct_events,
232 		.exp_events	= info->exp_events,
233 	};
234 	int ret;
235 
236 	if (info->flags & ~XT_CT_NOTRACK)
237 		return -EINVAL;
238 
239 	memcpy(info_v1.helper, info->helper, sizeof(info->helper));
240 
241 	ret = xt_ct_tg_check(par, &info_v1);
242 	if (ret < 0)
243 		return ret;
244 
245 	info->ct = info_v1.ct;
246 
247 	return ret;
248 }
249 
250 static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
251 {
252 	struct xt_ct_target_info_v1 *info = par->targinfo;
253 
254 	if (info->flags & ~XT_CT_NOTRACK)
255 		return -EINVAL;
256 
257 	return xt_ct_tg_check(par, par->targinfo);
258 }
259 
260 static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par)
261 {
262 	struct xt_ct_target_info_v1 *info = par->targinfo;
263 
264 	if (info->flags & ~XT_CT_MASK)
265 		return -EINVAL;
266 
267 	return xt_ct_tg_check(par, par->targinfo);
268 }
269 
270 static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
271 			     struct xt_ct_target_info_v1 *info)
272 {
273 	struct nf_conn *ct = info->ct;
274 	struct nf_conn_help *help;
275 
276 	if (ct) {
277 		help = nfct_help(ct);
278 		if (help)
279 			nf_conntrack_helper_put(help->helper);
280 
281 		nf_ct_netns_put(par->net, par->family);
282 
283 		nf_ct_destroy_timeout(ct);
284 		nf_ct_put(info->ct);
285 	}
286 }
287 
288 static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
289 {
290 	struct xt_ct_target_info *info = par->targinfo;
291 	struct xt_ct_target_info_v1 info_v1 = {
292 		.flags 		= info->flags,
293 		.zone		= info->zone,
294 		.ct_events	= info->ct_events,
295 		.exp_events	= info->exp_events,
296 		.ct		= info->ct,
297 	};
298 	memcpy(info_v1.helper, info->helper, sizeof(info->helper));
299 
300 	xt_ct_tg_destroy(par, &info_v1);
301 }
302 
303 static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
304 {
305 	xt_ct_tg_destroy(par, par->targinfo);
306 }
307 
308 static struct xt_target xt_ct_tg_reg[] __read_mostly = {
309 	{
310 		.name		= "CT",
311 		.family		= NFPROTO_UNSPEC,
312 		.targetsize	= sizeof(struct xt_ct_target_info),
313 		.usersize	= offsetof(struct xt_ct_target_info, ct),
314 		.checkentry	= xt_ct_tg_check_v0,
315 		.destroy	= xt_ct_tg_destroy_v0,
316 		.target		= xt_ct_target_v0,
317 		.table		= "raw",
318 		.me		= THIS_MODULE,
319 	},
320 	{
321 		.name		= "CT",
322 		.family		= NFPROTO_UNSPEC,
323 		.revision	= 1,
324 		.targetsize	= sizeof(struct xt_ct_target_info_v1),
325 		.usersize	= offsetof(struct xt_ct_target_info, ct),
326 		.checkentry	= xt_ct_tg_check_v1,
327 		.destroy	= xt_ct_tg_destroy_v1,
328 		.target		= xt_ct_target_v1,
329 		.table		= "raw",
330 		.me		= THIS_MODULE,
331 	},
332 	{
333 		.name		= "CT",
334 		.family		= NFPROTO_UNSPEC,
335 		.revision	= 2,
336 		.targetsize	= sizeof(struct xt_ct_target_info_v1),
337 		.usersize	= offsetof(struct xt_ct_target_info, ct),
338 		.checkentry	= xt_ct_tg_check_v2,
339 		.destroy	= xt_ct_tg_destroy_v1,
340 		.target		= xt_ct_target_v1,
341 		.table		= "raw",
342 		.me		= THIS_MODULE,
343 	},
344 };
345 
346 static unsigned int
347 notrack_tg(struct sk_buff *skb, const struct xt_action_param *par)
348 {
349 	/* Previously seen (loopback)? Ignore. */
350 	if (skb->_nfct != 0)
351 		return XT_CONTINUE;
352 
353 	nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
354 
355 	return XT_CONTINUE;
356 }
357 
358 static int notrack_chk(const struct xt_tgchk_param *par)
359 {
360 	if (!par->net->xt.notrack_deprecated_warning) {
361 		pr_info("netfilter: NOTRACK target is deprecated, "
362 			"use CT instead or upgrade iptables\n");
363 		par->net->xt.notrack_deprecated_warning = true;
364 	}
365 	return 0;
366 }
367 
368 static struct xt_target notrack_tg_reg __read_mostly = {
369 	.name		= "NOTRACK",
370 	.revision	= 0,
371 	.family		= NFPROTO_UNSPEC,
372 	.checkentry	= notrack_chk,
373 	.target		= notrack_tg,
374 	.table		= "raw",
375 	.me		= THIS_MODULE,
376 };
377 
378 static int __init xt_ct_tg_init(void)
379 {
380 	int ret;
381 
382 	ret = xt_register_target(&notrack_tg_reg);
383 	if (ret < 0)
384 		return ret;
385 
386 	ret = xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
387 	if (ret < 0) {
388 		xt_unregister_target(&notrack_tg_reg);
389 		return ret;
390 	}
391 	return 0;
392 }
393 
394 static void __exit xt_ct_tg_exit(void)
395 {
396 	xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
397 	xt_unregister_target(&notrack_tg_reg);
398 }
399 
400 module_init(xt_ct_tg_init);
401 module_exit(xt_ct_tg_exit);
402 
403 MODULE_LICENSE("GPL");
404 MODULE_DESCRIPTION("Xtables: connection tracking target");
405 MODULE_ALIAS("ipt_CT");
406 MODULE_ALIAS("ip6t_CT");
407 MODULE_ALIAS("ipt_NOTRACK");
408 MODULE_ALIAS("ip6t_NOTRACK");
409