1/*
2 * This module is used to copy security markings from packets
3 * to connections, and restore security markings from connections
4 * back to packets.  This would normally be performed in conjunction
5 * with the SECMARK target and state match.
6 *
7 * Based somewhat on CONNMARK:
8 *   Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
9 *    by Henrik Nordstrom <hno@marasystems.com>
10 *
11 * (C) 2006,2008 Red Hat, Inc., James Morris <jmorris@redhat.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 *
17 */
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19#include <linux/module.h>
20#include <linux/skbuff.h>
21#include <linux/netfilter/x_tables.h>
22#include <linux/netfilter/xt_CONNSECMARK.h>
23#include <net/netfilter/nf_conntrack.h>
24#include <net/netfilter/nf_conntrack_ecache.h>
25
26MODULE_LICENSE("GPL");
27MODULE_AUTHOR("James Morris <jmorris@redhat.com>");
28MODULE_DESCRIPTION("Xtables: target for copying between connection and security mark");
29MODULE_ALIAS("ipt_CONNSECMARK");
30MODULE_ALIAS("ip6t_CONNSECMARK");
31
32/*
33 * If the packet has a security mark and the connection does not, copy
34 * the security mark from the packet to the connection.
35 */
36static void secmark_save(const struct sk_buff *skb)
37{
38	if (skb->secmark) {
39		struct nf_conn *ct;
40		enum ip_conntrack_info ctinfo;
41
42		ct = nf_ct_get(skb, &ctinfo);
43		if (ct && !ct->secmark) {
44			ct->secmark = skb->secmark;
45			nf_conntrack_event_cache(IPCT_SECMARK, ct);
46		}
47	}
48}
49
50/*
51 * If packet has no security mark, and the connection does, restore the
52 * security mark from the connection to the packet.
53 */
54static void secmark_restore(struct sk_buff *skb)
55{
56	if (!skb->secmark) {
57		const struct nf_conn *ct;
58		enum ip_conntrack_info ctinfo;
59
60		ct = nf_ct_get(skb, &ctinfo);
61		if (ct && ct->secmark)
62			skb->secmark = ct->secmark;
63	}
64}
65
66static unsigned int
67connsecmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
68{
69	const struct xt_connsecmark_target_info *info = par->targinfo;
70
71	switch (info->mode) {
72	case CONNSECMARK_SAVE:
73		secmark_save(skb);
74		break;
75
76	case CONNSECMARK_RESTORE:
77		secmark_restore(skb);
78		break;
79
80	default:
81		BUG();
82	}
83
84	return XT_CONTINUE;
85}
86
87static int connsecmark_tg_check(const struct xt_tgchk_param *par)
88{
89	const struct xt_connsecmark_target_info *info = par->targinfo;
90	int ret;
91
92	if (strcmp(par->table, "mangle") != 0 &&
93	    strcmp(par->table, "security") != 0) {
94		pr_info("target only valid in the \'mangle\' "
95			"or \'security\' tables, not \'%s\'.\n", par->table);
96		return -EINVAL;
97	}
98
99	switch (info->mode) {
100	case CONNSECMARK_SAVE:
101	case CONNSECMARK_RESTORE:
102		break;
103
104	default:
105		pr_info("invalid mode: %hu\n", info->mode);
106		return -EINVAL;
107	}
108
109	ret = nf_ct_l3proto_try_module_get(par->family);
110	if (ret < 0)
111		pr_info("cannot load conntrack support for proto=%u\n",
112			par->family);
113	return ret;
114}
115
116static void connsecmark_tg_destroy(const struct xt_tgdtor_param *par)
117{
118	nf_ct_l3proto_module_put(par->family);
119}
120
121static struct xt_target connsecmark_tg_reg __read_mostly = {
122	.name       = "CONNSECMARK",
123	.revision   = 0,
124	.family     = NFPROTO_UNSPEC,
125	.checkentry = connsecmark_tg_check,
126	.destroy    = connsecmark_tg_destroy,
127	.target     = connsecmark_tg,
128	.targetsize = sizeof(struct xt_connsecmark_target_info),
129	.me         = THIS_MODULE,
130};
131
132static int __init connsecmark_tg_init(void)
133{
134	return xt_register_target(&connsecmark_tg_reg);
135}
136
137static void __exit connsecmark_tg_exit(void)
138{
139	xt_unregister_target(&connsecmark_tg_reg);
140}
141
142module_init(connsecmark_tg_init);
143module_exit(connsecmark_tg_exit);
144