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