fib_notifier.c 4.7 KB

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