1 /** @file
2 * @brief Route handling.
3 *
4 */
5
6 /*
7 * Copyright (c) 2016 Intel Corporation
8 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(net_route, CONFIG_NET_ROUTE_LOG_LEVEL);
14
15 #include <zephyr/kernel.h>
16 #include <limits.h>
17 #include <zephyr/types.h>
18 #include <zephyr/sys/slist.h>
19
20 #include <zephyr/net/net_pkt.h>
21 #include <zephyr/net/net_core.h>
22 #include <zephyr/net/net_stats.h>
23 #include <zephyr/net/net_mgmt.h>
24 #include <zephyr/net/net_ip.h>
25
26 #include "net_private.h"
27 #include "ipv6.h"
28 #include "icmpv6.h"
29 #include "nbr.h"
30 #include "route.h"
31
32 /* We keep track of the routes in a separate list so that we can remove
33 * the oldest routes (at tail) if needed.
34 */
35 static sys_slist_t routes;
36
37 /* Track currently active route lifetime timers */
38 static sys_slist_t active_route_lifetime_timers;
39
40 /* Timer that manages expired route entries. */
41 static struct k_work_delayable route_lifetime_timer;
42
net_route_nexthop_remove(struct net_nbr * nbr)43 static void net_route_nexthop_remove(struct net_nbr *nbr)
44 {
45 NET_DBG("Nexthop %p removed", nbr);
46 }
47
48 /*
49 * This pool contains information next hop neighbors.
50 */
51 NET_NBR_POOL_INIT(net_route_nexthop_pool,
52 CONFIG_NET_MAX_NEXTHOPS,
53 sizeof(struct net_route_nexthop),
54 net_route_nexthop_remove);
55
net_nexthop_data(struct net_nbr * nbr)56 static inline struct net_route_nexthop *net_nexthop_data(struct net_nbr *nbr)
57 {
58 return (struct net_route_nexthop *)nbr->data;
59 }
60
get_nexthop_nbr(struct net_nbr * start,int idx)61 static inline struct net_nbr *get_nexthop_nbr(struct net_nbr *start, int idx)
62 {
63 NET_ASSERT(idx < CONFIG_NET_MAX_NEXTHOPS, "idx %d >= max %d",
64 idx, CONFIG_NET_MAX_NEXTHOPS);
65
66 return (struct net_nbr *)((uint8_t *)start +
67 ((sizeof(struct net_nbr) + start->size) * idx));
68 }
69
release_nexthop_route(struct net_route_nexthop * route_nexthop)70 static void release_nexthop_route(struct net_route_nexthop *route_nexthop)
71 {
72 struct net_nbr *nbr = CONTAINER_OF((uint8_t *)route_nexthop, struct net_nbr, __nbr[0]);
73
74 net_nbr_unref(nbr);
75 }
76
get_nexthop_route(void)77 static struct net_nbr *get_nexthop_route(void)
78 {
79 int i;
80
81 for (i = 0; i < CONFIG_NET_MAX_NEXTHOPS; i++) {
82 struct net_nbr *nbr = get_nexthop_nbr(
83 (struct net_nbr *)net_route_nexthop_pool, i);
84
85 if (!nbr->ref) {
86 nbr->data = nbr->__nbr;
87
88 nbr->idx = NET_NBR_LLADDR_UNKNOWN;
89
90 return net_nbr_ref(nbr);
91 }
92 }
93
94 return NULL;
95 }
96
net_route_entry_remove(struct net_nbr * nbr)97 static void net_route_entry_remove(struct net_nbr *nbr)
98 {
99 NET_DBG("Route %p removed", nbr);
100 }
101
net_route_entries_table_clear(struct net_nbr_table * table)102 static void net_route_entries_table_clear(struct net_nbr_table *table)
103 {
104 NET_DBG("Route table %p cleared", table);
105 }
106
107 /*
108 * This pool contains routing table entries.
109 */
110 NET_NBR_POOL_INIT(net_route_entries_pool,
111 CONFIG_NET_MAX_ROUTES,
112 sizeof(struct net_route_entry),
113 net_route_entry_remove);
114
115 NET_NBR_TABLE_INIT(NET_NBR_LOCAL, nbr_routes, net_route_entries_pool,
116 net_route_entries_table_clear);
117
get_nbr(int idx)118 static inline struct net_nbr *get_nbr(int idx)
119 {
120 return &net_route_entries_pool[idx].nbr;
121 }
122
net_route_data(struct net_nbr * nbr)123 static inline struct net_route_entry *net_route_data(struct net_nbr *nbr)
124 {
125 return (struct net_route_entry *)nbr->data;
126 }
127
net_route_get_nbr(struct net_route_entry * route)128 struct net_nbr *net_route_get_nbr(struct net_route_entry *route)
129 {
130 struct net_nbr *ret = NULL;
131 int i;
132
133 NET_ASSERT(route);
134
135 net_ipv6_nbr_lock();
136
137 for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) {
138 struct net_nbr *nbr = get_nbr(i);
139
140 if (!nbr->ref) {
141 continue;
142 }
143
144 if (nbr->data == (uint8_t *)route) {
145 if (!nbr->ref) {
146 break;
147 }
148
149 ret = nbr;
150 break;
151 }
152 }
153
154 net_ipv6_nbr_unlock();
155 return ret;
156 }
157
net_routes_print(void)158 void net_routes_print(void)
159 {
160 int i;
161
162 net_ipv6_nbr_lock();
163
164 for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) {
165 struct net_nbr *nbr = get_nbr(i);
166
167 if (!nbr->ref) {
168 continue;
169 }
170
171 NET_DBG("[%d] %p %d addr %s/%d",
172 i, nbr, nbr->ref,
173 net_sprint_ipv6_addr(&net_route_data(nbr)->addr),
174 net_route_data(nbr)->prefix_len);
175 NET_DBG(" iface %p idx %d ll %s",
176 nbr->iface, nbr->idx,
177 nbr->idx == NET_NBR_LLADDR_UNKNOWN ? "?" :
178 net_sprint_ll_addr(
179 net_nbr_get_lladdr(nbr->idx)->addr,
180 net_nbr_get_lladdr(nbr->idx)->len));
181 }
182
183 net_ipv6_nbr_unlock();
184 }
185
nbr_free(struct net_nbr * nbr)186 static inline void nbr_free(struct net_nbr *nbr)
187 {
188 NET_DBG("nbr %p", nbr);
189
190 net_nbr_unref(nbr);
191 }
192
nbr_new(struct net_if * iface,struct in6_addr * addr,uint8_t prefix_len)193 static struct net_nbr *nbr_new(struct net_if *iface,
194 struct in6_addr *addr,
195 uint8_t prefix_len)
196 {
197 struct net_nbr *nbr = net_nbr_get(&net_nbr_routes.table);
198
199 if (!nbr) {
200 return NULL;
201 }
202
203 nbr->iface = iface;
204
205 net_ipaddr_copy(&net_route_data(nbr)->addr, addr);
206 net_route_data(nbr)->prefix_len = prefix_len;
207
208 NET_DBG("[%d] nbr %p iface %p IPv6 %s/%d",
209 nbr->idx, nbr, iface,
210 net_sprint_ipv6_addr(&net_route_data(nbr)->addr),
211 prefix_len);
212
213 return nbr;
214 }
215
nbr_nexthop_get(struct net_if * iface,struct in6_addr * addr)216 static struct net_nbr *nbr_nexthop_get(struct net_if *iface,
217 struct in6_addr *addr)
218 {
219 /* Note that the nexthop host must be already in the neighbor
220 * cache. We just increase the ref count of an existing entry.
221 */
222 struct net_nbr *nbr;
223
224 nbr = net_ipv6_nbr_lookup(iface, addr);
225 if (nbr == NULL) {
226 NET_DBG("Next hop neighbor not found!");
227 return NULL;
228 }
229
230 net_nbr_ref(nbr);
231
232 NET_DBG("[%d] nbr %p iface %p IPv6 %s",
233 nbr->idx, nbr, iface,
234 net_sprint_ipv6_addr(addr));
235
236 return nbr;
237 }
238
nbr_nexthop_put(struct net_nbr * nbr)239 static int nbr_nexthop_put(struct net_nbr *nbr)
240 {
241 NET_ASSERT(nbr);
242
243 NET_DBG("[%d] nbr %p iface %p", nbr->idx, nbr, nbr->iface);
244
245 net_nbr_unref(nbr);
246
247 return 0;
248 }
249
250 #define net_route_info(str, route, dst) \
251 do { \
252 if (CONFIG_NET_ROUTE_LOG_LEVEL >= LOG_LEVEL_DBG) { \
253 struct in6_addr *naddr = net_route_get_nexthop(route); \
254 \
255 NET_ASSERT(naddr, "Unknown nexthop address"); \
256 \
257 NET_DBG("%s route to %s via %s (iface %p)", str, \
258 net_sprint_ipv6_addr(dst), \
259 net_sprint_ipv6_addr(naddr), \
260 route->iface); \
261 } } while (false)
262
263 /* Route was accessed, so place it in front of the routes list */
update_route_access(struct net_route_entry * route)264 static inline void update_route_access(struct net_route_entry *route)
265 {
266 sys_slist_find_and_remove(&routes, &route->node);
267 sys_slist_prepend(&routes, &route->node);
268 }
269
net_route_lookup(struct net_if * iface,struct in6_addr * dst)270 struct net_route_entry *net_route_lookup(struct net_if *iface,
271 struct in6_addr *dst)
272 {
273 struct net_route_entry *route, *found = NULL;
274 uint8_t longest_match = 0U;
275 int i;
276
277 net_ipv6_nbr_lock();
278
279 for (i = 0; i < CONFIG_NET_MAX_ROUTES && longest_match < 128; i++) {
280 struct net_nbr *nbr = get_nbr(i);
281
282 if (!nbr->ref) {
283 continue;
284 }
285
286 if (iface && nbr->iface != iface) {
287 continue;
288 }
289
290 route = net_route_data(nbr);
291
292 if (route->prefix_len >= longest_match &&
293 net_ipv6_is_prefix(dst->s6_addr,
294 route->addr.s6_addr,
295 route->prefix_len)) {
296 found = route;
297 longest_match = route->prefix_len;
298 }
299 }
300
301 if (found) {
302 net_route_info("Found", found, dst);
303
304 update_route_access(found);
305 }
306
307 net_ipv6_nbr_unlock();
308 return found;
309 }
310
route_preference_is_lower(uint8_t old,uint8_t new)311 static inline bool route_preference_is_lower(uint8_t old, uint8_t new)
312 {
313 if (new == NET_ROUTE_PREFERENCE_RESERVED || (new & 0xfc) != 0) {
314 return true;
315 }
316
317 /* Transform valid preference values into comparable integers */
318 old = (old + 1) & 0x3;
319 new = (new + 1) & 0x3;
320
321 return new < old;
322 }
323
net_route_add(struct net_if * iface,struct in6_addr * addr,uint8_t prefix_len,struct in6_addr * nexthop,uint32_t lifetime,uint8_t preference)324 struct net_route_entry *net_route_add(struct net_if *iface,
325 struct in6_addr *addr,
326 uint8_t prefix_len,
327 struct in6_addr *nexthop,
328 uint32_t lifetime,
329 uint8_t preference)
330 {
331 struct net_linkaddr_storage *nexthop_lladdr;
332 struct net_nbr *nbr, *nbr_nexthop, *tmp;
333 struct net_route_nexthop *nexthop_route;
334 struct net_route_entry *route = NULL;
335 #if defined(CONFIG_NET_MGMT_EVENT_INFO)
336 struct net_event_ipv6_route info;
337 #endif
338
339 NET_ASSERT(addr);
340 NET_ASSERT(iface);
341 NET_ASSERT(nexthop);
342
343 if (net_ipv6_addr_cmp(addr, net_ipv6_unspecified_address())) {
344 NET_DBG("Route cannot be towards unspecified address");
345 return NULL;
346 }
347
348 net_ipv6_nbr_lock();
349
350 nbr_nexthop = net_ipv6_nbr_lookup(iface, nexthop);
351 if (!nbr_nexthop) {
352 NET_DBG("No such neighbor %s found",
353 net_sprint_ipv6_addr(nexthop));
354 goto exit;
355 }
356
357 if (nbr_nexthop && nbr_nexthop->idx != NET_NBR_LLADDR_UNKNOWN) {
358 nexthop_lladdr = net_nbr_get_lladdr(nbr_nexthop->idx);
359 NET_ASSERT(nexthop_lladdr);
360 NET_DBG("Nexthop %s lladdr is %s", net_sprint_ipv6_addr(nexthop),
361 net_sprint_ll_addr(nexthop_lladdr->addr, nexthop_lladdr->len));
362 }
363
364 route = net_route_lookup(iface, addr);
365 if (route) {
366 /* Update nexthop if not the same */
367 struct in6_addr *nexthop_addr;
368
369 nexthop_addr = net_route_get_nexthop(route);
370 if (nexthop_addr && net_ipv6_addr_cmp(nexthop, nexthop_addr)) {
371 NET_DBG("No changes, return old route %p", route);
372
373 /* Reset lifetime timer. */
374 net_route_update_lifetime(route, lifetime);
375
376 route->preference = preference;
377
378 goto exit;
379 }
380
381 if (route_preference_is_lower(route->preference, preference)) {
382 NET_DBG("No changes, ignoring route with lower preference");
383 route = NULL;
384 goto exit;
385 }
386
387 NET_DBG("Old route to %s found",
388 net_sprint_ipv6_addr(nexthop_addr));
389
390 net_route_del(route);
391 }
392
393 nbr = nbr_new(iface, addr, prefix_len);
394 if (!nbr) {
395 /* Remove the oldest route and try again */
396 sys_snode_t *last = sys_slist_peek_tail(&routes);
397
398 sys_slist_find_and_remove(&routes, last);
399
400 route = CONTAINER_OF(last,
401 struct net_route_entry,
402 node);
403
404 if (CONFIG_NET_ROUTE_LOG_LEVEL >= LOG_LEVEL_DBG) {
405 struct in6_addr *in6_addr_tmp;
406 struct net_linkaddr_storage *llstorage;
407
408 in6_addr_tmp = net_route_get_nexthop(route);
409 nbr = net_ipv6_nbr_lookup(iface, in6_addr_tmp);
410 if (nbr) {
411 llstorage = net_nbr_get_lladdr(nbr->idx);
412
413 NET_DBG("Removing the oldest route %s "
414 "via %s [%s]",
415 net_sprint_ipv6_addr(&route->addr),
416 net_sprint_ipv6_addr(in6_addr_tmp),
417 net_sprint_ll_addr(llstorage->addr,
418 llstorage->len));
419 }
420 }
421
422 net_route_del(route);
423
424 nbr = nbr_new(iface, addr, prefix_len);
425 if (!nbr) {
426 NET_ERR("Neighbor route alloc failed!");
427 route = NULL;
428 goto exit;
429 }
430 }
431
432 tmp = get_nexthop_route();
433 if (!tmp) {
434 NET_ERR("No nexthop route available!");
435 route = NULL;
436 goto exit;
437 }
438
439 nexthop_route = net_nexthop_data(tmp);
440
441 route = net_route_data(nbr);
442 route->iface = iface;
443 route->preference = preference;
444
445 net_route_update_lifetime(route, lifetime);
446
447 sys_slist_prepend(&routes, &route->node);
448
449 tmp = nbr_nexthop_get(iface, nexthop);
450
451 NET_ASSERT(tmp == nbr_nexthop);
452
453 nexthop_route->nbr = tmp;
454
455 sys_slist_init(&route->nexthop);
456 sys_slist_prepend(&route->nexthop, &nexthop_route->node);
457
458 net_route_info("Added", route, addr);
459
460 #if defined(CONFIG_NET_MGMT_EVENT_INFO)
461 net_ipaddr_copy(&info.addr, addr);
462 net_ipaddr_copy(&info.nexthop, nexthop);
463 info.prefix_len = prefix_len;
464
465 net_mgmt_event_notify_with_info(NET_EVENT_IPV6_ROUTE_ADD,
466 iface, (void *) &info,
467 sizeof(struct net_event_ipv6_route));
468 #else
469 net_mgmt_event_notify(NET_EVENT_IPV6_ROUTE_ADD, iface);
470 #endif
471
472 exit:
473 net_ipv6_nbr_unlock();
474 return route;
475 }
476
route_expired(struct net_route_entry * route)477 static void route_expired(struct net_route_entry *route)
478 {
479 NET_DBG("Route to %s expired",
480 net_sprint_ipv6_addr(&route->addr));
481
482 sys_slist_find_and_remove(&active_route_lifetime_timers,
483 &route->lifetime.node);
484
485 net_route_del(route);
486 }
487
route_lifetime_timeout(struct k_work * work)488 static void route_lifetime_timeout(struct k_work *work)
489 {
490 uint32_t next_update = UINT32_MAX;
491 uint32_t current_time = k_uptime_get_32();
492 struct net_route_entry *current, *next;
493
494 ARG_UNUSED(work);
495
496 net_ipv6_nbr_lock();
497
498 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&active_route_lifetime_timers,
499 current, next, lifetime.node) {
500 struct net_timeout *timeout = ¤t->lifetime;
501 uint32_t this_update = net_timeout_evaluate(timeout,
502 current_time);
503
504 if (this_update == 0U) {
505 route_expired(current);
506 continue;
507 }
508
509 if (this_update < next_update) {
510 next_update = this_update;
511 }
512 }
513
514 if (next_update != UINT32_MAX) {
515 k_work_reschedule(&route_lifetime_timer, K_MSEC(next_update));
516 }
517
518 net_ipv6_nbr_unlock();
519 }
520
net_route_update_lifetime(struct net_route_entry * route,uint32_t lifetime)521 void net_route_update_lifetime(struct net_route_entry *route, uint32_t lifetime)
522 {
523 NET_DBG("Updating route lifetime of %s to %u secs",
524 net_sprint_ipv6_addr(&route->addr),
525 lifetime);
526
527 if (!route) {
528 return;
529 }
530
531 net_ipv6_nbr_lock();
532
533 if (lifetime == NET_IPV6_ND_INFINITE_LIFETIME) {
534 route->is_infinite = true;
535
536 (void)sys_slist_find_and_remove(&active_route_lifetime_timers,
537 &route->lifetime.node);
538 } else {
539 route->is_infinite = false;
540
541 net_timeout_set(&route->lifetime, lifetime, k_uptime_get_32());
542
543 (void)sys_slist_find_and_remove(&active_route_lifetime_timers,
544 &route->lifetime.node);
545 sys_slist_append(&active_route_lifetime_timers,
546 &route->lifetime.node);
547 k_work_reschedule(&route_lifetime_timer, K_NO_WAIT);
548 }
549
550 net_ipv6_nbr_unlock();
551 }
552
net_route_del(struct net_route_entry * route)553 int net_route_del(struct net_route_entry *route)
554 {
555 struct net_nbr *nbr;
556 struct net_route_nexthop *nexthop_route;
557 #if defined(CONFIG_NET_MGMT_EVENT_INFO)
558 struct net_event_ipv6_route info;
559 #endif
560
561 if (!route) {
562 return -EINVAL;
563 }
564
565 net_ipv6_nbr_lock();
566
567 #if defined(CONFIG_NET_MGMT_EVENT_INFO)
568 net_ipaddr_copy(&info.addr, &route->addr);
569 info.prefix_len = route->prefix_len;
570 net_ipaddr_copy(&info.nexthop,
571 net_route_get_nexthop(route));
572
573 net_mgmt_event_notify_with_info(NET_EVENT_IPV6_ROUTE_DEL,
574 route->iface, (void *) &info,
575 sizeof(struct net_event_ipv6_route));
576 #else
577 net_mgmt_event_notify(NET_EVENT_IPV6_ROUTE_DEL, route->iface);
578 #endif
579
580 if (!route->is_infinite) {
581 sys_slist_find_and_remove(&active_route_lifetime_timers,
582 &route->lifetime.node);
583
584 if (sys_slist_is_empty(&active_route_lifetime_timers)) {
585 k_work_cancel_delayable(&route_lifetime_timer);
586 }
587 }
588
589 sys_slist_find_and_remove(&routes, &route->node);
590
591 nbr = net_route_get_nbr(route);
592 if (!nbr) {
593 net_ipv6_nbr_unlock();
594 return -ENOENT;
595 }
596
597 net_route_info("Deleted", route, &route->addr);
598
599 SYS_SLIST_FOR_EACH_CONTAINER(&route->nexthop, nexthop_route, node) {
600 if (!nexthop_route->nbr) {
601 continue;
602 }
603
604 nbr_nexthop_put(nexthop_route->nbr);
605 release_nexthop_route(nexthop_route);
606 }
607
608 nbr_free(nbr);
609
610 net_ipv6_nbr_unlock();
611 return 0;
612 }
613
net_route_del_by_nexthop(struct net_if * iface,struct in6_addr * nexthop)614 int net_route_del_by_nexthop(struct net_if *iface, struct in6_addr *nexthop)
615 {
616 int count = 0, status = 0;
617 struct net_nbr *nbr_nexthop;
618 struct net_route_nexthop *nexthop_route;
619 int i, ret;
620
621 NET_ASSERT(iface);
622 NET_ASSERT(nexthop);
623
624 net_ipv6_nbr_lock();
625
626 nbr_nexthop = net_ipv6_nbr_lookup(iface, nexthop);
627
628 for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) {
629 struct net_nbr *nbr = get_nbr(i);
630 struct net_route_entry *route = net_route_data(nbr);
631
632 if (!route) {
633 continue;
634 }
635
636 SYS_SLIST_FOR_EACH_CONTAINER(&route->nexthop, nexthop_route,
637 node) {
638 if (nexthop_route->nbr == nbr_nexthop) {
639 /* This route contains this nexthop */
640 ret = net_route_del(route);
641 if (!ret) {
642 count++;
643 } else {
644 status = ret;
645 }
646 break;
647 }
648 }
649 }
650
651 net_ipv6_nbr_unlock();
652
653 if (count) {
654 return count;
655 } else if (status < 0) {
656 return status;
657 }
658
659 return 0;
660 }
661
net_route_get_nexthop(struct net_route_entry * route)662 struct in6_addr *net_route_get_nexthop(struct net_route_entry *route)
663 {
664 struct net_route_nexthop *nexthop_route;
665 struct net_ipv6_nbr_data *ipv6_nbr_data;
666
667 if (!route) {
668 return NULL;
669 }
670
671 net_ipv6_nbr_lock();
672
673 SYS_SLIST_FOR_EACH_CONTAINER(&route->nexthop, nexthop_route, node) {
674 struct in6_addr *addr;
675
676 ipv6_nbr_data = net_ipv6_nbr_data(nexthop_route->nbr);
677 if (ipv6_nbr_data) {
678 addr = &ipv6_nbr_data->addr;
679 NET_ASSERT(addr);
680
681 net_ipv6_nbr_unlock();
682 return addr;
683 } else {
684 NET_ERR("could not get neighbor data from next hop");
685 }
686 }
687
688 net_ipv6_nbr_unlock();
689 return NULL;
690 }
691
net_route_foreach(net_route_cb_t cb,void * user_data)692 int net_route_foreach(net_route_cb_t cb, void *user_data)
693 {
694 int i, ret = 0;
695
696 net_ipv6_nbr_lock();
697
698 for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) {
699 struct net_route_entry *route;
700 struct net_nbr *nbr;
701
702 nbr = get_nbr(i);
703 if (!nbr) {
704 continue;
705 }
706
707 if (!nbr->ref) {
708 continue;
709 }
710
711 route = net_route_data(nbr);
712 if (!route) {
713 continue;
714 }
715
716 cb(route, user_data);
717
718 ret++;
719 }
720
721 net_ipv6_nbr_unlock();
722 return ret;
723 }
724
725 #if defined(CONFIG_NET_ROUTE_MCAST)
726 /*
727 * This array contains multicast routing entries.
728 */
729 static
730 struct net_route_entry_mcast route_mcast_entries[CONFIG_NET_MAX_MCAST_ROUTES];
731
mcast_route_iface_lookup(struct net_route_entry_mcast * entry,struct net_if * iface)732 static int mcast_route_iface_lookup(struct net_route_entry_mcast *entry, struct net_if *iface)
733 {
734 ARRAY_FOR_EACH(entry->ifaces, i) {
735 if (entry->ifaces[i] == iface) {
736 return i;
737 }
738 }
739
740 return -1;
741 }
742
net_route_mcast_iface_add(struct net_route_entry_mcast * entry,struct net_if * iface)743 bool net_route_mcast_iface_add(struct net_route_entry_mcast *entry, struct net_if *iface)
744 {
745 if (!net_if_flag_is_set(iface, NET_IF_FORWARD_MULTICASTS)) {
746 return false;
747 }
748
749 if (mcast_route_iface_lookup(entry, iface) >= 0) {
750 /* Interface is already added */
751 return true;
752 }
753
754 ARRAY_FOR_EACH(entry->ifaces, i) {
755 if (entry->ifaces[i] == NULL) {
756 entry->ifaces[i] = iface;
757
758 return true;
759 }
760 }
761
762 /* There are no empty slots */
763 return false;
764 }
765
net_route_mcast_iface_del(struct net_route_entry_mcast * entry,struct net_if * iface)766 bool net_route_mcast_iface_del(struct net_route_entry_mcast *entry,
767 struct net_if *iface)
768 {
769 int pos = mcast_route_iface_lookup(entry, iface);
770
771 if (pos < 0) {
772 return false;
773 }
774
775 entry->ifaces[pos] = NULL;
776
777 return true;
778 }
779
780 #if defined(CONFIG_NET_MCAST_ROUTE_MLD_REPORTS)
781 struct mcast_route_mld_event {
782 struct in6_addr *addr;
783 uint8_t mode;
784 };
785
send_mld_event(struct net_if * iface,void * user_data)786 static void send_mld_event(struct net_if *iface, void *user_data)
787 {
788 struct mcast_route_mld_event *event = (struct mcast_route_mld_event *)user_data;
789
790 /* Do not send events for ifaces without IPv6, without MLD, already or still in
791 * a given group
792 */
793 if (!iface->config.ip.ipv6 || net_if_flag_is_set(iface, NET_IF_IPV6_NO_MLD) ||
794 net_if_ipv6_maddr_lookup(event->addr, &iface)) {
795 return;
796 }
797
798 net_ipv6_mld_send_single(iface, event->addr, event->mode);
799 }
800
propagate_mld_event(struct net_route_entry_mcast * route,bool route_added)801 static void propagate_mld_event(struct net_route_entry_mcast *route, bool route_added)
802 {
803 struct mcast_route_mld_event mld_event;
804
805 /* Apply only for complete addresses */
806 if (route->prefix_len == 128) {
807 mld_event.addr = &route->group;
808 mld_event.mode = route_added ? NET_IPV6_MLDv2_CHANGE_TO_EXCLUDE_MODE :
809 NET_IPV6_MLDv2_CHANGE_TO_INCLUDE_MODE;
810
811 net_if_foreach(send_mld_event, &mld_event);
812 }
813 }
814 #else
815 #define propagate_mld_event(...)
816 #endif /* CONFIG_NET_MCAST_ROUTE_MLD_REPORTS */
817
net_route_mcast_forward_packet(struct net_pkt * pkt,struct net_ipv6_hdr * hdr)818 int net_route_mcast_forward_packet(struct net_pkt *pkt, struct net_ipv6_hdr *hdr)
819 {
820 int ret = 0, err = 0;
821
822 /* At this point, the original pkt has already stored the hop limit in its metadata.
823 * Change its value in a common buffer so the forwardee has a proper count. As we have
824 * a direct access to the buffer there is no need to perform read/write operations.
825 */
826 hdr->hop_limit--;
827
828 ARRAY_FOR_EACH_PTR(route_mcast_entries, route) {
829 struct net_pkt *pkt_cpy = NULL;
830
831 if (!route->is_used ||
832 !net_ipv6_is_prefix(hdr->dst, route->group.s6_addr, route->prefix_len)) {
833 continue;
834 }
835
836 ARRAY_FOR_EACH(route->ifaces, i) {
837 if (!route->ifaces[i] || pkt->iface == route->ifaces[i] ||
838 !net_if_flag_is_set(route->ifaces[i], NET_IF_FORWARD_MULTICASTS)) {
839 continue;
840 }
841
842 pkt_cpy = net_pkt_shallow_clone(pkt, K_NO_WAIT);
843
844 if (pkt_cpy == NULL) {
845 err--;
846 continue;
847 }
848
849 net_pkt_set_forwarding(pkt_cpy, true);
850 net_pkt_set_orig_iface(pkt_cpy, pkt->iface);
851 net_pkt_set_iface(pkt_cpy, route->ifaces[i]);
852
853 if (net_send_data(pkt_cpy) >= 0) {
854 ++ret;
855 } else {
856 net_pkt_unref(pkt_cpy);
857 --err;
858 }
859 }
860 }
861
862 return (err == 0) ? ret : err;
863 }
864
net_route_mcast_foreach(net_route_mcast_cb_t cb,struct in6_addr * skip,void * user_data)865 int net_route_mcast_foreach(net_route_mcast_cb_t cb,
866 struct in6_addr *skip,
867 void *user_data)
868 {
869 int ret = 0;
870
871 ARRAY_FOR_EACH_PTR(route_mcast_entries, route) {
872 if (route->is_used) {
873 if (skip && net_ipv6_is_prefix(skip->s6_addr,
874 route->group.s6_addr,
875 route->prefix_len)) {
876 continue;
877 }
878
879 cb(route, user_data);
880
881 ret++;
882 }
883 }
884
885 return ret;
886 }
887
net_route_mcast_add(struct net_if * iface,struct in6_addr * group,uint8_t prefix_len)888 struct net_route_entry_mcast *net_route_mcast_add(struct net_if *iface,
889 struct in6_addr *group,
890 uint8_t prefix_len)
891 {
892 net_ipv6_nbr_lock();
893
894 if ((!net_if_flag_is_set(iface, NET_IF_FORWARD_MULTICASTS)) ||
895 (!net_ipv6_is_addr_mcast(group)) ||
896 (net_ipv6_is_addr_mcast_iface(group)) ||
897 (net_ipv6_is_addr_mcast_link(group))) {
898 net_ipv6_nbr_unlock();
899 return NULL;
900 }
901
902 ARRAY_FOR_EACH_PTR(route_mcast_entries, route) {
903 if (!route->is_used) {
904 net_ipaddr_copy(&route->group, group);
905
906 ARRAY_FOR_EACH(route->ifaces, i) {
907 route->ifaces[i] = NULL;
908 }
909
910 route->prefix_len = prefix_len;
911 route->ifaces[0] = iface;
912 route->is_used = true;
913
914 propagate_mld_event(route, true);
915
916 net_ipv6_nbr_unlock();
917 return route;
918 }
919 }
920
921 net_ipv6_nbr_unlock();
922 return NULL;
923 }
924
net_route_mcast_del(struct net_route_entry_mcast * route)925 bool net_route_mcast_del(struct net_route_entry_mcast *route)
926 {
927 if (route > &route_mcast_entries[CONFIG_NET_MAX_MCAST_ROUTES - 1] ||
928 route < &route_mcast_entries[0]) {
929 return false;
930 }
931
932 NET_ASSERT(route->is_used,
933 "Multicast route %p to %s was already removed", route,
934 net_sprint_ipv6_addr(&route->group));
935
936 propagate_mld_event(route, false);
937
938 route->is_used = false;
939
940 return true;
941 }
942
943 struct net_route_entry_mcast *
net_route_mcast_lookup(struct in6_addr * group)944 net_route_mcast_lookup(struct in6_addr *group)
945 {
946 ARRAY_FOR_EACH_PTR(route_mcast_entries, route) {
947 if (!route->is_used) {
948 continue;
949 }
950
951 if (net_ipv6_is_prefix(group->s6_addr,
952 route->group.s6_addr,
953 route->prefix_len)) {
954 return route;
955 }
956 }
957
958 return NULL;
959 }
960 #endif /* CONFIG_NET_ROUTE_MCAST */
961
net_route_get_info(struct net_if * iface,struct in6_addr * dst,struct net_route_entry ** route,struct in6_addr ** nexthop)962 bool net_route_get_info(struct net_if *iface,
963 struct in6_addr *dst,
964 struct net_route_entry **route,
965 struct in6_addr **nexthop)
966 {
967 struct net_if_router *router;
968 bool ret = false;
969
970 net_ipv6_nbr_lock();
971
972 /* Search in neighbor table first, if not search in routing table. */
973 if (net_ipv6_nbr_lookup(iface, dst)) {
974 /* Found nexthop, no need to look into routing table. */
975 *route = NULL;
976 *nexthop = dst;
977
978 ret = true;
979 goto exit;
980 }
981
982 *route = net_route_lookup(iface, dst);
983 if (*route) {
984 *nexthop = net_route_get_nexthop(*route);
985 if (!*nexthop) {
986 goto exit;
987 }
988
989 ret = true;
990 goto exit;
991 } else {
992 /* No specific route to this host, use the default
993 * route instead.
994 */
995 router = net_if_ipv6_router_find_default(NULL, dst);
996 if (!router) {
997 goto exit;
998 }
999
1000 *nexthop = &router->address.in6_addr;
1001
1002 ret = true;
1003 goto exit;
1004 }
1005
1006 exit:
1007 net_ipv6_nbr_unlock();
1008 return ret;
1009 }
1010
is_ll_addr_supported(struct net_if * iface)1011 static bool is_ll_addr_supported(struct net_if *iface)
1012 {
1013 #if defined(CONFIG_NET_L2_DUMMY)
1014 if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) {
1015 return false;
1016 }
1017 #endif
1018 #if defined(CONFIG_NET_L2_PPP)
1019 if (net_if_l2(iface) == &NET_L2_GET_NAME(PPP)) {
1020 return false;
1021 }
1022 #endif
1023 #if defined(CONFIG_NET_L2_OPENTHREAD)
1024 if (net_if_l2(iface) == &NET_L2_GET_NAME(OPENTHREAD)) {
1025 return false;
1026 }
1027 #endif
1028
1029 return true;
1030 }
1031
net_route_packet(struct net_pkt * pkt,struct in6_addr * nexthop)1032 int net_route_packet(struct net_pkt *pkt, struct in6_addr *nexthop)
1033 {
1034 struct net_linkaddr_storage *lladdr = NULL;
1035 struct net_nbr *nbr;
1036 int err;
1037
1038 net_ipv6_nbr_lock();
1039
1040 nbr = net_ipv6_nbr_lookup(NULL, nexthop);
1041 if (!nbr) {
1042 NET_DBG("Cannot find %s neighbor",
1043 net_sprint_ipv6_addr(nexthop));
1044 err = -ENOENT;
1045 goto error;
1046 }
1047
1048 if (is_ll_addr_supported(nbr->iface) && is_ll_addr_supported(net_pkt_iface(pkt)) &&
1049 is_ll_addr_supported(net_pkt_orig_iface(pkt))) {
1050 lladdr = net_nbr_get_lladdr(nbr->idx);
1051 if (!lladdr) {
1052 NET_DBG("Cannot find %s neighbor link layer address.",
1053 net_sprint_ipv6_addr(nexthop));
1054 err = -ESRCH;
1055 goto error;
1056 }
1057
1058 if (!net_pkt_lladdr_src(pkt)->addr) {
1059 NET_DBG("Link layer source address not set");
1060 err = -EINVAL;
1061 goto error;
1062 }
1063
1064 /* Sanitycheck: If src and dst ll addresses are going
1065 * to be same, then something went wrong in route
1066 * lookup.
1067 */
1068 if (!memcmp(net_pkt_lladdr_src(pkt)->addr, lladdr->addr,
1069 lladdr->len)) {
1070 NET_ERR("Src ll and Dst ll are same");
1071 err = -EINVAL;
1072 goto error;
1073 }
1074 }
1075
1076 net_pkt_set_forwarding(pkt, true);
1077
1078 /* Set the source ll address of the iface (if relevant) and the
1079 * destination address to be the nexthop recipient.
1080 */
1081 if (is_ll_addr_supported(net_pkt_iface(pkt))) {
1082 net_pkt_lladdr_src(pkt)->addr = net_pkt_lladdr_if(pkt)->addr;
1083 net_pkt_lladdr_src(pkt)->type = net_pkt_lladdr_if(pkt)->type;
1084 net_pkt_lladdr_src(pkt)->len = net_pkt_lladdr_if(pkt)->len;
1085 }
1086
1087 if (lladdr) {
1088 net_pkt_lladdr_dst(pkt)->addr = lladdr->addr;
1089 net_pkt_lladdr_dst(pkt)->type = lladdr->type;
1090 net_pkt_lladdr_dst(pkt)->len = lladdr->len;
1091 }
1092
1093 net_pkt_set_iface(pkt, nbr->iface);
1094
1095 net_ipv6_nbr_unlock();
1096
1097 return net_send_data(pkt);
1098
1099 error:
1100 net_ipv6_nbr_unlock();
1101 return err;
1102 }
1103
net_route_packet_if(struct net_pkt * pkt,struct net_if * iface)1104 int net_route_packet_if(struct net_pkt *pkt, struct net_if *iface)
1105 {
1106 /* The destination is reachable via iface. But since no valid nexthop
1107 * is known, net_pkt_lladdr_dst(pkt) cannot be set here.
1108 */
1109 net_pkt_set_orig_iface(pkt, net_pkt_iface(pkt));
1110 net_pkt_set_iface(pkt, iface);
1111
1112 net_pkt_set_forwarding(pkt, true);
1113
1114 /* Set source LL address if only if relevant */
1115 if (is_ll_addr_supported(iface)) {
1116 net_pkt_lladdr_src(pkt)->addr = net_pkt_lladdr_if(pkt)->addr;
1117 net_pkt_lladdr_src(pkt)->type = net_pkt_lladdr_if(pkt)->type;
1118 net_pkt_lladdr_src(pkt)->len = net_pkt_lladdr_if(pkt)->len;
1119 }
1120
1121 return net_send_data(pkt);
1122 }
1123
net_route_init(void)1124 void net_route_init(void)
1125 {
1126 NET_DBG("Allocated %d routing entries (%zu bytes)",
1127 CONFIG_NET_MAX_ROUTES, sizeof(net_route_entries_pool));
1128
1129 NET_DBG("Allocated %d nexthop entries (%zu bytes)",
1130 CONFIG_NET_MAX_NEXTHOPS, sizeof(net_route_nexthop_pool));
1131
1132 #if defined(CONFIG_NET_ROUTE_MCAST)
1133 memset(route_mcast_entries, 0, sizeof(route_mcast_entries));
1134 #endif
1135 k_work_init_delayable(&route_lifetime_timer, route_lifetime_timeout);
1136 }
1137