123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- /*
- * Copyright (c) 2018 Cumulus Networks. All rights reserved.
- * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
- *
- * This software is licensed under the GNU General License Version 2,
- * June 1991 as shown in the file COPYING in the top-level directory of this
- * source tree.
- *
- * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
- * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
- * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
- * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
- */
- #include <net/fib_notifier.h>
- #include <net/ip_fib.h>
- #include <net/ip6_fib.h>
- #include <net/fib_rules.h>
- #include <net/netns/generic.h>
- #include "netdevsim.h"
- struct nsim_fib_entry {
- u64 max;
- u64 num;
- };
- struct nsim_per_fib_data {
- struct nsim_fib_entry fib;
- struct nsim_fib_entry rules;
- };
- struct nsim_fib_data {
- struct nsim_per_fib_data ipv4;
- struct nsim_per_fib_data ipv6;
- };
- static unsigned int nsim_fib_net_id;
- u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
- {
- struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
- struct nsim_fib_entry *entry;
- switch (res_id) {
- case NSIM_RESOURCE_IPV4_FIB:
- entry = &fib_data->ipv4.fib;
- break;
- case NSIM_RESOURCE_IPV4_FIB_RULES:
- entry = &fib_data->ipv4.rules;
- break;
- case NSIM_RESOURCE_IPV6_FIB:
- entry = &fib_data->ipv6.fib;
- break;
- case NSIM_RESOURCE_IPV6_FIB_RULES:
- entry = &fib_data->ipv6.rules;
- break;
- default:
- return 0;
- }
- return max ? entry->max : entry->num;
- }
- int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
- struct netlink_ext_ack *extack)
- {
- struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
- struct nsim_fib_entry *entry;
- int err = 0;
- switch (res_id) {
- case NSIM_RESOURCE_IPV4_FIB:
- entry = &fib_data->ipv4.fib;
- break;
- case NSIM_RESOURCE_IPV4_FIB_RULES:
- entry = &fib_data->ipv4.rules;
- break;
- case NSIM_RESOURCE_IPV6_FIB:
- entry = &fib_data->ipv6.fib;
- break;
- case NSIM_RESOURCE_IPV6_FIB_RULES:
- entry = &fib_data->ipv6.rules;
- break;
- default:
- return 0;
- }
- /* not allowing a new max to be less than curren occupancy
- * --> no means of evicting entries
- */
- if (val < entry->num) {
- NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
- err = -EINVAL;
- } else {
- entry->max = val;
- }
- return err;
- }
- static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
- struct netlink_ext_ack *extack)
- {
- int err = 0;
- if (add) {
- if (entry->num < entry->max) {
- entry->num++;
- } else {
- err = -ENOSPC;
- NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
- }
- } else {
- entry->num--;
- }
- return err;
- }
- static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
- {
- struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
- struct netlink_ext_ack *extack = info->extack;
- int err = 0;
- switch (info->family) {
- case AF_INET:
- err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
- break;
- case AF_INET6:
- err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
- break;
- }
- return err;
- }
- static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
- struct netlink_ext_ack *extack)
- {
- int err = 0;
- if (add) {
- if (entry->num < entry->max) {
- entry->num++;
- } else {
- err = -ENOSPC;
- NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
- }
- } else {
- entry->num--;
- }
- return err;
- }
- static int nsim_fib_event(struct fib_notifier_info *info, bool add)
- {
- struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
- struct netlink_ext_ack *extack = info->extack;
- int err = 0;
- switch (info->family) {
- case AF_INET:
- err = nsim_fib_account(&data->ipv4.fib, add, extack);
- break;
- case AF_INET6:
- err = nsim_fib_account(&data->ipv6.fib, add, extack);
- break;
- }
- return err;
- }
- static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
- void *ptr)
- {
- struct fib_notifier_info *info = ptr;
- int err = 0;
- switch (event) {
- case FIB_EVENT_RULE_ADD: /* fall through */
- case FIB_EVENT_RULE_DEL:
- err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
- break;
- case FIB_EVENT_ENTRY_ADD: /* fall through */
- case FIB_EVENT_ENTRY_DEL:
- err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
- break;
- }
- return notifier_from_errno(err);
- }
- /* inconsistent dump, trying again */
- static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
- {
- struct nsim_fib_data *data;
- struct net *net;
- rcu_read_lock();
- for_each_net_rcu(net) {
- data = net_generic(net, nsim_fib_net_id);
- data->ipv4.fib.num = 0ULL;
- data->ipv4.rules.num = 0ULL;
- data->ipv6.fib.num = 0ULL;
- data->ipv6.rules.num = 0ULL;
- }
- rcu_read_unlock();
- }
- static struct notifier_block nsim_fib_nb = {
- .notifier_call = nsim_fib_event_nb,
- };
- /* Initialize per network namespace state */
- static int __net_init nsim_fib_netns_init(struct net *net)
- {
- struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
- data->ipv4.fib.max = (u64)-1;
- data->ipv4.rules.max = (u64)-1;
- data->ipv6.fib.max = (u64)-1;
- data->ipv6.rules.max = (u64)-1;
- return 0;
- }
- static struct pernet_operations nsim_fib_net_ops = {
- .init = nsim_fib_netns_init,
- .id = &nsim_fib_net_id,
- .size = sizeof(struct nsim_fib_data),
- };
- void nsim_fib_exit(void)
- {
- unregister_fib_notifier(&nsim_fib_nb);
- unregister_pernet_subsys(&nsim_fib_net_ops);
- }
- int nsim_fib_init(void)
- {
- int err;
- err = register_pernet_subsys(&nsim_fib_net_ops);
- if (err < 0) {
- pr_err("Failed to register pernet subsystem\n");
- goto err_out;
- }
- err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
- if (err < 0) {
- pr_err("Failed to register fib notifier\n");
- unregister_pernet_subsys(&nsim_fib_net_ops);
- goto err_out;
- }
- err_out:
- return err;
- }
|