fib.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Copyright (c) 2018 Cumulus Networks. All rights reserved.
  3. * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
  4. *
  5. * This software is licensed under the GNU General License Version 2,
  6. * June 1991 as shown in the file COPYING in the top-level directory of this
  7. * source tree.
  8. *
  9. * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
  10. * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
  11. * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  12. * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
  13. * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
  14. * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
  15. */
  16. #include <net/fib_notifier.h>
  17. #include <net/ip_fib.h>
  18. #include <net/ip6_fib.h>
  19. #include <net/fib_rules.h>
  20. #include <net/netns/generic.h>
  21. #include "netdevsim.h"
  22. struct nsim_fib_entry {
  23. u64 max;
  24. u64 num;
  25. };
  26. struct nsim_per_fib_data {
  27. struct nsim_fib_entry fib;
  28. struct nsim_fib_entry rules;
  29. };
  30. struct nsim_fib_data {
  31. struct nsim_per_fib_data ipv4;
  32. struct nsim_per_fib_data ipv6;
  33. };
  34. static unsigned int nsim_fib_net_id;
  35. u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
  36. {
  37. struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
  38. struct nsim_fib_entry *entry;
  39. switch (res_id) {
  40. case NSIM_RESOURCE_IPV4_FIB:
  41. entry = &fib_data->ipv4.fib;
  42. break;
  43. case NSIM_RESOURCE_IPV4_FIB_RULES:
  44. entry = &fib_data->ipv4.rules;
  45. break;
  46. case NSIM_RESOURCE_IPV6_FIB:
  47. entry = &fib_data->ipv6.fib;
  48. break;
  49. case NSIM_RESOURCE_IPV6_FIB_RULES:
  50. entry = &fib_data->ipv6.rules;
  51. break;
  52. default:
  53. return 0;
  54. }
  55. return max ? entry->max : entry->num;
  56. }
  57. int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
  58. struct netlink_ext_ack *extack)
  59. {
  60. struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
  61. struct nsim_fib_entry *entry;
  62. int err = 0;
  63. switch (res_id) {
  64. case NSIM_RESOURCE_IPV4_FIB:
  65. entry = &fib_data->ipv4.fib;
  66. break;
  67. case NSIM_RESOURCE_IPV4_FIB_RULES:
  68. entry = &fib_data->ipv4.rules;
  69. break;
  70. case NSIM_RESOURCE_IPV6_FIB:
  71. entry = &fib_data->ipv6.fib;
  72. break;
  73. case NSIM_RESOURCE_IPV6_FIB_RULES:
  74. entry = &fib_data->ipv6.rules;
  75. break;
  76. default:
  77. return 0;
  78. }
  79. /* not allowing a new max to be less than curren occupancy
  80. * --> no means of evicting entries
  81. */
  82. if (val < entry->num) {
  83. NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
  84. err = -EINVAL;
  85. } else {
  86. entry->max = val;
  87. }
  88. return err;
  89. }
  90. static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
  91. struct netlink_ext_ack *extack)
  92. {
  93. int err = 0;
  94. if (add) {
  95. if (entry->num < entry->max) {
  96. entry->num++;
  97. } else {
  98. err = -ENOSPC;
  99. NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
  100. }
  101. } else {
  102. entry->num--;
  103. }
  104. return err;
  105. }
  106. static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
  107. {
  108. struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
  109. struct netlink_ext_ack *extack = info->extack;
  110. int err = 0;
  111. switch (info->family) {
  112. case AF_INET:
  113. err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
  114. break;
  115. case AF_INET6:
  116. err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
  117. break;
  118. }
  119. return err;
  120. }
  121. static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
  122. struct netlink_ext_ack *extack)
  123. {
  124. int err = 0;
  125. if (add) {
  126. if (entry->num < entry->max) {
  127. entry->num++;
  128. } else {
  129. err = -ENOSPC;
  130. NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
  131. }
  132. } else {
  133. entry->num--;
  134. }
  135. return err;
  136. }
  137. static int nsim_fib_event(struct fib_notifier_info *info, bool add)
  138. {
  139. struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
  140. struct netlink_ext_ack *extack = info->extack;
  141. int err = 0;
  142. switch (info->family) {
  143. case AF_INET:
  144. err = nsim_fib_account(&data->ipv4.fib, add, extack);
  145. break;
  146. case AF_INET6:
  147. err = nsim_fib_account(&data->ipv6.fib, add, extack);
  148. break;
  149. }
  150. return err;
  151. }
  152. static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
  153. void *ptr)
  154. {
  155. struct fib_notifier_info *info = ptr;
  156. int err = 0;
  157. switch (event) {
  158. case FIB_EVENT_RULE_ADD: /* fall through */
  159. case FIB_EVENT_RULE_DEL:
  160. err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
  161. break;
  162. case FIB_EVENT_ENTRY_ADD: /* fall through */
  163. case FIB_EVENT_ENTRY_DEL:
  164. err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
  165. break;
  166. }
  167. return notifier_from_errno(err);
  168. }
  169. /* inconsistent dump, trying again */
  170. static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
  171. {
  172. struct nsim_fib_data *data;
  173. struct net *net;
  174. rcu_read_lock();
  175. for_each_net_rcu(net) {
  176. data = net_generic(net, nsim_fib_net_id);
  177. data->ipv4.fib.num = 0ULL;
  178. data->ipv4.rules.num = 0ULL;
  179. data->ipv6.fib.num = 0ULL;
  180. data->ipv6.rules.num = 0ULL;
  181. }
  182. rcu_read_unlock();
  183. }
  184. static struct notifier_block nsim_fib_nb = {
  185. .notifier_call = nsim_fib_event_nb,
  186. };
  187. /* Initialize per network namespace state */
  188. static int __net_init nsim_fib_netns_init(struct net *net)
  189. {
  190. struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
  191. data->ipv4.fib.max = (u64)-1;
  192. data->ipv4.rules.max = (u64)-1;
  193. data->ipv6.fib.max = (u64)-1;
  194. data->ipv6.rules.max = (u64)-1;
  195. return 0;
  196. }
  197. static struct pernet_operations nsim_fib_net_ops = {
  198. .init = nsim_fib_netns_init,
  199. .id = &nsim_fib_net_id,
  200. .size = sizeof(struct nsim_fib_data),
  201. };
  202. void nsim_fib_exit(void)
  203. {
  204. unregister_fib_notifier(&nsim_fib_nb);
  205. unregister_pernet_subsys(&nsim_fib_net_ops);
  206. }
  207. int nsim_fib_init(void)
  208. {
  209. int err;
  210. err = register_pernet_subsys(&nsim_fib_net_ops);
  211. if (err < 0) {
  212. pr_err("Failed to register pernet subsystem\n");
  213. goto err_out;
  214. }
  215. err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
  216. if (err < 0) {
  217. pr_err("Failed to register fib notifier\n");
  218. unregister_pernet_subsys(&nsim_fib_net_ops);
  219. goto err_out;
  220. }
  221. err_out:
  222. return err;
  223. }