blob: 8c57ba7477726650bb6c0b188a2c8ebb989b14d7 [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>
David Ahern37923ed2018-03-27 18:22:00 -070021
22#include "netdevsim.h"
23
24struct nsim_fib_entry {
25 u64 max;
26 u64 num;
27};
28
29struct nsim_per_fib_data {
30 struct nsim_fib_entry fib;
31 struct nsim_fib_entry rules;
32};
33
34struct nsim_fib_data {
Jiri Pirko5fc49422019-04-25 15:59:42 +020035 struct notifier_block fib_nb;
David Ahern37923ed2018-03-27 18:22:00 -070036 struct nsim_per_fib_data ipv4;
37 struct nsim_per_fib_data ipv6;
38};
39
Jiri Pirko5fc49422019-04-25 15:59:42 +020040u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
41 enum nsim_resource_id res_id, bool max)
David Ahern37923ed2018-03-27 18:22:00 -070042{
David Ahern37923ed2018-03-27 18:22:00 -070043 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
Jiri Pirko5fc49422019-04-25 15:59:42 +020065int nsim_fib_set_max(struct nsim_fib_data *fib_data,
66 enum nsim_resource_id res_id, u64 val,
David Ahern7fa76d72018-06-05 08:14:10 -070067 struct netlink_ext_ack *extack)
David Ahern37923ed2018-03-27 18:22:00 -070068{
David Ahern37923ed2018-03-27 18:22:00 -070069 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 */
David Ahern7fa76d72018-06-05 08:14:10 -070092 if (val < entry->num) {
93 NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
David Ahern37923ed2018-03-27 18:22:00 -070094 err = -EINVAL;
David Ahern7fa76d72018-06-05 08:14:10 -070095 } else {
David Ahern37923ed2018-03-27 18:22:00 -070096 entry->max = val;
David Ahern7fa76d72018-06-05 08:14:10 -070097 }
David Ahern37923ed2018-03-27 18:22:00 -070098
99 return err;
100}
101
102static 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
Jiri Pirko5fc49422019-04-25 15:59:42 +0200121static int nsim_fib_rule_event(struct nsim_fib_data *data,
122 struct fib_notifier_info *info, bool add)
David Ahern37923ed2018-03-27 18:22:00 -0700123{
David Ahern37923ed2018-03-27 18:22:00 -0700124 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
139static 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
Jiri Pirko5fc49422019-04-25 15:59:42 +0200158static int nsim_fib_event(struct nsim_fib_data *data,
159 struct fib_notifier_info *info, bool add)
David Ahern37923ed2018-03-27 18:22:00 -0700160{
David Ahern37923ed2018-03-27 18:22:00 -0700161 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
176static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
177 void *ptr)
178{
Jiri Pirko5fc49422019-04-25 15:59:42 +0200179 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
180 fib_nb);
David Ahern37923ed2018-03-27 18:22:00 -0700181 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:
Jiri Pirko5fc49422019-04-25 15:59:42 +0200187 err = nsim_fib_rule_event(data, info,
188 event == FIB_EVENT_RULE_ADD);
David Ahern37923ed2018-03-27 18:22:00 -0700189 break;
190
191 case FIB_EVENT_ENTRY_ADD: /* fall through */
192 case FIB_EVENT_ENTRY_DEL:
Jiri Pirko5fc49422019-04-25 15:59:42 +0200193 err = nsim_fib_event(data, info,
194 event == FIB_EVENT_ENTRY_ADD);
David Ahern37923ed2018-03-27 18:22:00 -0700195 break;
196 }
197
198 return notifier_from_errno(err);
199}
200
201/* inconsistent dump, trying again */
202static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
203{
Jiri Pirko5fc49422019-04-25 15:59:42 +0200204 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
205 fib_nb);
David Ahern37923ed2018-03-27 18:22:00 -0700206
Jiri Pirko5fc49422019-04-25 15:59:42 +0200207 data->ipv4.fib.num = 0ULL;
208 data->ipv4.rules.num = 0ULL;
209 data->ipv6.fib.num = 0ULL;
210 data->ipv6.rules.num = 0ULL;
David Ahern37923ed2018-03-27 18:22:00 -0700211}
212
Jiri Pirko5fc49422019-04-25 15:59:42 +0200213struct nsim_fib_data *nsim_fib_create(void)
David Ahern37923ed2018-03-27 18:22:00 -0700214{
Jiri Pirko5fc49422019-04-25 15:59:42 +0200215 struct nsim_fib_data *data;
216 int err;
217
218 data = kzalloc(sizeof(*data), GFP_KERNEL);
219 if (!data)
220 return ERR_PTR(-ENOMEM);
David Ahern37923ed2018-03-27 18:22:00 -0700221
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
Jiri Pirko5fc49422019-04-25 15:59:42 +0200228 data->fib_nb.notifier_call = nsim_fib_event_nb;
229 err = register_fib_notifier(&data->fib_nb, nsim_fib_dump_inconsistent);
230 if (err) {
David Ahern37923ed2018-03-27 18:22:00 -0700231 pr_err("Failed to register fib notifier\n");
232 goto err_out;
233 }
234
Jiri Pirko5fc49422019-04-25 15:59:42 +0200235 return data;
236
David Ahern37923ed2018-03-27 18:22:00 -0700237err_out:
Jiri Pirko5fc49422019-04-25 15:59:42 +0200238 kfree(data);
239 return ERR_PTR(err);
240}
241
242void nsim_fib_destroy(struct nsim_fib_data *data)
243{
244 unregister_fib_notifier(&data->fib_nb);
245 kfree(data);
David Ahern37923ed2018-03-27 18:22:00 -0700246}