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