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