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