Commit 085db2c04557d31db61541f361bd8b4de92c9939

Authored by Eric W. Biederman
Committed by Pablo Neira Ayuso
1 parent 0edcf282b0

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>

Showing 3 changed files with 173 additions and 24 deletions Side-by-side Diff

include/linux/netfilter.h
... ... @@ -11,6 +11,8 @@
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)
... ... @@ -118,6 +120,13 @@
118 120 };
119 121  
120 122 /* Function to register/unregister hook points. */
  123 +int nf_register_net_hook(struct net *net, const struct nf_hook_ops *ops);
  124 +void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *ops);
  125 +int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,
  126 + unsigned int n);
  127 +void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,
  128 + unsigned int n);
  129 +
121 130 int nf_register_hook(struct nf_hook_ops *reg);
122 131 void nf_unregister_hook(struct nf_hook_ops *reg);
123 132 int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n);
... ... @@ -128,8 +137,6 @@
128 137 int nf_register_sockopt(struct nf_sockopt_ops *reg);
129 138 void nf_unregister_sockopt(struct nf_sockopt_ops *reg);
130 139  
131   -extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
132   -
133 140 #ifdef HAVE_JUMP_LABEL
134 141 extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
135 142  
... ... @@ -167,7 +174,8 @@
167 174 int (*okfn)(struct sock *, struct sk_buff *),
168 175 int thresh)
169 176 {
170   - struct list_head *nf_hook_list = &nf_hooks[pf][hook];
  177 + struct net *net = dev_net(indev ? indev : outdev);
  178 + struct list_head *nf_hook_list = &net->nf.hooks[pf][hook];
171 179  
172 180 if (nf_hook_list_active(nf_hook_list, pf, hook)) {
173 181 struct nf_hook_state state;
include/net/netns/netfilter.h
... ... @@ -14,6 +14,7 @@
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
net/netfilter/core.c
... ... @@ -52,9 +52,6 @@
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);
61 58  
62 59  
63 60  
64 61  
65 62  
... ... @@ -62,27 +59,40 @@
62 59  
63 60 static DEFINE_MUTEX(nf_hook_mutex);
64 61  
65   -static struct list_head *find_nf_hook_list(const struct nf_hook_ops *reg)
  62 +static struct list_head *find_nf_hook_list(struct net *net,
  63 + const struct nf_hook_ops *reg)
66 64 {
67 65 struct list_head *nf_hook_list = NULL;
68 66  
69 67 if (reg->pf != NFPROTO_NETDEV)
70   - nf_hook_list = &nf_hooks[reg->pf][reg->hooknum];
  68 + nf_hook_list = &net->nf.hooks[reg->pf][reg->hooknum];
71 69 else if (reg->hooknum == NF_NETDEV_INGRESS) {
72 70 #ifdef CONFIG_NETFILTER_INGRESS
73   - if (reg->dev)
  71 + if (reg->dev && dev_net(reg->dev) == net)
74 72 nf_hook_list = &reg->dev->nf_hooks_ingress;
75 73 #endif
76 74 }
77 75 return nf_hook_list;
78 76 }
79 77  
80   -int nf_register_hook(struct nf_hook_ops *reg)
  78 +int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
81 79 {
82 80 struct list_head *nf_hook_list;
83   - struct nf_hook_ops *elem;
  81 + struct nf_hook_ops *elem, *new;
84 82  
85   - nf_hook_list = find_nf_hook_list(reg);
  83 + new = kzalloc(sizeof(*new), GFP_KERNEL);
  84 + if (!new)
  85 + return -ENOMEM;
  86 +
  87 + new->hook = reg->hook;
  88 + new->dev = reg->dev;
  89 + new->owner = reg->owner;
  90 + new->priv = reg->priv;
  91 + new->pf = reg->pf;
  92 + new->hooknum = reg->hooknum;
  93 + new->priority = reg->priority;
  94 +
  95 + nf_hook_list = find_nf_hook_list(net, reg);
86 96 if (!nf_hook_list)
87 97 return -ENOENT;
88 98  
... ... @@ -91,7 +101,7 @@
91 101 if (reg->priority < elem->priority)
92 102 break;
93 103 }
94   - list_add_rcu(&reg->list, elem->list.prev);
  104 + list_add_rcu(&new->list, elem->list.prev);
95 105 mutex_unlock(&nf_hook_mutex);
96 106 #ifdef CONFIG_NETFILTER_INGRESS
97 107 if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
98 108  
99 109  
100 110  
101 111  
... ... @@ -102,13 +112,35 @@
102 112 #endif
103 113 return 0;
104 114 }
105   -EXPORT_SYMBOL(nf_register_hook);
  115 +EXPORT_SYMBOL(nf_register_net_hook);
