Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: Per network namespace netfilter hooks.

- Add a new set of functions for registering and unregistering per
network namespace hooks.

- Modify the old global namespace hook functions to use the per
network namespace hooks in their implementation, so their remains a
single list that needs to be walked for any hook (this is important
for keeping the hook priority working and for keeping the code
walking the hooks simple).

- Only allow registering the per netdevice hooks in the network
namespace where the network device lives.

- Dynamically allocate the structures in the per network namespace
hook list in nf_register_net_hook, and unregister them in
nf_unregister_net_hook.

Dynamic allocate is required somewhere as the number of network
namespaces are not fixed so we might as well allocate them in the
registration function.

The chain of registered hooks on any list is expected to be small so
the cost of walking that list to find the entry we are unregistering
should also be small.

Performing the management of the dynamically allocated list entries
in the registration and unregistration functions keeps the complexity
from spreading.

Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>

authored by

Eric W. Biederman and committed by
Pablo Neira Ayuso
085db2c0 0edcf282

+173 -24
+11 -3
include/linux/netfilter.h
··· 11 11 #include <linux/list.h> 12 12 #include <linux/static_key.h> 13 13 #include <linux/netfilter_defs.h> 14 + #include <linux/netdevice.h> 15 + #include <net/net_namespace.h> 14 16 15 17 #ifdef CONFIG_NETFILTER 16 18 static inline int NF_DROP_GETERR(int verdict) ··· 120 118 }; 121 119 122 120 /* Function to register/unregister hook points. */ 121 + int nf_register_net_hook(struct net *net, const struct nf_hook_ops *ops); 122 + void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *ops); 123 + int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg, 124 + unsigned int n); 125 + void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, 126 + unsigned int n); 127 + 123 128 int nf_register_hook(struct nf_hook_ops *reg); 124 129 void nf_unregister_hook(struct nf_hook_ops *reg); 125 130 int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n); ··· 136 127 need to check permissions yourself! */ 137 128 int nf_register_sockopt(struct nf_sockopt_ops *reg); 138 129 void nf_unregister_sockopt(struct nf_sockopt_ops *reg); 139 - 140 - extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; 141 130 142 131 #ifdef HAVE_JUMP_LABEL 143 132 extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; ··· 174 167 int (*okfn)(struct sock *, struct sk_buff *), 175 168 int thresh) 176 169 { 177 - struct list_head *nf_hook_list = &nf_hooks[pf][hook]; 170 + struct net *net = dev_net(indev ? indev : outdev); 171 + struct list_head *nf_hook_list = &net->nf.hooks[pf][hook]; 178 172 179 173 if (nf_hook_list_active(nf_hook_list, pf, hook)) { 180 174 struct nf_hook_state state;
+1
include/net/netns/netfilter.h
··· 14 14 #ifdef CONFIG_SYSCTL 15 15 struct ctl_table_header *nf_log_dir_header; 16 16 #endif 17 + struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; 17 18 }; 18 19 #endif
+161 -21
net/netfilter/core.c
··· 52 52 } 53 53 EXPORT_SYMBOL_GPL(nf_unregister_afinfo); 54 54 55 - struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS] __read_mostly; 56 - EXPORT_SYMBOL(nf_hooks); 57 - 58 55 #ifdef HAVE_JUMP_LABEL 59 56 struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; 60 57 EXPORT_SYMBOL(nf_hooks_needed); ··· 59 62 60 63 static DEFINE_MUTEX(nf_hook_mutex); 61 64 62 - static struct list_head *find_nf_hook_list(const struct nf_hook_ops *reg) 65 + static struct list_head *find_nf_hook_list(struct net *net, 66 + const struct nf_hook_ops *reg) 63 67 { 64 68 struct list_head *nf_hook_list = NULL; 65 69 66 70 if (reg->pf != NFPROTO_NETDEV) 67 - nf_hook_list = &nf_hooks[reg->pf][reg->hooknum]; 71 + nf_hook_list = &net->nf.hooks[reg->pf][reg->hooknum]; 68 72 else if (reg->hooknum == NF_NETDEV_INGRESS) { 69 73 #ifdef CONFIG_NETFILTER_INGRESS 70 - if (reg->dev) 74 + if (reg->dev && dev_net(reg->dev) == net) 71 75 nf_hook_list = &reg->dev->nf_hooks_ingress; 72 76 #endif 73 77 } 74 78 return nf_hook_list; 75 79 } 76 80 77 - int nf_register_hook(struct nf_hook_ops *reg) 81 + int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) 78 82 { 79 83 struct list_head *nf_hook_list; 80 - struct nf_hook_ops *elem; 84 + struct nf_hook_ops *elem, *new; 81 85 82 - nf_hook_list = find_nf_hook_list(reg); 86 + new = kzalloc(sizeof(*new), GFP_KERNEL); 87 + if (!new) 88 + return -ENOMEM; 89 + 90 + new->hook = reg->hook; 91 + new->dev = reg->dev; 92 + new->owner = reg->owner; 93 + new->priv = reg->priv; 94 + new->pf = reg->pf; 95 + new->hooknum = reg->hooknum; 96 + new->priority = reg->priority; 97 + 98 + nf_hook_list = find_nf_hook_list(net, reg); 83 99 if (!nf_hook_list) 84 100 return -ENOENT; 85 101 ··· 101 91 if (reg->priority < elem->priority) 102 92 break; 103 93 } 104 - list_add_rcu(&reg->list, elem->list.prev); 94 + list_add_rcu(&new->list, elem->list.prev); 105 95 mutex_unlock(&nf_hook_mutex); 106 96 #ifdef CONFIG_NETFILTER_INGRESS 107 97 if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) ··· 112 102 #endif 113 103 return 0; 114 104 } 115 - EXPORT_SYMBOL(nf_register_hook); 105 + EXPORT_SYMBOL(nf_register_net_hook); 116 106 117 - void nf_unregister_hook(struct nf_hook_ops *reg) 107 + void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) 118 108 { 109 + struct list_head *nf_hook_list; 110 + struct nf_hook_ops *elem; 111 + 112 + nf_hook_list = find_nf_hook_list(net, reg); 113 + if (!nf_hook_list) 114 + return; 115 + 119 116 mutex_lock(&nf_hook_mutex); 120 - list_del_rcu(&reg->list); 117 + list_for_each_entry(elem, nf_hook_list, list) { 118 + if ((reg->hook == elem->hook) && 119 + (reg->dev == elem->dev) && 120 + (reg->owner == elem->owner) && 121 + (reg->priv == elem->priv) && 122 + (reg->pf == elem->pf) && 123 + (reg->hooknum == elem->hooknum) && 124 + (reg->priority == elem->priority)) { 125 + list_del_rcu(&elem->list); 126 + break; 127 + } 128 + } 121 129 mutex_unlock(&nf_hook_mutex); 130 + if (&elem->list == nf_hook_list) { 131 + WARN(1, "nf_unregister_net_hook: hook not found!\n"); 132 + return; 133 + } 122 134 #ifdef CONFIG_NETFILTER_INGRESS 123 135 if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) 124 136 net_dec_ingress_queue(); ··· 149 117 static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]); 150 118 #endif 151 119 synchronize_net(); 152 - nf_queue_nf_hook_drop(reg); 120 + nf_queue_nf_hook_drop(elem); 121 + kfree(elem); 122 + } 123 + EXPORT_SYMBOL(nf_unregister_net_hook); 124 + 125 + int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg, 126 + unsigned int n) 127 + { 128 + unsigned int i; 129 + int err = 0; 130 + 131 + for (i = 0; i < n; i++) { 132 + err = nf_register_net_hook(net, &reg[i]); 133 + if (err) 134 + goto err; 135 + } 136 + return err; 137 + 138 + err: 139 + if (i > 0) 140 + nf_unregister_net_hooks(net, reg, i); 141 + return err; 142 + } 143 + EXPORT_SYMBOL(nf_register_net_hooks); 144 + 145 + void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, 146 + unsigned int n) 147 + { 148 + while (n-- > 0) 149 + nf_unregister_net_hook(net, &reg[n]); 150 + } 151 + EXPORT_SYMBOL(nf_unregister_net_hooks); 152 + 153 + static LIST_HEAD(nf_hook_list); 154 + 155 + int nf_register_hook(struct nf_hook_ops *reg) 156 + { 157 + struct net *net, *last; 158 + int ret; 159 + 160 + rtnl_lock(); 161 + for_each_net(net) { 162 + ret = nf_register_net_hook(net, reg); 163 + if (ret && ret != -ENOENT) 164 + goto rollback; 165 + } 166 + list_add_tail(&reg->list, &nf_hook_list); 167 + rtnl_unlock(); 168 + 169 + return 0; 170 + rollback: 171 + last = net; 172 + for_each_net(net) { 173 + if (net == last) 174 + break; 175 + nf_unregister_net_hook(net, reg); 176 + } 177 + rtnl_unlock(); 178 + return ret; 179 + } 180 + EXPORT_SYMBOL(nf_register_hook); 181 + 182 + void nf_unregister_hook(struct nf_hook_ops *reg) 183 + { 184 + struct net *net; 185 + 186 + rtnl_lock(); 187 + list_del(&reg->list); 188 + for_each_net(net) 189 + nf_unregister_net_hook(net, reg); 190 + rtnl_unlock(); 153 191 } 154 192 EXPORT_SYMBOL(nf_unregister_hook); 155 193 ··· 396 294 EXPORT_SYMBOL(nf_nat_decode_session_hook); 397 295 #endif 398 296 297 + static int nf_register_hook_list(struct net *net) 298 + { 299 + struct nf_hook_ops *elem; 300 + int ret; 301 + 302 + rtnl_lock(); 303 + list_for_each_entry(elem, &nf_hook_list, list) { 304 + ret = nf_register_net_hook(net, elem); 305 + if (ret && ret != -ENOENT) 306 + goto out_undo; 307 + } 308 + rtnl_unlock(); 309 + return 0; 310 + 311 + out_undo: 312 + list_for_each_entry_continue_reverse(elem, &nf_hook_list, list) 313 + nf_unregister_net_hook(net, elem); 314 + rtnl_unlock(); 315 + return ret; 316 + } 317 + 318 + static void nf_unregister_hook_list(struct net *net) 319 + { 320 + struct nf_hook_ops *elem; 321 + 322 + rtnl_lock(); 323 + list_for_each_entry(elem, &nf_hook_list, list) 324 + nf_unregister_net_hook(net, elem); 325 + rtnl_unlock(); 326 + } 327 + 399 328 static int __net_init netfilter_net_init(struct net *net) 400 329 { 330 + int i, h, ret; 331 + 332 + for (i = 0; i < ARRAY_SIZE(net->nf.hooks); i++) { 333 + for (h = 0; h < NF_MAX_HOOKS; h++) 334 + INIT_LIST_HEAD(&net->nf.hooks[i][h]); 335 + } 336 + 401 337 #ifdef CONFIG_PROC_FS 402 338 net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter", 403 339 net->proc_net); ··· 446 306 return -ENOMEM; 447 307 } 448 308 #endif 449 - return 0; 309 + ret = nf_register_hook_list(net); 310 + if (ret) 311 + remove_proc_entry("netfilter", net->proc_net); 312 + 313 + return ret; 450 314 } 451 315 452 316 static void __net_exit netfilter_net_exit(struct net *net) 453 317 { 318 + nf_unregister_hook_list(net); 454 319 remove_proc_entry("netfilter", net->proc_net); 455 320 } 456 321 ··· 466 321 467 322 int __init netfilter_init(void) 468 323 { 469 - int i, h, ret; 470 - 471 - for (i = 0; i < ARRAY_SIZE(nf_hooks); i++) { 472 - for (h = 0; h < NF_MAX_HOOKS; h++) 473 - INIT_LIST_HEAD(&nf_hooks[i][h]); 474 - } 324 + int ret; 475 325 476 326 ret = register_pernet_subsys(&netfilter_net_ops); 477 327 if (ret < 0)