fib_notifier.c 4.8 KB

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