106 116  
107   -void nf_unregister_hook(struct nf_hook_ops *reg)
  117 +void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
108 118 {
  119 + struct list_head *nf_hook_list;
  120 + struct nf_hook_ops *elem;
  121 +
  122 + nf_hook_list = find_nf_hook_list(net, reg);
  123 + if (!nf_hook_list)
  124 + return;
  125 +
109 126 mutex_lock(&nf_hook_mutex);
110   - list_del_rcu(&reg->list);
  127 + list_for_each_entry(elem, nf_hook_list, list) {
  128 + if ((reg->hook == elem->hook) &&
  129 + (reg->dev == elem->dev) &&
  130 + (reg->owner == elem->owner) &&
  131 + (reg->priv == elem->priv) &&
  132 + (reg->pf == elem->pf) &&
  133 + (reg->hooknum == elem->hooknum) &&
  134 + (reg->priority == elem->priority)) {
  135 + list_del_rcu(&elem->list);
  136 + break;
  137 + }
  138 + }
111 139 mutex_unlock(&nf_hook_mutex);
  140 + if (&elem->list == nf_hook_list) {
  141 + WARN(1, "nf_unregister_net_hook: hook not found!\n");
  142 + return;
  143 + }
112 144 #ifdef CONFIG_NETFILTER_INGRESS
113 145 if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
114 146 net_dec_ingress_queue();
115 147  
... ... @@ -117,8 +149,78 @@
117 149 static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]);
118 150 #endif
119 151 synchronize_net();
120   - nf_queue_nf_hook_drop(reg);
  152 + nf_queue_nf_hook_drop(elem);
  153 + kfree(elem);
121 154 }
  155 +EXPORT_SYMBOL(nf_unregister_net_hook);
  156 +
  157 +int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,
  158 + unsigned int n)
  159 +{
  160 + unsigned int i;
  161 + int err = 0;
  162 +
  163 + for (i = 0; i < n; i++) {
  164 + err = nf_register_net_hook(net, &reg[i]);
  165 + if (err)
  166 + goto err;
  167 + }
  168 + return err;
  169 +
  170 +err:
  171 + if (i > 0)
  172 + nf_unregister_net_hooks(net, reg, i);
  173 + return err;
  174 +}
  175 +EXPORT_SYMBOL(nf_register_net_hooks);
  176 +
  177 +void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,
  178 + unsigned int n)
  179 +{
  180 + while (n-- > 0)
  181 + nf_unregister_net_hook(net, &reg[n]);
  182 +}
  183 +EXPORT_SYMBOL(nf_unregister_net_hooks);
  184 +
  185 +static LIST_HEAD(nf_hook_list);
  186 +
  187 +int nf_register_hook(struct nf_hook_ops *reg)
  188 +{
  189 + struct net *net, *last;
  190 + int ret;
  191 +
  192 + rtnl_lock();
  193 + for_each_net(net) {
  194 + ret = nf_register_net_hook(net, reg);
  195 + if (ret && ret != -ENOENT)
  196 + goto rollback;
  197 + }
  198 + list_add_tail(&reg->list, &nf_hook_list);
  199 + rtnl_unlock();
  200 +
  201 + return 0;
  202 +rollback:
  203 + last = net;
  204 + for_each_net(net) {
  205 + if (net == last)
  206 + break;
  207 + nf_unregister_net_hook(net, reg);
  208 + }
  209 + rtnl_unlock();
  210 + return ret;
  211 +}
  212 +EXPORT_SYMBOL(nf_register_hook);
  213 +
  214 +void nf_unregister_hook(struct nf_hook_ops *reg)
  215 +{
  216 + struct net *net;
  217 +
  218 + rtnl_lock();
  219 + list_del(&reg->list);
  220 + for_each_net(net)
  221 + nf_unregister_net_hook(net, reg);
  222 + rtnl_unlock();
  223 +}
