linux/net/core/fib_notifier.c
<<
>>
Prefs
   1#include <linux/rtnetlink.h>
   2#include <linux/notifier.h>
   3#include <linux/rcupdate.h>
   4#include <linux/kernel.h>
   5#include <linux/module.h>
   6#include <linux/init.h>
   7#include <net/net_namespace.h>
   8#include <net/netns/generic.h>
   9#include <net/fib_notifier.h>
  10
  11static unsigned int fib_notifier_net_id;
  12
  13struct fib_notifier_net {
  14        struct list_head fib_notifier_ops;
  15        struct atomic_notifier_head fib_chain;
  16};
  17
  18int call_fib_notifier(struct notifier_block *nb,
  19                      enum fib_event_type event_type,
  20                      struct fib_notifier_info *info)
  21{
  22        int err;
  23
  24        err = nb->notifier_call(nb, event_type, info);
  25        return notifier_to_errno(err);
  26}
  27EXPORT_SYMBOL(call_fib_notifier);
  28
  29int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
  30                       struct fib_notifier_info *info)
  31{
  32        struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
  33        int err;
  34
  35        err = atomic_notifier_call_chain(&fn_net->fib_chain, event_type, info);
  36        return notifier_to_errno(err);
  37}
  38EXPORT_SYMBOL(call_fib_notifiers);
  39
  40static unsigned int fib_seq_sum(struct net *net)
  41{
  42        struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
  43        struct fib_notifier_ops *ops;
  44        unsigned int fib_seq = 0;
  45
  46        rtnl_lock();
  47        rcu_read_lock();
  48        list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) {
  49                if (!try_module_get(ops->owner))
  50                        continue;
  51                fib_seq += ops->fib_seq_read(net);
  52                module_put(ops->owner);
  53        }
  54        rcu_read_unlock();
  55        rtnl_unlock();
  56
  57        return fib_seq;
  58}
  59
  60static int fib_net_dump(struct net *net, struct notifier_block *nb,
  61                        struct netlink_ext_ack *extack)
  62{
  63        struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
  64        struct fib_notifier_ops *ops;
  65        int err = 0;
  66
  67        rcu_read_lock();
  68        list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) {
  69                if (!try_module_get(ops->owner))
  70                        continue;
  71                err = ops->fib_dump(net, nb, extack);
  72                module_put(ops->owner);
  73                if (err)
  74                        goto unlock;
  75        }
  76
  77unlock:
  78        rcu_read_unlock();
  79
  80        return err;
  81}
  82
  83static bool fib_dump_is_consistent(struct net *net, struct notifier_block *nb,
  84                                   void (*cb)(struct notifier_block *nb),
  85                                   unsigned int fib_seq)
  86{
  87        struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
  88
  89        atomic_notifier_chain_register(&fn_net->fib_chain, nb);
  90        if (fib_seq == fib_seq_sum(net))
  91                return true;
  92        atomic_notifier_chain_unregister(&fn_net->fib_chain, nb);
  93        if (cb)
  94                cb(nb);
  95        return false;
  96}
  97
  98#define FIB_DUMP_MAX_RETRIES 5
  99int register_fib_notifier(struct net *net, struct notifier_block *nb,
 100                          void (*cb)(struct notifier_block *nb),
 101                          struct netlink_ext_ack *extack)
 102{
 103        int retries = 0;
 104        int err;
 105
 106        do {
 107                unsigned int fib_seq = fib_seq_sum(net);
 108
 109                err = fib_net_dump(net, nb, extack);
 110                if (err)
 111                        return err;
 112
 113                if (fib_dump_is_consistent(net, nb, cb, fib_seq))
 114                        return 0;
 115        } while (++retries < FIB_DUMP_MAX_RETRIES);
 116
 117        return -EBUSY;
 118}
 119EXPORT_SYMBOL(register_fib_notifier);
 120
 121int unregister_fib_notifier(struct net *net, struct notifier_block *nb)
 122{
 123        struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
 124
 125        return atomic_notifier_chain_unregister(&fn_net->fib_chain, nb);
 126}
 127EXPORT_SYMBOL(unregister_fib_notifier);
 128
 129static int __fib_notifier_ops_register(struct fib_notifier_ops *ops,
 130                                       struct net *net)
 131{
 132        struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
 133        struct fib_notifier_ops *o;
 134
 135        list_for_each_entry(o, &fn_net->fib_notifier_ops, list)
 136                if (ops->family == o->family)
 137                        return -EEXIST;
 138        list_add_tail_rcu(&ops->list, &fn_net->fib_notifier_ops);
 139        return 0;
 140}
 141
 142struct fib_notifier_ops *
 143fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net)
 144{
 145        struct fib_notifier_ops *ops;
 146        int err;
 147
 148        ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL);
 149        if (!ops)
 150                return ERR_PTR(-ENOMEM);
 151
 152        err = __fib_notifier_ops_register(ops, net);
 153        if (err)
 154                goto err_register;
 155
 156        return ops;
 157
 158err_register:
 159        kfree(ops);
 160        return ERR_PTR(err);
 161}
 162EXPORT_SYMBOL(fib_notifier_ops_register);
 163
 164void fib_notifier_ops_unregister(struct fib_notifier_ops *ops)
 165{
 166        list_del_rcu(&ops->list);
 167        kfree_rcu(ops, rcu);
 168}
 169EXPORT_SYMBOL(fib_notifier_ops_unregister);
 170
 171static int __net_init fib_notifier_net_init(struct net *net)
 172{
 173        struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
 174
 175        INIT_LIST_HEAD(&fn_net->fib_notifier_ops);
 176        ATOMIC_INIT_NOTIFIER_HEAD(&fn_net->fib_chain);
 177        return 0;
 178}
 179
 180static void __net_exit fib_notifier_net_exit(struct net *net)
 181{
 182        struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
 183
 184        WARN_ON_ONCE(!list_empty(&fn_net->fib_notifier_ops));
 185}
 186
 187static struct pernet_operations fib_notifier_net_ops = {
 188        .init = fib_notifier_net_init,
 189        .exit = fib_notifier_net_exit,
 190        .id = &fib_notifier_net_id,
 191        .size = sizeof(struct fib_notifier_net),
 192};
 193
 194static int __init fib_notifier_init(void)
 195{
 196        return register_pernet_subsys(&fib_notifier_net_ops);
 197}
 198
 199subsys_initcall(fib_notifier_init);
 200