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