1 #include <linux/rtnetlink.h> 2 #include <linux/notifier.h> 3 #include <linux/rcupdate.h> 4 #include <linux/kernel.h> 5 #include <net/net_namespace.h> 6 #include <net/netns/ipv4.h> 7 #include <net/ip_fib.h> 8 9 static ATOMIC_NOTIFIER_HEAD(fib_chain); 10 11 int call_fib_notifier(struct notifier_block *nb, struct net *net, 12 enum fib_event_type event_type, 13 struct fib_notifier_info *info) 14 { 15 info->net = net; 16 return nb->notifier_call(nb, event_type, info); 17 } 18 19 int call_fib_notifiers(struct net *net, enum fib_event_type event_type, 20 struct fib_notifier_info *info) 21 { 22 net->ipv4.fib_seq++; 23 info->net = net; 24 return atomic_notifier_call_chain(&fib_chain, event_type, info); 25 } 26 27 static unsigned int fib_seq_sum(void) 28 { 29 unsigned int fib_seq = 0; 30 struct net *net; 31 32 rtnl_lock(); 33 for_each_net(net) 34 fib_seq += net->ipv4.fib_seq; 35 rtnl_unlock(); 36 37 return fib_seq; 38 } 39 40 static bool fib_dump_is_consistent(struct notifier_block *nb, 41 void (*cb)(struct notifier_block *nb), 42 unsigned int fib_seq) 43 { 44 atomic_notifier_chain_register(&fib_chain, nb); 45 if (fib_seq == fib_seq_sum()) 46 return true; 47 atomic_notifier_chain_unregister(&fib_chain, nb); 48 if (cb) 49 cb(nb); 50 return false; 51 } 52 53 #define FIB_DUMP_MAX_RETRIES 5 54 int register_fib_notifier(struct notifier_block *nb, 55 void (*cb)(struct notifier_block *nb)) 56 { 57 int retries = 0; 58 59 do { 60 unsigned int fib_seq = fib_seq_sum(); 61 struct net *net; 62 63 /* Mutex semantics guarantee that every change done to 64 * FIB tries before we read the change sequence counter 65 * is now visible to us. 66 */ 67 rcu_read_lock(); 68 for_each_net_rcu(net) { 69 fib_rules_notify(net, nb); 70 fib_notify(net, nb); 71 } 72 rcu_read_unlock(); 73 74 if (fib_dump_is_consistent(nb, cb, fib_seq)) 75 return 0; 76 } while (++retries < FIB_DUMP_MAX_RETRIES); 77 78 return -EBUSY; 79 } 80 EXPORT_SYMBOL(register_fib_notifier); 81 82 int unregister_fib_notifier(struct notifier_block *nb) 83 { 84 return atomic_notifier_chain_unregister(&fib_chain, nb); 85 } 86 EXPORT_SYMBOL(unregister_fib_notifier); 87