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