]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/route/route_ctl.c
routing: split nexthop creation and rtentry creation.
[FreeBSD/FreeBSD.git] / sys / net / route / route_ctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2020 Alexander V. Chernikov
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 #include "opt_inet.h"
31 #include "opt_inet6.h"
32 #include "opt_route.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39 #include <sys/sysctl.h>
40 #include <sys/syslog.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/rmlock.h>
44
45 #include <net/if.h>
46 #include <net/if_var.h>
47 #include <net/if_dl.h>
48 #include <net/vnet.h>
49 #include <net/route.h>
50 #include <net/route/route_ctl.h>
51 #include <net/route/route_var.h>
52 #include <net/route/nhop_utils.h>
53 #include <net/route/nhop.h>
54 #include <net/route/nhop_var.h>
55 #include <netinet/in.h>
56 #include <netinet6/scope6_var.h>
57
58 #include <vm/uma.h>
59
60 #define DEBUG_MOD_NAME  route_ctl
61 #define DEBUG_MAX_LEVEL LOG_DEBUG
62 #include <net/route/route_debug.h>
63 _DECLARE_DEBUG(LOG_INFO);
64
65 /*
66  * This file contains control plane routing tables functions.
67  *
68  * All functions assumes they are called in net epoch.
69  */
70
71 struct rib_subscription {
72         CK_STAILQ_ENTRY(rib_subscription)       next;
73         rib_subscription_cb_t                   *func;
74         void                                    *arg;
75         struct rib_head                         *rnh;
76         enum rib_subscription_type              type;
77         struct epoch_context                    epoch_ctx;
78 };
79
80 static int add_route_byinfo(struct rib_head *rnh, struct rt_addrinfo *info,
81     struct rib_cmd_info *rc);
82 static int change_route_byinfo(struct rib_head *rnh, struct rtentry *rt,
83     struct rt_addrinfo *info, struct route_nhop_data *nhd_orig,
84     struct rib_cmd_info *rc);
85
86 static int add_route(struct rib_head *rnh, struct rtentry *rt,
87     struct route_nhop_data *rnd, struct rib_cmd_info *rc);
88 static int delete_route(struct rib_head *rnh, struct rtentry *rt,
89     struct rib_cmd_info *rc);
90 static int rt_delete_conditional(struct rib_head *rnh, struct rtentry *rt,
91     int prio, rib_filter_f_t *cb, void *cbdata, struct rib_cmd_info *rc);
92
93 static void rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
94     struct rib_cmd_info *rc);
95
96 static int get_prio_from_info(const struct rt_addrinfo *info);
97 static int nhop_get_prio(const struct nhop_object *nh);
98
99 static void destroy_subscription_epoch(epoch_context_t ctx);
100 #ifdef ROUTE_MPATH
101 static bool rib_can_multipath(struct rib_head *rh);
102 #endif
103
104 /* Per-vnet multipath routing configuration */
105 SYSCTL_DECL(_net_route);
106 #define V_rib_route_multipath   VNET(rib_route_multipath)
107 #ifdef ROUTE_MPATH
108 #define _MP_FLAGS       CTLFLAG_RW
109 #else
110 #define _MP_FLAGS       CTLFLAG_RD
111 #endif
112 VNET_DEFINE(u_int, rib_route_multipath) = 1;
113 SYSCTL_UINT(_net_route, OID_AUTO, multipath, _MP_FLAGS | CTLFLAG_VNET,
114     &VNET_NAME(rib_route_multipath), 0, "Enable route multipath");
115 #undef _MP_FLAGS
116
117 #if defined(INET) && defined(INET6)
118 FEATURE(ipv4_rfc5549_support, "Route IPv4 packets via IPv6 nexthops");
119 #define V_rib_route_ipv6_nexthop VNET(rib_route_ipv6_nexthop)
120 VNET_DEFINE(u_int, rib_route_ipv6_nexthop) = 1;
121 SYSCTL_UINT(_net_route, OID_AUTO, ipv6_nexthop, CTLFLAG_RW | CTLFLAG_VNET,
122     &VNET_NAME(rib_route_ipv6_nexthop), 0, "Enable IPv4 route via IPv6 Next Hop address");
123 #endif
124
125 /* Routing table UMA zone */
126 VNET_DEFINE_STATIC(uma_zone_t, rtzone);
127 #define V_rtzone        VNET(rtzone)
128
129 /* Debug bits */
130 SYSCTL_NODE(_net_route, OID_AUTO, debug, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
131
132 void
133 vnet_rtzone_init(void)
134 {
135
136         V_rtzone = uma_zcreate("rtentry", sizeof(struct rtentry),
137                 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
138 }
139
140 #ifdef VIMAGE
141 void
142 vnet_rtzone_destroy(void)
143 {
144
145         uma_zdestroy(V_rtzone);
146 }
147 #endif
148
149 static void
150 destroy_rtentry(struct rtentry *rt)
151 {
152 #ifdef VIMAGE
153         struct nhop_object *nh = rt->rt_nhop;
154
155         /*
156          * At this moment rnh, nh_control may be already freed.
157          * nhop interface may have been migrated to a different vnet.
158          * Use vnet stored in the nexthop to delete the entry.
159          */
160 #ifdef ROUTE_MPATH
161         if (NH_IS_NHGRP(nh)) {
162                 const struct weightened_nhop *wn;
163                 uint32_t num_nhops;
164                 wn = nhgrp_get_nhops((struct nhgrp_object *)nh, &num_nhops);
165                 nh = wn[0].nh;
166         }
167 #endif
168         CURVNET_SET(nhop_get_vnet(nh));
169 #endif
170
171         /* Unreference nexthop */
172         nhop_free_any(rt->rt_nhop);
173
174         uma_zfree(V_rtzone, rt);
175
176         CURVNET_RESTORE();
177 }
178
179 /*
180  * Epoch callback indicating rtentry is safe to destroy
181  */
182 static void
183 destroy_rtentry_epoch(epoch_context_t ctx)
184 {
185         struct rtentry *rt;
186
187         rt = __containerof(ctx, struct rtentry, rt_epoch_ctx);
188
189         destroy_rtentry(rt);
190 }
191
192 /*
193  * Schedule rtentry deletion
194  */
195 static void
196 rtfree(struct rtentry *rt)
197 {
198
199         KASSERT(rt != NULL, ("%s: NULL rt", __func__));
200
201         epoch_call(net_epoch_preempt, destroy_rtentry_epoch,
202             &rt->rt_epoch_ctx);
203 }
204
205 static struct rib_head *
206 get_rnh(uint32_t fibnum, const struct rt_addrinfo *info)
207 {
208         struct rib_head *rnh;
209         struct sockaddr *dst;
210
211         KASSERT((fibnum < rt_numfibs), ("rib_add_route: bad fibnum"));
212
213         dst = info->rti_info[RTAX_DST];
214         rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
215
216         return (rnh);
217 }
218
219 #if defined(INET) && defined(INET6)
220 static bool
221 rib_can_ipv6_nexthop_address(struct rib_head *rh)
222 {
223         int result;
224
225         CURVNET_SET(rh->rib_vnet);
226         result = !!V_rib_route_ipv6_nexthop;
227         CURVNET_RESTORE();
228
229         return (result);
230 }
231 #endif
232
233 #ifdef ROUTE_MPATH
234 static bool
235 rib_can_multipath(struct rib_head *rh)
236 {
237         int result;
238
239         CURVNET_SET(rh->rib_vnet);
240         result = !!V_rib_route_multipath;
241         CURVNET_RESTORE();
242
243         return (result);
244 }
245
246 /*
247  * Check is nhop is multipath-eligible.
248  * Avoid nhops without gateways and redirects.
249  *
250  * Returns 1 for multipath-eligible nexthop,
251  * 0 otherwise.
252  */
253 bool
254 nhop_can_multipath(const struct nhop_object *nh)
255 {
256
257         if ((nh->nh_flags & NHF_MULTIPATH) != 0)
258                 return (1);
259         if ((nh->nh_flags & NHF_GATEWAY) == 0)
260                 return (0);
261         if ((nh->nh_flags & NHF_REDIRECT) != 0)
262                 return (0);
263
264         return (1);
265 }
266 #endif
267
268 static int
269 get_info_weight(const struct rt_addrinfo *info, uint32_t default_weight)
270 {
271         uint32_t weight;
272
273         if (info->rti_mflags & RTV_WEIGHT)
274                 weight = info->rti_rmx->rmx_weight;
275         else
276                 weight = default_weight;
277         /* Keep upper 1 byte for adm distance purposes */
278         if (weight > RT_MAX_WEIGHT)
279                 weight = RT_MAX_WEIGHT;
280         else if (weight == 0)
281                 weight = default_weight;
282
283         return (weight);
284 }
285
286 bool
287 rt_is_host(const struct rtentry *rt)
288 {
289
290         return (rt->rte_flags & RTF_HOST);
291 }
292
293 sa_family_t
294 rt_get_family(const struct rtentry *rt)
295 {
296         const struct sockaddr *dst;
297
298         dst = (const struct sockaddr *)rt_key_const(rt);
299
300         return (dst->sa_family);
301 }
302
303 /*
304  * Returns pointer to nexthop or nexthop group
305  * associated with @rt
306  */
307 struct nhop_object *
308 rt_get_raw_nhop(const struct rtentry *rt)
309 {
310
311         return (rt->rt_nhop);
312 }
313
314 #ifdef INET
315 /*
316  * Stores IPv4 address and prefix length of @rt inside
317  *  @paddr and @plen.
318  * @pscopeid is currently always set to 0.
319  */
320 void
321 rt_get_inet_prefix_plen(const struct rtentry *rt, struct in_addr *paddr,
322     int *plen, uint32_t *pscopeid)
323 {
324         const struct sockaddr_in *dst;
325
326         dst = (const struct sockaddr_in *)rt_key_const(rt);
327         KASSERT((dst->sin_family == AF_INET),
328             ("rt family is %d, not inet", dst->sin_family));
329         *paddr = dst->sin_addr;
330         dst = (const struct sockaddr_in *)rt_mask_const(rt);
331         if (dst == NULL)
332                 *plen = 32;
333         else
334                 *plen = bitcount32(dst->sin_addr.s_addr);
335         *pscopeid = 0;
336 }
337
338 /*
339  * Stores IPv4 address and prefix mask of @rt inside
340  *  @paddr and @pmask. Sets mask to INADDR_ANY for host routes.
341  * @pscopeid is currently always set to 0.
342  */
343 void
344 rt_get_inet_prefix_pmask(const struct rtentry *rt, struct in_addr *paddr,
345     struct in_addr *pmask, uint32_t *pscopeid)
346 {
347         const struct sockaddr_in *dst;
348
349         dst = (const struct sockaddr_in *)rt_key_const(rt);
350         KASSERT((dst->sin_family == AF_INET),
351             ("rt family is %d, not inet", dst->sin_family));
352         *paddr = dst->sin_addr;
353         dst = (const struct sockaddr_in *)rt_mask_const(rt);
354         if (dst == NULL)
355                 pmask->s_addr = INADDR_BROADCAST;
356         else
357                 *pmask = dst->sin_addr;
358         *pscopeid = 0;
359 }
360 #endif
361
362 #ifdef INET6
363 static int
364 inet6_get_plen(const struct in6_addr *addr)
365 {
366
367         return (bitcount32(addr->s6_addr32[0]) + bitcount32(addr->s6_addr32[1]) +
368             bitcount32(addr->s6_addr32[2]) + bitcount32(addr->s6_addr32[3]));
369 }
370
371 /*
372  * Stores IPv6 address and prefix length of @rt inside
373  *  @paddr and @plen. Addresses are returned in de-embedded form.
374  * Scopeid is set to 0 for non-LL addresses.
375  */
376 void
377 rt_get_inet6_prefix_plen(const struct rtentry *rt, struct in6_addr *paddr,
378     int *plen, uint32_t *pscopeid)
379 {
380         const struct sockaddr_in6 *dst;
381
382         dst = (const struct sockaddr_in6 *)rt_key_const(rt);
383         KASSERT((dst->sin6_family == AF_INET6),
384             ("rt family is %d, not inet6", dst->sin6_family));
385         if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr))
386                 in6_splitscope(&dst->sin6_addr, paddr, pscopeid);
387         else
388                 *paddr = dst->sin6_addr;
389         dst = (const struct sockaddr_in6 *)rt_mask_const(rt);
390         if (dst == NULL)
391                 *plen = 128;
392         else
393                 *plen = inet6_get_plen(&dst->sin6_addr);
394 }
395
396 /*
397  * Stores IPv6 address and prefix mask of @rt inside
398  *  @paddr and @pmask. Addresses are returned in de-embedded form.
399  * Scopeid is set to 0 for non-LL addresses.
400  */
401 void
402 rt_get_inet6_prefix_pmask(const struct rtentry *rt, struct in6_addr *paddr,
403     struct in6_addr *pmask, uint32_t *pscopeid)
404 {
405         const struct sockaddr_in6 *dst;
406
407         dst = (const struct sockaddr_in6 *)rt_key_const(rt);
408         KASSERT((dst->sin6_family == AF_INET6),
409             ("rt family is %d, not inet", dst->sin6_family));
410         if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr))
411                 in6_splitscope(&dst->sin6_addr, paddr, pscopeid);
412         else
413                 *paddr = dst->sin6_addr;
414         dst = (const struct sockaddr_in6 *)rt_mask_const(rt);
415         if (dst == NULL)
416                 memset(pmask, 0xFF, sizeof(struct in6_addr));
417         else
418                 *pmask = dst->sin6_addr;
419 }
420 #endif
421
422 /*
423  * Check if specified @gw matches gw data in the nexthop @nh.
424  *
425  * Returns true if matches, false otherwise.
426  */
427 bool
428 match_nhop_gw(const struct nhop_object *nh, const struct sockaddr *gw)
429 {
430
431         if (nh->gw_sa.sa_family != gw->sa_family)
432                 return (false);
433
434         switch (gw->sa_family) {
435         case AF_INET:
436                 return (nh->gw4_sa.sin_addr.s_addr ==
437                     ((const struct sockaddr_in *)gw)->sin_addr.s_addr);
438         case AF_INET6:
439                 {
440                         const struct sockaddr_in6 *gw6;
441                         gw6 = (const struct sockaddr_in6 *)gw;
442
443                         /*
444                          * Currently (2020-09) IPv6 gws in kernel have their
445                          * scope embedded. Once this becomes false, this code
446                          * has to be revisited.
447                          */
448                         if (IN6_ARE_ADDR_EQUAL(&nh->gw6_sa.sin6_addr,
449                             &gw6->sin6_addr))
450                                 return (true);
451                         return (false);
452                 }
453         case AF_LINK:
454                 {
455                         const struct sockaddr_dl *sdl;
456                         sdl = (const struct sockaddr_dl *)gw;
457                         return (nh->gwl_sa.sdl_index == sdl->sdl_index);
458                 }
459         default:
460                 return (memcmp(&nh->gw_sa, gw, nh->gw_sa.sa_len) == 0);
461         }
462
463         /* NOTREACHED */
464         return (false);
465 }
466
467 struct gw_filter_data {
468         const struct sockaddr *gw;
469         int count;
470 };
471
472 static int
473 gw_filter_func(const struct rtentry *rt, const struct nhop_object *nh, void *_data)
474 {
475         struct gw_filter_data *gwd = (struct gw_filter_data *)_data;
476
477         /* Return only first match to make rtsock happy */
478         if (match_nhop_gw(nh, gwd->gw) && gwd->count++ == 0)
479                 return (1);
480         return (0);
481 }
482
483 /*
484  * Checks if data in @info matches nexhop @nh.
485  *
486  * Returns 0 on success,
487  * ESRCH if not matched,
488  * ENOENT if filter function returned false
489  */
490 int
491 check_info_match_nhop(const struct rt_addrinfo *info, const struct rtentry *rt,
492     const struct nhop_object *nh)
493 {
494         const struct sockaddr *gw = info->rti_info[RTAX_GATEWAY];
495
496         if (info->rti_filter != NULL) {
497             if (info->rti_filter(rt, nh, info->rti_filterdata) == 0)
498                     return (ENOENT);
499             else
500                     return (0);
501         }
502         if ((gw != NULL) && !match_nhop_gw(nh, gw))
503                 return (ESRCH);
504
505         return (0);
506 }
507
508 /*
509  * Runs exact prefix match based on @dst and @netmask.
510  * Returns matched @rtentry if found or NULL.
511  * If rtentry was found, saves nexthop / weight value into @rnd.
512  */
513 static struct rtentry *
514 lookup_prefix_bysa(struct rib_head *rnh, const struct sockaddr *dst,
515     const struct sockaddr *netmask, struct route_nhop_data *rnd)
516 {
517         struct rtentry *rt;
518
519         RIB_LOCK_ASSERT(rnh);
520
521         rt = (struct rtentry *)rnh->rnh_lookup(dst, netmask, &rnh->head);
522         if (rt != NULL) {
523                 rnd->rnd_nhop = rt->rt_nhop;
524                 rnd->rnd_weight = rt->rt_weight;
525         } else {
526                 rnd->rnd_nhop = NULL;
527                 rnd->rnd_weight = 0;
528         }
529
530         return (rt);
531 }
532
533 struct rtentry *
534 lookup_prefix_rt(struct rib_head *rnh, const struct rtentry *rt,
535     struct route_nhop_data *rnd)
536 {
537         return (lookup_prefix_bysa(rnh, rt_key_const(rt), rt_mask_const(rt), rnd));
538 }
539
540 /*
541  * Runs exact prefix match based on dst/netmask from @info.
542  * Assumes RIB lock is held.
543  * Returns matched @rtentry if found or NULL.
544  * If rtentry was found, saves nexthop / weight value into @rnd.
545  */
546 struct rtentry *
547 lookup_prefix(struct rib_head *rnh, const struct rt_addrinfo *info,
548     struct route_nhop_data *rnd)
549 {
550         struct rtentry *rt;
551
552         rt = lookup_prefix_bysa(rnh, info->rti_info[RTAX_DST],
553             info->rti_info[RTAX_NETMASK], rnd);
554
555         return (rt);
556 }
557
558 /*
559  * Adds route defined by @info into the kernel table specified by @fibnum and
560  * sa_family in @info->rti_info[RTAX_DST].
561  *
562  * Returns 0 on success and fills in operation metadata into @rc.
563  */
564 int
565 rib_add_route(uint32_t fibnum, struct rt_addrinfo *info,
566     struct rib_cmd_info *rc)
567 {
568         struct rib_head *rnh;
569         int error;
570
571         NET_EPOCH_ASSERT();
572
573         rnh = get_rnh(fibnum, info);
574         if (rnh == NULL)
575                 return (EAFNOSUPPORT);
576
577         /*
578          * Check consistency between RTF_HOST flag and netmask
579          * existence.
580          */
581         if (info->rti_flags & RTF_HOST)
582                 info->rti_info[RTAX_NETMASK] = NULL;
583         else if (info->rti_info[RTAX_NETMASK] == NULL) {
584                 FIB_RH_LOG(LOG_DEBUG, rnh, "error: no RTF_HOST and empty netmask");
585                 return (EINVAL);
586         }
587
588         bzero(rc, sizeof(struct rib_cmd_info));
589         rc->rc_cmd = RTM_ADD;
590
591         error = add_route_byinfo(rnh, info, rc);
592         if (error == 0)
593                 rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
594
595         return (error);
596 }
597
598 /*
599  * Checks if @dst and @gateway is valid combination.
600  *
601  * Returns true if is valid, false otherwise.
602  */
603 static bool
604 check_gateway(struct rib_head *rnh, struct sockaddr *dst,
605     struct sockaddr *gateway)
606 {
607         if (dst->sa_family == gateway->sa_family)
608                 return (true);
609         else if (gateway->sa_family == AF_UNSPEC)
610                 return (true);
611         else if (gateway->sa_family == AF_LINK)
612                 return (true);
613 #if defined(INET) && defined(INET6)
614         else if (dst->sa_family == AF_INET && gateway->sa_family == AF_INET6 &&
615                 rib_can_ipv6_nexthop_address(rnh))
616                 return (true);
617 #endif
618         else
619                 return (false);
620 }
621
622 /*
623  * Creates rtentry and nexthop based on @info data.
624  * Return 0 and fills in rtentry into @prt on success,
625  * Note: rtentry mask will be set to RTAX_NETMASK, thus its pointer is required
626  *  to be stable till the end of the operation (radix rt insertion/change/removal).
627  * return errno otherwise.
628  */
629 static struct rtentry *
630 create_rtentry(struct rib_head *rnh, const struct sockaddr *dst,
631     struct sockaddr *netmask)
632 {
633         MPASS(dst->sa_len <= sizeof(((struct rtentry *)NULL)->rt_dstb));
634
635         struct rtentry *rt = uma_zalloc(V_rtzone, M_NOWAIT | M_ZERO);
636         if (rt == NULL)
637                 return (NULL);
638         rt->rte_flags = RTF_UP | (netmask == NULL ? RTF_HOST : 0);
639
640         /* Fill in dst, ensuring it's masked if needed. */
641         if (netmask != NULL) {
642                 rt_maskedcopy(dst, &rt->rt_dst, netmask);
643         } else
644                 bcopy(dst, &rt->rt_dst, dst->sa_len);
645         rt_key(rt) = &rt->rt_dst;
646         /* Set netmask to the storage from info. It will be updated upon insertion */
647         rt_mask(rt) = netmask;
648
649         return (rt);
650 }
651
652 static int
653 add_route_byinfo(struct rib_head *rnh, struct rt_addrinfo *info,
654     struct rib_cmd_info *rc)
655 {
656         struct nhop_object *nh_orig;
657         struct route_nhop_data rnd_orig, rnd_add;
658         struct nhop_object *nh;
659         struct rtentry *rt, *rt_orig;
660         struct sockaddr *dst, *gateway, *netmask;
661         int error, flags;
662
663         dst = info->rti_info[RTAX_DST];
664         gateway = info->rti_info[RTAX_GATEWAY];
665         netmask = info->rti_info[RTAX_NETMASK];
666         flags = info->rti_flags;
667
668         if ((flags & RTF_GATEWAY) && !gateway) {
669                 FIB_RH_LOG(LOG_DEBUG, rnh, "error: RTF_GATEWAY set with empty gw");
670                 return (EINVAL);
671         }
672         if (dst && gateway && !check_gateway(rnh, dst, gateway)) {
673                 FIB_RH_LOG(LOG_DEBUG, rnh,
674                     "error: invalid dst/gateway family combination (%d, %d)",
675                     dst->sa_family, gateway->sa_family);
676                 return (EINVAL);
677         }
678
679         if (dst->sa_len > sizeof(((struct rtentry *)NULL)->rt_dstb)) {
680                 FIB_RH_LOG(LOG_DEBUG, rnh, "error: dst->sa_len too large: %d",
681                     dst->sa_len);
682                 return (EINVAL);
683         }
684
685         if (info->rti_ifa == NULL) {
686                 error = rt_getifa_fib(info, rnh->rib_fibnum);
687                 if (error)
688                         return (error);
689         }
690
691         if ((rt = create_rtentry(rnh, dst, netmask)) == NULL)
692                 return (ENOBUFS);
693
694         error = nhop_create_from_info(rnh, info, &nh);
695         if (error != 0) {
696                 uma_zfree(V_rtzone, rt);
697                 return (error);
698         }
699
700         rnd_add.rnd_nhop = nh;
701         rnd_add.rnd_weight = get_info_weight(info, RT_DEFAULT_WEIGHT);
702
703         RIB_WLOCK(rnh);
704         error = add_route(rnh, rt, &rnd_add, rc);
705         if (error == 0) {
706                 RIB_WUNLOCK(rnh);
707                 return (0);
708         }
709
710         /* addition failed. Lookup prefix in the rib to determine the cause */
711         rt_orig = lookup_prefix(rnh, info, &rnd_orig);
712         if (rt_orig == NULL) {
713                 /* No prefix -> rnh_addaddr() failed to allocate memory */
714                 RIB_WUNLOCK(rnh);
715                 nhop_free(nh);
716                 uma_zfree(V_rtzone, rt);
717                 return (ENOMEM);
718         }
719
720         /* We have existing route in the RIB. */
721         nh_orig = rnd_orig.rnd_nhop;
722         /* Check if new route has higher preference */
723         if (get_prio_from_info(info) > nhop_get_prio(nh_orig)) {
724                 /* Update nexthop to the new route */
725                 change_route(rnh, rt_orig, &rnd_add, rc);
726                 RIB_WUNLOCK(rnh);
727                 uma_zfree(V_rtzone, rt);
728                 nhop_free(nh_orig);
729                 return (0);
730         }
731
732         RIB_WUNLOCK(rnh);
733
734 #ifdef ROUTE_MPATH
735         if (rib_can_multipath(rnh) && nhop_can_multipath(rnd_add.rnd_nhop) &&
736             nhop_can_multipath(rnd_orig.rnd_nhop))
737                 error = add_route_mpath(rnh, info, rt, &rnd_add, &rnd_orig, rc);
738         else
739 #endif
740         /* Unable to add - another route with the same preference exists */
741         error = EEXIST;
742
743         /*
744          * ROUTE_MPATH disabled: failed to add route, free both nhop and rt.
745          * ROUTE_MPATH enabled: original nhop reference is unused in any case,
746          *  free rt only if not _adding_ new route to rib (e.g. the case
747          *  when initial lookup returned existing route, but then it got
748          *  deleted prior to multipath group insertion, leading to a simple
749          *  non-multipath add as a result).
750          */
751         nhop_free(nh);
752         if ((error != 0) || rc->rc_cmd != RTM_ADD)
753                 uma_zfree(V_rtzone, rt);
754
755         return (error);
756 }
757
758 /*
759  * Removes route defined by @info from the kernel table specified by @fibnum and
760  * sa_family in @info->rti_info[RTAX_DST].
761  *
762  * Returns 0 on success and fills in operation metadata into @rc.
763  */
764 int
765 rib_del_route(uint32_t fibnum, struct rt_addrinfo *info, struct rib_cmd_info *rc)
766 {
767         struct rib_head *rnh;
768         struct sockaddr *dst, *netmask;
769         struct sockaddr_storage mdst;
770         int error;
771
772         NET_EPOCH_ASSERT();
773
774         rnh = get_rnh(fibnum, info);
775         if (rnh == NULL)
776                 return (EAFNOSUPPORT);
777
778         bzero(rc, sizeof(struct rib_cmd_info));
779         rc->rc_cmd = RTM_DELETE;
780
781         dst = info->rti_info[RTAX_DST];
782         netmask = info->rti_info[RTAX_NETMASK];
783
784         if (netmask != NULL) {
785                 /* Ensure @dst is always properly masked */
786                 if (dst->sa_len > sizeof(mdst)) {
787                         FIB_RH_LOG(LOG_DEBUG, rnh, "error: dst->sa_len too large");
788                         return (EINVAL);
789                 }
790                 rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask);
791                 dst = (struct sockaddr *)&mdst;
792         }
793
794         rib_filter_f_t *filter_func = NULL;
795         void *filter_arg = NULL;
796         struct gw_filter_data gwd = { .gw = info->rti_info[RTAX_GATEWAY] };
797
798         if (info->rti_filter != NULL) {
799                 filter_func = info->rti_filter;
800                 filter_arg = info->rti_filterdata;
801         } else if (gwd.gw != NULL) {
802                 filter_func = gw_filter_func;
803                 filter_arg = &gwd;
804         }
805
806         int prio = get_prio_from_info(info);
807
808         RIB_WLOCK(rnh);
809         struct route_nhop_data rnd;
810         struct rtentry *rt = lookup_prefix_bysa(rnh, dst, netmask, &rnd);
811         if (rt != NULL) {
812                 error = rt_delete_conditional(rnh, rt, prio, filter_func,
813                     filter_arg, rc);
814         } else
815                 error = ESRCH;
816         RIB_WUNLOCK(rnh);
817
818         if (error != 0)
819                 return (error);
820
821         rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
822
823         if (rc->rc_cmd == RTM_DELETE)
824                 rtfree(rc->rc_rt);
825 #ifdef ROUTE_MPATH
826         else {
827                 /*
828                  * Deleting 1 path may result in RTM_CHANGE to
829                  * a different mpath group/nhop.
830                  * Free old mpath group.
831                  */
832                 nhop_free_any(rc->rc_nh_old);
833         }
834 #endif
835
836         return (0);
837 }
838
839 /*
840  * File-local concept for distingushing between the normal and
841  * RTF_PINNED routes tha can override the "normal" one.
842  */
843 #define NH_PRIORITY_HIGH        2
844 #define NH_PRIORITY_NORMAL      1
845 static int
846 get_prio_from_info(const struct rt_addrinfo *info)
847 {
848         if (info->rti_flags & RTF_PINNED)
849                 return (NH_PRIORITY_HIGH);
850         return (NH_PRIORITY_NORMAL);
851 }
852
853 static int
854 nhop_get_prio(const struct nhop_object *nh)
855 {
856         if (NH_IS_PINNED(nh))
857                 return (NH_PRIORITY_HIGH);
858         return (NH_PRIORITY_NORMAL);
859 }
860
861 /*
862  * Conditionally unlinks rtentry paths from @rnh matching @cb.
863  * Returns 0 on success with operation result stored in @rc.
864  * On error, returns:
865  * ESRCH - if prefix was not found or filter function failed to match
866  * EADDRINUSE - if trying to delete higher priority route.
867  */
868 static int
869 rt_delete_conditional(struct rib_head *rnh, struct rtentry *rt,
870     int prio, rib_filter_f_t *cb, void *cbdata, struct rib_cmd_info *rc)
871 {
872         struct nhop_object *nh = rt->rt_nhop;
873         struct route_nhop_data rnd;
874
875 #ifdef ROUTE_MPATH
876         if (NH_IS_NHGRP(nh)) {
877                 struct nhgrp_object *nhg = (struct nhgrp_object *)nh;
878                 int error;
879
880                 if (cb == NULL)
881                         return (ESRCH);
882                 error = nhgrp_get_filtered_group(rnh, rt, nhg, cb, cbdata, &rnd);
883                 if (error == 0) {
884                         if (rnd.rnd_nhgrp == nhg) {
885                                 /* No match, unreference new group and return. */
886                                 nhop_free_any(rnd.rnd_nhop);
887                                 return (ESRCH);
888                         }
889                         error = change_route(rnh, rt, &rnd, rc);
890                 }
891                 return (error);
892         }
893 #endif
894         if (cb != NULL && !cb(rt, nh, cbdata))
895                 return (ESRCH);
896
897         if (prio < nhop_get_prio(nh))
898                 return (EADDRINUSE);
899
900         return (delete_route(rnh, rt, rc));
901 }
902
903 int
904 rib_change_route(uint32_t fibnum, struct rt_addrinfo *info,
905     struct rib_cmd_info *rc)
906 {
907         RIB_RLOCK_TRACKER;
908         struct route_nhop_data rnd_orig;
909         struct rib_head *rnh;
910         struct rtentry *rt;
911         int error;
912
913         NET_EPOCH_ASSERT();
914
915         rnh = get_rnh(fibnum, info);
916         if (rnh == NULL)
917                 return (EAFNOSUPPORT);
918
919         bzero(rc, sizeof(struct rib_cmd_info));
920         rc->rc_cmd = RTM_CHANGE;
921
922         /* Check if updated gateway exists */
923         if ((info->rti_flags & RTF_GATEWAY) &&
924             (info->rti_info[RTAX_GATEWAY] == NULL)) {
925
926                 /*
927                  * route(8) adds RTF_GATEWAY flag if -interface is not set.
928                  * Remove RTF_GATEWAY to enforce consistency and maintain
929                  * compatibility..
930                  */
931                 info->rti_flags &= ~RTF_GATEWAY;
932         }
933
934         /*
935          * route change is done in multiple steps, with dropping and
936          * reacquiring lock. In the situations with multiple processes
937          * changes the same route in can lead to the case when route
938          * is changed between the steps. Address it by retrying the operation
939          * multiple times before failing.
940          */
941
942         RIB_RLOCK(rnh);
943         rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
944             info->rti_info[RTAX_NETMASK], &rnh->head);
945
946         if (rt == NULL) {
947                 RIB_RUNLOCK(rnh);
948                 return (ESRCH);
949         }
950
951         rnd_orig.rnd_nhop = rt->rt_nhop;
952         rnd_orig.rnd_weight = rt->rt_weight;
953
954         RIB_RUNLOCK(rnh);
955
956         for (int i = 0; i < RIB_MAX_RETRIES; i++) {
957                 error = change_route_byinfo(rnh, rt, info, &rnd_orig, rc);
958                 if (error != EAGAIN)
959                         break;
960         }
961
962         return (error);
963 }
964
965 static int
966 change_nhop(struct rib_head *rnh, struct rt_addrinfo *info,
967     struct nhop_object *nh_orig, struct nhop_object **nh_new)
968 {
969         int error;
970
971         /*
972          * New gateway could require new ifaddr, ifp;
973          * flags may also be different; ifp may be specified
974          * by ll sockaddr when protocol address is ambiguous
975          */
976         if (((nh_orig->nh_flags & NHF_GATEWAY) &&
977             info->rti_info[RTAX_GATEWAY] != NULL) ||
978             info->rti_info[RTAX_IFP] != NULL ||
979             (info->rti_info[RTAX_IFA] != NULL &&
980              !sa_equal(info->rti_info[RTAX_IFA], nh_orig->nh_ifa->ifa_addr))) {
981                 error = rt_getifa_fib(info, rnh->rib_fibnum);
982
983                 if (error != 0) {
984                         info->rti_ifa = NULL;
985                         return (error);
986                 }
987         }
988
989         error = nhop_create_from_nhop(rnh, nh_orig, info, nh_new);
990         info->rti_ifa = NULL;
991
992         return (error);
993 }
994
995 #ifdef ROUTE_MPATH
996 static int
997 change_mpath_route(struct rib_head *rnh, struct rtentry *rt,
998     struct rt_addrinfo *info, struct route_nhop_data *rnd_orig,
999     struct rib_cmd_info *rc)
1000 {
1001         int error = 0, found_idx = 0;
1002         struct nhop_object *nh_orig = NULL, *nh_new;
1003         struct route_nhop_data rnd_new = {};
1004         const struct weightened_nhop *wn = NULL;
1005         struct weightened_nhop *wn_new;
1006         uint32_t num_nhops;
1007
1008         wn = nhgrp_get_nhops(rnd_orig->rnd_nhgrp, &num_nhops);
1009         for (int i = 0; i < num_nhops; i++) {
1010                 if (check_info_match_nhop(info, NULL, wn[i].nh) == 0) {
1011                         nh_orig = wn[i].nh;
1012                         found_idx = i;
1013                         break;
1014                 }
1015         }
1016
1017         if (nh_orig == NULL)
1018                 return (ESRCH);
1019
1020         error = change_nhop(rnh, info, nh_orig, &nh_new);
1021         if (error != 0)
1022                 return (error);
1023
1024         wn_new = mallocarray(num_nhops, sizeof(struct weightened_nhop),
1025             M_TEMP, M_NOWAIT | M_ZERO);
1026         if (wn_new == NULL) {
1027                 nhop_free(nh_new);
1028                 return (EAGAIN);
1029         }
1030
1031         memcpy(wn_new, wn, num_nhops * sizeof(struct weightened_nhop));
1032         wn_new[found_idx].nh = nh_new;
1033         wn_new[found_idx].weight = get_info_weight(info, wn[found_idx].weight);
1034
1035         error = nhgrp_get_group(rnh, wn_new, num_nhops, &rnd_new.rnd_nhgrp);
1036         nhop_free(nh_new);
1037         free(wn_new, M_TEMP);
1038
1039         if (error != 0)
1040                 return (error);
1041
1042         error = change_route_conditional(rnh, rt, rnd_orig, &rnd_new, rc);
1043
1044         return (error);
1045 }
1046 #endif
1047
1048 static int
1049 change_route_byinfo(struct rib_head *rnh, struct rtentry *rt,
1050     struct rt_addrinfo *info, struct route_nhop_data *rnd_orig,
1051     struct rib_cmd_info *rc)
1052 {
1053         int error = 0;
1054         struct nhop_object *nh_orig;
1055         struct route_nhop_data rnd_new;
1056
1057         nh_orig = rnd_orig->rnd_nhop;
1058         if (nh_orig == NULL)
1059                 return (ESRCH);
1060
1061 #ifdef ROUTE_MPATH
1062         if (NH_IS_NHGRP(nh_orig))
1063                 return (change_mpath_route(rnh, rt, info, rnd_orig, rc));
1064 #endif
1065
1066         rnd_new.rnd_weight = get_info_weight(info, rnd_orig->rnd_weight);
1067         error = change_nhop(rnh, info, nh_orig, &rnd_new.rnd_nhop);
1068         if (error != 0)
1069                 return (error);
1070         error = change_route_conditional(rnh, rt, rnd_orig, &rnd_new, rc);
1071
1072         return (error);
1073 }
1074
1075 /*
1076  * Insert @rt with nhop data from @rnd_new to @rnh.
1077  * Returns 0 on success and stores operation results in @rc.
1078  */
1079 static int
1080 add_route(struct rib_head *rnh, struct rtentry *rt,
1081     struct route_nhop_data *rnd, struct rib_cmd_info *rc)
1082 {
1083         struct radix_node *rn;
1084
1085         RIB_WLOCK_ASSERT(rnh);
1086
1087         rt->rt_nhop = rnd->rnd_nhop;
1088         rt->rt_weight = rnd->rnd_weight;
1089         rn = rnh->rnh_addaddr(rt_key(rt), rt_mask_const(rt), &rnh->head, rt->rt_nodes);
1090
1091         if (rn != NULL) {
1092                 if (!NH_IS_NHGRP(rnd->rnd_nhop) && nhop_get_expire(rnd->rnd_nhop))
1093                         tmproutes_update(rnh, rt, rnd->rnd_nhop);
1094
1095                 /* Finalize notification */
1096                 rib_bump_gen(rnh);
1097                 rnh->rnh_prefixes++;
1098
1099                 rc->rc_cmd = RTM_ADD;
1100                 rc->rc_rt = rt;
1101                 rc->rc_nh_old = NULL;
1102                 rc->rc_nh_new = rnd->rnd_nhop;
1103                 rc->rc_nh_weight = rnd->rnd_weight;
1104
1105                 rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
1106                 return (0);
1107         }
1108
1109         /* Existing route or memory allocation failure. */
1110         return (EEXIST);
1111 }
1112
1113 /*
1114  * Unconditionally deletes @rt from @rnh.
1115  */
1116 static int
1117 delete_route(struct rib_head *rnh, struct rtentry *rt, struct rib_cmd_info *rc)
1118 {
1119         RIB_WLOCK_ASSERT(rnh);
1120
1121         /* Route deletion requested. */
1122         struct radix_node *rn;
1123
1124         rn = rnh->rnh_deladdr(rt_key_const(rt), rt_mask_const(rt), &rnh->head);
1125         if (rn == NULL)
1126                 return (ESRCH);
1127         rt = RNTORT(rn);
1128         rt->rte_flags &= ~RTF_UP;
1129
1130         rib_bump_gen(rnh);
1131         rnh->rnh_prefixes--;
1132
1133         rc->rc_cmd = RTM_DELETE;
1134         rc->rc_rt = rt;
1135         rc->rc_nh_old = rt->rt_nhop;
1136         rc->rc_nh_new = NULL;
1137         rc->rc_nh_weight = rt->rt_weight;
1138
1139         rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
1140
1141         return (0);
1142 }
1143
1144 /*
1145  * Switch @rt nhop/weigh to the ones specified in @rnd.
1146  * Returns 0 on success.
1147  */
1148 int
1149 change_route(struct rib_head *rnh, struct rtentry *rt,
1150     struct route_nhop_data *rnd, struct rib_cmd_info *rc)
1151 {
1152         struct nhop_object *nh_orig;
1153
1154         RIB_WLOCK_ASSERT(rnh);
1155
1156         nh_orig = rt->rt_nhop;
1157
1158         if (rnd->rnd_nhop == NULL)
1159                 return (delete_route(rnh, rt, rc));
1160
1161         /* Changing nexthop & weight to a new one */
1162         rt->rt_nhop = rnd->rnd_nhop;
1163         rt->rt_weight = rnd->rnd_weight;
1164         if (!NH_IS_NHGRP(rnd->rnd_nhop) && nhop_get_expire(rnd->rnd_nhop))
1165                 tmproutes_update(rnh, rt, rnd->rnd_nhop);
1166
1167         /* Finalize notification */
1168         rib_bump_gen(rnh);
1169         rc->rc_cmd = RTM_CHANGE;
1170         rc->rc_rt = rt;
1171         rc->rc_nh_old = nh_orig;
1172         rc->rc_nh_new = rnd->rnd_nhop;
1173         rc->rc_nh_weight = rnd->rnd_weight;
1174
1175         rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
1176
1177         return (0);
1178 }
1179
1180 /*
1181  * Conditionally update route nhop/weight IFF data in @nhd_orig is
1182  *  consistent with the current route data.
1183  * Nexthop in @nhd_new is consumed.
1184  */
1185 int
1186 change_route_conditional(struct rib_head *rnh, struct rtentry *rt,
1187     struct route_nhop_data *rnd_orig, struct route_nhop_data *rnd_new,
1188     struct rib_cmd_info *rc)
1189 {
1190         struct rtentry *rt_new;
1191         int error = 0;
1192
1193 #if DEBUG_MAX_LEVEL >= LOG_DEBUG2
1194         {
1195                 char buf_old[NHOP_PRINT_BUFSIZE], buf_new[NHOP_PRINT_BUFSIZE];
1196                 nhop_print_buf_any(rnd_orig->rnd_nhop, buf_old, NHOP_PRINT_BUFSIZE);
1197                 nhop_print_buf_any(rnd_new->rnd_nhop, buf_new, NHOP_PRINT_BUFSIZE);
1198                 FIB_LOG(LOG_DEBUG2, rnh->rib_fibnum, rnh->rib_family,
1199                     "trying change %s -> %s", buf_old, buf_new);
1200         }
1201 #endif
1202         RIB_WLOCK(rnh);
1203
1204         struct route_nhop_data rnd;
1205         rt_new = lookup_prefix_rt(rnh, rt, &rnd);
1206
1207         if (rt_new == NULL) {
1208                 if (rnd_orig->rnd_nhop == NULL)
1209                         error = add_route(rnh, rt, rnd_new, rc);
1210                 else {
1211                         /*
1212                          * Prefix does not exist, which was not our assumption.
1213                          * Update @rnd_orig with the new data and return
1214                          */
1215                         rnd_orig->rnd_nhop = NULL;
1216                         rnd_orig->rnd_weight = 0;
1217                         error = EAGAIN;
1218                 }
1219         } else {
1220                 /* Prefix exists, try to update */
1221                 if (rnd_orig->rnd_nhop == rt_new->rt_nhop) {
1222                         /*
1223                          * Nhop/mpath group hasn't changed. Flip
1224                          * to the new precalculated one and return
1225                          */
1226                         error = change_route(rnh, rt_new, rnd_new, rc);
1227                 } else {
1228                         /* Update and retry */
1229                         rnd_orig->rnd_nhop = rt_new->rt_nhop;
1230                         rnd_orig->rnd_weight = rt_new->rt_weight;
1231                         error = EAGAIN;
1232                 }
1233         }
1234
1235         RIB_WUNLOCK(rnh);
1236
1237         if (error == 0) {
1238                 rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
1239
1240                 if (rnd_orig->rnd_nhop != NULL)
1241                         nhop_free_any(rnd_orig->rnd_nhop);
1242
1243         } else {
1244                 if (rnd_new->rnd_nhop != NULL)
1245                         nhop_free_any(rnd_new->rnd_nhop);
1246         }
1247
1248         return (error);
1249 }
1250
1251 /*
1252  * Performs modification of routing table specificed by @action.
1253  * Table is specified by @fibnum and sa_family in @info->rti_info[RTAX_DST].
1254  * Needs to be run in network epoch.
1255  *
1256  * Returns 0 on success and fills in @rc with action result.
1257  */
1258 int
1259 rib_action(uint32_t fibnum, int action, struct rt_addrinfo *info,
1260     struct rib_cmd_info *rc)
1261 {
1262         int error;
1263
1264         switch (action) {
1265         case RTM_ADD:
1266                 error = rib_add_route(fibnum, info, rc);
1267                 break;
1268         case RTM_DELETE:
1269                 error = rib_del_route(fibnum, info, rc);
1270                 break;
1271         case RTM_CHANGE:
1272                 error = rib_change_route(fibnum, info, rc);
1273                 break;
1274         default:
1275                 error = ENOTSUP;
1276         }
1277
1278         return (error);
1279 }
1280
1281 struct rt_delinfo
1282 {
1283         struct rib_head *rnh;
1284         struct rtentry *head;
1285         rib_filter_f_t *filter_f;
1286         void *filter_arg;
1287         int prio;
1288         struct rib_cmd_info rc;
1289 };
1290
1291 /*
1292  * Conditionally unlinks rtenties or paths from radix tree based
1293  * on the callback data passed in @arg.
1294  */
1295 static int
1296 rt_checkdelroute(struct radix_node *rn, void *arg)
1297 {
1298         struct rt_delinfo *di = (struct rt_delinfo *)arg;
1299         struct rtentry *rt = (struct rtentry *)rn;
1300
1301         if (rt_delete_conditional(di->rnh, rt, di->prio,
1302             di->filter_f, di->filter_arg, &di->rc) != 0)
1303                 return (0);
1304
1305         /*
1306          * Add deleted rtentries to the list to GC them
1307          *  after dropping the lock.
1308          *
1309          * XXX: Delayed notifications not implemented
1310          *  for nexthop updates.
1311          */
1312         if (di->rc.rc_cmd == RTM_DELETE) {
1313                 /* Add to the list and return */
1314                 rt->rt_chain = di->head;
1315                 di->head = rt;
1316 #ifdef ROUTE_MPATH
1317         } else {
1318                 /*
1319                  * RTM_CHANGE to a different nexthop or nexthop group.
1320                  * Free old multipath group.
1321                  */
1322                 nhop_free_any(di->rc.rc_nh_old);
1323 #endif
1324         }
1325
1326         return (0);
1327 }
1328
1329 /*
1330  * Iterates over a routing table specified by @fibnum and @family and
1331  *  deletes elements marked by @filter_f.
1332  * @fibnum: rtable id
1333  * @family: AF_ address family
1334  * @filter_f: function returning non-zero value for items to delete
1335  * @arg: data to pass to the @filter_f function
1336  * @report: true if rtsock notification is needed.
1337  */
1338 void
1339 rib_walk_del(u_int fibnum, int family, rib_filter_f_t *filter_f, void *filter_arg,
1340     bool report)
1341 {
1342         struct rib_head *rnh;
1343         struct rtentry *rt;
1344         struct nhop_object *nh;
1345         struct epoch_tracker et;
1346
1347         rnh = rt_tables_get_rnh(fibnum, family);
1348         if (rnh == NULL)
1349                 return;
1350
1351         struct rt_delinfo di = {
1352                 .rnh = rnh,
1353                 .filter_f = filter_f,
1354                 .filter_arg = filter_arg,
1355                 .prio = NH_PRIORITY_NORMAL,
1356         };
1357
1358         NET_EPOCH_ENTER(et);
1359
1360         RIB_WLOCK(rnh);
1361         rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di);
1362         RIB_WUNLOCK(rnh);
1363
1364         /* We might have something to reclaim. */
1365         bzero(&di.rc, sizeof(di.rc));
1366         di.rc.rc_cmd = RTM_DELETE;
1367         while (di.head != NULL) {
1368                 rt = di.head;
1369                 di.head = rt->rt_chain;
1370                 rt->rt_chain = NULL;
1371                 nh = rt->rt_nhop;
1372
1373                 di.rc.rc_rt = rt;
1374                 di.rc.rc_nh_old = nh;
1375                 rib_notify(rnh, RIB_NOTIFY_DELAYED, &di.rc);
1376
1377                 if (report) {
1378 #ifdef ROUTE_MPATH
1379                         struct nhgrp_object *nhg;
1380                         const struct weightened_nhop *wn;
1381                         uint32_t num_nhops;
1382                         if (NH_IS_NHGRP(nh)) {
1383                                 nhg = (struct nhgrp_object *)nh;
1384                                 wn = nhgrp_get_nhops(nhg, &num_nhops);
1385                                 for (int i = 0; i < num_nhops; i++)
1386                                         rt_routemsg(RTM_DELETE, rt, wn[i].nh, fibnum);
1387                         } else
1388 #endif
1389                         rt_routemsg(RTM_DELETE, rt, nh, fibnum);
1390                 }
1391                 rtfree(rt);
1392         }
1393
1394         NET_EPOCH_EXIT(et);
1395 }
1396
1397 static int
1398 rt_delete_unconditional(struct radix_node *rn, void *arg)
1399 {
1400         struct rtentry *rt = RNTORT(rn);
1401         struct rib_head *rnh = (struct rib_head *)arg;
1402
1403         rn = rnh->rnh_deladdr(rt_key(rt), rt_mask(rt), &rnh->head);
1404         if (RNTORT(rn) == rt)
1405                 rtfree(rt);
1406
1407         return (0);
1408 }
1409
1410 /*
1411  * Removes all routes from the routing table without executing notifications.
1412  * rtentres will be removed after the end of a current epoch.
1413  */
1414 static void
1415 rib_flush_routes(struct rib_head *rnh)
1416 {
1417         RIB_WLOCK(rnh);
1418         rnh->rnh_walktree(&rnh->head, rt_delete_unconditional, rnh);
1419         RIB_WUNLOCK(rnh);
1420 }
1421
1422 void
1423 rib_flush_routes_family(int family)
1424 {
1425         struct rib_head *rnh;
1426
1427         for (uint32_t fibnum = 0; fibnum < rt_numfibs; fibnum++) {
1428                 if ((rnh = rt_tables_get_rnh(fibnum, family)) != NULL)
1429                         rib_flush_routes(rnh);
1430         }
1431 }
1432
1433 const char *
1434 rib_print_family(int family)
1435 {
1436         switch (family) {
1437         case AF_INET:
1438                 return ("inet");
1439         case AF_INET6:
1440                 return ("inet6");
1441         case AF_LINK:
1442                 return ("link");
1443         }
1444         return ("unknown");
1445 }
1446
1447 static void
1448 rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
1449     struct rib_cmd_info *rc)
1450 {
1451         struct rib_subscription *rs;
1452
1453         CK_STAILQ_FOREACH(rs, &rnh->rnh_subscribers, next) {
1454                 if (rs->type == type)
1455                         rs->func(rnh, rc, rs->arg);
1456         }
1457 }
1458
1459 static struct rib_subscription *
1460 allocate_subscription(rib_subscription_cb_t *f, void *arg,
1461     enum rib_subscription_type type, bool waitok)
1462 {
1463         struct rib_subscription *rs;
1464         int flags = M_ZERO | (waitok ? M_WAITOK : M_NOWAIT);
1465
1466         rs = malloc(sizeof(struct rib_subscription), M_RTABLE, flags);
1467         if (rs == NULL)
1468                 return (NULL);
1469
1470         rs->func = f;
1471         rs->arg = arg;
1472         rs->type = type;
1473
1474         return (rs);
1475 }
1476
1477 /*
1478  * Subscribe for the changes in the routing table specified by @fibnum and
1479  *  @family.
1480  *
1481  * Returns pointer to the subscription structure on success.
1482  */
1483 struct rib_subscription *
1484 rib_subscribe(uint32_t fibnum, int family, rib_subscription_cb_t *f, void *arg,
1485     enum rib_subscription_type type, bool waitok)
1486 {
1487         struct rib_head *rnh;
1488         struct epoch_tracker et;
1489
1490         NET_EPOCH_ENTER(et);
1491         KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
1492         rnh = rt_tables_get_rnh(fibnum, family);
1493         NET_EPOCH_EXIT(et);
1494
1495         return (rib_subscribe_internal(rnh, f, arg, type, waitok));
1496 }
1497
1498 struct rib_subscription *
1499 rib_subscribe_internal(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
1500     enum rib_subscription_type type, bool waitok)
1501 {
1502         struct rib_subscription *rs;
1503         struct epoch_tracker et;
1504
1505         if ((rs = allocate_subscription(f, arg, type, waitok)) == NULL)
1506                 return (NULL);
1507         rs->rnh = rnh;
1508
1509         NET_EPOCH_ENTER(et);
1510         RIB_WLOCK(rnh);
1511         CK_STAILQ_INSERT_HEAD(&rnh->rnh_subscribers, rs, next);
1512         RIB_WUNLOCK(rnh);
1513         NET_EPOCH_EXIT(et);
1514
1515         return (rs);
1516 }
1517
1518 struct rib_subscription *
1519 rib_subscribe_locked(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
1520     enum rib_subscription_type type)
1521 {
1522         struct rib_subscription *rs;
1523
1524         NET_EPOCH_ASSERT();
1525         RIB_WLOCK_ASSERT(rnh);
1526
1527         if ((rs = allocate_subscription(f, arg, type, false)) == NULL)
1528                 return (NULL);
1529         rs->rnh = rnh;
1530
1531         CK_STAILQ_INSERT_HEAD(&rnh->rnh_subscribers, rs, next);
1532
1533         return (rs);
1534 }
1535
1536 /*
1537  * Remove rtable subscription @rs from the routing table.
1538  * Needs to be run in network epoch.
1539  */
1540 void
1541 rib_unsubscribe(struct rib_subscription *rs)
1542 {
1543         struct rib_head *rnh = rs->rnh;
1544
1545         NET_EPOCH_ASSERT();
1546
1547         RIB_WLOCK(rnh);
1548         CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
1549         RIB_WUNLOCK(rnh);
1550
1551         epoch_call(net_epoch_preempt, destroy_subscription_epoch,
1552             &rs->epoch_ctx);
1553 }
1554
1555 void
1556 rib_unsubscribe_locked(struct rib_subscription *rs)
1557 {
1558         struct rib_head *rnh = rs->rnh;
1559
1560         NET_EPOCH_ASSERT();
1561         RIB_WLOCK_ASSERT(rnh);
1562
1563         CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
1564
1565         epoch_call(net_epoch_preempt, destroy_subscription_epoch,
1566             &rs->epoch_ctx);
1567 }
1568
1569 /*
1570  * Epoch callback indicating subscription is safe to destroy
1571  */
1572 static void
1573 destroy_subscription_epoch(epoch_context_t ctx)
1574 {
1575         struct rib_subscription *rs;
1576
1577         rs = __containerof(ctx, struct rib_subscription, epoch_ctx);
1578
1579         free(rs, M_RTABLE);
1580 }
1581
1582 void
1583 rib_init_subscriptions(struct rib_head *rnh)
1584 {
1585
1586         CK_STAILQ_INIT(&rnh->rnh_subscribers);
1587 }
1588
1589 void
1590 rib_destroy_subscriptions(struct rib_head *rnh)
1591 {
1592         struct rib_subscription *rs;
1593         struct epoch_tracker et;
1594
1595         NET_EPOCH_ENTER(et);
1596         RIB_WLOCK(rnh);
1597         while ((rs = CK_STAILQ_FIRST(&rnh->rnh_subscribers)) != NULL) {
1598                 CK_STAILQ_REMOVE_HEAD(&rnh->rnh_subscribers, next);
1599                 epoch_call(net_epoch_preempt, destroy_subscription_epoch,
1600                     &rs->epoch_ctx);
1601         }
1602         RIB_WUNLOCK(rnh);
1603         NET_EPOCH_EXIT(et);
1604 }