Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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
 17#include <net/fib_notifier.h>
 18#include <net/ip_fib.h>
 19#include <net/ip6_fib.h>
 20#include <net/fib_rules.h>
 21#include <net/netns/generic.h>
 22
 23#include "netdevsim.h"
 24
 25struct nsim_fib_entry {
 26	u64 max;
 27	u64 num;
 28};
 29
 30struct nsim_per_fib_data {
 31	struct nsim_fib_entry fib;
 32	struct nsim_fib_entry rules;
 33};
 34
 35struct nsim_fib_data {
 36	struct nsim_per_fib_data ipv4;
 37	struct nsim_per_fib_data ipv6;
 38};
 39
 40static unsigned int nsim_fib_net_id;
 41
 42u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
 43{
 44	struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
 45	struct nsim_fib_entry *entry;
 46
 47	switch (res_id) {
 48	case NSIM_RESOURCE_IPV4_FIB:
 49		entry = &fib_data->ipv4.fib;
 50		break;
 51	case NSIM_RESOURCE_IPV4_FIB_RULES:
 52		entry = &fib_data->ipv4.rules;
 53		break;
 54	case NSIM_RESOURCE_IPV6_FIB:
 55		entry = &fib_data->ipv6.fib;
 56		break;
 57	case NSIM_RESOURCE_IPV6_FIB_RULES:
 58		entry = &fib_data->ipv6.rules;
 59		break;
 60	default:
 61		return 0;
 62	}
 63
 64	return max ? entry->max : entry->num;
 65}
 66
 67int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
 68		     struct netlink_ext_ack *extack)
 69{
 70	struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
 71	struct nsim_fib_entry *entry;
 72	int err = 0;
 73
 74	switch (res_id) {
 75	case NSIM_RESOURCE_IPV4_FIB:
 76		entry = &fib_data->ipv4.fib;
 77		break;
 78	case NSIM_RESOURCE_IPV4_FIB_RULES:
 79		entry = &fib_data->ipv4.rules;
 80		break;
 81	case NSIM_RESOURCE_IPV6_FIB:
 82		entry = &fib_data->ipv6.fib;
 83		break;
 84	case NSIM_RESOURCE_IPV6_FIB_RULES:
 85		entry = &fib_data->ipv6.rules;
 86		break;
 87	default:
 88		return 0;
 89	}
 90
 91	/* not allowing a new max to be less than curren occupancy
 92	 * --> no means of evicting entries
 93	 */
 94	if (val < entry->num) {
 95		NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
 96		err = -EINVAL;
 97	} else {
 98		entry->max = val;
 99	}
100
101	return err;
102}
103
104static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
105				 struct netlink_ext_ack *extack)
106{
107	int err = 0;
108
109	if (add) {
110		if (entry->num < entry->max) {
111			entry->num++;
112		} else {
113			err = -ENOSPC;
114			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
115		}
116	} else {
117		entry->num--;
118	}
119
120	return err;
121}
122
123static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
124{
125	struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
126	struct netlink_ext_ack *extack = info->extack;
127	int err = 0;
128
129	switch (info->family) {
130	case AF_INET:
131		err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
132		break;
133	case AF_INET6:
134		err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
135		break;
136	}
137
138	return err;
139}
140
141static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
142			    struct netlink_ext_ack *extack)
143{
144	int err = 0;
145
146	if (add) {
147		if (entry->num < entry->max) {
148			entry->num++;
149		} else {
150			err = -ENOSPC;
151			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
152		}
153	} else {
154		entry->num--;
155	}
156
157	return err;
158}
159
160static int nsim_fib_event(struct fib_notifier_info *info, bool add)
161{
162	struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
163	struct netlink_ext_ack *extack = info->extack;
164	int err = 0;
165
166	switch (info->family) {
167	case AF_INET:
168		err = nsim_fib_account(&data->ipv4.fib, add, extack);
169		break;
170	case AF_INET6:
171		err = nsim_fib_account(&data->ipv6.fib, add, extack);
172		break;
173	}
174
175	return err;
176}
177
178static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
179			     void *ptr)
180{
181	struct fib_notifier_info *info = ptr;
182	int err = 0;
183
184	switch (event) {
185	case FIB_EVENT_RULE_ADD: /* fall through */
186	case FIB_EVENT_RULE_DEL:
187		err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
188		break;
189
190	case FIB_EVENT_ENTRY_ADD:  /* fall through */
191	case FIB_EVENT_ENTRY_DEL:
192		err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
193		break;
194	}
195
196	return notifier_from_errno(err);
197}
198
199/* inconsistent dump, trying again */
200static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
201{
202	struct nsim_fib_data *data;
203	struct net *net;
204
205	rcu_read_lock();
206	for_each_net_rcu(net) {
207		data = net_generic(net, nsim_fib_net_id);
208
209		data->ipv4.fib.num = 0ULL;
210		data->ipv4.rules.num = 0ULL;
211
212		data->ipv6.fib.num = 0ULL;
213		data->ipv6.rules.num = 0ULL;
214	}
215	rcu_read_unlock();
216}
217
218static struct notifier_block nsim_fib_nb = {
219	.notifier_call = nsim_fib_event_nb,
220};
221
222/* Initialize per network namespace state */
223static int __net_init nsim_fib_netns_init(struct net *net)
224{
225	struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
226
227	data->ipv4.fib.max = (u64)-1;
228	data->ipv4.rules.max = (u64)-1;
229
230	data->ipv6.fib.max = (u64)-1;
231	data->ipv6.rules.max = (u64)-1;
232
233	return 0;
234}
235
236static struct pernet_operations nsim_fib_net_ops = {
237	.init = nsim_fib_netns_init,
238	.id   = &nsim_fib_net_id,
239	.size = sizeof(struct nsim_fib_data),
240};
241
242void nsim_fib_exit(void)
243{
244	unregister_fib_notifier(&nsim_fib_nb);
245	unregister_pernet_subsys(&nsim_fib_net_ops);
246}
247
248int nsim_fib_init(void)
249{
250	int err;
251
252	err = register_pernet_subsys(&nsim_fib_net_ops);
253	if (err < 0) {
254		pr_err("Failed to register pernet subsystem\n");
255		goto err_out;
256	}
257
258	err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
259	if (err < 0) {
260		pr_err("Failed to register fib notifier\n");
261		unregister_pernet_subsys(&nsim_fib_net_ops);
262		goto err_out;
263	}
264
265err_out:
266	return err;
267}