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