blob: 13540dee7364695fe3c06bdcd11d433d4f224757 [file] [log] [blame]
David Ahern37923ed2018-03-27 18:22:00 -07001/*
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>
Jiri Pirkoa5facc42019-10-03 11:49:26 +020021#include <net/net_namespace.h>
David Ahern37923ed2018-03-27 18:22:00 -070022
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 {
Jiri Pirkoa5facc42019-10-03 11:49:26 +020036 struct notifier_block fib_nb;
David Ahern37923ed2018-03-27 18:22:00 -070037 struct nsim_per_fib_data ipv4;
38 struct nsim_per_fib_data ipv6;
39};
40
Jiri Pirkoa5facc42019-10-03 11:49:26 +020041u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
42 enum nsim_resource_id res_id, bool max)
David Ahern37923ed2018-03-27 18:22:00 -070043{
David Ahern37923ed2018-03-27 18:22:00 -070044 struct nsim_fib_entry *entry;
45
46 switch (res_id) {
47 case NSIM_RESOURCE_IPV4_FIB:
48 entry = &fib_data->ipv4.fib;
49 break;
50 case NSIM_RESOURCE_IPV4_FIB_RULES:
51 entry = &fib_data->ipv4.rules;
52 break;
53 case NSIM_RESOURCE_IPV6_FIB:
54 entry = &fib_data->ipv6.fib;
55 break;
56 case NSIM_RESOURCE_IPV6_FIB_RULES:
57 entry = &fib_data->ipv6.rules;
58 break;
59 default:
60 return 0;
61 }
62
63 return max ? entry->max : entry->num;
64}
65
Jiri Pirko75ba0292019-10-03 11:49:36 +020066static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
67 enum nsim_resource_id res_id, u64 val)
David Ahern37923ed2018-03-27 18:22:00 -070068{
David Ahern37923ed2018-03-27 18:22:00 -070069 struct nsim_fib_entry *entry;
David Ahern37923ed2018-03-27 18:22:00 -070070
71 switch (res_id) {
72 case NSIM_RESOURCE_IPV4_FIB:
73 entry = &fib_data->ipv4.fib;
74 break;
75 case NSIM_RESOURCE_IPV4_FIB_RULES:
76 entry = &fib_data->ipv4.rules;
77 break;
78 case NSIM_RESOURCE_IPV6_FIB:
79 entry = &fib_data->ipv6.fib;
80 break;
81 case NSIM_RESOURCE_IPV6_FIB_RULES:
82 entry = &fib_data->ipv6.rules;
83 break;
84 default:
Jiri Pirko75ba0292019-10-03 11:49:36 +020085 WARN_ON(1);
86 return;
David Ahern37923ed2018-03-27 18:22:00 -070087 }
Jiri Pirko75ba0292019-10-03 11:49:36 +020088 entry->max = val;
David Ahern37923ed2018-03-27 18:22:00 -070089}
90
91static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
92 struct netlink_ext_ack *extack)
93{
94 int err = 0;
95
96 if (add) {
97 if (entry->num < entry->max) {
98 entry->num++;
99 } else {
100 err = -ENOSPC;
101 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
102 }
103 } else {
104 entry->num--;
105 }
106
107 return err;
108}
109
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200110static int nsim_fib_rule_event(struct nsim_fib_data *data,
111 struct fib_notifier_info *info, bool add)
David Ahern37923ed2018-03-27 18:22:00 -0700112{
David Ahern37923ed2018-03-27 18:22:00 -0700113 struct netlink_ext_ack *extack = info->extack;
114 int err = 0;
115
116 switch (info->family) {
117 case AF_INET:
118 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
119 break;
120 case AF_INET6:
121 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
122 break;
123 }
124
125 return err;
126}
127
128static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
129 struct netlink_ext_ack *extack)
130{
131 int err = 0;
132
133 if (add) {
134 if (entry->num < entry->max) {
135 entry->num++;
136 } else {
137 err = -ENOSPC;
138 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
139 }
140 } else {
141 entry->num--;
142 }
143
144 return err;
145}
146
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200147static int nsim_fib_event(struct nsim_fib_data *data,
148 struct fib_notifier_info *info, bool add)
David Ahern37923ed2018-03-27 18:22:00 -0700149{
David Ahern37923ed2018-03-27 18:22:00 -0700150 struct netlink_ext_ack *extack = info->extack;
151 int err = 0;
152
153 switch (info->family) {
154 case AF_INET:
155 err = nsim_fib_account(&data->ipv4.fib, add, extack);
156 break;
157 case AF_INET6:
158 err = nsim_fib_account(&data->ipv6.fib, add, extack);
159 break;
160 }
161
162 return err;
163}
164
165static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
166 void *ptr)
167{
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200168 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
169 fib_nb);
David Ahern37923ed2018-03-27 18:22:00 -0700170 struct fib_notifier_info *info = ptr;
171 int err = 0;
172
173 switch (event) {
174 case FIB_EVENT_RULE_ADD: /* fall through */
175 case FIB_EVENT_RULE_DEL:
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200176 err = nsim_fib_rule_event(data, info,
177 event == FIB_EVENT_RULE_ADD);
David Ahern37923ed2018-03-27 18:22:00 -0700178 break;
179
180 case FIB_EVENT_ENTRY_ADD: /* fall through */
181 case FIB_EVENT_ENTRY_DEL:
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200182 err = nsim_fib_event(data, info,
183 event == FIB_EVENT_ENTRY_ADD);
David Ahern37923ed2018-03-27 18:22:00 -0700184 break;
185 }
186
187 return notifier_from_errno(err);
188}
189
190/* inconsistent dump, trying again */
191static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
192{
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200193 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
194 fib_nb);
David Ahern37923ed2018-03-27 18:22:00 -0700195
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200196 data->ipv4.fib.num = 0ULL;
197 data->ipv4.rules.num = 0ULL;
198 data->ipv6.fib.num = 0ULL;
199 data->ipv6.rules.num = 0ULL;
David Ahern37923ed2018-03-27 18:22:00 -0700200}
201
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200202static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
David Ahern59c84b92019-08-06 12:15:17 -0700203{
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200204 struct nsim_fib_data *data = priv;
205
206 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
207}
208
209static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
210{
211 struct nsim_fib_data *data = priv;
212
213 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
214}
215
216static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
217{
218 struct nsim_fib_data *data = priv;
219
220 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
221}
222
223static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
224{
225 struct nsim_fib_data *data = priv;
226
227 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
228}
229
Jiri Pirko75ba0292019-10-03 11:49:36 +0200230static void nsim_fib_set_max_all(struct nsim_fib_data *data,
231 struct devlink *devlink)
232{
233 enum nsim_resource_id res_ids[] = {
234 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
235 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
236 };
237 int i;
238
239 for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
240 int err;
241 u64 val;
242
243 err = devlink_resource_size_get(devlink, res_ids[i], &val);
244 if (err)
245 val = (u64) -1;
246 nsim_fib_set_max(data, res_ids[i], val);
247 }
248}
249
250struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
251 struct netlink_ext_ack *extack)
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200252{
253 struct nsim_fib_data *data;
254 int err;
255
256 data = kzalloc(sizeof(*data), GFP_KERNEL);
257 if (!data)
258 return ERR_PTR(-ENOMEM);
David Ahern37923ed2018-03-27 18:22:00 -0700259
Jiri Pirko75ba0292019-10-03 11:49:36 +0200260 nsim_fib_set_max_all(data, devlink);
David Ahern37923ed2018-03-27 18:22:00 -0700261
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200262 data->fib_nb.notifier_call = nsim_fib_event_nb;
Jiri Pirko4f174bb2019-10-03 11:49:38 +0200263 err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
Jiri Pirko75ba0292019-10-03 11:49:36 +0200264 nsim_fib_dump_inconsistent, extack);
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200265 if (err) {
David Ahern37923ed2018-03-27 18:22:00 -0700266 pr_err("Failed to register fib notifier\n");
267 goto err_out;
268 }
269
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200270 devlink_resource_occ_get_register(devlink,
271 NSIM_RESOURCE_IPV4_FIB,
272 nsim_fib_ipv4_resource_occ_get,
273 data);
274 devlink_resource_occ_get_register(devlink,
275 NSIM_RESOURCE_IPV4_FIB_RULES,
276 nsim_fib_ipv4_rules_res_occ_get,
277 data);
278 devlink_resource_occ_get_register(devlink,
279 NSIM_RESOURCE_IPV6_FIB,
280 nsim_fib_ipv6_resource_occ_get,
281 data);
282 devlink_resource_occ_get_register(devlink,
283 NSIM_RESOURCE_IPV6_FIB_RULES,
284 nsim_fib_ipv6_rules_res_occ_get,
285 data);
286 return data;
287
David Ahern37923ed2018-03-27 18:22:00 -0700288err_out:
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200289 kfree(data);
290 return ERR_PTR(err);
291}
292
293void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
294{
295 devlink_resource_occ_get_unregister(devlink,
296 NSIM_RESOURCE_IPV6_FIB_RULES);
297 devlink_resource_occ_get_unregister(devlink,
298 NSIM_RESOURCE_IPV6_FIB);
299 devlink_resource_occ_get_unregister(devlink,
300 NSIM_RESOURCE_IPV4_FIB_RULES);
301 devlink_resource_occ_get_unregister(devlink,
302 NSIM_RESOURCE_IPV4_FIB);
Jiri Pirko4f174bb2019-10-03 11:49:38 +0200303 unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
Jiri Pirkoa5facc42019-10-03 11:49:26 +0200304 kfree(data);
David Ahern37923ed2018-03-27 18:22:00 -0700305}