305 lines · 7.1 KB · C
RawDownload
/* * 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/net_namespace.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 notifier_block fib_nb;	struct nsim_per_fib_data ipv4;	struct nsim_per_fib_data ipv6;}; u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,		     enum nsim_resource_id res_id, bool max){	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;} static void nsim_fib_set_max(struct nsim_fib_data *fib_data,			     enum nsim_resource_id res_id, u64 val){	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:		WARN_ON(1);		return;	}	entry->max = val;} 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 nsim_fib_data *data,			       struct fib_notifier_info *info, bool add){	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 nsim_fib_data *data,			  struct fib_notifier_info *info, bool add){	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 nsim_fib_data *data = container_of(nb, struct nsim_fib_data,						  fib_nb);	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(data, info,					  event == FIB_EVENT_RULE_ADD);		break; 	case FIB_EVENT_ENTRY_ADD:  /* fall through */	case FIB_EVENT_ENTRY_DEL:		err = nsim_fib_event(data, 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 = container_of(nb, struct nsim_fib_data,						  fib_nb); 	data->ipv4.fib.num = 0ULL;	data->ipv4.rules.num = 0ULL;	data->ipv6.fib.num = 0ULL;	data->ipv6.rules.num = 0ULL;} static u64 nsim_fib_ipv4_resource_occ_get(void *priv){	struct nsim_fib_data *data = priv; 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);} static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv){	struct nsim_fib_data *data = priv; 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);} static u64 nsim_fib_ipv6_resource_occ_get(void *priv){	struct nsim_fib_data *data = priv; 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);} static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv){	struct nsim_fib_data *data = priv; 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);} static void nsim_fib_set_max_all(struct nsim_fib_data *data,				 struct devlink *devlink){	enum nsim_resource_id res_ids[] = {		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES	};	int i; 	for (i = 0; i < ARRAY_SIZE(res_ids); i++) {		int err;		u64 val; 		err = devlink_resource_size_get(devlink, res_ids[i], &val);		if (err)			val = (u64) -1;		nsim_fib_set_max(data, res_ids[i], val);	}} struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,				      struct netlink_ext_ack *extack){	struct nsim_fib_data *data;	int err; 	data = kzalloc(sizeof(*data), GFP_KERNEL);	if (!data)		return ERR_PTR(-ENOMEM); 	nsim_fib_set_max_all(data, devlink); 	data->fib_nb.notifier_call = nsim_fib_event_nb;	err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,				    nsim_fib_dump_inconsistent, extack);	if (err) {		pr_err("Failed to register fib notifier\n");		goto err_out;	} 	devlink_resource_occ_get_register(devlink,					  NSIM_RESOURCE_IPV4_FIB,					  nsim_fib_ipv4_resource_occ_get,					  data);	devlink_resource_occ_get_register(devlink,					  NSIM_RESOURCE_IPV4_FIB_RULES,					  nsim_fib_ipv4_rules_res_occ_get,					  data);	devlink_resource_occ_get_register(devlink,					  NSIM_RESOURCE_IPV6_FIB,					  nsim_fib_ipv6_resource_occ_get,					  data);	devlink_resource_occ_get_register(devlink,					  NSIM_RESOURCE_IPV6_FIB_RULES,					  nsim_fib_ipv6_rules_res_occ_get,					  data);	return data; err_out:	kfree(data);	return ERR_PTR(err);} void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data){	devlink_resource_occ_get_unregister(devlink,					    NSIM_RESOURCE_IPV6_FIB_RULES);	devlink_resource_occ_get_unregister(devlink,					    NSIM_RESOURCE_IPV6_FIB);	devlink_resource_occ_get_unregister(devlink,					    NSIM_RESOURCE_IPV4_FIB_RULES);	devlink_resource_occ_get_unregister(devlink,					    NSIM_RESOURCE_IPV4_FIB);	unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);	kfree(data);}