1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2019-2021 Marvell International Ltd. All rights reserved */ |
3 | |
4 | #include <linux/kernel.h> |
5 | #include <linux/types.h> |
6 | #include <linux/inetdevice.h> |
7 | #include <net/inet_dscp.h> |
8 | #include <net/switchdev.h> |
9 | #include <linux/rhashtable.h> |
10 | #include <net/nexthop.h> |
11 | #include <net/arp.h> |
12 | #include <linux/if_vlan.h> |
13 | #include <linux/if_macvlan.h> |
14 | #include <net/netevent.h> |
15 | |
16 | #include "prestera.h" |
17 | #include "prestera_router_hw.h" |
18 | |
19 | #define PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH |
20 | #define PRESTERA_NH_PROBE_INTERVAL 5000 /* ms */ |
21 | |
22 | struct prestera_kern_neigh_cache_key { |
23 | struct prestera_ip_addr addr; |
24 | struct net_device *dev; |
25 | }; |
26 | |
27 | struct prestera_kern_neigh_cache { |
28 | struct prestera_kern_neigh_cache_key key; |
29 | struct rhash_head ht_node; |
30 | struct list_head kern_fib_cache_list; |
31 | /* Hold prepared nh_neigh info if is in_kernel */ |
32 | struct prestera_neigh_info nh_neigh_info; |
33 | /* Indicate if neighbour is reachable by direct route */ |
34 | bool reachable; |
35 | /* Lock cache if neigh is present in kernel */ |
36 | bool in_kernel; |
37 | }; |
38 | |
39 | struct prestera_kern_fib_cache_key { |
40 | struct prestera_ip_addr addr; |
41 | u32 prefix_len; |
42 | u32 kern_tb_id; /* tb_id from kernel (not fixed) */ |
43 | }; |
44 | |
45 | /* Subscribing on neighbours in kernel */ |
46 | struct prestera_kern_fib_cache { |
47 | struct prestera_kern_fib_cache_key key; |
48 | struct { |
49 | struct prestera_fib_key fib_key; |
50 | enum prestera_fib_type fib_type; |
51 | struct prestera_nexthop_group_key nh_grp_key; |
52 | } lpm_info; /* hold prepared lpm info */ |
53 | /* Indicate if route is not overlapped by another table */ |
54 | struct rhash_head ht_node; /* node of prestera_router */ |
55 | struct prestera_kern_neigh_cache_head { |
56 | struct prestera_kern_fib_cache *this; |
57 | struct list_head head; |
58 | struct prestera_kern_neigh_cache *n_cache; |
59 | } kern_neigh_cache_head[PRESTERA_NHGR_SIZE_MAX]; |
60 | union { |
61 | struct fib_notifier_info info; /* point to any of 4/6 */ |
62 | struct fib_entry_notifier_info fen4_info; |
63 | }; |
64 | bool reachable; |
65 | }; |
66 | |
67 | static const struct rhashtable_params __prestera_kern_neigh_cache_ht_params = { |
68 | .key_offset = offsetof(struct prestera_kern_neigh_cache, key), |
69 | .head_offset = offsetof(struct prestera_kern_neigh_cache, ht_node), |
70 | .key_len = sizeof(struct prestera_kern_neigh_cache_key), |
71 | .automatic_shrinking = true, |
72 | }; |
73 | |
74 | static const struct rhashtable_params __prestera_kern_fib_cache_ht_params = { |
75 | .key_offset = offsetof(struct prestera_kern_fib_cache, key), |
76 | .head_offset = offsetof(struct prestera_kern_fib_cache, ht_node), |
77 | .key_len = sizeof(struct prestera_kern_fib_cache_key), |
78 | .automatic_shrinking = true, |
79 | }; |
80 | |
81 | /* This util to be used, to convert kernel rules for default vr in hw_vr */ |
82 | static u32 prestera_fix_tb_id(u32 tb_id) |
83 | { |
84 | if (tb_id == RT_TABLE_UNSPEC || |
85 | tb_id == RT_TABLE_LOCAL || |
86 | tb_id == RT_TABLE_DEFAULT) |
87 | tb_id = RT_TABLE_MAIN; |
88 | |
89 | return tb_id; |
90 | } |
91 | |
92 | static void |
93 | prestera_util_fen_info2fib_cache_key(struct fib_notifier_info *info, |
94 | struct prestera_kern_fib_cache_key *key) |
95 | { |
96 | struct fib_entry_notifier_info *fen_info = |
97 | container_of(info, struct fib_entry_notifier_info, info); |
98 | |
99 | memset(key, 0, sizeof(*key)); |
100 | key->addr.v = PRESTERA_IPV4; |
101 | key->addr.u.ipv4 = cpu_to_be32(fen_info->dst); |
102 | key->prefix_len = fen_info->dst_len; |
103 | key->kern_tb_id = fen_info->tb_id; |
104 | } |
105 | |
106 | static int prestera_util_nhc2nc_key(struct prestera_switch *sw, |
107 | struct fib_nh_common *nhc, |
108 | struct prestera_kern_neigh_cache_key *nk) |
109 | { |
110 | memset(nk, 0, sizeof(*nk)); |
111 | if (nhc->nhc_gw_family == AF_INET) { |
112 | nk->addr.v = PRESTERA_IPV4; |
113 | nk->addr.u.ipv4 = nhc->nhc_gw.ipv4; |
114 | } else { |
115 | nk->addr.v = PRESTERA_IPV6; |
116 | nk->addr.u.ipv6 = nhc->nhc_gw.ipv6; |
117 | } |
118 | |
119 | nk->dev = nhc->nhc_dev; |
120 | return 0; |
121 | } |
122 | |
123 | static void |
124 | prestera_util_nc_key2nh_key(struct prestera_kern_neigh_cache_key *ck, |
125 | struct prestera_nh_neigh_key *nk) |
126 | { |
127 | memset(nk, 0, sizeof(*nk)); |
128 | nk->addr = ck->addr; |
129 | nk->rif = (void *)ck->dev; |
130 | } |
131 | |
132 | static bool |
133 | prestera_util_nhc_eq_n_cache_key(struct prestera_switch *sw, |
134 | struct fib_nh_common *nhc, |
135 | struct prestera_kern_neigh_cache_key *nk) |
136 | { |
137 | struct prestera_kern_neigh_cache_key tk; |
138 | int err; |
139 | |
140 | err = prestera_util_nhc2nc_key(sw, nhc, nk: &tk); |
141 | if (err) |
142 | return false; |
143 | |
144 | if (memcmp(p: &tk, q: nk, size: sizeof(tk))) |
145 | return false; |
146 | |
147 | return true; |
148 | } |
149 | |
150 | static int |
151 | prestera_util_neigh2nc_key(struct prestera_switch *sw, struct neighbour *n, |
152 | struct prestera_kern_neigh_cache_key *key) |
153 | { |
154 | memset(key, 0, sizeof(*key)); |
155 | if (n->tbl->family == AF_INET) { |
156 | key->addr.v = PRESTERA_IPV4; |
157 | key->addr.u.ipv4 = *(__be32 *)n->primary_key; |
158 | } else { |
159 | return -ENOENT; |
160 | } |
161 | |
162 | key->dev = n->dev; |
163 | |
164 | return 0; |
165 | } |
166 | |
167 | static bool __prestera_fi_is_direct(struct fib_info *fi) |
168 | { |
169 | struct fib_nh_common *fib_nhc; |
170 | |
171 | if (fib_info_num_path(fi) == 1) { |
172 | fib_nhc = fib_info_nhc(fi, nhsel: 0); |
173 | if (fib_nhc->nhc_gw_family == AF_UNSPEC) |
174 | return true; |
175 | } |
176 | |
177 | return false; |
178 | } |
179 | |
180 | static bool prestera_fi_is_direct(struct fib_info *fi) |
181 | { |
182 | if (fi->fib_type != RTN_UNICAST) |
183 | return false; |
184 | |
185 | return __prestera_fi_is_direct(fi); |
186 | } |
187 | |
188 | static bool prestera_fi_is_nh(struct fib_info *fi) |
189 | { |
190 | if (fi->fib_type != RTN_UNICAST) |
191 | return false; |
192 | |
193 | return !__prestera_fi_is_direct(fi); |
194 | } |
195 | |
196 | static bool __prestera_fi6_is_direct(struct fib6_info *fi) |
197 | { |
198 | if (!fi->fib6_nh->nh_common.nhc_gw_family) |
199 | return true; |
200 | |
201 | return false; |
202 | } |
203 | |
204 | static bool prestera_fi6_is_direct(struct fib6_info *fi) |
205 | { |
206 | if (fi->fib6_type != RTN_UNICAST) |
207 | return false; |
208 | |
209 | return __prestera_fi6_is_direct(fi); |
210 | } |
211 | |
212 | static bool prestera_fi6_is_nh(struct fib6_info *fi) |
213 | { |
214 | if (fi->fib6_type != RTN_UNICAST) |
215 | return false; |
216 | |
217 | return !__prestera_fi6_is_direct(fi); |
218 | } |
219 | |
220 | static bool prestera_fib_info_is_direct(struct fib_notifier_info *info) |
221 | { |
222 | struct fib6_entry_notifier_info *fen6_info = |
223 | container_of(info, struct fib6_entry_notifier_info, info); |
224 | struct fib_entry_notifier_info *fen_info = |
225 | container_of(info, struct fib_entry_notifier_info, info); |
226 | |
227 | if (info->family == AF_INET) |
228 | return prestera_fi_is_direct(fi: fen_info->fi); |
229 | else |
230 | return prestera_fi6_is_direct(fi: fen6_info->rt); |
231 | } |
232 | |
233 | static bool prestera_fib_info_is_nh(struct fib_notifier_info *info) |
234 | { |
235 | struct fib6_entry_notifier_info *fen6_info = |
236 | container_of(info, struct fib6_entry_notifier_info, info); |
237 | struct fib_entry_notifier_info *fen_info = |
238 | container_of(info, struct fib_entry_notifier_info, info); |
239 | |
240 | if (info->family == AF_INET) |
241 | return prestera_fi_is_nh(fi: fen_info->fi); |
242 | else |
243 | return prestera_fi6_is_nh(fi: fen6_info->rt); |
244 | } |
245 | |
246 | /* must be called with rcu_read_lock() */ |
247 | static int prestera_util_kern_get_route(struct fib_result *res, u32 tb_id, |
248 | __be32 *addr) |
249 | { |
250 | struct flowi4 fl4; |
251 | |
252 | /* TODO: walkthrough appropriate tables in kernel |
253 | * to know if the same prefix exists in several tables |
254 | */ |
255 | memset(&fl4, 0, sizeof(fl4)); |
256 | fl4.daddr = *addr; |
257 | return fib_lookup(net: &init_net, flp: &fl4, res, flags: 0 /* FIB_LOOKUP_NOREF */); |
258 | } |
259 | |
260 | static bool |
261 | __prestera_util_kern_n_is_reachable_v4(u32 tb_id, __be32 *addr, |
262 | struct net_device *dev) |
263 | { |
264 | struct fib_nh_common *fib_nhc; |
265 | struct fib_result res; |
266 | bool reachable; |
267 | |
268 | reachable = false; |
269 | |
270 | if (!prestera_util_kern_get_route(res: &res, tb_id, addr)) |
271 | if (prestera_fi_is_direct(fi: res.fi)) { |
272 | fib_nhc = fib_info_nhc(fi: res.fi, nhsel: 0); |
273 | if (dev == fib_nhc->nhc_dev) |
274 | reachable = true; |
275 | } |
276 | |
277 | return reachable; |
278 | } |
279 | |
280 | /* Check if neigh route is reachable */ |
281 | static bool |
282 | prestera_util_kern_n_is_reachable(u32 tb_id, |
283 | struct prestera_ip_addr *addr, |
284 | struct net_device *dev) |
285 | { |
286 | if (addr->v == PRESTERA_IPV4) |
287 | return __prestera_util_kern_n_is_reachable_v4(tb_id, |
288 | addr: &addr->u.ipv4, |
289 | dev); |
290 | else |
291 | return false; |
292 | } |
293 | |
294 | static void prestera_util_kern_set_neigh_offload(struct neighbour *n, |
295 | bool offloaded) |
296 | { |
297 | if (offloaded) |
298 | n->flags |= NTF_OFFLOADED; |
299 | else |
300 | n->flags &= ~NTF_OFFLOADED; |
301 | } |
302 | |
303 | static void |
304 | prestera_util_kern_set_nh_offload(struct fib_nh_common *nhc, bool offloaded, bool trap) |
305 | { |
306 | if (offloaded) |
307 | nhc->nhc_flags |= RTNH_F_OFFLOAD; |
308 | else |
309 | nhc->nhc_flags &= ~RTNH_F_OFFLOAD; |
310 | |
311 | if (trap) |
312 | nhc->nhc_flags |= RTNH_F_TRAP; |
313 | else |
314 | nhc->nhc_flags &= ~RTNH_F_TRAP; |
315 | } |
316 | |
317 | static struct fib_nh_common * |
318 | prestera_kern_fib_info_nhc(struct fib_notifier_info *info, int n) |
319 | { |
320 | struct fib6_entry_notifier_info *fen6_info; |
321 | struct fib_entry_notifier_info *fen4_info; |
322 | struct fib6_info *iter; |
323 | |
324 | if (info->family == AF_INET) { |
325 | fen4_info = container_of(info, struct fib_entry_notifier_info, |
326 | info); |
327 | return fib_info_nhc(fi: fen4_info->fi, nhsel: n); |
328 | } else if (info->family == AF_INET6) { |
329 | fen6_info = container_of(info, struct fib6_entry_notifier_info, |
330 | info); |
331 | if (!n) |
332 | return &fen6_info->rt->fib6_nh->nh_common; |
333 | |
334 | list_for_each_entry(iter, &fen6_info->rt->fib6_siblings, |
335 | fib6_siblings) { |
336 | if (!--n) |
337 | return &iter->fib6_nh->nh_common; |
338 | } |
339 | } |
340 | |
341 | /* if family is incorrect - than upper functions has BUG */ |
342 | /* if doesn't find requested index - there is alsi bug, because |
343 | * valid index must be produced by nhs, which checks list length |
344 | */ |
345 | WARN(1, "Invalid parameters passed to %s n=%d i=%p" , |
346 | __func__, n, info); |
347 | return NULL; |
348 | } |
349 | |
350 | static int prestera_kern_fib_info_nhs(struct fib_notifier_info *info) |
351 | { |
352 | struct fib6_entry_notifier_info *fen6_info; |
353 | struct fib_entry_notifier_info *fen4_info; |
354 | |
355 | if (info->family == AF_INET) { |
356 | fen4_info = container_of(info, struct fib_entry_notifier_info, |
357 | info); |
358 | return fib_info_num_path(fi: fen4_info->fi); |
359 | } else if (info->family == AF_INET6) { |
360 | fen6_info = container_of(info, struct fib6_entry_notifier_info, |
361 | info); |
362 | return fen6_info->rt->fib6_nsiblings + 1; |
363 | } |
364 | |
365 | return 0; |
366 | } |
367 | |
368 | static unsigned char |
369 | prestera_kern_fib_info_type(struct fib_notifier_info *info) |
370 | { |
371 | struct fib6_entry_notifier_info *fen6_info; |
372 | struct fib_entry_notifier_info *fen4_info; |
373 | |
374 | if (info->family == AF_INET) { |
375 | fen4_info = container_of(info, struct fib_entry_notifier_info, |
376 | info); |
377 | return fen4_info->fi->fib_type; |
378 | } else if (info->family == AF_INET6) { |
379 | fen6_info = container_of(info, struct fib6_entry_notifier_info, |
380 | info); |
381 | /* TODO: ECMP in ipv6 is several routes. |
382 | * Every route has single nh. |
383 | */ |
384 | return fen6_info->rt->fib6_type; |
385 | } |
386 | |
387 | return RTN_UNSPEC; |
388 | } |
389 | |
390 | /* Decided, that uc_nh route with key==nh is obviously neighbour route */ |
391 | static bool |
392 | prestera_fib_node_util_is_neighbour(struct prestera_fib_node *fib_node) |
393 | { |
394 | if (fib_node->info.type != PRESTERA_FIB_TYPE_UC_NH) |
395 | return false; |
396 | |
397 | if (fib_node->info.nh_grp->nh_neigh_head[1].neigh) |
398 | return false; |
399 | |
400 | if (!fib_node->info.nh_grp->nh_neigh_head[0].neigh) |
401 | return false; |
402 | |
403 | if (memcmp(p: &fib_node->info.nh_grp->nh_neigh_head[0].neigh->key.addr, |
404 | q: &fib_node->key.addr, size: sizeof(struct prestera_ip_addr))) |
405 | return false; |
406 | |
407 | return true; |
408 | } |
409 | |
410 | static int prestera_dev_if_type(const struct net_device *dev) |
411 | { |
412 | struct macvlan_dev *vlan; |
413 | |
414 | if (is_vlan_dev(dev) && |
415 | netif_is_bridge_master(dev: vlan_dev_real_dev(dev))) { |
416 | return PRESTERA_IF_VID_E; |
417 | } else if (netif_is_bridge_master(dev)) { |
418 | return PRESTERA_IF_VID_E; |
419 | } else if (netif_is_lag_master(dev)) { |
420 | return PRESTERA_IF_LAG_E; |
421 | } else if (netif_is_macvlan(dev)) { |
422 | vlan = netdev_priv(dev); |
423 | return prestera_dev_if_type(dev: vlan->lowerdev); |
424 | } else { |
425 | return PRESTERA_IF_PORT_E; |
426 | } |
427 | } |
428 | |
429 | static int |
430 | prestera_neigh_iface_init(struct prestera_switch *sw, |
431 | struct prestera_iface *iface, |
432 | struct neighbour *n) |
433 | { |
434 | struct prestera_port *port; |
435 | |
436 | iface->vlan_id = 0; /* TODO: vlan egress */ |
437 | iface->type = prestera_dev_if_type(dev: n->dev); |
438 | if (iface->type != PRESTERA_IF_PORT_E) |
439 | return -EINVAL; |
440 | |
441 | if (!prestera_netdev_check(dev: n->dev)) |
442 | return -EINVAL; |
443 | |
444 | port = netdev_priv(dev: n->dev); |
445 | iface->dev_port.hw_dev_num = port->dev_id; |
446 | iface->dev_port.port_num = port->hw_id; |
447 | |
448 | return 0; |
449 | } |
450 | |
451 | static struct prestera_kern_neigh_cache * |
452 | prestera_kern_neigh_cache_find(struct prestera_switch *sw, |
453 | struct prestera_kern_neigh_cache_key *key) |
454 | { |
455 | struct prestera_kern_neigh_cache *n_cache; |
456 | |
457 | n_cache = |
458 | rhashtable_lookup_fast(ht: &sw->router->kern_neigh_cache_ht, key, |
459 | params: __prestera_kern_neigh_cache_ht_params); |
460 | return n_cache; |
461 | } |
462 | |
463 | static void |
464 | __prestera_kern_neigh_cache_destruct(struct prestera_switch *sw, |
465 | struct prestera_kern_neigh_cache *n_cache) |
466 | { |
467 | dev_put(dev: n_cache->key.dev); |
468 | } |
469 | |
470 | static void |
471 | __prestera_kern_neigh_cache_destroy(struct prestera_switch *sw, |
472 | struct prestera_kern_neigh_cache *n_cache) |
473 | { |
474 | rhashtable_remove_fast(ht: &sw->router->kern_neigh_cache_ht, |
475 | obj: &n_cache->ht_node, |
476 | params: __prestera_kern_neigh_cache_ht_params); |
477 | __prestera_kern_neigh_cache_destruct(sw, n_cache); |
478 | kfree(objp: n_cache); |
479 | } |
480 | |
481 | static struct prestera_kern_neigh_cache * |
482 | __prestera_kern_neigh_cache_create(struct prestera_switch *sw, |
483 | struct prestera_kern_neigh_cache_key *key) |
484 | { |
485 | struct prestera_kern_neigh_cache *n_cache; |
486 | int err; |
487 | |
488 | n_cache = kzalloc(size: sizeof(*n_cache), GFP_KERNEL); |
489 | if (!n_cache) |
490 | goto err_kzalloc; |
491 | |
492 | memcpy(&n_cache->key, key, sizeof(*key)); |
493 | dev_hold(dev: n_cache->key.dev); |
494 | |
495 | INIT_LIST_HEAD(list: &n_cache->kern_fib_cache_list); |
496 | err = rhashtable_insert_fast(ht: &sw->router->kern_neigh_cache_ht, |
497 | obj: &n_cache->ht_node, |
498 | params: __prestera_kern_neigh_cache_ht_params); |
499 | if (err) |
500 | goto err_ht_insert; |
501 | |
502 | return n_cache; |
503 | |
504 | err_ht_insert: |
505 | dev_put(dev: n_cache->key.dev); |
506 | kfree(objp: n_cache); |
507 | err_kzalloc: |
508 | return NULL; |
509 | } |
510 | |
511 | static struct prestera_kern_neigh_cache * |
512 | prestera_kern_neigh_cache_get(struct prestera_switch *sw, |
513 | struct prestera_kern_neigh_cache_key *key) |
514 | { |
515 | struct prestera_kern_neigh_cache *n_cache; |
516 | |
517 | n_cache = prestera_kern_neigh_cache_find(sw, key); |
518 | if (!n_cache) |
519 | n_cache = __prestera_kern_neigh_cache_create(sw, key); |
520 | |
521 | return n_cache; |
522 | } |
523 | |
524 | static struct prestera_kern_neigh_cache * |
525 | prestera_kern_neigh_cache_put(struct prestera_switch *sw, |
526 | struct prestera_kern_neigh_cache *n_cache) |
527 | { |
528 | if (!n_cache->in_kernel && |
529 | list_empty(head: &n_cache->kern_fib_cache_list)) { |
530 | __prestera_kern_neigh_cache_destroy(sw, n_cache); |
531 | return NULL; |
532 | } |
533 | |
534 | return n_cache; |
535 | } |
536 | |
537 | static struct prestera_kern_fib_cache * |
538 | prestera_kern_fib_cache_find(struct prestera_switch *sw, |
539 | struct prestera_kern_fib_cache_key *key) |
540 | { |
541 | struct prestera_kern_fib_cache *fib_cache; |
542 | |
543 | fib_cache = |
544 | rhashtable_lookup_fast(ht: &sw->router->kern_fib_cache_ht, key, |
545 | params: __prestera_kern_fib_cache_ht_params); |
546 | return fib_cache; |
547 | } |
548 | |
549 | static void |
550 | __prestera_kern_fib_cache_destruct(struct prestera_switch *sw, |
551 | struct prestera_kern_fib_cache *fib_cache) |
552 | { |
553 | struct prestera_kern_neigh_cache *n_cache; |
554 | int i; |
555 | |
556 | for (i = 0; i < PRESTERA_NHGR_SIZE_MAX; i++) { |
557 | n_cache = fib_cache->kern_neigh_cache_head[i].n_cache; |
558 | if (n_cache) { |
559 | list_del(entry: &fib_cache->kern_neigh_cache_head[i].head); |
560 | prestera_kern_neigh_cache_put(sw, n_cache); |
561 | } |
562 | } |
563 | |
564 | fib_info_put(fi: fib_cache->fen4_info.fi); |
565 | } |
566 | |
567 | static void |
568 | prestera_kern_fib_cache_destroy(struct prestera_switch *sw, |
569 | struct prestera_kern_fib_cache *fib_cache) |
570 | { |
571 | rhashtable_remove_fast(ht: &sw->router->kern_fib_cache_ht, |
572 | obj: &fib_cache->ht_node, |
573 | params: __prestera_kern_fib_cache_ht_params); |
574 | __prestera_kern_fib_cache_destruct(sw, fib_cache); |
575 | kfree(objp: fib_cache); |
576 | } |
577 | |
578 | static int |
579 | __prestera_kern_fib_cache_create_nhs(struct prestera_switch *sw, |
580 | struct prestera_kern_fib_cache *fc) |
581 | { |
582 | struct prestera_kern_neigh_cache_key nc_key; |
583 | struct prestera_kern_neigh_cache *n_cache; |
584 | struct fib_nh_common *nhc; |
585 | int i, nhs, err; |
586 | |
587 | if (!prestera_fib_info_is_nh(info: &fc->info)) |
588 | return 0; |
589 | |
590 | nhs = prestera_kern_fib_info_nhs(info: &fc->info); |
591 | if (nhs > PRESTERA_NHGR_SIZE_MAX) |
592 | return 0; |
593 | |
594 | for (i = 0; i < nhs; i++) { |
595 | nhc = prestera_kern_fib_info_nhc(info: &fc->fen4_info.info, n: i); |
596 | err = prestera_util_nhc2nc_key(sw, nhc, nk: &nc_key); |
597 | if (err) |
598 | return 0; |
599 | |
600 | n_cache = prestera_kern_neigh_cache_get(sw, key: &nc_key); |
601 | if (!n_cache) |
602 | return 0; |
603 | |
604 | fc->kern_neigh_cache_head[i].this = fc; |
605 | fc->kern_neigh_cache_head[i].n_cache = n_cache; |
606 | list_add(new: &fc->kern_neigh_cache_head[i].head, |
607 | head: &n_cache->kern_fib_cache_list); |
608 | } |
609 | |
610 | return 0; |
611 | } |
612 | |
613 | /* Operations on fi (offload, etc) must be wrapped in utils. |
614 | * This function just create storage. |
615 | */ |
616 | static struct prestera_kern_fib_cache * |
617 | prestera_kern_fib_cache_create(struct prestera_switch *sw, |
618 | struct prestera_kern_fib_cache_key *key, |
619 | struct fib_notifier_info *info) |
620 | { |
621 | struct fib_entry_notifier_info *fen_info = |
622 | container_of(info, struct fib_entry_notifier_info, info); |
623 | struct prestera_kern_fib_cache *fib_cache; |
624 | int err; |
625 | |
626 | fib_cache = kzalloc(size: sizeof(*fib_cache), GFP_KERNEL); |
627 | if (!fib_cache) |
628 | goto err_kzalloc; |
629 | |
630 | memcpy(&fib_cache->key, key, sizeof(*key)); |
631 | fib_info_hold(fi: fen_info->fi); |
632 | memcpy(&fib_cache->fen4_info, fen_info, sizeof(*fen_info)); |
633 | |
634 | err = rhashtable_insert_fast(ht: &sw->router->kern_fib_cache_ht, |
635 | obj: &fib_cache->ht_node, |
636 | params: __prestera_kern_fib_cache_ht_params); |
637 | if (err) |
638 | goto err_ht_insert; |
639 | |
640 | /* Handle nexthops */ |
641 | err = __prestera_kern_fib_cache_create_nhs(sw, fc: fib_cache); |
642 | if (err) |
643 | goto out; /* Not critical */ |
644 | |
645 | out: |
646 | return fib_cache; |
647 | |
648 | err_ht_insert: |
649 | fib_info_put(fi: fen_info->fi); |
650 | kfree(objp: fib_cache); |
651 | err_kzalloc: |
652 | return NULL; |
653 | } |
654 | |
655 | static void |
656 | __prestera_k_arb_fib_nh_offload_set(struct prestera_switch *sw, |
657 | struct prestera_kern_fib_cache *fibc, |
658 | struct prestera_kern_neigh_cache *nc, |
659 | bool offloaded, bool trap) |
660 | { |
661 | struct fib_nh_common *nhc; |
662 | int i, nhs; |
663 | |
664 | nhs = prestera_kern_fib_info_nhs(info: &fibc->info); |
665 | for (i = 0; i < nhs; i++) { |
666 | nhc = prestera_kern_fib_info_nhc(info: &fibc->info, n: i); |
667 | if (!nc) { |
668 | prestera_util_kern_set_nh_offload(nhc, offloaded, trap); |
669 | continue; |
670 | } |
671 | |
672 | if (prestera_util_nhc_eq_n_cache_key(sw, nhc, nk: &nc->key)) { |
673 | prestera_util_kern_set_nh_offload(nhc, offloaded, trap); |
674 | break; |
675 | } |
676 | } |
677 | } |
678 | |
679 | static void |
680 | __prestera_k_arb_n_offload_set(struct prestera_switch *sw, |
681 | struct prestera_kern_neigh_cache *nc, |
682 | bool offloaded) |
683 | { |
684 | struct neighbour *n; |
685 | |
686 | n = neigh_lookup(tbl: &arp_tbl, pkey: &nc->key.addr.u.ipv4, |
687 | dev: nc->key.dev); |
688 | if (!n) |
689 | return; |
690 | |
691 | prestera_util_kern_set_neigh_offload(n, offloaded); |
692 | neigh_release(neigh: n); |
693 | } |
694 | |
695 | static void |
696 | __prestera_k_arb_fib_lpm_offload_set(struct prestera_switch *sw, |
697 | struct prestera_kern_fib_cache *fc, |
698 | bool fail, bool offload, bool trap) |
699 | { |
700 | struct fib_rt_info fri; |
701 | |
702 | switch (fc->key.addr.v) { |
703 | case PRESTERA_IPV4: |
704 | fri.fi = fc->fen4_info.fi; |
705 | fri.tb_id = fc->key.kern_tb_id; |
706 | fri.dst = fc->key.addr.u.ipv4; |
707 | fri.dst_len = fc->key.prefix_len; |
708 | fri.dscp = fc->fen4_info.dscp; |
709 | fri.type = fc->fen4_info.type; |
710 | /* flags begin */ |
711 | fri.offload = offload; |
712 | fri.trap = trap; |
713 | fri.offload_failed = fail; |
714 | /* flags end */ |
715 | fib_alias_hw_flags_set(net: &init_net, fri: &fri); |
716 | return; |
717 | case PRESTERA_IPV6: |
718 | /* TODO */ |
719 | return; |
720 | } |
721 | } |
722 | |
723 | static void |
724 | __prestera_k_arb_n_lpm_set(struct prestera_switch *sw, |
725 | struct prestera_kern_neigh_cache *n_cache, |
726 | bool enabled) |
727 | { |
728 | struct prestera_nexthop_group_key nh_grp_key; |
729 | struct prestera_kern_fib_cache_key fc_key; |
730 | struct prestera_kern_fib_cache *fib_cache; |
731 | struct prestera_fib_node *fib_node; |
732 | struct prestera_fib_key fib_key; |
733 | |
734 | /* Exception for fc with prefix 32: LPM entry is already used by fib */ |
735 | memset(&fc_key, 0, sizeof(fc_key)); |
736 | fc_key.addr = n_cache->key.addr; |
737 | fc_key.prefix_len = PRESTERA_IP_ADDR_PLEN(n_cache->key.addr.v); |
738 | /* But better to use tb_id of route, which pointed to this neighbour. */ |
739 | /* We take it from rif, because rif inconsistent. |
740 | * Must be separated in_rif and out_rif. |
741 | * Also note: for each fib pointed to this neigh should be separated |
742 | * neigh lpm entry (for each ingress vr) |
743 | */ |
744 | fc_key.kern_tb_id = l3mdev_fib_table(dev: n_cache->key.dev); |
745 | fib_cache = prestera_kern_fib_cache_find(sw, key: &fc_key); |
746 | memset(&fib_key, 0, sizeof(fib_key)); |
747 | fib_key.addr = n_cache->key.addr; |
748 | fib_key.prefix_len = PRESTERA_IP_ADDR_PLEN(n_cache->key.addr.v); |
749 | fib_key.tb_id = prestera_fix_tb_id(tb_id: fc_key.kern_tb_id); |
750 | fib_node = prestera_fib_node_find(sw, key: &fib_key); |
751 | if (!fib_cache || !fib_cache->reachable) { |
752 | if (!enabled && fib_node) { |
753 | if (prestera_fib_node_util_is_neighbour(fib_node)) |
754 | prestera_fib_node_destroy(sw, fib_node); |
755 | return; |
756 | } |
757 | } |
758 | |
759 | if (enabled && !fib_node) { |
760 | memset(&nh_grp_key, 0, sizeof(nh_grp_key)); |
761 | prestera_util_nc_key2nh_key(ck: &n_cache->key, |
762 | nk: &nh_grp_key.neigh[0]); |
763 | fib_node = prestera_fib_node_create(sw, key: &fib_key, |
764 | fib_type: PRESTERA_FIB_TYPE_UC_NH, |
765 | nh_grp_key: &nh_grp_key); |
766 | if (!fib_node) |
767 | pr_err("%s failed ip=%pI4n" , "prestera_fib_node_create" , |
768 | &fib_key.addr.u.ipv4); |
769 | return; |
770 | } |
771 | } |
772 | |
773 | static void |
774 | __prestera_k_arb_nc_kern_fib_fetch(struct prestera_switch *sw, |
775 | struct prestera_kern_neigh_cache *nc) |
776 | { |
777 | if (prestera_util_kern_n_is_reachable(tb_id: l3mdev_fib_table(dev: nc->key.dev), |
778 | addr: &nc->key.addr, dev: nc->key.dev)) |
779 | nc->reachable = true; |
780 | else |
781 | nc->reachable = false; |
782 | } |
783 | |
784 | /* Kernel neighbour -> neigh_cache info */ |
785 | static void |
786 | __prestera_k_arb_nc_kern_n_fetch(struct prestera_switch *sw, |
787 | struct prestera_kern_neigh_cache *nc) |
788 | { |
789 | struct neighbour *n; |
790 | int err; |
791 | |
792 | memset(&nc->nh_neigh_info, 0, sizeof(nc->nh_neigh_info)); |
793 | n = neigh_lookup(tbl: &arp_tbl, pkey: &nc->key.addr.u.ipv4, dev: nc->key.dev); |
794 | if (!n) |
795 | goto out; |
796 | |
797 | read_lock_bh(&n->lock); |
798 | if (n->nud_state & NUD_VALID && !n->dead) { |
799 | err = prestera_neigh_iface_init(sw, iface: &nc->nh_neigh_info.iface, |
800 | n); |
801 | if (err) |
802 | goto n_read_out; |
803 | |
804 | memcpy(&nc->nh_neigh_info.ha[0], &n->ha[0], ETH_ALEN); |
805 | nc->nh_neigh_info.connected = true; |
806 | } |
807 | n_read_out: |
808 | read_unlock_bh(&n->lock); |
809 | out: |
810 | nc->in_kernel = nc->nh_neigh_info.connected; |
811 | if (n) |
812 | neigh_release(neigh: n); |
813 | } |
814 | |
815 | /* neigh_cache info -> lpm update */ |
816 | static void |
817 | __prestera_k_arb_nc_apply(struct prestera_switch *sw, |
818 | struct prestera_kern_neigh_cache *nc) |
819 | { |
820 | struct prestera_kern_neigh_cache_head *nhead; |
821 | struct prestera_nh_neigh_key nh_key; |
822 | struct prestera_nh_neigh *nh_neigh; |
823 | int err; |
824 | |
825 | __prestera_k_arb_n_lpm_set(sw, n_cache: nc, enabled: nc->reachable && nc->in_kernel); |
826 | __prestera_k_arb_n_offload_set(sw, nc, offloaded: nc->reachable && nc->in_kernel); |
827 | |
828 | prestera_util_nc_key2nh_key(ck: &nc->key, nk: &nh_key); |
829 | nh_neigh = prestera_nh_neigh_find(sw, key: &nh_key); |
830 | if (!nh_neigh) |
831 | goto out; |
832 | |
833 | /* Do hw update only if something changed to prevent nh flap */ |
834 | if (memcmp(p: &nc->nh_neigh_info, q: &nh_neigh->info, |
835 | size: sizeof(nh_neigh->info))) { |
836 | memcpy(&nh_neigh->info, &nc->nh_neigh_info, |
837 | sizeof(nh_neigh->info)); |
838 | err = prestera_nh_neigh_set(sw, neigh: nh_neigh); |
839 | if (err) { |
840 | pr_err("%s failed with err=%d ip=%pI4n mac=%pM" , |
841 | "prestera_nh_neigh_set" , err, |
842 | &nh_neigh->key.addr.u.ipv4, |
843 | &nh_neigh->info.ha[0]); |
844 | goto out; |
845 | } |
846 | } |
847 | |
848 | out: |
849 | list_for_each_entry(nhead, &nc->kern_fib_cache_list, head) { |
850 | __prestera_k_arb_fib_nh_offload_set(sw, fibc: nhead->this, nc, |
851 | offloaded: nc->in_kernel, |
852 | trap: !nc->in_kernel); |
853 | } |
854 | } |
855 | |
856 | static int |
857 | __prestera_pr_k_arb_fc_lpm_info_calc(struct prestera_switch *sw, |
858 | struct prestera_kern_fib_cache *fc) |
859 | { |
860 | struct fib_nh_common *nhc; |
861 | int nh_cnt; |
862 | |
863 | memset(&fc->lpm_info, 0, sizeof(fc->lpm_info)); |
864 | |
865 | switch (prestera_kern_fib_info_type(info: &fc->info)) { |
866 | case RTN_UNICAST: |
867 | if (prestera_fib_info_is_direct(info: &fc->info) && |
868 | fc->key.prefix_len == |
869 | PRESTERA_IP_ADDR_PLEN(fc->key.addr.v)) { |
870 | /* This is special case. |
871 | * When prefix is 32. Than we will have conflict in lpm |
872 | * for direct route - once TRAP added, there is no |
873 | * place for neighbour entry. So represent direct route |
874 | * with prefix 32, as NH. So neighbour will be resolved |
875 | * as nexthop of this route. |
876 | */ |
877 | nhc = prestera_kern_fib_info_nhc(info: &fc->info, n: 0); |
878 | fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_UC_NH; |
879 | fc->lpm_info.nh_grp_key.neigh[0].addr = |
880 | fc->key.addr; |
881 | fc->lpm_info.nh_grp_key.neigh[0].rif = |
882 | nhc->nhc_dev; |
883 | |
884 | break; |
885 | } |
886 | |
887 | /* We can also get nh_grp_key from fi. This will be correct to |
888 | * because cache not always represent, what actually written to |
889 | * lpm. But we use nh cache, as well for now (for this case). |
890 | */ |
891 | for (nh_cnt = 0; nh_cnt < PRESTERA_NHGR_SIZE_MAX; nh_cnt++) { |
892 | if (!fc->kern_neigh_cache_head[nh_cnt].n_cache) |
893 | break; |
894 | |
895 | fc->lpm_info.nh_grp_key.neigh[nh_cnt].addr = |
896 | fc->kern_neigh_cache_head[nh_cnt].n_cache->key.addr; |
897 | fc->lpm_info.nh_grp_key.neigh[nh_cnt].rif = |
898 | fc->kern_neigh_cache_head[nh_cnt].n_cache->key.dev; |
899 | } |
900 | |
901 | fc->lpm_info.fib_type = nh_cnt ? |
902 | PRESTERA_FIB_TYPE_UC_NH : |
903 | PRESTERA_FIB_TYPE_TRAP; |
904 | break; |
905 | /* Unsupported. Leave it for kernel: */ |
906 | case RTN_BROADCAST: |
907 | case RTN_MULTICAST: |
908 | /* Routes we must trap by design: */ |
909 | case RTN_LOCAL: |
910 | case RTN_UNREACHABLE: |
911 | case RTN_PROHIBIT: |
912 | fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_TRAP; |
913 | break; |
914 | case RTN_BLACKHOLE: |
915 | fc->lpm_info.fib_type = PRESTERA_FIB_TYPE_DROP; |
916 | break; |
917 | default: |
918 | dev_err(sw->dev->dev, "Unsupported fib_type" ); |
919 | return -EOPNOTSUPP; |
920 | } |
921 | |
922 | fc->lpm_info.fib_key.addr = fc->key.addr; |
923 | fc->lpm_info.fib_key.prefix_len = fc->key.prefix_len; |
924 | fc->lpm_info.fib_key.tb_id = prestera_fix_tb_id(tb_id: fc->key.kern_tb_id); |
925 | |
926 | return 0; |
927 | } |
928 | |
929 | static int __prestera_k_arb_f_lpm_set(struct prestera_switch *sw, |
930 | struct prestera_kern_fib_cache *fc, |
931 | bool enabled) |
932 | { |
933 | struct prestera_fib_node *fib_node; |
934 | |
935 | fib_node = prestera_fib_node_find(sw, key: &fc->lpm_info.fib_key); |
936 | if (fib_node) |
937 | prestera_fib_node_destroy(sw, fib_node); |
938 | |
939 | if (!enabled) |
940 | return 0; |
941 | |
942 | fib_node = prestera_fib_node_create(sw, key: &fc->lpm_info.fib_key, |
943 | fib_type: fc->lpm_info.fib_type, |
944 | nh_grp_key: &fc->lpm_info.nh_grp_key); |
945 | |
946 | if (!fib_node) { |
947 | dev_err(sw->dev->dev, "fib_node=NULL %pI4n/%d kern_tb_id = %d" , |
948 | &fc->key.addr.u.ipv4, fc->key.prefix_len, |
949 | fc->key.kern_tb_id); |
950 | return -ENOENT; |
951 | } |
952 | |
953 | return 0; |
954 | } |
955 | |
956 | static int __prestera_k_arb_fc_apply(struct prestera_switch *sw, |
957 | struct prestera_kern_fib_cache *fc) |
958 | { |
959 | int err; |
960 | |
961 | err = __prestera_pr_k_arb_fc_lpm_info_calc(sw, fc); |
962 | if (err) |
963 | return err; |
964 | |
965 | err = __prestera_k_arb_f_lpm_set(sw, fc, enabled: fc->reachable); |
966 | if (err) { |
967 | __prestera_k_arb_fib_lpm_offload_set(sw, fc, |
968 | fail: true, offload: false, trap: false); |
969 | return err; |
970 | } |
971 | |
972 | switch (fc->lpm_info.fib_type) { |
973 | case PRESTERA_FIB_TYPE_UC_NH: |
974 | __prestera_k_arb_fib_lpm_offload_set(sw, fc, fail: false, |
975 | offload: fc->reachable, trap: false); |
976 | break; |
977 | case PRESTERA_FIB_TYPE_TRAP: |
978 | __prestera_k_arb_fib_lpm_offload_set(sw, fc, fail: false, |
979 | offload: false, trap: fc->reachable); |
980 | break; |
981 | case PRESTERA_FIB_TYPE_DROP: |
982 | __prestera_k_arb_fib_lpm_offload_set(sw, fc, fail: false, offload: true, |
983 | trap: fc->reachable); |
984 | break; |
985 | case PRESTERA_FIB_TYPE_INVALID: |
986 | break; |
987 | } |
988 | |
989 | return 0; |
990 | } |
991 | |
992 | static struct prestera_kern_fib_cache * |
993 | __prestera_k_arb_util_fib_overlaps(struct prestera_switch *sw, |
994 | struct prestera_kern_fib_cache *fc) |
995 | { |
996 | struct prestera_kern_fib_cache_key fc_key; |
997 | struct prestera_kern_fib_cache *rfc; |
998 | |
999 | /* TODO: parse kernel rules */ |
1000 | rfc = NULL; |
1001 | if (fc->key.kern_tb_id == RT_TABLE_LOCAL) { |
1002 | memcpy(&fc_key, &fc->key, sizeof(fc_key)); |
1003 | fc_key.kern_tb_id = RT_TABLE_MAIN; |
1004 | rfc = prestera_kern_fib_cache_find(sw, key: &fc_key); |
1005 | } |
1006 | |
1007 | return rfc; |
1008 | } |
1009 | |
1010 | static struct prestera_kern_fib_cache * |
1011 | __prestera_k_arb_util_fib_overlapped(struct prestera_switch *sw, |
1012 | struct prestera_kern_fib_cache *fc) |
1013 | { |
1014 | struct prestera_kern_fib_cache_key fc_key; |
1015 | struct prestera_kern_fib_cache *rfc; |
1016 | |
1017 | /* TODO: parse kernel rules */ |
1018 | rfc = NULL; |
1019 | if (fc->key.kern_tb_id == RT_TABLE_MAIN) { |
1020 | memcpy(&fc_key, &fc->key, sizeof(fc_key)); |
1021 | fc_key.kern_tb_id = RT_TABLE_LOCAL; |
1022 | rfc = prestera_kern_fib_cache_find(sw, key: &fc_key); |
1023 | } |
1024 | |
1025 | return rfc; |
1026 | } |
1027 | |
1028 | static void __prestera_k_arb_hw_state_upd(struct prestera_switch *sw, |
1029 | struct prestera_kern_neigh_cache *nc) |
1030 | { |
1031 | struct prestera_nh_neigh_key nh_key; |
1032 | struct prestera_nh_neigh *nh_neigh; |
1033 | struct neighbour *n; |
1034 | bool hw_active; |
1035 | |
1036 | prestera_util_nc_key2nh_key(ck: &nc->key, nk: &nh_key); |
1037 | nh_neigh = prestera_nh_neigh_find(sw, key: &nh_key); |
1038 | if (!nh_neigh) { |
1039 | pr_err("Cannot find nh_neigh for cached %pI4n" , |
1040 | &nc->key.addr.u.ipv4); |
1041 | return; |
1042 | } |
1043 | |
1044 | hw_active = prestera_nh_neigh_util_hw_state(sw, nh_neigh); |
1045 | |
1046 | #ifdef PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH |
1047 | if (!hw_active && nc->in_kernel) |
1048 | goto out; |
1049 | #else /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */ |
1050 | if (!hw_active) |
1051 | goto out; |
1052 | #endif /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */ |
1053 | |
1054 | if (nc->key.addr.v == PRESTERA_IPV4) { |
1055 | n = neigh_lookup(tbl: &arp_tbl, pkey: &nc->key.addr.u.ipv4, |
1056 | dev: nc->key.dev); |
1057 | if (!n) |
1058 | n = neigh_create(tbl: &arp_tbl, pkey: &nc->key.addr.u.ipv4, |
1059 | dev: nc->key.dev); |
1060 | } else { |
1061 | n = NULL; |
1062 | } |
1063 | |
1064 | if (!IS_ERR(ptr: n) && n) { |
1065 | neigh_event_send(neigh: n, NULL); |
1066 | neigh_release(neigh: n); |
1067 | } else { |
1068 | pr_err("Cannot create neighbour %pI4n" , &nc->key.addr.u.ipv4); |
1069 | } |
1070 | |
1071 | out: |
1072 | return; |
1073 | } |
1074 | |
1075 | /* Propagate hw state to kernel */ |
1076 | static void prestera_k_arb_hw_evt(struct prestera_switch *sw) |
1077 | { |
1078 | struct prestera_kern_neigh_cache *n_cache; |
1079 | struct rhashtable_iter iter; |
1080 | |
1081 | rhashtable_walk_enter(ht: &sw->router->kern_neigh_cache_ht, iter: &iter); |
1082 | rhashtable_walk_start(iter: &iter); |
1083 | while (1) { |
1084 | n_cache = rhashtable_walk_next(iter: &iter); |
1085 | |
1086 | if (!n_cache) |
1087 | break; |
1088 | |
1089 | if (IS_ERR(ptr: n_cache)) |
1090 | continue; |
1091 | |
1092 | rhashtable_walk_stop(iter: &iter); |
1093 | __prestera_k_arb_hw_state_upd(sw, nc: n_cache); |
1094 | rhashtable_walk_start(iter: &iter); |
1095 | } |
1096 | rhashtable_walk_stop(iter: &iter); |
1097 | rhashtable_walk_exit(iter: &iter); |
1098 | } |
1099 | |
1100 | /* Propagate kernel event to hw */ |
1101 | static void prestera_k_arb_n_evt(struct prestera_switch *sw, |
1102 | struct neighbour *n) |
1103 | { |
1104 | struct prestera_kern_neigh_cache_key n_key; |
1105 | struct prestera_kern_neigh_cache *n_cache; |
1106 | int err; |
1107 | |
1108 | err = prestera_util_neigh2nc_key(sw, n, key: &n_key); |
1109 | if (err) |
1110 | return; |
1111 | |
1112 | n_cache = prestera_kern_neigh_cache_find(sw, key: &n_key); |
1113 | if (!n_cache) { |
1114 | n_cache = prestera_kern_neigh_cache_get(sw, key: &n_key); |
1115 | if (!n_cache) |
1116 | return; |
1117 | __prestera_k_arb_nc_kern_fib_fetch(sw, nc: n_cache); |
1118 | } |
1119 | |
1120 | __prestera_k_arb_nc_kern_n_fetch(sw, nc: n_cache); |
1121 | __prestera_k_arb_nc_apply(sw, nc: n_cache); |
1122 | |
1123 | prestera_kern_neigh_cache_put(sw, n_cache); |
1124 | } |
1125 | |
1126 | static void __prestera_k_arb_fib_evt2nc(struct prestera_switch *sw) |
1127 | { |
1128 | struct prestera_kern_neigh_cache *n_cache; |
1129 | struct rhashtable_iter iter; |
1130 | |
1131 | rhashtable_walk_enter(ht: &sw->router->kern_neigh_cache_ht, iter: &iter); |
1132 | rhashtable_walk_start(iter: &iter); |
1133 | while (1) { |
1134 | n_cache = rhashtable_walk_next(iter: &iter); |
1135 | |
1136 | if (!n_cache) |
1137 | break; |
1138 | |
1139 | if (IS_ERR(ptr: n_cache)) |
1140 | continue; |
1141 | |
1142 | rhashtable_walk_stop(iter: &iter); |
1143 | __prestera_k_arb_nc_kern_fib_fetch(sw, nc: n_cache); |
1144 | __prestera_k_arb_nc_apply(sw, nc: n_cache); |
1145 | rhashtable_walk_start(iter: &iter); |
1146 | } |
1147 | rhashtable_walk_stop(iter: &iter); |
1148 | rhashtable_walk_exit(iter: &iter); |
1149 | } |
1150 | |
1151 | static int |
1152 | prestera_k_arb_fib_evt(struct prestera_switch *sw, |
1153 | bool replace, /* replace or del */ |
1154 | struct fib_notifier_info *info) |
1155 | { |
1156 | struct prestera_kern_fib_cache *tfib_cache, *bfib_cache; /* top/btm */ |
1157 | struct prestera_kern_fib_cache_key fc_key; |
1158 | struct prestera_kern_fib_cache *fib_cache; |
1159 | int err; |
1160 | |
1161 | prestera_util_fen_info2fib_cache_key(info, key: &fc_key); |
1162 | fib_cache = prestera_kern_fib_cache_find(sw, key: &fc_key); |
1163 | if (fib_cache) { |
1164 | fib_cache->reachable = false; |
1165 | err = __prestera_k_arb_fc_apply(sw, fc: fib_cache); |
1166 | if (err) |
1167 | dev_err(sw->dev->dev, |
1168 | "Applying destroyed fib_cache failed" ); |
1169 | |
1170 | bfib_cache = __prestera_k_arb_util_fib_overlaps(sw, fc: fib_cache); |
1171 | tfib_cache = __prestera_k_arb_util_fib_overlapped(sw, fc: fib_cache); |
1172 | if (!tfib_cache && bfib_cache) { |
1173 | bfib_cache->reachable = true; |
1174 | err = __prestera_k_arb_fc_apply(sw, fc: bfib_cache); |
1175 | if (err) |
1176 | dev_err(sw->dev->dev, |
1177 | "Applying fib_cache btm failed" ); |
1178 | } |
1179 | |
1180 | prestera_kern_fib_cache_destroy(sw, fib_cache); |
1181 | } |
1182 | |
1183 | if (replace) { |
1184 | fib_cache = prestera_kern_fib_cache_create(sw, key: &fc_key, info); |
1185 | if (!fib_cache) { |
1186 | dev_err(sw->dev->dev, "fib_cache == NULL" ); |
1187 | return -ENOENT; |
1188 | } |
1189 | |
1190 | bfib_cache = __prestera_k_arb_util_fib_overlaps(sw, fc: fib_cache); |
1191 | tfib_cache = __prestera_k_arb_util_fib_overlapped(sw, fc: fib_cache); |
1192 | if (!tfib_cache) |
1193 | fib_cache->reachable = true; |
1194 | |
1195 | if (bfib_cache) { |
1196 | bfib_cache->reachable = false; |
1197 | err = __prestera_k_arb_fc_apply(sw, fc: bfib_cache); |
1198 | if (err) |
1199 | dev_err(sw->dev->dev, |
1200 | "Applying fib_cache btm failed" ); |
1201 | } |
1202 | |
1203 | err = __prestera_k_arb_fc_apply(sw, fc: fib_cache); |
1204 | if (err) |
1205 | dev_err(sw->dev->dev, "Applying fib_cache failed" ); |
1206 | } |
1207 | |
1208 | /* Update all neighs to resolve overlapped and apply related */ |
1209 | __prestera_k_arb_fib_evt2nc(sw); |
1210 | |
1211 | return 0; |
1212 | } |
1213 | |
1214 | static void __prestera_k_arb_abort_neigh_ht_cb(void *ptr, void *arg) |
1215 | { |
1216 | struct prestera_kern_neigh_cache *n_cache = ptr; |
1217 | struct prestera_switch *sw = arg; |
1218 | |
1219 | if (!list_empty(head: &n_cache->kern_fib_cache_list)) { |
1220 | WARN_ON(1); /* BUG */ |
1221 | return; |
1222 | } |
1223 | __prestera_k_arb_n_offload_set(sw, nc: n_cache, offloaded: false); |
1224 | n_cache->in_kernel = false; |
1225 | /* No need to destroy lpm. |
1226 | * It will be aborted by destroy_ht |
1227 | */ |
1228 | __prestera_kern_neigh_cache_destruct(sw, n_cache); |
1229 | kfree(objp: n_cache); |
1230 | } |
1231 | |
1232 | static void __prestera_k_arb_abort_fib_ht_cb(void *ptr, void *arg) |
1233 | { |
1234 | struct prestera_kern_fib_cache *fib_cache = ptr; |
1235 | struct prestera_switch *sw = arg; |
1236 | |
1237 | __prestera_k_arb_fib_lpm_offload_set(sw, fc: fib_cache, |
1238 | fail: false, offload: false, |
1239 | trap: false); |
1240 | __prestera_k_arb_fib_nh_offload_set(sw, fibc: fib_cache, NULL, |
1241 | offloaded: false, trap: false); |
1242 | /* No need to destroy lpm. |
1243 | * It will be aborted by destroy_ht |
1244 | */ |
1245 | __prestera_kern_fib_cache_destruct(sw, fib_cache); |
1246 | kfree(objp: fib_cache); |
1247 | } |
1248 | |
1249 | static void prestera_k_arb_abort(struct prestera_switch *sw) |
1250 | { |
1251 | /* Function to remove all arbiter entries and related hw objects. */ |
1252 | /* Sequence: |
1253 | * 1) Clear arbiter tables, but don't touch hw |
1254 | * 2) Clear hw |
1255 | * We use such approach, because arbiter object is not directly mapped |
1256 | * to hw. So deletion of one arbiter object may even lead to creation of |
1257 | * hw object (e.g. in case of overlapped routes). |
1258 | */ |
1259 | rhashtable_free_and_destroy(ht: &sw->router->kern_fib_cache_ht, |
1260 | free_fn: __prestera_k_arb_abort_fib_ht_cb, |
1261 | arg: sw); |
1262 | rhashtable_free_and_destroy(ht: &sw->router->kern_neigh_cache_ht, |
1263 | free_fn: __prestera_k_arb_abort_neigh_ht_cb, |
1264 | arg: sw); |
1265 | } |
1266 | |
1267 | static int __prestera_inetaddr_port_event(struct net_device *port_dev, |
1268 | unsigned long event, |
1269 | struct netlink_ext_ack *extack) |
1270 | { |
1271 | struct prestera_port *port = netdev_priv(dev: port_dev); |
1272 | struct prestera_rif_entry_key re_key = {}; |
1273 | struct prestera_rif_entry *re; |
1274 | u32 kern_tb_id; |
1275 | int err; |
1276 | |
1277 | err = prestera_is_valid_mac_addr(port, addr: port_dev->dev_addr); |
1278 | if (err) { |
1279 | NL_SET_ERR_MSG_MOD(extack, "RIF MAC must have the same prefix" ); |
1280 | return err; |
1281 | } |
1282 | |
1283 | kern_tb_id = l3mdev_fib_table(dev: port_dev); |
1284 | re_key.iface.type = PRESTERA_IF_PORT_E; |
1285 | re_key.iface.dev_port.hw_dev_num = port->dev_id; |
1286 | re_key.iface.dev_port.port_num = port->hw_id; |
1287 | re = prestera_rif_entry_find(sw: port->sw, k: &re_key); |
1288 | |
1289 | switch (event) { |
1290 | case NETDEV_UP: |
1291 | if (re) { |
1292 | NL_SET_ERR_MSG_MOD(extack, "RIF already exist" ); |
1293 | return -EEXIST; |
1294 | } |
1295 | re = prestera_rif_entry_create(sw: port->sw, k: &re_key, |
1296 | tb_id: prestera_fix_tb_id(tb_id: kern_tb_id), |
1297 | addr: port_dev->dev_addr); |
1298 | if (!re) { |
1299 | NL_SET_ERR_MSG_MOD(extack, "Can't create RIF" ); |
1300 | return -EINVAL; |
1301 | } |
1302 | dev_hold(dev: port_dev); |
1303 | break; |
1304 | case NETDEV_DOWN: |
1305 | if (!re) { |
1306 | NL_SET_ERR_MSG_MOD(extack, "Can't find RIF" ); |
1307 | return -EEXIST; |
1308 | } |
1309 | prestera_rif_entry_destroy(sw: port->sw, e: re); |
1310 | dev_put(dev: port_dev); |
1311 | break; |
1312 | } |
1313 | |
1314 | return 0; |
1315 | } |
1316 | |
1317 | static int __prestera_inetaddr_event(struct prestera_switch *sw, |
1318 | struct net_device *dev, |
1319 | unsigned long event, |
1320 | struct netlink_ext_ack *extack) |
1321 | { |
1322 | if (!prestera_netdev_check(dev) || netif_is_any_bridge_port(dev) || |
1323 | netif_is_lag_port(dev)) |
1324 | return 0; |
1325 | |
1326 | return __prestera_inetaddr_port_event(port_dev: dev, event, extack); |
1327 | } |
1328 | |
1329 | static int __prestera_inetaddr_cb(struct notifier_block *nb, |
1330 | unsigned long event, void *ptr) |
1331 | { |
1332 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; |
1333 | struct net_device *dev = ifa->ifa_dev->dev; |
1334 | struct prestera_router *router = container_of(nb, |
1335 | struct prestera_router, |
1336 | inetaddr_nb); |
1337 | struct in_device *idev; |
1338 | int err = 0; |
1339 | |
1340 | if (event != NETDEV_DOWN) |
1341 | goto out; |
1342 | |
1343 | /* Ignore if this is not latest address */ |
1344 | idev = __in_dev_get_rtnl(dev); |
1345 | if (idev && idev->ifa_list) |
1346 | goto out; |
1347 | |
1348 | err = __prestera_inetaddr_event(sw: router->sw, dev, event, NULL); |
1349 | out: |
1350 | return notifier_from_errno(err); |
1351 | } |
1352 | |
1353 | static int __prestera_inetaddr_valid_cb(struct notifier_block *nb, |
1354 | unsigned long event, void *ptr) |
1355 | { |
1356 | struct in_validator_info *ivi = (struct in_validator_info *)ptr; |
1357 | struct net_device *dev = ivi->ivi_dev->dev; |
1358 | struct prestera_router *router = container_of(nb, |
1359 | struct prestera_router, |
1360 | inetaddr_valid_nb); |
1361 | struct in_device *idev; |
1362 | int err = 0; |
1363 | |
1364 | if (event != NETDEV_UP) |
1365 | goto out; |
1366 | |
1367 | /* Ignore if this is not first address */ |
1368 | idev = __in_dev_get_rtnl(dev); |
1369 | if (idev && idev->ifa_list) |
1370 | goto out; |
1371 | |
1372 | if (ipv4_is_multicast(addr: ivi->ivi_addr)) { |
1373 | NL_SET_ERR_MSG_MOD(ivi->extack, |
1374 | "Multicast addr on RIF is not supported" ); |
1375 | err = -EINVAL; |
1376 | goto out; |
1377 | } |
1378 | |
1379 | err = __prestera_inetaddr_event(sw: router->sw, dev, event, extack: ivi->extack); |
1380 | out: |
1381 | return notifier_from_errno(err); |
1382 | } |
1383 | |
1384 | struct prestera_fib_event_work { |
1385 | struct work_struct work; |
1386 | struct prestera_switch *sw; |
1387 | struct fib_entry_notifier_info fen_info; |
1388 | unsigned long event; |
1389 | }; |
1390 | |
1391 | static void __prestera_router_fib_event_work(struct work_struct *work) |
1392 | { |
1393 | struct prestera_fib_event_work *fib_work = |
1394 | container_of(work, struct prestera_fib_event_work, work); |
1395 | struct prestera_switch *sw = fib_work->sw; |
1396 | int err; |
1397 | |
1398 | rtnl_lock(); |
1399 | |
1400 | switch (fib_work->event) { |
1401 | case FIB_EVENT_ENTRY_REPLACE: |
1402 | err = prestera_k_arb_fib_evt(sw, replace: true, |
1403 | info: &fib_work->fen_info.info); |
1404 | if (err) |
1405 | goto err_out; |
1406 | |
1407 | break; |
1408 | case FIB_EVENT_ENTRY_DEL: |
1409 | err = prestera_k_arb_fib_evt(sw, replace: false, |
1410 | info: &fib_work->fen_info.info); |
1411 | if (err) |
1412 | goto err_out; |
1413 | |
1414 | break; |
1415 | } |
1416 | |
1417 | goto out; |
1418 | |
1419 | err_out: |
1420 | dev_err(sw->dev->dev, "Error when processing %pI4h/%d" , |
1421 | &fib_work->fen_info.dst, |
1422 | fib_work->fen_info.dst_len); |
1423 | out: |
1424 | fib_info_put(fi: fib_work->fen_info.fi); |
1425 | rtnl_unlock(); |
1426 | kfree(objp: fib_work); |
1427 | } |
1428 | |
1429 | /* Called with rcu_read_lock() */ |
1430 | static int __prestera_router_fib_event(struct notifier_block *nb, |
1431 | unsigned long event, void *ptr) |
1432 | { |
1433 | struct prestera_fib_event_work *fib_work; |
1434 | struct fib_entry_notifier_info *fen_info; |
1435 | struct fib_notifier_info *info = ptr; |
1436 | struct prestera_router *router; |
1437 | |
1438 | if (info->family != AF_INET) |
1439 | return NOTIFY_DONE; |
1440 | |
1441 | router = container_of(nb, struct prestera_router, fib_nb); |
1442 | |
1443 | switch (event) { |
1444 | case FIB_EVENT_ENTRY_REPLACE: |
1445 | case FIB_EVENT_ENTRY_DEL: |
1446 | fen_info = container_of(info, struct fib_entry_notifier_info, |
1447 | info); |
1448 | if (!fen_info->fi) |
1449 | return NOTIFY_DONE; |
1450 | |
1451 | fib_work = kzalloc(size: sizeof(*fib_work), GFP_ATOMIC); |
1452 | if (WARN_ON(!fib_work)) |
1453 | return NOTIFY_BAD; |
1454 | |
1455 | fib_info_hold(fi: fen_info->fi); |
1456 | fib_work->fen_info = *fen_info; |
1457 | fib_work->event = event; |
1458 | fib_work->sw = router->sw; |
1459 | INIT_WORK(&fib_work->work, __prestera_router_fib_event_work); |
1460 | prestera_queue_work(work: &fib_work->work); |
1461 | break; |
1462 | default: |
1463 | return NOTIFY_DONE; |
1464 | } |
1465 | |
1466 | return NOTIFY_DONE; |
1467 | } |
1468 | |
1469 | struct prestera_netevent_work { |
1470 | struct work_struct work; |
1471 | struct prestera_switch *sw; |
1472 | struct neighbour *n; |
1473 | }; |
1474 | |
1475 | static void prestera_router_neigh_event_work(struct work_struct *work) |
1476 | { |
1477 | struct prestera_netevent_work *net_work = |
1478 | container_of(work, struct prestera_netevent_work, work); |
1479 | struct prestera_switch *sw = net_work->sw; |
1480 | struct neighbour *n = net_work->n; |
1481 | |
1482 | /* neigh - its not hw related object. It stored only in kernel. So... */ |
1483 | rtnl_lock(); |
1484 | |
1485 | prestera_k_arb_n_evt(sw, n); |
1486 | |
1487 | neigh_release(neigh: n); |
1488 | rtnl_unlock(); |
1489 | kfree(objp: net_work); |
1490 | } |
1491 | |
1492 | static int prestera_router_netevent_event(struct notifier_block *nb, |
1493 | unsigned long event, void *ptr) |
1494 | { |
1495 | struct prestera_netevent_work *net_work; |
1496 | struct prestera_router *router; |
1497 | struct neighbour *n = ptr; |
1498 | |
1499 | router = container_of(nb, struct prestera_router, netevent_nb); |
1500 | |
1501 | switch (event) { |
1502 | case NETEVENT_NEIGH_UPDATE: |
1503 | if (n->tbl->family != AF_INET) |
1504 | return NOTIFY_DONE; |
1505 | |
1506 | net_work = kzalloc(size: sizeof(*net_work), GFP_ATOMIC); |
1507 | if (WARN_ON(!net_work)) |
1508 | return NOTIFY_BAD; |
1509 | |
1510 | neigh_clone(neigh: n); |
1511 | net_work->n = n; |
1512 | net_work->sw = router->sw; |
1513 | INIT_WORK(&net_work->work, prestera_router_neigh_event_work); |
1514 | prestera_queue_work(work: &net_work->work); |
1515 | } |
1516 | |
1517 | return NOTIFY_DONE; |
1518 | } |
1519 | |
1520 | static void prestera_router_update_neighs_work(struct work_struct *work) |
1521 | { |
1522 | struct prestera_router *router; |
1523 | |
1524 | router = container_of(work, struct prestera_router, |
1525 | neighs_update.dw.work); |
1526 | rtnl_lock(); |
1527 | |
1528 | prestera_k_arb_hw_evt(sw: router->sw); |
1529 | |
1530 | rtnl_unlock(); |
1531 | prestera_queue_delayed_work(work: &router->neighs_update.dw, |
1532 | delay: msecs_to_jiffies(PRESTERA_NH_PROBE_INTERVAL)); |
1533 | } |
1534 | |
1535 | static int prestera_neigh_work_init(struct prestera_switch *sw) |
1536 | { |
1537 | INIT_DELAYED_WORK(&sw->router->neighs_update.dw, |
1538 | prestera_router_update_neighs_work); |
1539 | prestera_queue_delayed_work(work: &sw->router->neighs_update.dw, delay: 0); |
1540 | return 0; |
1541 | } |
1542 | |
1543 | static void prestera_neigh_work_fini(struct prestera_switch *sw) |
1544 | { |
1545 | cancel_delayed_work_sync(dwork: &sw->router->neighs_update.dw); |
1546 | } |
1547 | |
1548 | int prestera_router_init(struct prestera_switch *sw) |
1549 | { |
1550 | struct prestera_router *router; |
1551 | int err, nhgrp_cache_bytes; |
1552 | |
1553 | router = kzalloc(size: sizeof(*sw->router), GFP_KERNEL); |
1554 | if (!router) |
1555 | return -ENOMEM; |
1556 | |
1557 | sw->router = router; |
1558 | router->sw = sw; |
1559 | |
1560 | err = prestera_router_hw_init(sw); |
1561 | if (err) |
1562 | goto err_router_lib_init; |
1563 | |
1564 | err = rhashtable_init(ht: &router->kern_fib_cache_ht, |
1565 | params: &__prestera_kern_fib_cache_ht_params); |
1566 | if (err) |
1567 | goto err_kern_fib_cache_ht_init; |
1568 | |
1569 | err = rhashtable_init(ht: &router->kern_neigh_cache_ht, |
1570 | params: &__prestera_kern_neigh_cache_ht_params); |
1571 | if (err) |
1572 | goto err_kern_neigh_cache_ht_init; |
1573 | |
1574 | nhgrp_cache_bytes = sw->size_tbl_router_nexthop / 8 + 1; |
1575 | router->nhgrp_hw_state_cache = kzalloc(size: nhgrp_cache_bytes, GFP_KERNEL); |
1576 | if (!router->nhgrp_hw_state_cache) { |
1577 | err = -ENOMEM; |
1578 | goto err_nh_state_cache_alloc; |
1579 | } |
1580 | |
1581 | err = prestera_neigh_work_init(sw); |
1582 | if (err) |
1583 | goto err_neigh_work_init; |
1584 | |
1585 | router->inetaddr_valid_nb.notifier_call = __prestera_inetaddr_valid_cb; |
1586 | err = register_inetaddr_validator_notifier(nb: &router->inetaddr_valid_nb); |
1587 | if (err) |
1588 | goto err_register_inetaddr_validator_notifier; |
1589 | |
1590 | router->inetaddr_nb.notifier_call = __prestera_inetaddr_cb; |
1591 | err = register_inetaddr_notifier(nb: &router->inetaddr_nb); |
1592 | if (err) |
1593 | goto err_register_inetaddr_notifier; |
1594 | |
1595 | router->netevent_nb.notifier_call = prestera_router_netevent_event; |
1596 | err = register_netevent_notifier(nb: &router->netevent_nb); |
1597 | if (err) |
1598 | goto err_register_netevent_notifier; |
1599 | |
1600 | router->fib_nb.notifier_call = __prestera_router_fib_event; |
1601 | err = register_fib_notifier(net: &init_net, nb: &router->fib_nb, |
1602 | /* TODO: flush fib entries */ NULL, NULL); |
1603 | if (err) |
1604 | goto err_register_fib_notifier; |
1605 | |
1606 | return 0; |
1607 | |
1608 | err_register_fib_notifier: |
1609 | unregister_netevent_notifier(nb: &router->netevent_nb); |
1610 | err_register_netevent_notifier: |
1611 | unregister_inetaddr_notifier(nb: &router->inetaddr_nb); |
1612 | err_register_inetaddr_notifier: |
1613 | unregister_inetaddr_validator_notifier(nb: &router->inetaddr_valid_nb); |
1614 | err_register_inetaddr_validator_notifier: |
1615 | prestera_neigh_work_fini(sw); |
1616 | err_neigh_work_init: |
1617 | kfree(objp: router->nhgrp_hw_state_cache); |
1618 | err_nh_state_cache_alloc: |
1619 | rhashtable_destroy(ht: &router->kern_neigh_cache_ht); |
1620 | err_kern_neigh_cache_ht_init: |
1621 | rhashtable_destroy(ht: &router->kern_fib_cache_ht); |
1622 | err_kern_fib_cache_ht_init: |
1623 | prestera_router_hw_fini(sw); |
1624 | err_router_lib_init: |
1625 | kfree(objp: sw->router); |
1626 | return err; |
1627 | } |
1628 | |
1629 | void prestera_router_fini(struct prestera_switch *sw) |
1630 | { |
1631 | unregister_fib_notifier(net: &init_net, nb: &sw->router->fib_nb); |
1632 | unregister_netevent_notifier(nb: &sw->router->netevent_nb); |
1633 | unregister_inetaddr_notifier(nb: &sw->router->inetaddr_nb); |
1634 | unregister_inetaddr_validator_notifier(nb: &sw->router->inetaddr_valid_nb); |
1635 | prestera_neigh_work_fini(sw); |
1636 | prestera_queue_drain(); |
1637 | |
1638 | prestera_k_arb_abort(sw); |
1639 | |
1640 | kfree(objp: sw->router->nhgrp_hw_state_cache); |
1641 | rhashtable_destroy(ht: &sw->router->kern_fib_cache_ht); |
1642 | prestera_router_hw_fini(sw); |
1643 | kfree(objp: sw->router); |
1644 | sw->router = NULL; |
1645 | } |
1646 | |