blob: f61d094746c069bb7ac1c6db9eb77fe0820d2562 [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>
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
David Ahern7fa76d72018-06-05 08:14:10 -070067int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
68 struct netlink_ext_ack *extack)
David Ahern37923ed2018-03-27 18:22:00 -070069{
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 */
David Ahern7fa76d72018-06-05 08:14:10 -070094 if (val < entry->num) {
95 NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
David Ahern37923ed2018-03-27 18:22:00 -070096 err = -EINVAL;
David Ahern7fa76d72018-06-05 08:14:10 -070097 } else {
David Ahern37923ed2018-03-27 18:22:00 -070098 entry->max = val;
David Ahern7fa76d72018-06-05 08:14:10 -070099 }
David Ahern37923ed2018-03-27 18:22:00 -0700100
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
Arnd Bergmann87248d32018-04-04 14:12:39 +0200236static struct pernet_operations nsim_fib_net_ops = {
David Ahern37923ed2018-03-27 18:22:00 -0700237 .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_pernet_subsys(&nsim_fib_net_ops);
245 unregister_fib_notifier(&nsim_fib_nb);
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 goto err_out;
262 }
263
264err_out:
265 return err;
266}