1 /* 2 * "TEE" target extension for Xtables 3 * Copyright © Sebastian Claßen, 2007 4 * Jan Engelhardt, 2007-2010 5 * 6 * based on ipt_ROUTE.c from Cédric de Launois 7 * <delaunois@info.ucl.be> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * version 2 or later, as published by the Free Software Foundation. 12 */ 13 #include <linux/module.h> 14 #include <linux/skbuff.h> 15 #include <linux/route.h> 16 #include <linux/netfilter/x_tables.h> 17 #include <net/net_namespace.h> 18 #include <net/netns/generic.h> 19 #include <net/route.h> 20 #include <net/netfilter/ipv4/nf_dup_ipv4.h> 21 #include <net/netfilter/ipv6/nf_dup_ipv6.h> 22 #include <linux/netfilter/xt_TEE.h> 23 24 struct xt_tee_priv { 25 struct list_head list; 26 struct xt_tee_tginfo *tginfo; 27 int oif; 28 }; 29 30 static unsigned int tee_net_id __read_mostly; 31 static const union nf_inet_addr tee_zero_address; 32 33 struct tee_net { 34 struct list_head priv_list; 35 /* lock protects the priv_list */ 36 struct mutex lock; 37 }; 38 39 static unsigned int 40 tee_tg4(struct sk_buff *skb, const struct xt_action_param *par) 41 { 42 const struct xt_tee_tginfo *info = par->targinfo; 43 int oif = info->priv ? info->priv->oif : 0; 44 45 nf_dup_ipv4(xt_net(par), skb, xt_hooknum(par), &info->gw.in, oif); 46 47 return XT_CONTINUE; 48 } 49 50 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 51 static unsigned int 52 tee_tg6(struct sk_buff *skb, const struct xt_action_param *par) 53 { 54 const struct xt_tee_tginfo *info = par->targinfo; 55 int oif = info->priv ? info->priv->oif : 0; 56 57 nf_dup_ipv6(xt_net(par), skb, xt_hooknum(par), &info->gw.in6, oif); 58 59 return XT_CONTINUE; 60 } 61 #endif 62 63 static int tee_netdev_event(struct notifier_block *this, unsigned long event, 64 void *ptr) 65 { 66 struct net_device *dev = netdev_notifier_info_to_dev(ptr); 67 struct net *net = dev_net(dev); 68 struct tee_net *tn = net_generic(net, tee_net_id); 69 struct xt_tee_priv *priv; 70 71 mutex_lock(&tn->lock); 72 list_for_each_entry(priv, &tn->priv_list, list) { 73 switch (event) { 74 case NETDEV_REGISTER: 75 if (!strcmp(dev->name, priv->tginfo->oif)) 76 priv->oif = dev->ifindex; 77 break; 78 case NETDEV_UNREGISTER: 79 if (dev->ifindex == priv->oif) 80 priv->oif = -1; 81 break; 82 case NETDEV_CHANGENAME: 83 if (!strcmp(dev->name, priv->tginfo->oif)) 84 priv->oif = dev->ifindex; 85 else if (dev->ifindex == priv->oif) 86 priv->oif = -1; 87 break; 88 } 89 } 90 mutex_unlock(&tn->lock); 91 92 return NOTIFY_DONE; 93 } 94 95 static int tee_tg_check(const struct xt_tgchk_param *par) 96 { 97 struct tee_net *tn = net_generic(par->net, tee_net_id); 98 struct xt_tee_tginfo *info = par->targinfo; 99 struct xt_tee_priv *priv; 100 101 /* 0.0.0.0 and :: not allowed */ 102 if (memcmp(&info->gw, &tee_zero_address, 103 sizeof(tee_zero_address)) == 0) 104 return -EINVAL; 105 106 if (info->oif[0]) { 107 struct net_device *dev; 108 109 if (info->oif[sizeof(info->oif)-1] != '\0') 110 return -EINVAL; 111 112 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 113 if (priv == NULL) 114 return -ENOMEM; 115 116 priv->tginfo = info; 117 priv->oif = -1; 118 info->priv = priv; 119 120 dev = dev_get_by_name(par->net, info->oif); 121 if (dev) { 122 priv->oif = dev->ifindex; 123 dev_put(dev); 124 } 125 mutex_lock(&tn->lock); 126 list_add(&priv->list, &tn->priv_list); 127 mutex_unlock(&tn->lock); 128 } else 129 info->priv = NULL; 130 131 static_key_slow_inc(&xt_tee_enabled); 132 return 0; 133 } 134 135 static void tee_tg_destroy(const struct xt_tgdtor_param *par) 136 { 137 struct tee_net *tn = net_generic(par->net, tee_net_id); 138 struct xt_tee_tginfo *info = par->targinfo; 139 140 if (info->priv) { 141 mutex_lock(&tn->lock); 142 list_del(&info->priv->list); 143 mutex_unlock(&tn->lock); 144 kfree(info->priv); 145 } 146 static_key_slow_dec(&xt_tee_enabled); 147 } 148 149 static struct xt_target tee_tg_reg[] __read_mostly = { 150 { 151 .name = "TEE", 152 .revision = 1, 153 .family = NFPROTO_IPV4, 154 .target = tee_tg4, 155 .targetsize = sizeof(struct xt_tee_tginfo), 156 .usersize = offsetof(struct xt_tee_tginfo, priv), 157 .checkentry = tee_tg_check, 158 .destroy = tee_tg_destroy, 159 .me = THIS_MODULE, 160 }, 161 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 162 { 163 .name = "TEE", 164 .revision = 1, 165 .family = NFPROTO_IPV6, 166 .target = tee_tg6, 167 .targetsize = sizeof(struct xt_tee_tginfo), 168 .usersize = offsetof(struct xt_tee_tginfo, priv), 169 .checkentry = tee_tg_check, 170 .destroy = tee_tg_destroy, 171 .me = THIS_MODULE, 172 }, 173 #endif 174 }; 175 176 static int __net_init tee_net_init(struct net *net) 177 { 178 struct tee_net *tn = net_generic(net, tee_net_id); 179 180 INIT_LIST_HEAD(&tn->priv_list); 181 mutex_init(&tn->lock); 182 return 0; 183 } 184 185 static struct pernet_operations tee_net_ops = { 186 .init = tee_net_init, 187 .id = &tee_net_id, 188 .size = sizeof(struct tee_net), 189 }; 190 191 static struct notifier_block tee_netdev_notifier = { 192 .notifier_call = tee_netdev_event, 193 }; 194 195 static int __init tee_tg_init(void) 196 { 197 int ret; 198 199 ret = register_pernet_subsys(&tee_net_ops); 200 if (ret < 0) 201 return ret; 202 203 ret = xt_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); 204 if (ret < 0) 205 goto cleanup_subsys; 206 207 ret = register_netdevice_notifier(&tee_netdev_notifier); 208 if (ret < 0) 209 goto unregister_targets; 210 211 return 0; 212 213 unregister_targets: 214 xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); 215 cleanup_subsys: 216 unregister_pernet_subsys(&tee_net_ops); 217 return ret; 218 } 219 220 static void __exit tee_tg_exit(void) 221 { 222 unregister_netdevice_notifier(&tee_netdev_notifier); 223 xt_unregister_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg)); 224 unregister_pernet_subsys(&tee_net_ops); 225 } 226 227 module_init(tee_tg_init); 228 module_exit(tee_tg_exit); 229 MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>"); 230 MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); 231 MODULE_DESCRIPTION("Xtables: Reroute packet copy"); 232 MODULE_LICENSE("GPL"); 233 MODULE_ALIAS("ipt_TEE"); 234 MODULE_ALIAS("ip6t_TEE"); 235