122 224 EXPORT_SYMBOL(nf_unregister_hook);
123 225  
124 226 int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n)
125 227  
... ... @@ -294,8 +396,46 @@
294 396 EXPORT_SYMBOL(nf_nat_decode_session_hook);
295 397 #endif
296 398  
  399 +static int nf_register_hook_list(struct net *net)
  400 +{
  401 + struct nf_hook_ops *elem;
  402 + int ret;
  403 +
  404 + rtnl_lock();
  405 + list_for_each_entry(elem, &nf_hook_list, list) {
  406 + ret = nf_register_net_hook(net, elem);
  407 + if (ret && ret != -ENOENT)
  408 + goto out_undo;
  409 + }
  410 + rtnl_unlock();
  411 + return 0;
  412 +
  413 +out_undo:
  414 + list_for_each_entry_continue_reverse(elem, &nf_hook_list, list)
  415 + nf_unregister_net_hook(net, elem);
  416 + rtnl_unlock();
  417 + return ret;
  418 +}
  419 +
  420 +static void nf_unregister_hook_list(struct net *net)
  421 +{
  422 + struct nf_hook_ops *elem;
  423 +
  424 + rtnl_lock();
  425 + list_for_each_entry(elem, &nf_hook_list, list)
  426 + nf_unregister_net_hook(net, elem);
  427 + rtnl_unlock();
  428 +}
  429 +
297 430 static int __net_init netfilter_net_init(struct net *net)
298 431 {
  432 + int i, h, ret;
  433 +
  434 + for (i = 0; i < ARRAY_SIZE(net->nf.hooks); i++) {
  435 + for (h = 0; h < NF_MAX_HOOKS; h++)
  436 + INIT_LIST_HEAD(&net->nf.hooks[i][h]);
  437 + }
  438 +
299 439 #ifdef CONFIG_PROC_FS
300 440 net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter",
301 441 net->proc_net);
302 442  
... ... @@ -306,11 +446,16 @@
306 446 return -ENOMEM;
307 447 }
308 448 #endif
309   - return 0;
  449 + ret = nf_register_hook_list(net);
  450 + if (ret)
  451 + remove_proc_entry("netfilter", net->proc_net);
  452 +
  453 + return ret;
310 454 }
311 455  
312 456 static void __net_exit netfilter_net_exit(struct net *net)
313 457 {
  458 + nf_unregister_hook_list(net);
314 459 remove_proc_entry("netfilter", net->proc_net);
315 460 }
316 461  
... ... @@ -321,12 +466,7 @@
321 466  
322 467 int __init netfilter_init(void)
323 468 {
324   - int i, h, ret;
325   -
326   - for (i = 0; i < ARRAY_SIZE(nf_hooks); i++) {
327   - for (h = 0; h < NF_MAX_HOOKS; h++)
328   - INIT_LIST_HEAD(&nf_hooks[i][h]);
329   - }
  469 + int ret;
330 470  
331 471 ret = register_pernet_subsys(&netfilter_net_ops);
332 472 if (ret < 0)