diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/core/fib_rules.c | 91 |
1 files changed, 54 insertions, 37 deletions
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index e12e9f583949..c5f78fed6885 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c | |||
@@ -15,9 +15,6 @@ | |||
15 | #include <net/sock.h> | 15 | #include <net/sock.h> |
16 | #include <net/fib_rules.h> | 16 | #include <net/fib_rules.h> |
17 | 17 | ||
18 | static LIST_HEAD(rules_ops); | ||
19 | static DEFINE_SPINLOCK(rules_mod_lock); | ||
20 | |||
21 | int fib_default_rule_add(struct fib_rules_ops *ops, | 18 | int fib_default_rule_add(struct fib_rules_ops *ops, |
22 | u32 pref, u32 table, u32 flags) | 19 | u32 pref, u32 table, u32 flags) |
23 | { | 20 | { |
@@ -40,16 +37,17 @@ int fib_default_rule_add(struct fib_rules_ops *ops, | |||
40 | } | 37 | } |
41 | EXPORT_SYMBOL(fib_default_rule_add); | 38 | EXPORT_SYMBOL(fib_default_rule_add); |
42 | 39 | ||
43 | static void notify_rule_change(int event, struct fib_rule *rule, | 40 | static void notify_rule_change(struct net *net, int event, |
41 | struct fib_rule *rule, | ||
44 | struct fib_rules_ops *ops, struct nlmsghdr *nlh, | 42 | struct fib_rules_ops *ops, struct nlmsghdr *nlh, |
45 | u32 pid); | 43 | u32 pid); |
46 | 44 | ||
47 | static struct fib_rules_ops *lookup_rules_ops(int family) | 45 | static struct fib_rules_ops *lookup_rules_ops(struct net *net, int family) |
48 | { | 46 | { |
49 | struct fib_rules_ops *ops; | 47 | struct fib_rules_ops *ops; |
50 | 48 | ||
51 | rcu_read_lock(); | 49 | rcu_read_lock(); |
52 | list_for_each_entry_rcu(ops, &rules_ops, list) { | 50 | list_for_each_entry_rcu(ops, &net->rules_ops, list) { |
53 | if (ops->family == family) { | 51 | if (ops->family == family) { |
54 | if (!try_module_get(ops->owner)) | 52 | if (!try_module_get(ops->owner)) |
55 | ops = NULL; | 53 | ops = NULL; |
@@ -87,15 +85,16 @@ int fib_rules_register(struct net *net, struct fib_rules_ops *ops) | |||
87 | ops->action == NULL) | 85 | ops->action == NULL) |
88 | return -EINVAL; | 86 | return -EINVAL; |
89 | 87 | ||
90 | spin_lock(&rules_mod_lock); | 88 | spin_lock(&net->rules_mod_lock); |
91 | list_for_each_entry(o, &rules_ops, list) | 89 | list_for_each_entry(o, &net->rules_ops, list) |
92 | if (ops->family == o->family) | 90 | if (ops->family == o->family) |
93 | goto errout; | 91 | goto errout; |
94 | 92 | ||
95 | list_add_tail_rcu(&ops->list, &rules_ops); | 93 | hold_net(net); |
94 | list_add_tail_rcu(&ops->list, &net->rules_ops); | ||
96 | err = 0; | 95 | err = 0; |
97 | errout: | 96 | errout: |
98 | spin_unlock(&rules_mod_lock); | 97 | spin_unlock(&net->rules_mod_lock); |
99 | 98 | ||
100 | return err; | 99 | return err; |
101 | } | 100 | } |
@@ -118,8 +117,8 @@ int fib_rules_unregister(struct net *net, struct fib_rules_ops *ops) | |||
118 | int err = 0; | 117 | int err = 0; |
119 | struct fib_rules_ops *o; | 118 | struct fib_rules_ops *o; |
120 | 119 | ||
121 | spin_lock(&rules_mod_lock); | 120 | spin_lock(&net->rules_mod_lock); |
122 | list_for_each_entry(o, &rules_ops, list) { | 121 | list_for_each_entry(o, &net->rules_ops, list) { |
123 | if (o == ops) { | 122 | if (o == ops) { |
124 | list_del_rcu(&o->list); | 123 | list_del_rcu(&o->list); |
125 | fib_rules_cleanup_ops(ops); | 124 | fib_rules_cleanup_ops(ops); |
@@ -129,9 +128,11 @@ int fib_rules_unregister(struct net *net, struct fib_rules_ops *ops) | |||
129 | 128 | ||
130 | err = -ENOENT; | 129 | err = -ENOENT; |
131 | out: | 130 | out: |
132 | spin_unlock(&rules_mod_lock); | 131 | spin_unlock(&net->rules_mod_lock); |
133 | 132 | ||
134 | synchronize_rcu(); | 133 | synchronize_rcu(); |
134 | if (!err) | ||
135 | release_net(net); | ||
135 | 136 | ||
136 | return err; | 137 | return err; |
137 | } | 138 | } |
@@ -229,13 +230,10 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
229 | struct nlattr *tb[FRA_MAX+1]; | 230 | struct nlattr *tb[FRA_MAX+1]; |
230 | int err = -EINVAL, unresolved = 0; | 231 | int err = -EINVAL, unresolved = 0; |
231 | 232 | ||
232 | if (net != &init_net) | ||
233 | return -EINVAL; | ||
234 | |||
235 | if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) | 233 | if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) |
236 | goto errout; | 234 | goto errout; |
237 | 235 | ||
238 | ops = lookup_rules_ops(frh->family); | 236 | ops = lookup_rules_ops(net, frh->family); |
239 | if (ops == NULL) { | 237 | if (ops == NULL) { |
240 | err = EAFNOSUPPORT; | 238 | err = EAFNOSUPPORT; |
241 | goto errout; | 239 | goto errout; |
@@ -348,7 +346,7 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
348 | else | 346 | else |
349 | list_add_rcu(&rule->list, &ops->rules_list); | 347 | list_add_rcu(&rule->list, &ops->rules_list); |
350 | 348 | ||
351 | notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid); | 349 | notify_rule_change(net, RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid); |
352 | flush_route_cache(ops); | 350 | flush_route_cache(ops); |
353 | rules_ops_put(ops); | 351 | rules_ops_put(ops); |
354 | return 0; | 352 | return 0; |
@@ -369,13 +367,10 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
369 | struct nlattr *tb[FRA_MAX+1]; | 367 | struct nlattr *tb[FRA_MAX+1]; |
370 | int err = -EINVAL; | 368 | int err = -EINVAL; |
371 | 369 | ||
372 | if (net != &init_net) | ||
373 | return -EINVAL; | ||
374 | |||
375 | if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) | 370 | if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) |
376 | goto errout; | 371 | goto errout; |
377 | 372 | ||
378 | ops = lookup_rules_ops(frh->family); | 373 | ops = lookup_rules_ops(net, frh->family); |
379 | if (ops == NULL) { | 374 | if (ops == NULL) { |
380 | err = EAFNOSUPPORT; | 375 | err = EAFNOSUPPORT; |
381 | goto errout; | 376 | goto errout; |
@@ -441,7 +436,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) | |||
441 | } | 436 | } |
442 | 437 | ||
443 | synchronize_rcu(); | 438 | synchronize_rcu(); |
444 | notify_rule_change(RTM_DELRULE, rule, ops, nlh, | 439 | notify_rule_change(net, RTM_DELRULE, rule, ops, nlh, |
445 | NETLINK_CB(skb).pid); | 440 | NETLINK_CB(skb).pid); |
446 | fib_rule_put(rule); | 441 | fib_rule_put(rule); |
447 | flush_route_cache(ops); | 442 | flush_route_cache(ops); |
@@ -551,13 +546,10 @@ static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) | |||
551 | struct fib_rules_ops *ops; | 546 | struct fib_rules_ops *ops; |
552 | int idx = 0, family; | 547 | int idx = 0, family; |
553 | 548 | ||
554 | if (net != &init_net) | ||
555 | return -EINVAL; | ||
556 | |||
557 | family = rtnl_msg_family(cb->nlh); | 549 | family = rtnl_msg_family(cb->nlh); |
558 | if (family != AF_UNSPEC) { | 550 | if (family != AF_UNSPEC) { |
559 | /* Protocol specific dump request */ | 551 | /* Protocol specific dump request */ |
560 | ops = lookup_rules_ops(family); | 552 | ops = lookup_rules_ops(net, family); |
561 | if (ops == NULL) | 553 | if (ops == NULL) |
562 | return -EAFNOSUPPORT; | 554 | return -EAFNOSUPPORT; |
563 | 555 | ||
@@ -565,7 +557,7 @@ static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) | |||
565 | } | 557 | } |
566 | 558 | ||
567 | rcu_read_lock(); | 559 | rcu_read_lock(); |
568 | list_for_each_entry_rcu(ops, &rules_ops, list) { | 560 | list_for_each_entry_rcu(ops, &net->rules_ops, list) { |
569 | if (idx < cb->args[0] || !try_module_get(ops->owner)) | 561 | if (idx < cb->args[0] || !try_module_get(ops->owner)) |
570 | goto skip; | 562 | goto skip; |
571 | 563 | ||
@@ -582,7 +574,7 @@ static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) | |||
582 | return skb->len; | 574 | return skb->len; |
583 | } | 575 | } |
584 | 576 | ||
585 | static void notify_rule_change(int event, struct fib_rule *rule, | 577 | static void notify_rule_change(struct net *net, int event, struct fib_rule *rule, |
586 | struct fib_rules_ops *ops, struct nlmsghdr *nlh, | 578 | struct fib_rules_ops *ops, struct nlmsghdr *nlh, |
587 | u32 pid) | 579 | u32 pid) |
588 | { | 580 | { |
@@ -600,10 +592,10 @@ static void notify_rule_change(int event, struct fib_rule *rule, | |||
600 | kfree_skb(skb); | 592 | kfree_skb(skb); |
601 | goto errout; | 593 | goto errout; |
602 | } | 594 | } |
603 | err = rtnl_notify(skb, &init_net, pid, ops->nlgroup, nlh, GFP_KERNEL); | 595 | err = rtnl_notify(skb, net, pid, ops->nlgroup, nlh, GFP_KERNEL); |
604 | errout: | 596 | errout: |
605 | if (err < 0) | 597 | if (err < 0) |
606 | rtnl_set_sk_err(&init_net, ops->nlgroup, err); | 598 | rtnl_set_sk_err(net, ops->nlgroup, err); |
607 | } | 599 | } |
608 | 600 | ||
609 | static void attach_rules(struct list_head *rules, struct net_device *dev) | 601 | static void attach_rules(struct list_head *rules, struct net_device *dev) |
@@ -631,22 +623,20 @@ static int fib_rules_event(struct notifier_block *this, unsigned long event, | |||
631 | void *ptr) | 623 | void *ptr) |
632 | { | 624 | { |
633 | struct net_device *dev = ptr; | 625 | struct net_device *dev = ptr; |
626 | struct net *net = dev->nd_net; | ||
634 | struct fib_rules_ops *ops; | 627 | struct fib_rules_ops *ops; |
635 | 628 | ||
636 | if (dev->nd_net != &init_net) | ||
637 | return NOTIFY_DONE; | ||
638 | |||
639 | ASSERT_RTNL(); | 629 | ASSERT_RTNL(); |
640 | rcu_read_lock(); | 630 | rcu_read_lock(); |
641 | 631 | ||
642 | switch (event) { | 632 | switch (event) { |
643 | case NETDEV_REGISTER: | 633 | case NETDEV_REGISTER: |
644 | list_for_each_entry(ops, &rules_ops, list) | 634 | list_for_each_entry(ops, &net->rules_ops, list) |
645 | attach_rules(&ops->rules_list, dev); | 635 | attach_rules(&ops->rules_list, dev); |
646 | break; | 636 | break; |
647 | 637 | ||
648 | case NETDEV_UNREGISTER: | 638 | case NETDEV_UNREGISTER: |
649 | list_for_each_entry(ops, &rules_ops, list) | 639 | list_for_each_entry(ops, &net->rules_ops, list) |
650 | detach_rules(&ops->rules_list, dev); | 640 | detach_rules(&ops->rules_list, dev); |
651 | break; | 641 | break; |
652 | } | 642 | } |
@@ -660,13 +650,40 @@ static struct notifier_block fib_rules_notifier = { | |||
660 | .notifier_call = fib_rules_event, | 650 | .notifier_call = fib_rules_event, |
661 | }; | 651 | }; |
662 | 652 | ||
653 | static int fib_rules_net_init(struct net *net) | ||
654 | { | ||
655 | INIT_LIST_HEAD(&net->rules_ops); | ||
656 | spin_lock_init(&net->rules_mod_lock); | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static struct pernet_operations fib_rules_net_ops = { | ||
661 | .init = fib_rules_net_init, | ||
662 | }; | ||
663 | |||
663 | static int __init fib_rules_init(void) | 664 | static int __init fib_rules_init(void) |
664 | { | 665 | { |
666 | int err; | ||
665 | rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL); | 667 | rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL); |
666 | rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL); | 668 | rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL); |
667 | rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule); | 669 | rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule); |
668 | 670 | ||
669 | return register_netdevice_notifier(&fib_rules_notifier); | 671 | err = register_netdevice_notifier(&fib_rules_notifier); |
672 | if (err < 0) | ||
673 | goto fail; | ||
674 | |||
675 | err = register_pernet_subsys(&fib_rules_net_ops); | ||
676 | if (err < 0) | ||
677 | goto fail_unregister; | ||
678 | return 0; | ||
679 | |||
680 | fail_unregister: | ||
681 | unregister_netdevice_notifier(&fib_rules_notifier); | ||
682 | fail: | ||
683 | rtnl_unregister(PF_UNSPEC, RTM_NEWRULE); | ||
684 | rtnl_unregister(PF_UNSPEC, RTM_DELRULE); | ||
685 | rtnl_unregister(PF_UNSPEC, RTM_GETRULE); | ||
686 | return err; | ||
670 | } | 687 | } |
671 | 688 | ||
672 | subsys_initcall(fib_rules_init); | 689 | subsys_initcall(fib_rules_init); |