xref: /openbmc/linux/net/netfilter/xt_TEE.c (revision 3a63cbb8)
13a63cbb8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e281b198SJan Engelhardt /*
3e281b198SJan Engelhardt  *	"TEE" target extension for Xtables
4e281b198SJan Engelhardt  *	Copyright © Sebastian Claßen, 2007
5e281b198SJan Engelhardt  *	Jan Engelhardt, 2007-2010
6e281b198SJan Engelhardt  *
7e281b198SJan Engelhardt  *	based on ipt_ROUTE.c from Cédric de Launois
8e281b198SJan Engelhardt  *	<delaunois@info.ucl.be>
9e281b198SJan Engelhardt  */
10e281b198SJan Engelhardt #include <linux/module.h>
11e281b198SJan Engelhardt #include <linux/skbuff.h>
12bbde9fc1SPablo Neira Ayuso #include <linux/route.h>
13e281b198SJan Engelhardt #include <linux/netfilter/x_tables.h>
14f24d2d4fSTaehee Yoo #include <net/net_namespace.h>
15f24d2d4fSTaehee Yoo #include <net/netns/generic.h>
16bbde9fc1SPablo Neira Ayuso #include <net/route.h>
17bbde9fc1SPablo Neira Ayuso #include <net/netfilter/ipv4/nf_dup_ipv4.h>
18bbde9fc1SPablo Neira Ayuso #include <net/netfilter/ipv6/nf_dup_ipv6.h>
19e281b198SJan Engelhardt #include <linux/netfilter/xt_TEE.h>
20e281b198SJan Engelhardt 
2122265a5cSPatrick McHardy struct xt_tee_priv {
229e2f6c5dSKirill Tkhai 	struct list_head	list;
2322265a5cSPatrick McHardy 	struct xt_tee_tginfo	*tginfo;
2422265a5cSPatrick McHardy 	int			oif;
2522265a5cSPatrick McHardy };
2622265a5cSPatrick McHardy 
27f24d2d4fSTaehee Yoo static unsigned int tee_net_id __read_mostly;
28e281b198SJan Engelhardt static const union nf_inet_addr tee_zero_address;
29e281b198SJan Engelhardt 
30f24d2d4fSTaehee Yoo struct tee_net {
31f24d2d4fSTaehee Yoo 	struct list_head priv_list;
32f24d2d4fSTaehee Yoo 	/* lock protects the priv_list */
33f24d2d4fSTaehee Yoo 	struct mutex lock;
34f24d2d4fSTaehee Yoo };
35f24d2d4fSTaehee Yoo 
36e281b198SJan Engelhardt static unsigned int
tee_tg4(struct sk_buff * skb,const struct xt_action_param * par)374b560b44SJan Engelhardt tee_tg4(struct sk_buff *skb, const struct xt_action_param *par)
38e281b198SJan Engelhardt {
39e281b198SJan Engelhardt 	const struct xt_tee_tginfo *info = par->targinfo;
4045efccdbSEric Dumazet 	int oif = info->priv ? info->priv->oif : 0;
41e281b198SJan Engelhardt 
42613dbd95SPablo Neira Ayuso 	nf_dup_ipv4(xt_net(par), skb, xt_hooknum(par), &info->gw.in, oif);
43e281b198SJan Engelhardt 
44e281b198SJan Engelhardt 	return XT_CONTINUE;
45e281b198SJan Engelhardt }
46e281b198SJan Engelhardt 
475d400a49SMáté Eckl #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
48e281b198SJan Engelhardt static unsigned int
tee_tg6(struct sk_buff * skb,const struct xt_action_param * par)494b560b44SJan Engelhardt tee_tg6(struct sk_buff *skb, const struct xt_action_param *par)
50e281b198SJan Engelhardt {
51e281b198SJan Engelhardt 	const struct xt_tee_tginfo *info = par->targinfo;
5245efccdbSEric Dumazet 	int oif = info->priv ? info->priv->oif : 0;
53e281b198SJan Engelhardt 
54613dbd95SPablo Neira Ayuso 	nf_dup_ipv6(xt_net(par), skb, xt_hooknum(par), &info->gw.in6, oif);
55e281b198SJan Engelhardt 
56e281b198SJan Engelhardt 	return XT_CONTINUE;
57e281b198SJan Engelhardt }
58dfd56b8bSEric Dumazet #endif
59e281b198SJan Engelhardt 
tee_netdev_event(struct notifier_block * this,unsigned long event,void * ptr)6022265a5cSPatrick McHardy static int tee_netdev_event(struct notifier_block *this, unsigned long event,
6122265a5cSPatrick McHardy 			    void *ptr)
6222265a5cSPatrick McHardy {
63351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
64f24d2d4fSTaehee Yoo 	struct net *net = dev_net(dev);
65f24d2d4fSTaehee Yoo 	struct tee_net *tn = net_generic(net, tee_net_id);
6622265a5cSPatrick McHardy 	struct xt_tee_priv *priv;
6722265a5cSPatrick McHardy 
68f24d2d4fSTaehee Yoo 	mutex_lock(&tn->lock);
69f24d2d4fSTaehee Yoo 	list_for_each_entry(priv, &tn->priv_list, list) {
7022265a5cSPatrick McHardy 		switch (event) {
7122265a5cSPatrick McHardy 		case NETDEV_REGISTER:
7222265a5cSPatrick McHardy 			if (!strcmp(dev->name, priv->tginfo->oif))
7322265a5cSPatrick McHardy 				priv->oif = dev->ifindex;
7422265a5cSPatrick McHardy 			break;
7522265a5cSPatrick McHardy 		case NETDEV_UNREGISTER:
7622265a5cSPatrick McHardy 			if (dev->ifindex == priv->oif)
7722265a5cSPatrick McHardy 				priv->oif = -1;
7822265a5cSPatrick McHardy 			break;
7922265a5cSPatrick McHardy 		case NETDEV_CHANGENAME:
8022265a5cSPatrick McHardy 			if (!strcmp(dev->name, priv->tginfo->oif))
8122265a5cSPatrick McHardy 				priv->oif = dev->ifindex;
8222265a5cSPatrick McHardy 			else if (dev->ifindex == priv->oif)
8322265a5cSPatrick McHardy 				priv->oif = -1;
8422265a5cSPatrick McHardy 			break;
8522265a5cSPatrick McHardy 		}
869e2f6c5dSKirill Tkhai 	}
87f24d2d4fSTaehee Yoo 	mutex_unlock(&tn->lock);
8822265a5cSPatrick McHardy 
8922265a5cSPatrick McHardy 	return NOTIFY_DONE;
9022265a5cSPatrick McHardy }
9122265a5cSPatrick McHardy 
tee_tg_check(const struct xt_tgchk_param * par)92e281b198SJan Engelhardt static int tee_tg_check(const struct xt_tgchk_param *par)
93e281b198SJan Engelhardt {
94f24d2d4fSTaehee Yoo 	struct tee_net *tn = net_generic(par->net, tee_net_id);
9522265a5cSPatrick McHardy 	struct xt_tee_tginfo *info = par->targinfo;
9622265a5cSPatrick McHardy 	struct xt_tee_priv *priv;
97e281b198SJan Engelhardt 
9822265a5cSPatrick McHardy 	/* 0.0.0.0 and :: not allowed */
9922265a5cSPatrick McHardy 	if (memcmp(&info->gw, &tee_zero_address,
10022265a5cSPatrick McHardy 		   sizeof(tee_zero_address)) == 0)
10122265a5cSPatrick McHardy 		return -EINVAL;
10222265a5cSPatrick McHardy 
10322265a5cSPatrick McHardy 	if (info->oif[0]) {
10418c0ab87STaehee Yoo 		struct net_device *dev;
10518c0ab87STaehee Yoo 
106e281b198SJan Engelhardt 		if (info->oif[sizeof(info->oif)-1] != '\0')
107e281b198SJan Engelhardt 			return -EINVAL;
10822265a5cSPatrick McHardy 
10922265a5cSPatrick McHardy 		priv = kzalloc(sizeof(*priv), GFP_KERNEL);
11022265a5cSPatrick McHardy 		if (priv == NULL)
11122265a5cSPatrick McHardy 			return -ENOMEM;
11222265a5cSPatrick McHardy 
11322265a5cSPatrick McHardy 		priv->tginfo  = info;
11422265a5cSPatrick McHardy 		priv->oif     = -1;
11522265a5cSPatrick McHardy 		info->priv    = priv;
11622265a5cSPatrick McHardy 
11718c0ab87STaehee Yoo 		dev = dev_get_by_name(par->net, info->oif);
11818c0ab87STaehee Yoo 		if (dev) {
11918c0ab87STaehee Yoo 			priv->oif = dev->ifindex;
12018c0ab87STaehee Yoo 			dev_put(dev);
12118c0ab87STaehee Yoo 		}
122f24d2d4fSTaehee Yoo 		mutex_lock(&tn->lock);
123f24d2d4fSTaehee Yoo 		list_add(&priv->list, &tn->priv_list);
124f24d2d4fSTaehee Yoo 		mutex_unlock(&tn->lock);
12522265a5cSPatrick McHardy 	} else
12622265a5cSPatrick McHardy 		info->priv = NULL;
12722265a5cSPatrick McHardy 
128dcebd315SFlorian Westphal 	static_key_slow_inc(&xt_tee_enabled);
12922265a5cSPatrick McHardy 	return 0;
13022265a5cSPatrick McHardy }
13122265a5cSPatrick McHardy 
tee_tg_destroy(const struct xt_tgdtor_param * par)13222265a5cSPatrick McHardy static void tee_tg_destroy(const struct xt_tgdtor_param *par)
13322265a5cSPatrick McHardy {
134f24d2d4fSTaehee Yoo 	struct tee_net *tn = net_generic(par->net, tee_net_id);
13522265a5cSPatrick McHardy 	struct xt_tee_tginfo *info = par->targinfo;
13622265a5cSPatrick McHardy 
13722265a5cSPatrick McHardy 	if (info->priv) {
138f24d2d4fSTaehee Yoo 		mutex_lock(&tn->lock);
1399e2f6c5dSKirill Tkhai 		list_del(&info->priv->list);
140f24d2d4fSTaehee Yoo 		mutex_unlock(&tn->lock);
14122265a5cSPatrick McHardy 		kfree(info->priv);
14222265a5cSPatrick McHardy 	}
143dcebd315SFlorian Westphal 	static_key_slow_dec(&xt_tee_enabled);
144e281b198SJan Engelhardt }
145e281b198SJan Engelhardt 
146e281b198SJan Engelhardt static struct xt_target tee_tg_reg[] __read_mostly = {
147e281b198SJan Engelhardt 	{
148e281b198SJan Engelhardt 		.name       = "TEE",
149e281b198SJan Engelhardt 		.revision   = 1,
150e281b198SJan Engelhardt 		.family     = NFPROTO_IPV4,
151e281b198SJan Engelhardt 		.target     = tee_tg4,
152e281b198SJan Engelhardt 		.targetsize = sizeof(struct xt_tee_tginfo),
153ec231890SWillem de Bruijn 		.usersize   = offsetof(struct xt_tee_tginfo, priv),
154e281b198SJan Engelhardt 		.checkentry = tee_tg_check,
15522265a5cSPatrick McHardy 		.destroy    = tee_tg_destroy,
156e281b198SJan Engelhardt 		.me         = THIS_MODULE,
157e281b198SJan Engelhardt 	},
1585d400a49SMáté Eckl #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
159e281b198SJan Engelhardt 	{
160e281b198SJan Engelhardt 		.name       = "TEE",
161e281b198SJan Engelhardt 		.revision   = 1,
162e281b198SJan Engelhardt 		.family     = NFPROTO_IPV6,
163e281b198SJan Engelhardt 		.target     = tee_tg6,
164e281b198SJan Engelhardt 		.targetsize = sizeof(struct xt_tee_tginfo),
165ec231890SWillem de Bruijn 		.usersize   = offsetof(struct xt_tee_tginfo, priv),
166e281b198SJan Engelhardt 		.checkentry = tee_tg_check,
16722265a5cSPatrick McHardy 		.destroy    = tee_tg_destroy,
168e281b198SJan Engelhardt 		.me         = THIS_MODULE,
169e281b198SJan Engelhardt 	},
170e281b198SJan Engelhardt #endif
171e281b198SJan Engelhardt };
172e281b198SJan Engelhardt 
tee_net_init(struct net * net)173f24d2d4fSTaehee Yoo static int __net_init tee_net_init(struct net *net)
174f24d2d4fSTaehee Yoo {
175f24d2d4fSTaehee Yoo 	struct tee_net *tn = net_generic(net, tee_net_id);
176f24d2d4fSTaehee Yoo 
177f24d2d4fSTaehee Yoo 	INIT_LIST_HEAD(&tn->priv_list);
178f24d2d4fSTaehee Yoo 	mutex_init(&tn->lock);
179f24d2d4fSTaehee Yoo 	return 0;
180f24d2d4fSTaehee Yoo }
181f24d2d4fSTaehee Yoo 
182f24d2d4fSTaehee Yoo static struct pernet_operations tee_net_ops = {
183f24d2d4fSTaehee Yoo 	.init = tee_net_init,
184f24d2d4fSTaehee Yoo 	.id   = &tee_net_id,
185f24d2d4fSTaehee Yoo 	.size = sizeof(struct tee_net),
186f24d2d4fSTaehee Yoo };
187f24d2d4fSTaehee Yoo 
1889e2f6c5dSKirill Tkhai static struct notifier_block tee_netdev_notifier = {
1899e2f6c5dSKirill Tkhai 	.notifier_call = tee_netdev_event,
1909e2f6c5dSKirill Tkhai };
1919e2f6c5dSKirill Tkhai 
tee_tg_init(void)192e281b198SJan Engelhardt static int __init tee_tg_init(void)
193e281b198SJan Engelhardt {
1949e2f6c5dSKirill Tkhai 	int ret;
1959e2f6c5dSKirill Tkhai 
196f24d2d4fSTaehee Yoo 	ret = register_pernet_subsys(&tee_net_ops);
197f24d2d4fSTaehee Yoo 	if (ret < 0)
198f24d2d4fSTaehee Yoo 		return ret;
199f24d2d4fSTaehee Yoo 
2009e2f6c5dSKirill Tkhai 	ret = xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
201f24d2d4fSTaehee Yoo 	if (ret < 0)
202f24d2d4fSTaehee Yoo 		goto cleanup_subsys;
203f24d2d4fSTaehee Yoo 
2049e2f6c5dSKirill Tkhai 	ret = register_netdevice_notifier(&tee_netdev_notifier);
205f24d2d4fSTaehee Yoo 	if (ret < 0)
206f24d2d4fSTaehee Yoo 		goto unregister_targets;
2079e2f6c5dSKirill Tkhai 
2089e2f6c5dSKirill Tkhai 	return 0;
209f24d2d4fSTaehee Yoo 
210f24d2d4fSTaehee Yoo unregister_targets:
211f24d2d4fSTaehee Yoo 	xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
212f24d2d4fSTaehee Yoo cleanup_subsys:
213f24d2d4fSTaehee Yoo 	unregister_pernet_subsys(&tee_net_ops);
214f24d2d4fSTaehee Yoo 	return ret;
215e281b198SJan Engelhardt }
216e281b198SJan Engelhardt 
tee_tg_exit(void)217e281b198SJan Engelhardt static void __exit tee_tg_exit(void)
218e281b198SJan Engelhardt {
2199e2f6c5dSKirill Tkhai 	unregister_netdevice_notifier(&tee_netdev_notifier);
220e281b198SJan Engelhardt 	xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
221f24d2d4fSTaehee Yoo 	unregister_pernet_subsys(&tee_net_ops);
222e281b198SJan Engelhardt }
223e281b198SJan Engelhardt 
224e281b198SJan Engelhardt module_init(tee_tg_init);
225e281b198SJan Engelhardt module_exit(tee_tg_exit);
226e281b198SJan Engelhardt MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>");
227e281b198SJan Engelhardt MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
228e281b198SJan Engelhardt MODULE_DESCRIPTION("Xtables: Reroute packet copy");
229e281b198SJan Engelhardt MODULE_LICENSE("GPL");
230e281b198SJan Engelhardt MODULE_ALIAS("ipt_TEE");
231e281b198SJan Engelhardt MODULE_ALIAS("ip6t_TEE");
232