1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2100468e9SJames Morris /*
3100468e9SJames Morris  * This module is used to copy security markings from packets
4100468e9SJames Morris  * to connections, and restore security markings from connections
5100468e9SJames Morris  * back to packets.  This would normally be performed in conjunction
6100468e9SJames Morris  * with the SECMARK target and state match.
7100468e9SJames Morris  *
8100468e9SJames Morris  * Based somewhat on CONNMARK:
950935339SAlexander A. Klimov  *   Copyright (C) 2002,2004 MARA Systems AB <https://www.marasystems.com>
10100468e9SJames Morris  *    by Henrik Nordstrom <hno@marasystems.com>
11100468e9SJames Morris  *
12560ee653SJames Morris  * (C) 2006,2008 Red Hat, Inc., James Morris <jmorris@redhat.com>
13100468e9SJames Morris  */
148bee4badSJan Engelhardt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15100468e9SJames Morris #include <linux/module.h>
16100468e9SJames Morris #include <linux/skbuff.h>
17100468e9SJames Morris #include <linux/netfilter/x_tables.h>
18100468e9SJames Morris #include <linux/netfilter/xt_CONNSECMARK.h>
19587aa641SPatrick McHardy #include <net/netfilter/nf_conntrack.h>
2037fccd85SPablo Neira Ayuso #include <net/netfilter/nf_conntrack_ecache.h>
21100468e9SJames Morris 
22100468e9SJames Morris MODULE_LICENSE("GPL");
23100468e9SJames Morris MODULE_AUTHOR("James Morris <jmorris@redhat.com>");
242ae15b64SJan Engelhardt MODULE_DESCRIPTION("Xtables: target for copying between connection and security mark");
25100468e9SJames Morris MODULE_ALIAS("ipt_CONNSECMARK");
26100468e9SJames Morris MODULE_ALIAS("ip6t_CONNSECMARK");
27100468e9SJames Morris 
28100468e9SJames Morris /*
29100468e9SJames Morris  * If the packet has a security mark and the connection does not, copy
30100468e9SJames Morris  * the security mark from the packet to the connection.
31100468e9SJames Morris  */
secmark_save(const struct sk_buff * skb)32a47362a2SJan Engelhardt static void secmark_save(const struct sk_buff *skb)
33100468e9SJames Morris {
34100468e9SJames Morris 	if (skb->secmark) {
35587aa641SPatrick McHardy 		struct nf_conn *ct;
36100468e9SJames Morris 		enum ip_conntrack_info ctinfo;
37100468e9SJames Morris 
38587aa641SPatrick McHardy 		ct = nf_ct_get(skb, &ctinfo);
3937fccd85SPablo Neira Ayuso 		if (ct && !ct->secmark) {
40587aa641SPatrick McHardy 			ct->secmark = skb->secmark;
41a71996fcSAlexey Dobriyan 			nf_conntrack_event_cache(IPCT_SECMARK, ct);
4237fccd85SPablo Neira Ayuso 		}
43100468e9SJames Morris 	}
44100468e9SJames Morris }
45100468e9SJames Morris 
46100468e9SJames Morris /*
47100468e9SJames Morris  * If packet has no security mark, and the connection does, restore the
48100468e9SJames Morris  * security mark from the connection to the packet.
49100468e9SJames Morris  */
secmark_restore(struct sk_buff * skb)50100468e9SJames Morris static void secmark_restore(struct sk_buff *skb)
51100468e9SJames Morris {
52100468e9SJames Morris 	if (!skb->secmark) {
533cf93c96SJan Engelhardt 		const struct nf_conn *ct;
54100468e9SJames Morris 		enum ip_conntrack_info ctinfo;
55100468e9SJames Morris 
56587aa641SPatrick McHardy 		ct = nf_ct_get(skb, &ctinfo);
57587aa641SPatrick McHardy 		if (ct && ct->secmark)
58587aa641SPatrick McHardy 			skb->secmark = ct->secmark;
59100468e9SJames Morris 	}
60100468e9SJames Morris }
61100468e9SJames Morris 
62d3c5ee6dSJan Engelhardt static unsigned int
connsecmark_tg(struct sk_buff * skb,const struct xt_action_param * par)634b560b44SJan Engelhardt connsecmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
64100468e9SJames Morris {
657eb35586SJan Engelhardt 	const struct xt_connsecmark_target_info *info = par->targinfo;
66100468e9SJames Morris 
67100468e9SJames Morris 	switch (info->mode) {
68100468e9SJames Morris 	case CONNSECMARK_SAVE:
69100468e9SJames Morris 		secmark_save(skb);
70100468e9SJames Morris 		break;
71100468e9SJames Morris 
72100468e9SJames Morris 	case CONNSECMARK_RESTORE:
73100468e9SJames Morris 		secmark_restore(skb);
74100468e9SJames Morris 		break;
75100468e9SJames Morris 
76100468e9SJames Morris 	default:
77100468e9SJames Morris 		BUG();
78100468e9SJames Morris 	}
79100468e9SJames Morris 
80100468e9SJames Morris 	return XT_CONTINUE;
81100468e9SJames Morris }
82100468e9SJames Morris 
connsecmark_tg_check(const struct xt_tgchk_param * par)83135367b8SJan Engelhardt static int connsecmark_tg_check(const struct xt_tgchk_param *par)
84100468e9SJames Morris {
85af5d6dc2SJan Engelhardt 	const struct xt_connsecmark_target_info *info = par->targinfo;
864a5a5c73SJan Engelhardt 	int ret;
87100468e9SJames Morris 
88af5d6dc2SJan Engelhardt 	if (strcmp(par->table, "mangle") != 0 &&
89af5d6dc2SJan Engelhardt 	    strcmp(par->table, "security") != 0) {
90cc48baefSFlorian Westphal 		pr_info_ratelimited("only valid in \'mangle\' or \'security\' table, not \'%s\'\n",
91cc48baefSFlorian Westphal 				    par->table);
92d6b00a53SJan Engelhardt 		return -EINVAL;
93560ee653SJames Morris 	}
94560ee653SJames Morris 
95100468e9SJames Morris 	switch (info->mode) {
96100468e9SJames Morris 	case CONNSECMARK_SAVE:
97100468e9SJames Morris 	case CONNSECMARK_RESTORE:
98100468e9SJames Morris 		break;
99100468e9SJames Morris 
100100468e9SJames Morris 	default:
101b2606644SFlorian Westphal 		pr_info_ratelimited("invalid mode: %hu\n", info->mode);
1024a5a5c73SJan Engelhardt 		return -EINVAL;
103100468e9SJames Morris 	}
104100468e9SJames Morris 
105ecb2421bSFlorian Westphal 	ret = nf_ct_netns_get(par->net, par->family);
106f95c74e3SJan Engelhardt 	if (ret < 0)
107b2606644SFlorian Westphal 		pr_info_ratelimited("cannot load conntrack support for proto=%u\n",
1088bee4badSJan Engelhardt 				    par->family);
1094a5a5c73SJan Engelhardt 	return ret;
11067b4af29SJan Engelhardt }
111100468e9SJames Morris 
connsecmark_tg_destroy(const struct xt_tgdtor_param * par)112a2df1648SJan Engelhardt static void connsecmark_tg_destroy(const struct xt_tgdtor_param *par)
11311078c37SYasuyuki Kozakai {
114ecb2421bSFlorian Westphal 	nf_ct_netns_put(par->net, par->family);
11511078c37SYasuyuki Kozakai }
11611078c37SYasuyuki Kozakai 
11792f3b2b1SJan Engelhardt static struct xt_target connsecmark_tg_reg __read_mostly = {
118100468e9SJames Morris 	.name       = "CONNSECMARK",
11992f3b2b1SJan Engelhardt 	.revision   = 0,
12092f3b2b1SJan Engelhardt 	.family     = NFPROTO_UNSPEC,
121d3c5ee6dSJan Engelhardt 	.checkentry = connsecmark_tg_check,
122d3c5ee6dSJan Engelhardt 	.destroy    = connsecmark_tg_destroy,
123d3c5ee6dSJan Engelhardt 	.target     = connsecmark_tg,
124100468e9SJames Morris 	.targetsize = sizeof(struct xt_connsecmark_target_info),
125100468e9SJames Morris 	.me         = THIS_MODULE,
126100468e9SJames Morris };
127100468e9SJames Morris 
connsecmark_tg_init(void)128d3c5ee6dSJan Engelhardt static int __init connsecmark_tg_init(void)
129100468e9SJames Morris {
13092f3b2b1SJan Engelhardt 	return xt_register_target(&connsecmark_tg_reg);
131100468e9SJames Morris }
132100468e9SJames Morris 
connsecmark_tg_exit(void)133d3c5ee6dSJan Engelhardt static void __exit connsecmark_tg_exit(void)
134100468e9SJames Morris {
13592f3b2b1SJan Engelhardt 	xt_unregister_target(&connsecmark_tg_reg);
136100468e9SJames Morris }
137100468e9SJames Morris 
138d3c5ee6dSJan Engelhardt module_init(connsecmark_tg_init);
139d3c5ee6dSJan Engelhardt module_exit(connsecmark_tg_exit);
140