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