xref: /freebsd-src/sys/net/route/route_ctl.c (revision 1da4954c92ea7585b352ba830d3ee64ca69ada52)
1a6663252SAlexander V. Chernikov /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3a6663252SAlexander V. Chernikov  *
4a6663252SAlexander V. Chernikov  * Copyright (c) 2020 Alexander V. Chernikov
5a6663252SAlexander V. Chernikov  *
6a6663252SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
7a6663252SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
8a6663252SAlexander V. Chernikov  * are met:
9a6663252SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
10a6663252SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
11a6663252SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
12a6663252SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
13a6663252SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
14a6663252SAlexander V. Chernikov  *
15a6663252SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16a6663252SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17a6663252SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18a6663252SAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19a6663252SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20a6663252SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21a6663252SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22a6663252SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23a6663252SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24a6663252SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25a6663252SAlexander V. Chernikov  * SUCH DAMAGE.
26a6663252SAlexander V. Chernikov  */
27a6663252SAlexander V. Chernikov 
28a6663252SAlexander V. Chernikov #include <sys/cdefs.h>
29a6663252SAlexander V. Chernikov #include "opt_inet.h"
30a6663252SAlexander V. Chernikov #include "opt_inet6.h"
31fedeb08bSAlexander V. Chernikov #include "opt_route.h"
32a6663252SAlexander V. Chernikov 
33a6663252SAlexander V. Chernikov #include <sys/param.h>
34a6663252SAlexander V. Chernikov #include <sys/systm.h>
35a6663252SAlexander V. Chernikov #include <sys/malloc.h>
36a6663252SAlexander V. Chernikov #include <sys/mbuf.h>
37a6663252SAlexander V. Chernikov #include <sys/socket.h>
38a6663252SAlexander V. Chernikov #include <sys/sysctl.h>
39a6663252SAlexander V. Chernikov #include <sys/syslog.h>
40a6663252SAlexander V. Chernikov #include <sys/kernel.h>
41a6663252SAlexander V. Chernikov #include <sys/lock.h>
42a6663252SAlexander V. Chernikov #include <sys/rmlock.h>
43a6663252SAlexander V. Chernikov 
44a6663252SAlexander V. Chernikov #include <net/if.h>
45a6663252SAlexander V. Chernikov #include <net/if_var.h>
462c2b37adSJustin Hibbits #include <net/if_private.h>
47a6663252SAlexander V. Chernikov #include <net/if_dl.h>
48a6663252SAlexander V. Chernikov #include <net/vnet.h>
49a6663252SAlexander V. Chernikov #include <net/route.h>
50da187ddbSAlexander V. Chernikov #include <net/route/route_ctl.h>
51e7d8af4fSAlexander V. Chernikov #include <net/route/route_var.h>
52a6663252SAlexander V. Chernikov #include <net/route/nhop_utils.h>
53a6663252SAlexander V. Chernikov #include <net/route/nhop.h>
54a6663252SAlexander V. Chernikov #include <net/route/nhop_var.h>
55a6663252SAlexander V. Chernikov #include <netinet/in.h>
56df905392SAlexander V. Chernikov #include <netinet6/scope6_var.h>
572ce55385SAlexander V. Chernikov #include <netinet6/in6_var.h>
58a6663252SAlexander V. Chernikov 
59c38da70cSAlexander V. Chernikov #define	DEBUG_MOD_NAME	route_ctl
60c38da70cSAlexander V. Chernikov #define	DEBUG_MAX_LEVEL	LOG_DEBUG
61c38da70cSAlexander V. Chernikov #include <net/route/route_debug.h>
62c38da70cSAlexander V. Chernikov _DECLARE_DEBUG(LOG_INFO);
63c38da70cSAlexander V. Chernikov 
64a6663252SAlexander V. Chernikov /*
65a6663252SAlexander V. Chernikov  * This file contains control plane routing tables functions.
66a6663252SAlexander V. Chernikov  *
67a6663252SAlexander V. Chernikov  * All functions assumes they are called in net epoch.
68a6663252SAlexander V. Chernikov  */
69a6663252SAlexander V. Chernikov 
702ce55385SAlexander V. Chernikov union sockaddr_union {
712ce55385SAlexander V. Chernikov 	struct sockaddr		sa;
722ce55385SAlexander V. Chernikov 	struct sockaddr_in	sin;
732ce55385SAlexander V. Chernikov 	struct sockaddr_in6	sin6;
742ce55385SAlexander V. Chernikov 	char			_buf[32];
752ce55385SAlexander V. Chernikov };
762ce55385SAlexander V. Chernikov 
770d60e88bSAlexander V. Chernikov static int add_route_byinfo(struct rib_head *rnh, struct rt_addrinfo *info,
78136a1f8dSAlexander V. Chernikov     struct rib_cmd_info *rc);
790d60e88bSAlexander V. Chernikov static int change_route_byinfo(struct rib_head *rnh, struct rtentry *rt,
800d60e88bSAlexander V. Chernikov     struct rt_addrinfo *info, struct route_nhop_data *nhd_orig,
81136a1f8dSAlexander V. Chernikov     struct rib_cmd_info *rc);
82aa8f9f90SAlexander V. Chernikov 
832ce55385SAlexander V. Chernikov static int add_route_flags(struct rib_head *rnh, struct rtentry *rt,
842ce55385SAlexander V. Chernikov     struct route_nhop_data *rnd_add, int op_flags, struct rib_cmd_info *rc);
85258828d0SAlexander V. Chernikov #ifdef ROUTE_MPATH
862ce55385SAlexander V. Chernikov static int add_route_flags_mpath(struct rib_head *rnh, struct rtentry *rt,
872ce55385SAlexander V. Chernikov     struct route_nhop_data *rnd_add, struct route_nhop_data *rnd_orig,
882ce55385SAlexander V. Chernikov     int op_flags, struct rib_cmd_info *rc);
89258828d0SAlexander V. Chernikov #endif
902ce55385SAlexander V. Chernikov 
910d60e88bSAlexander V. Chernikov static int add_route(struct rib_head *rnh, struct rtentry *rt,
920d60e88bSAlexander V. Chernikov     struct route_nhop_data *rnd, struct rib_cmd_info *rc);
930d60e88bSAlexander V. Chernikov static int delete_route(struct rib_head *rnh, struct rtentry *rt,
940d60e88bSAlexander V. Chernikov     struct rib_cmd_info *rc);
95dedeec11SAlexander V. Chernikov static int rt_delete_conditional(struct rib_head *rnh, struct rtentry *rt,
96dedeec11SAlexander V. Chernikov     int prio, rib_filter_f_t *cb, void *cbdata, struct rib_cmd_info *rc);
97aa8f9f90SAlexander V. Chernikov 
98c35a43b2SAlexander V. Chernikov static bool fill_pxmask_family(int family, int plen, struct sockaddr *_dst,
99c35a43b2SAlexander V. Chernikov     struct sockaddr **pmask);
100dedeec11SAlexander V. Chernikov static int get_prio_from_info(const struct rt_addrinfo *info);
101dedeec11SAlexander V. Chernikov static int nhop_get_prio(const struct nhop_object *nh);
102dedeec11SAlexander V. Chernikov 
1039c584fa4SAlexander V. Chernikov #ifdef ROUTE_MPATH
104fedeb08bSAlexander V. Chernikov static bool rib_can_multipath(struct rib_head *rh);
1059c584fa4SAlexander V. Chernikov #endif
106fedeb08bSAlexander V. Chernikov 
107fedeb08bSAlexander V. Chernikov /* Per-vnet multipath routing configuration */
108fedeb08bSAlexander V. Chernikov SYSCTL_DECL(_net_route);
109fedeb08bSAlexander V. Chernikov #define	V_rib_route_multipath	VNET(rib_route_multipath)
110fedeb08bSAlexander V. Chernikov #ifdef ROUTE_MPATH
111fedeb08bSAlexander V. Chernikov #define _MP_FLAGS	CTLFLAG_RW
112fedeb08bSAlexander V. Chernikov #else
113fedeb08bSAlexander V. Chernikov #define _MP_FLAGS	CTLFLAG_RD
114fedeb08bSAlexander V. Chernikov #endif
115eb0b1b33SAlexander V. Chernikov VNET_DEFINE(u_int, rib_route_multipath) = 1;
116fedeb08bSAlexander V. Chernikov SYSCTL_UINT(_net_route, OID_AUTO, multipath, _MP_FLAGS | CTLFLAG_VNET,
117fedeb08bSAlexander V. Chernikov     &VNET_NAME(rib_route_multipath), 0, "Enable route multipath");
118fedeb08bSAlexander V. Chernikov #undef _MP_FLAGS
1194d2c2509SAlexander V. Chernikov 
1202ce55385SAlexander V. Chernikov #ifdef ROUTE_MPATH
1212ce55385SAlexander V. Chernikov VNET_DEFINE(u_int, fib_hash_outbound) = 0;
1222ce55385SAlexander V. Chernikov SYSCTL_UINT(_net_route, OID_AUTO, hash_outbound, CTLFLAG_RD | CTLFLAG_VNET,
1232ce55385SAlexander V. Chernikov     &VNET_NAME(fib_hash_outbound), 0,
1242ce55385SAlexander V. Chernikov     "Compute flowid for locally-originated packets");
1252ce55385SAlexander V. Chernikov 
1262ce55385SAlexander V. Chernikov /* Default entropy to add to the hash calculation for the outbound connections*/
1272ce55385SAlexander V. Chernikov uint8_t mpath_entropy_key[MPATH_ENTROPY_KEY_LEN] = {
1282ce55385SAlexander V. Chernikov 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
1292ce55385SAlexander V. Chernikov 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
1302ce55385SAlexander V. Chernikov 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
1312ce55385SAlexander V. Chernikov 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
1322ce55385SAlexander V. Chernikov 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
1332ce55385SAlexander V. Chernikov };
1342ce55385SAlexander V. Chernikov #endif
1352ce55385SAlexander V. Chernikov 
13662e1a437SZhenlei Huang #if defined(INET) && defined(INET6)
13762e1a437SZhenlei Huang FEATURE(ipv4_rfc5549_support, "Route IPv4 packets via IPv6 nexthops");
13862e1a437SZhenlei Huang #define V_rib_route_ipv6_nexthop VNET(rib_route_ipv6_nexthop)
139fe05d1ddSAlexander V. Chernikov VNET_DEFINE_STATIC(u_int, rib_route_ipv6_nexthop) = 1;
14062e1a437SZhenlei Huang SYSCTL_UINT(_net_route, OID_AUTO, ipv6_nexthop, CTLFLAG_RW | CTLFLAG_VNET,
14162e1a437SZhenlei Huang     &VNET_NAME(rib_route_ipv6_nexthop), 0, "Enable IPv4 route via IPv6 Next Hop address");
14262e1a437SZhenlei Huang #endif
14362e1a437SZhenlei Huang 
14463f7f392SAlexander V. Chernikov /* Debug bits */
14563f7f392SAlexander V. Chernikov SYSCTL_NODE(_net_route, OID_AUTO, debug, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
14663f7f392SAlexander V. Chernikov 
147da187ddbSAlexander V. Chernikov static struct rib_head *
148da187ddbSAlexander V. Chernikov get_rnh(uint32_t fibnum, const struct rt_addrinfo *info)
149da187ddbSAlexander V. Chernikov {
150da187ddbSAlexander V. Chernikov 	struct rib_head *rnh;
151da187ddbSAlexander V. Chernikov 	struct sockaddr *dst;
152da187ddbSAlexander V. Chernikov 
153da187ddbSAlexander V. Chernikov 	KASSERT((fibnum < rt_numfibs), ("rib_add_route: bad fibnum"));
154da187ddbSAlexander V. Chernikov 
155da187ddbSAlexander V. Chernikov 	dst = info->rti_info[RTAX_DST];
156da187ddbSAlexander V. Chernikov 	rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
157da187ddbSAlexander V. Chernikov 
158da187ddbSAlexander V. Chernikov 	return (rnh);
159da187ddbSAlexander V. Chernikov }
160da187ddbSAlexander V. Chernikov 
16162e1a437SZhenlei Huang #if defined(INET) && defined(INET6)
162fe05d1ddSAlexander V. Chernikov bool
163fe05d1ddSAlexander V. Chernikov rib_can_4o6_nhop(void)
16462e1a437SZhenlei Huang {
165fe05d1ddSAlexander V. Chernikov 	return (!!V_rib_route_ipv6_nexthop);
16662e1a437SZhenlei Huang }
16762e1a437SZhenlei Huang #endif
16862e1a437SZhenlei Huang 
169fedeb08bSAlexander V. Chernikov #ifdef ROUTE_MPATH
170fedeb08bSAlexander V. Chernikov static bool
171fedeb08bSAlexander V. Chernikov rib_can_multipath(struct rib_head *rh)
172fedeb08bSAlexander V. Chernikov {
173fedeb08bSAlexander V. Chernikov 	int result;
174fedeb08bSAlexander V. Chernikov 
175fedeb08bSAlexander V. Chernikov 	CURVNET_SET(rh->rib_vnet);
176fedeb08bSAlexander V. Chernikov 	result = !!V_rib_route_multipath;
177fedeb08bSAlexander V. Chernikov 	CURVNET_RESTORE();
178fedeb08bSAlexander V. Chernikov 
179fedeb08bSAlexander V. Chernikov 	return (result);
180fedeb08bSAlexander V. Chernikov }
181fedeb08bSAlexander V. Chernikov 
182fedeb08bSAlexander V. Chernikov /*
183fedeb08bSAlexander V. Chernikov  * Check is nhop is multipath-eligible.
184fedeb08bSAlexander V. Chernikov  * Avoid nhops without gateways and redirects.
185fedeb08bSAlexander V. Chernikov  *
186fedeb08bSAlexander V. Chernikov  * Returns 1 for multipath-eligible nexthop,
187fedeb08bSAlexander V. Chernikov  * 0 otherwise.
188fedeb08bSAlexander V. Chernikov  */
189fedeb08bSAlexander V. Chernikov bool
190fedeb08bSAlexander V. Chernikov nhop_can_multipath(const struct nhop_object *nh)
191fedeb08bSAlexander V. Chernikov {
192fedeb08bSAlexander V. Chernikov 
193fedeb08bSAlexander V. Chernikov 	if ((nh->nh_flags & NHF_MULTIPATH) != 0)
194fedeb08bSAlexander V. Chernikov 		return (1);
195fedeb08bSAlexander V. Chernikov 	if ((nh->nh_flags & NHF_GATEWAY) == 0)
196fedeb08bSAlexander V. Chernikov 		return (0);
197fedeb08bSAlexander V. Chernikov 	if ((nh->nh_flags & NHF_REDIRECT) != 0)
198fedeb08bSAlexander V. Chernikov 		return (0);
199fedeb08bSAlexander V. Chernikov 
200fedeb08bSAlexander V. Chernikov 	return (1);
201fedeb08bSAlexander V. Chernikov }
202fedeb08bSAlexander V. Chernikov #endif
203fedeb08bSAlexander V. Chernikov 
2042259a030SAlexander V. Chernikov static int
2052259a030SAlexander V. Chernikov get_info_weight(const struct rt_addrinfo *info, uint32_t default_weight)
2062259a030SAlexander V. Chernikov {
2072259a030SAlexander V. Chernikov 	uint32_t weight;
2082259a030SAlexander V. Chernikov 
2092259a030SAlexander V. Chernikov 	if (info->rti_mflags & RTV_WEIGHT)
2102259a030SAlexander V. Chernikov 		weight = info->rti_rmx->rmx_weight;
2112259a030SAlexander V. Chernikov 	else
2122259a030SAlexander V. Chernikov 		weight = default_weight;
2132259a030SAlexander V. Chernikov 	/* Keep upper 1 byte for adm distance purposes */
2142259a030SAlexander V. Chernikov 	if (weight > RT_MAX_WEIGHT)
2152259a030SAlexander V. Chernikov 		weight = RT_MAX_WEIGHT;
2160a3a377aSAlexander V. Chernikov 	else if (weight == 0)
2170a3a377aSAlexander V. Chernikov 		weight = default_weight;
2182259a030SAlexander V. Chernikov 
2192259a030SAlexander V. Chernikov 	return (weight);
2202259a030SAlexander V. Chernikov }
2212259a030SAlexander V. Chernikov 
222da187ddbSAlexander V. Chernikov /*
2232ce55385SAlexander V. Chernikov  * File-local concept for distingushing between the normal and
2242ce55385SAlexander V. Chernikov  * RTF_PINNED routes tha can override the "normal" one.
2252ce55385SAlexander V. Chernikov  */
2262ce55385SAlexander V. Chernikov #define	NH_PRIORITY_HIGH	2
2272ce55385SAlexander V. Chernikov #define	NH_PRIORITY_NORMAL	1
2282ce55385SAlexander V. Chernikov static int
2292ce55385SAlexander V. Chernikov get_prio_from_info(const struct rt_addrinfo *info)
2302ce55385SAlexander V. Chernikov {
2312ce55385SAlexander V. Chernikov 	if (info->rti_flags & RTF_PINNED)
2322ce55385SAlexander V. Chernikov 		return (NH_PRIORITY_HIGH);
2332ce55385SAlexander V. Chernikov 	return (NH_PRIORITY_NORMAL);
2342ce55385SAlexander V. Chernikov }
2352ce55385SAlexander V. Chernikov 
2362ce55385SAlexander V. Chernikov static int
2372ce55385SAlexander V. Chernikov nhop_get_prio(const struct nhop_object *nh)
2382ce55385SAlexander V. Chernikov {
2392ce55385SAlexander V. Chernikov 	if (NH_IS_PINNED(nh))
2402ce55385SAlexander V. Chernikov 		return (NH_PRIORITY_HIGH);
2412ce55385SAlexander V. Chernikov 	return (NH_PRIORITY_NORMAL);
2422ce55385SAlexander V. Chernikov }
2432ce55385SAlexander V. Chernikov 
2442ce55385SAlexander V. Chernikov /*
245aa8f9f90SAlexander V. Chernikov  * Check if specified @gw matches gw data in the nexthop @nh.
246aa8f9f90SAlexander V. Chernikov  *
247aa8f9f90SAlexander V. Chernikov  * Returns true if matches, false otherwise.
248aa8f9f90SAlexander V. Chernikov  */
249fedeb08bSAlexander V. Chernikov bool
250aa8f9f90SAlexander V. Chernikov match_nhop_gw(const struct nhop_object *nh, const struct sockaddr *gw)
251aa8f9f90SAlexander V. Chernikov {
252aa8f9f90SAlexander V. Chernikov 
253aa8f9f90SAlexander V. Chernikov 	if (nh->gw_sa.sa_family != gw->sa_family)
254aa8f9f90SAlexander V. Chernikov 		return (false);
255aa8f9f90SAlexander V. Chernikov 
256aa8f9f90SAlexander V. Chernikov 	switch (gw->sa_family) {
257aa8f9f90SAlexander V. Chernikov 	case AF_INET:
258aa8f9f90SAlexander V. Chernikov 		return (nh->gw4_sa.sin_addr.s_addr ==
259aa8f9f90SAlexander V. Chernikov 		    ((const struct sockaddr_in *)gw)->sin_addr.s_addr);
260aa8f9f90SAlexander V. Chernikov 	case AF_INET6:
261aa8f9f90SAlexander V. Chernikov 		{
262aa8f9f90SAlexander V. Chernikov 			const struct sockaddr_in6 *gw6;
263aa8f9f90SAlexander V. Chernikov 			gw6 = (const struct sockaddr_in6 *)gw;
264aa8f9f90SAlexander V. Chernikov 
265aa8f9f90SAlexander V. Chernikov 			/*
266aa8f9f90SAlexander V. Chernikov 			 * Currently (2020-09) IPv6 gws in kernel have their
267aa8f9f90SAlexander V. Chernikov 			 * scope embedded. Once this becomes false, this code
268aa8f9f90SAlexander V. Chernikov 			 * has to be revisited.
269aa8f9f90SAlexander V. Chernikov 			 */
270aa8f9f90SAlexander V. Chernikov 			if (IN6_ARE_ADDR_EQUAL(&nh->gw6_sa.sin6_addr,
271aa8f9f90SAlexander V. Chernikov 			    &gw6->sin6_addr))
272aa8f9f90SAlexander V. Chernikov 				return (true);
273aa8f9f90SAlexander V. Chernikov 			return (false);
274aa8f9f90SAlexander V. Chernikov 		}
275aa8f9f90SAlexander V. Chernikov 	case AF_LINK:
276aa8f9f90SAlexander V. Chernikov 		{
277aa8f9f90SAlexander V. Chernikov 			const struct sockaddr_dl *sdl;
278aa8f9f90SAlexander V. Chernikov 			sdl = (const struct sockaddr_dl *)gw;
279aa8f9f90SAlexander V. Chernikov 			return (nh->gwl_sa.sdl_index == sdl->sdl_index);
280aa8f9f90SAlexander V. Chernikov 		}
281aa8f9f90SAlexander V. Chernikov 	default:
282aa8f9f90SAlexander V. Chernikov 		return (memcmp(&nh->gw_sa, gw, nh->gw_sa.sa_len) == 0);
283aa8f9f90SAlexander V. Chernikov 	}
284aa8f9f90SAlexander V. Chernikov 
285aa8f9f90SAlexander V. Chernikov 	/* NOTREACHED */
286aa8f9f90SAlexander V. Chernikov 	return (false);
287aa8f9f90SAlexander V. Chernikov }
288aa8f9f90SAlexander V. Chernikov 
289730bfa28SAlexander V. Chernikov /*
290730bfa28SAlexander V. Chernikov  * Matches all nexthop with given @gw.
291730bfa28SAlexander V. Chernikov  * Can be used as rib_filter_f callback.
292730bfa28SAlexander V. Chernikov  */
293730bfa28SAlexander V. Chernikov int
294730bfa28SAlexander V. Chernikov rib_match_gw(const struct rtentry *rt, const struct nhop_object *nh, void *gw_sa)
295730bfa28SAlexander V. Chernikov {
296730bfa28SAlexander V. Chernikov 	const struct sockaddr *gw = (const struct sockaddr *)gw_sa;
297730bfa28SAlexander V. Chernikov 
298730bfa28SAlexander V. Chernikov 	return (match_nhop_gw(nh, gw));
299730bfa28SAlexander V. Chernikov }
300730bfa28SAlexander V. Chernikov 
301dedeec11SAlexander V. Chernikov struct gw_filter_data {
302dedeec11SAlexander V. Chernikov 	const struct sockaddr *gw;
303dedeec11SAlexander V. Chernikov 	int count;
304dedeec11SAlexander V. Chernikov };
305dedeec11SAlexander V. Chernikov 
306730bfa28SAlexander V. Chernikov /*
307730bfa28SAlexander V. Chernikov  * Matches first occurence of the gateway provided in @gwd
308730bfa28SAlexander V. Chernikov  */
309dedeec11SAlexander V. Chernikov static int
310730bfa28SAlexander V. Chernikov match_gw_one(const struct rtentry *rt, const struct nhop_object *nh, void *_data)
311dedeec11SAlexander V. Chernikov {
312dedeec11SAlexander V. Chernikov 	struct gw_filter_data *gwd = (struct gw_filter_data *)_data;
313dedeec11SAlexander V. Chernikov 
314dedeec11SAlexander V. Chernikov 	/* Return only first match to make rtsock happy */
315dedeec11SAlexander V. Chernikov 	if (match_nhop_gw(nh, gwd->gw) && gwd->count++ == 0)
316dedeec11SAlexander V. Chernikov 		return (1);
317dedeec11SAlexander V. Chernikov 	return (0);
318dedeec11SAlexander V. Chernikov }
319dedeec11SAlexander V. Chernikov 
320aa8f9f90SAlexander V. Chernikov /*
321aa8f9f90SAlexander V. Chernikov  * Checks if data in @info matches nexhop @nh.
322aa8f9f90SAlexander V. Chernikov  *
323aa8f9f90SAlexander V. Chernikov  * Returns 0 on success,
324aa8f9f90SAlexander V. Chernikov  * ESRCH if not matched,
325aa8f9f90SAlexander V. Chernikov  * ENOENT if filter function returned false
326aa8f9f90SAlexander V. Chernikov  */
327aa8f9f90SAlexander V. Chernikov int
328aa8f9f90SAlexander V. Chernikov check_info_match_nhop(const struct rt_addrinfo *info, const struct rtentry *rt,
329aa8f9f90SAlexander V. Chernikov     const struct nhop_object *nh)
330aa8f9f90SAlexander V. Chernikov {
331aa8f9f90SAlexander V. Chernikov 	const struct sockaddr *gw = info->rti_info[RTAX_GATEWAY];
332aa8f9f90SAlexander V. Chernikov 
333aa8f9f90SAlexander V. Chernikov 	if (info->rti_filter != NULL) {
334aa8f9f90SAlexander V. Chernikov 	    if (info->rti_filter(rt, nh, info->rti_filterdata) == 0)
335aa8f9f90SAlexander V. Chernikov 		    return (ENOENT);
336aa8f9f90SAlexander V. Chernikov 	    else
337aa8f9f90SAlexander V. Chernikov 		    return (0);
338aa8f9f90SAlexander V. Chernikov 	}
339aa8f9f90SAlexander V. Chernikov 	if ((gw != NULL) && !match_nhop_gw(nh, gw))
340aa8f9f90SAlexander V. Chernikov 		return (ESRCH);
341aa8f9f90SAlexander V. Chernikov 
342aa8f9f90SAlexander V. Chernikov 	return (0);
343aa8f9f90SAlexander V. Chernikov }
344aa8f9f90SAlexander V. Chernikov 
345aa8f9f90SAlexander V. Chernikov /*
346aa8f9f90SAlexander V. Chernikov  * Runs exact prefix match based on @dst and @netmask.
347aa8f9f90SAlexander V. Chernikov  * Returns matched @rtentry if found or NULL.
348aa8f9f90SAlexander V. Chernikov  * If rtentry was found, saves nexthop / weight value into @rnd.
349aa8f9f90SAlexander V. Chernikov  */
350aa8f9f90SAlexander V. Chernikov static struct rtentry *
351aa8f9f90SAlexander V. Chernikov lookup_prefix_bysa(struct rib_head *rnh, const struct sockaddr *dst,
352aa8f9f90SAlexander V. Chernikov     const struct sockaddr *netmask, struct route_nhop_data *rnd)
353aa8f9f90SAlexander V. Chernikov {
354aa8f9f90SAlexander V. Chernikov 	struct rtentry *rt;
355aa8f9f90SAlexander V. Chernikov 
356aa8f9f90SAlexander V. Chernikov 	RIB_LOCK_ASSERT(rnh);
357aa8f9f90SAlexander V. Chernikov 
3580d60e88bSAlexander V. Chernikov 	rt = (struct rtentry *)rnh->rnh_lookup(dst, netmask, &rnh->head);
359aa8f9f90SAlexander V. Chernikov 	if (rt != NULL) {
360aa8f9f90SAlexander V. Chernikov 		rnd->rnd_nhop = rt->rt_nhop;
361aa8f9f90SAlexander V. Chernikov 		rnd->rnd_weight = rt->rt_weight;
362aa8f9f90SAlexander V. Chernikov 	} else {
363aa8f9f90SAlexander V. Chernikov 		rnd->rnd_nhop = NULL;
364aa8f9f90SAlexander V. Chernikov 		rnd->rnd_weight = 0;
365aa8f9f90SAlexander V. Chernikov 	}
366aa8f9f90SAlexander V. Chernikov 
367aa8f9f90SAlexander V. Chernikov 	return (rt);
368aa8f9f90SAlexander V. Chernikov }
369aa8f9f90SAlexander V. Chernikov 
3700d60e88bSAlexander V. Chernikov struct rtentry *
3710d60e88bSAlexander V. Chernikov lookup_prefix_rt(struct rib_head *rnh, const struct rtentry *rt,
3720d60e88bSAlexander V. Chernikov     struct route_nhop_data *rnd)
3730d60e88bSAlexander V. Chernikov {
3740d60e88bSAlexander V. Chernikov 	return (lookup_prefix_bysa(rnh, rt_key_const(rt), rt_mask_const(rt), rnd));
3750d60e88bSAlexander V. Chernikov }
3760d60e88bSAlexander V. Chernikov 
377aa8f9f90SAlexander V. Chernikov /*
378aa8f9f90SAlexander V. Chernikov  * Runs exact prefix match based on dst/netmask from @info.
379aa8f9f90SAlexander V. Chernikov  * Assumes RIB lock is held.
380aa8f9f90SAlexander V. Chernikov  * Returns matched @rtentry if found or NULL.
381aa8f9f90SAlexander V. Chernikov  * If rtentry was found, saves nexthop / weight value into @rnd.
382aa8f9f90SAlexander V. Chernikov  */
383aa8f9f90SAlexander V. Chernikov struct rtentry *
384aa8f9f90SAlexander V. Chernikov lookup_prefix(struct rib_head *rnh, const struct rt_addrinfo *info,
385aa8f9f90SAlexander V. Chernikov     struct route_nhop_data *rnd)
386aa8f9f90SAlexander V. Chernikov {
387aa8f9f90SAlexander V. Chernikov 	struct rtentry *rt;
388aa8f9f90SAlexander V. Chernikov 
389aa8f9f90SAlexander V. Chernikov 	rt = lookup_prefix_bysa(rnh, info->rti_info[RTAX_DST],
390aa8f9f90SAlexander V. Chernikov 	    info->rti_info[RTAX_NETMASK], rnd);
391aa8f9f90SAlexander V. Chernikov 
392aa8f9f90SAlexander V. Chernikov 	return (rt);
393aa8f9f90SAlexander V. Chernikov }
394aa8f9f90SAlexander V. Chernikov 
395c35a43b2SAlexander V. Chernikov const struct rtentry *
396c35a43b2SAlexander V. Chernikov rib_lookup_prefix_plen(struct rib_head *rnh, struct sockaddr *dst, int plen,
397c35a43b2SAlexander V. Chernikov     struct route_nhop_data *rnd)
398c35a43b2SAlexander V. Chernikov {
399c35a43b2SAlexander V. Chernikov 	union sockaddr_union mask_storage;
400c35a43b2SAlexander V. Chernikov 	struct sockaddr *netmask = &mask_storage.sa;
401c35a43b2SAlexander V. Chernikov 
402c35a43b2SAlexander V. Chernikov 	if (fill_pxmask_family(dst->sa_family, plen, dst, &netmask))
403c35a43b2SAlexander V. Chernikov 		return (lookup_prefix_bysa(rnh, dst, netmask, rnd));
404c35a43b2SAlexander V. Chernikov 	return (NULL);
405c35a43b2SAlexander V. Chernikov }
406c35a43b2SAlexander V. Chernikov 
4072ce55385SAlexander V. Chernikov static bool
4082ce55385SAlexander V. Chernikov fill_pxmask_family(int family, int plen, struct sockaddr *_dst,
4092ce55385SAlexander V. Chernikov     struct sockaddr **pmask)
4102ce55385SAlexander V. Chernikov {
41102e05b8fSAlexander V. Chernikov 	if (plen == -1) {
41202e05b8fSAlexander V. Chernikov 		*pmask = NULL;
41302e05b8fSAlexander V. Chernikov 		return (true);
41402e05b8fSAlexander V. Chernikov 	}
41502e05b8fSAlexander V. Chernikov 
4162ce55385SAlexander V. Chernikov 	switch (family) {
4172ce55385SAlexander V. Chernikov #ifdef INET
4182ce55385SAlexander V. Chernikov 	case AF_INET:
4192ce55385SAlexander V. Chernikov 		{
42002e05b8fSAlexander V. Chernikov 			struct sockaddr_in *mask = (struct sockaddr_in *)(*pmask);
4212ce55385SAlexander V. Chernikov 			struct sockaddr_in *dst= (struct sockaddr_in *)_dst;
4222ce55385SAlexander V. Chernikov 
4232ce55385SAlexander V. Chernikov 			memset(mask, 0, sizeof(*mask));
4242ce55385SAlexander V. Chernikov 			mask->sin_family = family;
4252ce55385SAlexander V. Chernikov 			mask->sin_len = sizeof(*mask);
42602e05b8fSAlexander V. Chernikov 			if (plen == 32)
4272ce55385SAlexander V. Chernikov 				*pmask = NULL;
4282ce55385SAlexander V. Chernikov 			else if (plen > 32 || plen < 0)
4292ce55385SAlexander V. Chernikov 				return (false);
4302ce55385SAlexander V. Chernikov 			else {
4312ce55385SAlexander V. Chernikov 				uint32_t daddr, maddr;
4322ce55385SAlexander V. Chernikov 				maddr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
4332ce55385SAlexander V. Chernikov 				mask->sin_addr.s_addr = maddr;
4342ce55385SAlexander V. Chernikov 				daddr = dst->sin_addr.s_addr;
4352ce55385SAlexander V. Chernikov 				daddr = htonl(ntohl(daddr) & ntohl(maddr));
4362ce55385SAlexander V. Chernikov 				dst->sin_addr.s_addr = daddr;
4372ce55385SAlexander V. Chernikov 			}
4382ce55385SAlexander V. Chernikov 			return (true);
4392ce55385SAlexander V. Chernikov 		}
4402ce55385SAlexander V. Chernikov 		break;
4412ce55385SAlexander V. Chernikov #endif
4422ce55385SAlexander V. Chernikov #ifdef INET6
4432ce55385SAlexander V. Chernikov 	case AF_INET6:
4442ce55385SAlexander V. Chernikov 		{
44502e05b8fSAlexander V. Chernikov 			struct sockaddr_in6 *mask = (struct sockaddr_in6 *)(*pmask);
4462ce55385SAlexander V. Chernikov 			struct sockaddr_in6 *dst = (struct sockaddr_in6 *)_dst;
4472ce55385SAlexander V. Chernikov 
4482ce55385SAlexander V. Chernikov 			memset(mask, 0, sizeof(*mask));
4492ce55385SAlexander V. Chernikov 			mask->sin6_family = family;
4502ce55385SAlexander V. Chernikov 			mask->sin6_len = sizeof(*mask);
45102e05b8fSAlexander V. Chernikov 			if (plen == 128)
4522ce55385SAlexander V. Chernikov 				*pmask = NULL;
4532ce55385SAlexander V. Chernikov 			else if (plen > 128 || plen < 0)
4542ce55385SAlexander V. Chernikov 				return (false);
4552ce55385SAlexander V. Chernikov 			else {
4562ce55385SAlexander V. Chernikov 				ip6_writemask(&mask->sin6_addr, plen);
4572ce55385SAlexander V. Chernikov 				IN6_MASK_ADDR(&dst->sin6_addr, &mask->sin6_addr);
4582ce55385SAlexander V. Chernikov 			}
4592ce55385SAlexander V. Chernikov 			return (true);
4602ce55385SAlexander V. Chernikov 		}
4612ce55385SAlexander V. Chernikov 		break;
4622ce55385SAlexander V. Chernikov #endif
4632ce55385SAlexander V. Chernikov 	}
4642ce55385SAlexander V. Chernikov 	return (false);
4652ce55385SAlexander V. Chernikov }
4662ce55385SAlexander V. Chernikov 
4672ce55385SAlexander V. Chernikov /*
4682ce55385SAlexander V. Chernikov  * Attempts to add @dst/plen prefix with nexthop/nexhopgroup data @rnd
4692ce55385SAlexander V. Chernikov  * to the routing table.
4702ce55385SAlexander V. Chernikov  *
471a0aa160bSAlexander V. Chernikov  * @fibnum: verified kernel rtable id to insert route to
4722ce55385SAlexander V. Chernikov  * @dst: verified kernel-originated sockaddr, can be masked if plen non-empty
4732ce55385SAlexander V. Chernikov  * @plen: prefix length (or -1 if host route or not applicable for AF)
4742ce55385SAlexander V. Chernikov  * @op_flags: combination of RTM_F_ flags
4752ce55385SAlexander V. Chernikov  * @rc: storage to report operation result
4762ce55385SAlexander V. Chernikov  *
4772ce55385SAlexander V. Chernikov  * Returns 0 on success.
4782ce55385SAlexander V. Chernikov  */
4792ce55385SAlexander V. Chernikov int
48002e05b8fSAlexander V. Chernikov rib_add_route_px(uint32_t fibnum, struct sockaddr *dst, int plen,
4812ce55385SAlexander V. Chernikov     struct route_nhop_data *rnd, int op_flags, struct rib_cmd_info *rc)
4822ce55385SAlexander V. Chernikov {
4832ce55385SAlexander V. Chernikov 	union sockaddr_union mask_storage;
4842ce55385SAlexander V. Chernikov 	struct sockaddr *netmask = &mask_storage.sa;
485c24a8f19SAlexander V. Chernikov 	struct rtentry *rt = NULL;
4862ce55385SAlexander V. Chernikov 
4872ce55385SAlexander V. Chernikov 	NET_EPOCH_ASSERT();
4882ce55385SAlexander V. Chernikov 
4892ce55385SAlexander V. Chernikov 	bzero(rc, sizeof(struct rib_cmd_info));
4902ce55385SAlexander V. Chernikov 	rc->rc_cmd = RTM_ADD;
4912ce55385SAlexander V. Chernikov 
4922ce55385SAlexander V. Chernikov 	struct rib_head *rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
4932ce55385SAlexander V. Chernikov 	if (rnh == NULL)
4942ce55385SAlexander V. Chernikov 		return (EAFNOSUPPORT);
4952ce55385SAlexander V. Chernikov 
4962ce55385SAlexander V. Chernikov 	if (!fill_pxmask_family(dst->sa_family, plen, dst, &netmask)) {
4972ce55385SAlexander V. Chernikov 		FIB_RH_LOG(LOG_DEBUG, rnh, "error: invalid plen %d", plen);
4982ce55385SAlexander V. Chernikov 		return (EINVAL);
4992ce55385SAlexander V. Chernikov 	}
5002ce55385SAlexander V. Chernikov 
5012ce55385SAlexander V. Chernikov 	if (op_flags & RTM_F_CREATE) {
50202e05b8fSAlexander V. Chernikov 		if ((rt = rt_alloc(rnh, dst, netmask)) == NULL) {
50302e05b8fSAlexander V. Chernikov 			FIB_RH_LOG(LOG_INFO, rnh, "rtentry allocation failed");
5042ce55385SAlexander V. Chernikov 			return (ENOMEM);
50502e05b8fSAlexander V. Chernikov 		}
506a0aa160bSAlexander V. Chernikov 	} else {
507a0aa160bSAlexander V. Chernikov 		struct route_nhop_data rnd_tmp;
508a0aa160bSAlexander V. Chernikov 		RIB_RLOCK_TRACKER;
509a0aa160bSAlexander V. Chernikov 
510a0aa160bSAlexander V. Chernikov 		RIB_RLOCK(rnh);
511a0aa160bSAlexander V. Chernikov 		rt = lookup_prefix_bysa(rnh, dst, netmask, &rnd_tmp);
512a0aa160bSAlexander V. Chernikov 		RIB_RUNLOCK(rnh);
513a0aa160bSAlexander V. Chernikov 
514a0aa160bSAlexander V. Chernikov 		if (rt == NULL)
515a0aa160bSAlexander V. Chernikov 			return (ESRCH);
5162ce55385SAlexander V. Chernikov 	}
5172ce55385SAlexander V. Chernikov 
5182ce55385SAlexander V. Chernikov 	return (add_route_flags(rnh, rt, rnd, op_flags, rc));
5192ce55385SAlexander V. Chernikov }
5202ce55385SAlexander V. Chernikov 
5212ce55385SAlexander V. Chernikov /*
5222ce55385SAlexander V. Chernikov  * Attempts to delete @dst/plen prefix matching gateway @gw from the
5232ce55385SAlexander V. Chernikov  *  routing rable.
5242ce55385SAlexander V. Chernikov  *
5252ce55385SAlexander V. Chernikov  * @fibnum: rtable id to remove route from
5262ce55385SAlexander V. Chernikov  * @dst: verified kernel-originated sockaddr, can be masked if plen non-empty
5272ce55385SAlexander V. Chernikov  * @plen: prefix length (or -1 if host route or not applicable for AF)
5282ce55385SAlexander V. Chernikov  * @gw: gateway to match
5292ce55385SAlexander V. Chernikov  * @op_flags: combination of RTM_F_ flags
5302ce55385SAlexander V. Chernikov  * @rc: storage to report operation result
5312ce55385SAlexander V. Chernikov  *
5322ce55385SAlexander V. Chernikov  * Returns 0 on success.
5332ce55385SAlexander V. Chernikov  */
5342ce55385SAlexander V. Chernikov int
53502e05b8fSAlexander V. Chernikov rib_del_route_px_gw(uint32_t fibnum, struct sockaddr *dst, int plen,
5362ce55385SAlexander V. Chernikov     const struct sockaddr *gw, int op_flags, struct rib_cmd_info *rc)
5372ce55385SAlexander V. Chernikov {
5382ce55385SAlexander V. Chernikov 	struct gw_filter_data gwd = { .gw = gw };
5392ce55385SAlexander V. Chernikov 
540730bfa28SAlexander V. Chernikov 	return (rib_del_route_px(fibnum, dst, plen, match_gw_one, &gwd, op_flags, rc));
5412ce55385SAlexander V. Chernikov }
5422ce55385SAlexander V. Chernikov 
5432ce55385SAlexander V. Chernikov /*
5442ce55385SAlexander V. Chernikov  * Attempts to delete @dst/plen prefix matching @filter_func from the
5452ce55385SAlexander V. Chernikov  *  routing rable.
5462ce55385SAlexander V. Chernikov  *
5472ce55385SAlexander V. Chernikov  * @fibnum: rtable id to remove route from
5482ce55385SAlexander V. Chernikov  * @dst: verified kernel-originated sockaddr, can be masked if plen non-empty
5492ce55385SAlexander V. Chernikov  * @plen: prefix length (or -1 if host route or not applicable for AF)
5502ce55385SAlexander V. Chernikov  * @filter_func: func to be called for each nexthop of the prefix for matching
5512ce55385SAlexander V. Chernikov  * @filter_arg: argument to pass to @filter_func
5522ce55385SAlexander V. Chernikov  * @op_flags: combination of RTM_F_ flags
5532ce55385SAlexander V. Chernikov  * @rc: storage to report operation result
5542ce55385SAlexander V. Chernikov  *
5552ce55385SAlexander V. Chernikov  * Returns 0 on success.
5562ce55385SAlexander V. Chernikov  */
5572ce55385SAlexander V. Chernikov int
55802e05b8fSAlexander V. Chernikov rib_del_route_px(uint32_t fibnum, struct sockaddr *dst, int plen,
5592ce55385SAlexander V. Chernikov     rib_filter_f_t *filter_func, void *filter_arg, int op_flags,
5602ce55385SAlexander V. Chernikov     struct rib_cmd_info *rc)
5612ce55385SAlexander V. Chernikov {
5622ce55385SAlexander V. Chernikov 	union sockaddr_union mask_storage;
5632ce55385SAlexander V. Chernikov 	struct sockaddr *netmask = &mask_storage.sa;
5642ce55385SAlexander V. Chernikov 	int error;
5652ce55385SAlexander V. Chernikov 
5662ce55385SAlexander V. Chernikov 	NET_EPOCH_ASSERT();
5672ce55385SAlexander V. Chernikov 
5682ce55385SAlexander V. Chernikov 	bzero(rc, sizeof(struct rib_cmd_info));
5692ce55385SAlexander V. Chernikov 	rc->rc_cmd = RTM_DELETE;
5702ce55385SAlexander V. Chernikov 
5712ce55385SAlexander V. Chernikov 	struct rib_head *rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
5722ce55385SAlexander V. Chernikov 	if (rnh == NULL)
5732ce55385SAlexander V. Chernikov 		return (EAFNOSUPPORT);
5742ce55385SAlexander V. Chernikov 
5752ce55385SAlexander V. Chernikov 	if (dst->sa_len > sizeof(mask_storage)) {
5762ce55385SAlexander V. Chernikov 		FIB_RH_LOG(LOG_DEBUG, rnh, "error: dst->sa_len too big: %d", dst->sa_len);
5772ce55385SAlexander V. Chernikov 		return (EINVAL);
5782ce55385SAlexander V. Chernikov 	}
5792ce55385SAlexander V. Chernikov 
5802ce55385SAlexander V. Chernikov 	if (!fill_pxmask_family(dst->sa_family, plen, dst, &netmask)) {
5812ce55385SAlexander V. Chernikov 		FIB_RH_LOG(LOG_DEBUG, rnh, "error: invalid plen %d", plen);
5822ce55385SAlexander V. Chernikov 		return (EINVAL);
5832ce55385SAlexander V. Chernikov 	}
5842ce55385SAlexander V. Chernikov 
5852ce55385SAlexander V. Chernikov 	int prio = (op_flags & RTM_F_FORCE) ? NH_PRIORITY_HIGH : NH_PRIORITY_NORMAL;
5862ce55385SAlexander V. Chernikov 
5872ce55385SAlexander V. Chernikov 	RIB_WLOCK(rnh);
5882ce55385SAlexander V. Chernikov 	struct route_nhop_data rnd;
5892ce55385SAlexander V. Chernikov 	struct rtentry *rt = lookup_prefix_bysa(rnh, dst, netmask, &rnd);
5902ce55385SAlexander V. Chernikov 	if (rt != NULL) {
5912ce55385SAlexander V. Chernikov 		error = rt_delete_conditional(rnh, rt, prio, filter_func,
5922ce55385SAlexander V. Chernikov 		    filter_arg, rc);
5932ce55385SAlexander V. Chernikov 	} else
5942ce55385SAlexander V. Chernikov 		error = ESRCH;
5952ce55385SAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
5962ce55385SAlexander V. Chernikov 
5972ce55385SAlexander V. Chernikov 	if (error != 0)
5982ce55385SAlexander V. Chernikov 		return (error);
5992ce55385SAlexander V. Chernikov 
6002ce55385SAlexander V. Chernikov 	rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
6012ce55385SAlexander V. Chernikov 
6022ce55385SAlexander V. Chernikov 	if (rc->rc_cmd == RTM_DELETE)
6035c4d2252SAlexander V. Chernikov 		rt_free(rc->rc_rt);
6042ce55385SAlexander V. Chernikov #ifdef ROUTE_MPATH
6052ce55385SAlexander V. Chernikov 	else {
6062ce55385SAlexander V. Chernikov 		/*
6072ce55385SAlexander V. Chernikov 		 * Deleting 1 path may result in RTM_CHANGE to
6082ce55385SAlexander V. Chernikov 		 * a different mpath group/nhop.
6092ce55385SAlexander V. Chernikov 		 * Free old mpath group.
6102ce55385SAlexander V. Chernikov 		 */
6112ce55385SAlexander V. Chernikov 		nhop_free_any(rc->rc_nh_old);
6122ce55385SAlexander V. Chernikov 	}
6132ce55385SAlexander V. Chernikov #endif
6142ce55385SAlexander V. Chernikov 
6152ce55385SAlexander V. Chernikov 	return (0);
6162ce55385SAlexander V. Chernikov }
6172ce55385SAlexander V. Chernikov 
618aa8f9f90SAlexander V. Chernikov /*
61940503b79SAlexander V. Chernikov  * Tries to copy route @rt from one rtable to the rtable specified by @dst_rh.
62040503b79SAlexander V. Chernikov  * @rt: route to copy.
62140503b79SAlexander V. Chernikov  * @rnd_src: nhop and weight. Multipath routes are not supported
62240503b79SAlexander V. Chernikov  * @rh_dst: target rtable.
62340503b79SAlexander V. Chernikov  * @rc: operation result storage
62440503b79SAlexander V. Chernikov  *
62540503b79SAlexander V. Chernikov  * Return 0 on success.
62640503b79SAlexander V. Chernikov  */
62740503b79SAlexander V. Chernikov int
62840503b79SAlexander V. Chernikov rib_copy_route(struct rtentry *rt, const struct route_nhop_data *rnd_src,
62940503b79SAlexander V. Chernikov     struct rib_head *rh_dst, struct rib_cmd_info *rc)
63040503b79SAlexander V. Chernikov {
63169077c81SMateusz Guzik 	struct nhop_object __diagused *nh_src = rnd_src->rnd_nhop;
63240503b79SAlexander V. Chernikov 	int error;
63340503b79SAlexander V. Chernikov 
63440503b79SAlexander V. Chernikov 	MPASS((nh_src->nh_flags & NHF_MULTIPATH) == 0);
63540503b79SAlexander V. Chernikov 
636578a99c9SAlexander V. Chernikov 	IF_DEBUG_LEVEL(LOG_DEBUG2) {
63740503b79SAlexander V. Chernikov 		char nhbuf[NHOP_PRINT_BUFSIZE], rtbuf[NHOP_PRINT_BUFSIZE];
63840503b79SAlexander V. Chernikov 		nhop_print_buf_any(nh_src, nhbuf, sizeof(nhbuf));
63940503b79SAlexander V. Chernikov 		rt_print_buf(rt, rtbuf, sizeof(rtbuf));
64040503b79SAlexander V. Chernikov 		FIB_RH_LOG(LOG_DEBUG2, rh_dst, "copying %s -> %s from fib %u",
64140503b79SAlexander V. Chernikov 		    rtbuf, nhbuf, nhop_get_fibnum(nh_src));
642578a99c9SAlexander V. Chernikov 	}
64340503b79SAlexander V. Chernikov 	struct nhop_object *nh = nhop_alloc(rh_dst->rib_fibnum, rh_dst->rib_family);
64440503b79SAlexander V. Chernikov 	if (nh == NULL) {
64540503b79SAlexander V. Chernikov 		FIB_RH_LOG(LOG_INFO, rh_dst, "unable to allocate new nexthop");
64640503b79SAlexander V. Chernikov 		return (ENOMEM);
64740503b79SAlexander V. Chernikov 	}
64840503b79SAlexander V. Chernikov 	nhop_copy(nh, rnd_src->rnd_nhop);
649000250beSAlexander V. Chernikov 	nhop_set_origin(nh, nhop_get_origin(rnd_src->rnd_nhop));
65040503b79SAlexander V. Chernikov 	nhop_set_fibnum(nh, rh_dst->rib_fibnum);
65140503b79SAlexander V. Chernikov 	nh = nhop_get_nhop_internal(rh_dst, nh, &error);
65240503b79SAlexander V. Chernikov 	if (error != 0) {
65340503b79SAlexander V. Chernikov 		FIB_RH_LOG(LOG_INFO, rh_dst,
65440503b79SAlexander V. Chernikov 		    "unable to finalize new nexthop: error %d", error);
65540503b79SAlexander V. Chernikov 		return (ENOMEM);
65640503b79SAlexander V. Chernikov 	}
65740503b79SAlexander V. Chernikov 
65840503b79SAlexander V. Chernikov 	struct rtentry *rt_new = rt_alloc(rh_dst, rt_key(rt), rt_mask(rt));
65940503b79SAlexander V. Chernikov 	if (rt_new == NULL) {
66040503b79SAlexander V. Chernikov 		FIB_RH_LOG(LOG_INFO, rh_dst, "unable to create new rtentry");
66140503b79SAlexander V. Chernikov 		nhop_free(nh);
66240503b79SAlexander V. Chernikov 		return (ENOMEM);
66340503b79SAlexander V. Chernikov 	}
66440503b79SAlexander V. Chernikov 
66540503b79SAlexander V. Chernikov 	struct route_nhop_data rnd = {
66640503b79SAlexander V. Chernikov 		.rnd_nhop = nh,
66740503b79SAlexander V. Chernikov 		.rnd_weight = rnd_src->rnd_weight
66840503b79SAlexander V. Chernikov 	};
66940503b79SAlexander V. Chernikov 	int op_flags = RTM_F_CREATE | (NH_IS_PINNED(nh) ? RTM_F_FORCE : 0);
67040503b79SAlexander V. Chernikov 	error = add_route_flags(rh_dst, rt_new, &rnd, op_flags, rc);
67140503b79SAlexander V. Chernikov 
67240503b79SAlexander V. Chernikov 	if (error != 0) {
673578a99c9SAlexander V. Chernikov 		IF_DEBUG_LEVEL(LOG_DEBUG2) {
67440503b79SAlexander V. Chernikov 			char buf[NHOP_PRINT_BUFSIZE];
67540503b79SAlexander V. Chernikov 			rt_print_buf(rt_new, buf, sizeof(buf));
676578a99c9SAlexander V. Chernikov 			FIB_RH_LOG(LOG_DEBUG, rh_dst,
677578a99c9SAlexander V. Chernikov 			    "Unable to add route %s: error %d", buf, error);
678578a99c9SAlexander V. Chernikov 		}
67940503b79SAlexander V. Chernikov 		nhop_free(nh);
68040503b79SAlexander V. Chernikov 		rt_free_immediate(rt_new);
68140503b79SAlexander V. Chernikov 	}
68240503b79SAlexander V. Chernikov 	return (error);
68340503b79SAlexander V. Chernikov }
68440503b79SAlexander V. Chernikov 
68540503b79SAlexander V. Chernikov /*
686da187ddbSAlexander V. Chernikov  * Adds route defined by @info into the kernel table specified by @fibnum and
687da187ddbSAlexander V. Chernikov  * sa_family in @info->rti_info[RTAX_DST].
688da187ddbSAlexander V. Chernikov  *
689da187ddbSAlexander V. Chernikov  * Returns 0 on success and fills in operation metadata into @rc.
690da187ddbSAlexander V. Chernikov  */
691da187ddbSAlexander V. Chernikov int
692da187ddbSAlexander V. Chernikov rib_add_route(uint32_t fibnum, struct rt_addrinfo *info,
693da187ddbSAlexander V. Chernikov     struct rib_cmd_info *rc)
694da187ddbSAlexander V. Chernikov {
695da187ddbSAlexander V. Chernikov 	struct rib_head *rnh;
696aa8f9f90SAlexander V. Chernikov 	int error;
697da187ddbSAlexander V. Chernikov 
698da187ddbSAlexander V. Chernikov 	NET_EPOCH_ASSERT();
699da187ddbSAlexander V. Chernikov 
700da187ddbSAlexander V. Chernikov 	rnh = get_rnh(fibnum, info);
701da187ddbSAlexander V. Chernikov 	if (rnh == NULL)
702da187ddbSAlexander V. Chernikov 		return (EAFNOSUPPORT);
703da187ddbSAlexander V. Chernikov 
704da187ddbSAlexander V. Chernikov 	/*
705da187ddbSAlexander V. Chernikov 	 * Check consistency between RTF_HOST flag and netmask
706da187ddbSAlexander V. Chernikov 	 * existence.
707da187ddbSAlexander V. Chernikov 	 */
708da187ddbSAlexander V. Chernikov 	if (info->rti_flags & RTF_HOST)
709da187ddbSAlexander V. Chernikov 		info->rti_info[RTAX_NETMASK] = NULL;
7106fa8ed43SAlexander V. Chernikov 	else if (info->rti_info[RTAX_NETMASK] == NULL) {
7116fa8ed43SAlexander V. Chernikov 		FIB_RH_LOG(LOG_DEBUG, rnh, "error: no RTF_HOST and empty netmask");
712da187ddbSAlexander V. Chernikov 		return (EINVAL);
7136fa8ed43SAlexander V. Chernikov 	}
714da187ddbSAlexander V. Chernikov 
715da187ddbSAlexander V. Chernikov 	bzero(rc, sizeof(struct rib_cmd_info));
716da187ddbSAlexander V. Chernikov 	rc->rc_cmd = RTM_ADD;
717da187ddbSAlexander V. Chernikov 
7180d60e88bSAlexander V. Chernikov 	error = add_route_byinfo(rnh, info, rc);
719aa8f9f90SAlexander V. Chernikov 	if (error == 0)
720aa8f9f90SAlexander V. Chernikov 		rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
721aa8f9f90SAlexander V. Chernikov 
722aa8f9f90SAlexander V. Chernikov 	return (error);
723da187ddbSAlexander V. Chernikov }
724da187ddbSAlexander V. Chernikov 
72566230639SAlexander V. Chernikov static int
72666230639SAlexander V. Chernikov add_route_byinfo(struct rib_head *rnh, struct rt_addrinfo *info,
72766230639SAlexander V. Chernikov     struct rib_cmd_info *rc)
72866230639SAlexander V. Chernikov {
7292ce55385SAlexander V. Chernikov 	struct route_nhop_data rnd_add;
7304d2c2509SAlexander V. Chernikov 	struct nhop_object *nh;
7312ce55385SAlexander V. Chernikov 	struct rtentry *rt;
73266230639SAlexander V. Chernikov 	struct sockaddr *dst, *gateway, *netmask;
7332ce55385SAlexander V. Chernikov 	int error;
7344d2c2509SAlexander V. Chernikov 
7354d2c2509SAlexander V. Chernikov 	dst = info->rti_info[RTAX_DST];
7364d2c2509SAlexander V. Chernikov 	gateway = info->rti_info[RTAX_GATEWAY];
7374d2c2509SAlexander V. Chernikov 	netmask = info->rti_info[RTAX_NETMASK];
7384d2c2509SAlexander V. Chernikov 
7392ce55385SAlexander V. Chernikov 	if ((info->rti_flags & RTF_GATEWAY) && !gateway) {
7406fa8ed43SAlexander V. Chernikov 		FIB_RH_LOG(LOG_DEBUG, rnh, "error: RTF_GATEWAY set with empty gw");
7414d2c2509SAlexander V. Chernikov 		return (EINVAL);
7426fa8ed43SAlexander V. Chernikov 	}
743fe05d1ddSAlexander V. Chernikov 	if (dst && gateway && !nhop_check_gateway(dst->sa_family, gateway->sa_family)) {
7446fa8ed43SAlexander V. Chernikov 		FIB_RH_LOG(LOG_DEBUG, rnh,
7456fa8ed43SAlexander V. Chernikov 		    "error: invalid dst/gateway family combination (%d, %d)",
7466fa8ed43SAlexander V. Chernikov 		    dst->sa_family, gateway->sa_family);
7474d2c2509SAlexander V. Chernikov 		return (EINVAL);
7486fa8ed43SAlexander V. Chernikov 	}
7494d2c2509SAlexander V. Chernikov 
7506fa8ed43SAlexander V. Chernikov 	if (dst->sa_len > sizeof(((struct rtentry *)NULL)->rt_dstb)) {
7516fa8ed43SAlexander V. Chernikov 		FIB_RH_LOG(LOG_DEBUG, rnh, "error: dst->sa_len too large: %d",
7526fa8ed43SAlexander V. Chernikov 		    dst->sa_len);
7534d2c2509SAlexander V. Chernikov 		return (EINVAL);
7546fa8ed43SAlexander V. Chernikov 	}
7554d2c2509SAlexander V. Chernikov 
7564d2c2509SAlexander V. Chernikov 	if (info->rti_ifa == NULL) {
7574d2c2509SAlexander V. Chernikov 		error = rt_getifa_fib(info, rnh->rib_fibnum);
7584d2c2509SAlexander V. Chernikov 		if (error)
7594d2c2509SAlexander V. Chernikov 			return (error);
7604d2c2509SAlexander V. Chernikov 	}
7614d2c2509SAlexander V. Chernikov 
7625c4d2252SAlexander V. Chernikov 	if ((rt = rt_alloc(rnh, dst, netmask)) == NULL)
7634d2c2509SAlexander V. Chernikov 		return (ENOBUFS);
7644d2c2509SAlexander V. Chernikov 
76566230639SAlexander V. Chernikov 	error = nhop_create_from_info(rnh, info, &nh);
76666230639SAlexander V. Chernikov 	if (error != 0) {
7675c4d2252SAlexander V. Chernikov 		rt_free_immediate(rt);
768b122304fSAlexander V. Chernikov 		return (error);
76966230639SAlexander V. Chernikov 	}
770b122304fSAlexander V. Chernikov 
77166230639SAlexander V. Chernikov 	rnd_add.rnd_nhop = nh;
77266230639SAlexander V. Chernikov 	rnd_add.rnd_weight = get_info_weight(info, RT_DEFAULT_WEIGHT);
7734d2c2509SAlexander V. Chernikov 
7742ce55385SAlexander V. Chernikov 	int op_flags = RTM_F_CREATE;
7752ce55385SAlexander V. Chernikov 
776*1da4954cSAlexander V. Chernikov 	/*
777*1da4954cSAlexander V. Chernikov 	 * Set the desired action when the route already exists:
778*1da4954cSAlexander V. Chernikov 	 * If RTF_PINNED is present, assume the direct kernel routes that cannot be multipath.
779*1da4954cSAlexander V. Chernikov 	 * Otherwise, append the path.
780*1da4954cSAlexander V. Chernikov 	 */
781*1da4954cSAlexander V. Chernikov 	op_flags |= (info->rti_flags & RTF_PINNED) ? RTM_F_REPLACE : RTM_F_APPEND;
782*1da4954cSAlexander V. Chernikov 
783*1da4954cSAlexander V. Chernikov 	return (add_route_flags(rnh, rt, &rnd_add, op_flags, rc));
7842ce55385SAlexander V. Chernikov }
7852ce55385SAlexander V. Chernikov 
7862ce55385SAlexander V. Chernikov static int
7872ce55385SAlexander V. Chernikov add_route_flags(struct rib_head *rnh, struct rtentry *rt, struct route_nhop_data *rnd_add,
7882ce55385SAlexander V. Chernikov     int op_flags, struct rib_cmd_info *rc)
7892ce55385SAlexander V. Chernikov {
7902ce55385SAlexander V. Chernikov 	struct route_nhop_data rnd_orig;
7912ce55385SAlexander V. Chernikov 	struct nhop_object *nh;
7922ce55385SAlexander V. Chernikov 	struct rtentry *rt_orig;
7932ce55385SAlexander V. Chernikov 	int error = 0;
7942ce55385SAlexander V. Chernikov 
795a0aa160bSAlexander V. Chernikov 	MPASS(rt != NULL);
796a0aa160bSAlexander V. Chernikov 
7972ce55385SAlexander V. Chernikov 	nh = rnd_add->rnd_nhop;
7982ce55385SAlexander V. Chernikov 
7994d2c2509SAlexander V. Chernikov 	RIB_WLOCK(rnh);
8004d2c2509SAlexander V. Chernikov 
8012ce55385SAlexander V. Chernikov 	rt_orig = lookup_prefix_rt(rnh, rt, &rnd_orig);
8022ce55385SAlexander V. Chernikov 
803aa8f9f90SAlexander V. Chernikov 	if (rt_orig == NULL) {
8042ce55385SAlexander V. Chernikov 		if (op_flags & RTM_F_CREATE)
8052ce55385SAlexander V. Chernikov 			error = add_route(rnh, rt, rnd_add, rc);
8062ce55385SAlexander V. Chernikov 		else
807c24a8f19SAlexander V. Chernikov 			error = ESRCH; /* no entry but creation was not required */
808aa8f9f90SAlexander V. Chernikov 		RIB_WUNLOCK(rnh);
8092ce55385SAlexander V. Chernikov 		if (error != 0)
8102ce55385SAlexander V. Chernikov 			goto out;
8112ce55385SAlexander V. Chernikov 		return (0);
812da187ddbSAlexander V. Chernikov 	}
813aa8f9f90SAlexander V. Chernikov 
8142ce55385SAlexander V. Chernikov 	if (op_flags & RTM_F_EXCL) {
8152ce55385SAlexander V. Chernikov 		/* We have existing route in the RIB but not allowed to replace. */
816aa8f9f90SAlexander V. Chernikov 		RIB_WUNLOCK(rnh);
8172ce55385SAlexander V. Chernikov 		error = EEXIST;
8182ce55385SAlexander V. Chernikov 		goto out;
8192ce55385SAlexander V. Chernikov 	}
8202ce55385SAlexander V. Chernikov 
8212ce55385SAlexander V. Chernikov 	/* Now either append or replace */
8222ce55385SAlexander V. Chernikov 	if (op_flags & RTM_F_REPLACE) {
8232ce55385SAlexander V. Chernikov 		if (nhop_get_prio(rnd_orig.rnd_nhop) > nhop_get_prio(rnd_add->rnd_nhop)) {
8242ce55385SAlexander V. Chernikov 			/* Old path is "better" (e.g. has PINNED flag set) */
82506e87959SAlexander V. Chernikov 			RIB_WUNLOCK(rnh);
8262ce55385SAlexander V. Chernikov 			error = EEXIST;
8272ce55385SAlexander V. Chernikov 			goto out;
8282ce55385SAlexander V. Chernikov 		}
8292ce55385SAlexander V. Chernikov 		change_route(rnh, rt_orig, rnd_add, rc);
8302ce55385SAlexander V. Chernikov 		RIB_WUNLOCK(rnh);
8312ce55385SAlexander V. Chernikov 		nh = rc->rc_nh_old;
8322ce55385SAlexander V. Chernikov 		goto out;
8334d2c2509SAlexander V. Chernikov 	}
834aa8f9f90SAlexander V. Chernikov 
8354d2c2509SAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
8364d2c2509SAlexander V. Chernikov 
837fedeb08bSAlexander V. Chernikov #ifdef ROUTE_MPATH
8382ce55385SAlexander V. Chernikov 	if ((op_flags & RTM_F_APPEND) && rib_can_multipath(rnh) &&
8392ce55385SAlexander V. Chernikov 	    nhop_can_multipath(rnd_add->rnd_nhop) &&
8402ce55385SAlexander V. Chernikov 	    nhop_can_multipath(rnd_orig.rnd_nhop)) {
8412ce55385SAlexander V. Chernikov 
8422ce55385SAlexander V. Chernikov 		for (int i = 0; i < RIB_MAX_RETRIES; i++) {
843c24a8f19SAlexander V. Chernikov 			error = add_route_flags_mpath(rnh, rt_orig, rnd_add, &rnd_orig,
8442ce55385SAlexander V. Chernikov 			    op_flags, rc);
8452ce55385SAlexander V. Chernikov 			if (error != EAGAIN)
8462ce55385SAlexander V. Chernikov 				break;
8472ce55385SAlexander V. Chernikov 			RTSTAT_INC(rts_add_retry);
8482ce55385SAlexander V. Chernikov 		}
84941e66f4eSAlexander V. Chernikov 
850fedeb08bSAlexander V. Chernikov 		/*
8512ce55385SAlexander V. Chernikov 		 *  Original nhop reference is unused in any case.
852fedeb08bSAlexander V. Chernikov 		 */
8532ce55385SAlexander V. Chernikov 		nhop_free_any(rnd_add->rnd_nhop);
8542ce55385SAlexander V. Chernikov 		if (op_flags & RTM_F_CREATE) {
8552ce55385SAlexander V. Chernikov 			if (error != 0 || rc->rc_cmd != RTM_ADD)
8565c4d2252SAlexander V. Chernikov 				rt_free_immediate(rt);
8572ce55385SAlexander V. Chernikov 		}
8582ce55385SAlexander V. Chernikov 		return (error);
8592ce55385SAlexander V. Chernikov 	}
8602ce55385SAlexander V. Chernikov #endif
8612ce55385SAlexander V. Chernikov 	/* Out of options - free state and return error */
8622ce55385SAlexander V. Chernikov 	error = EEXIST;
8632ce55385SAlexander V. Chernikov out:
8642ce55385SAlexander V. Chernikov 	if (op_flags & RTM_F_CREATE)
8655c4d2252SAlexander V. Chernikov 		rt_free_immediate(rt);
8662ce55385SAlexander V. Chernikov 	nhop_free_any(nh);
8674d2c2509SAlexander V. Chernikov 
868b122304fSAlexander V. Chernikov 	return (error);
8694d2c2509SAlexander V. Chernikov }
8704d2c2509SAlexander V. Chernikov 
871258828d0SAlexander V. Chernikov #ifdef ROUTE_MPATH
8722ce55385SAlexander V. Chernikov static int
8732ce55385SAlexander V. Chernikov add_route_flags_mpath(struct rib_head *rnh, struct rtentry *rt,
8742ce55385SAlexander V. Chernikov     struct route_nhop_data *rnd_add, struct route_nhop_data *rnd_orig,
8752ce55385SAlexander V. Chernikov     int op_flags, struct rib_cmd_info *rc)
8762ce55385SAlexander V. Chernikov {
8772ce55385SAlexander V. Chernikov 	RIB_RLOCK_TRACKER;
8782ce55385SAlexander V. Chernikov 	struct route_nhop_data rnd_new;
8792ce55385SAlexander V. Chernikov 	int error = 0;
8802ce55385SAlexander V. Chernikov 
8812ce55385SAlexander V. Chernikov 	error = nhgrp_get_addition_group(rnh, rnd_orig, rnd_add, &rnd_new);
8822ce55385SAlexander V. Chernikov 	if (error != 0) {
8832ce55385SAlexander V. Chernikov 		if (error == EAGAIN) {
8842ce55385SAlexander V. Chernikov 			/*
8852ce55385SAlexander V. Chernikov 			 * Group creation failed, most probably because
8862ce55385SAlexander V. Chernikov 			 * @rnd_orig data got scheduled for deletion.
8872ce55385SAlexander V. Chernikov 			 * Refresh @rnd_orig data and retry.
8882ce55385SAlexander V. Chernikov 			 */
8892ce55385SAlexander V. Chernikov 			RIB_RLOCK(rnh);
8902ce55385SAlexander V. Chernikov 			lookup_prefix_rt(rnh, rt, rnd_orig);
8912ce55385SAlexander V. Chernikov 			RIB_RUNLOCK(rnh);
8922ce55385SAlexander V. Chernikov 			if (rnd_orig == NULL && !(op_flags & RTM_F_CREATE)) {
8932ce55385SAlexander V. Chernikov 				/* In this iteration route doesn't exist */
8942ce55385SAlexander V. Chernikov 				error = ENOENT;
8952ce55385SAlexander V. Chernikov 			}
8962ce55385SAlexander V. Chernikov 		}
8972ce55385SAlexander V. Chernikov 		return (error);
8982ce55385SAlexander V. Chernikov 	}
8992ce55385SAlexander V. Chernikov 	error = change_route_conditional(rnh, rt, rnd_orig, &rnd_new, rc);
9002ce55385SAlexander V. Chernikov 	if (error != 0)
9012ce55385SAlexander V. Chernikov 		return (error);
9022ce55385SAlexander V. Chernikov 
9032ce55385SAlexander V. Chernikov 	if (V_fib_hash_outbound == 0 && NH_IS_NHGRP(rc->rc_nh_new)) {
9042ce55385SAlexander V. Chernikov 		/*
9052ce55385SAlexander V. Chernikov 		 * First multipath route got installed. Enable local
9062ce55385SAlexander V. Chernikov 		 * outbound connections hashing.
9072ce55385SAlexander V. Chernikov 		 */
9082ce55385SAlexander V. Chernikov 		if (bootverbose)
9092ce55385SAlexander V. Chernikov 			printf("FIB: enabled flowid calculation for locally-originated packets\n");
9102ce55385SAlexander V. Chernikov 		V_fib_hash_outbound = 1;
9112ce55385SAlexander V. Chernikov 	}
9122ce55385SAlexander V. Chernikov 
9132ce55385SAlexander V. Chernikov 	return (0);
9142ce55385SAlexander V. Chernikov }
915258828d0SAlexander V. Chernikov #endif
9162ce55385SAlexander V. Chernikov 
9174d2c2509SAlexander V. Chernikov /*
918da187ddbSAlexander V. Chernikov  * Removes route defined by @info from the kernel table specified by @fibnum and
919da187ddbSAlexander V. Chernikov  * sa_family in @info->rti_info[RTAX_DST].
920da187ddbSAlexander V. Chernikov  *
921da187ddbSAlexander V. Chernikov  * Returns 0 on success and fills in operation metadata into @rc.
922da187ddbSAlexander V. Chernikov  */
923da187ddbSAlexander V. Chernikov int
924da187ddbSAlexander V. Chernikov rib_del_route(uint32_t fibnum, struct rt_addrinfo *info, struct rib_cmd_info *rc)
925da187ddbSAlexander V. Chernikov {
926da187ddbSAlexander V. Chernikov 	struct rib_head *rnh;
927dedeec11SAlexander V. Chernikov 	struct sockaddr *dst, *netmask;
928aa8f9f90SAlexander V. Chernikov 	struct sockaddr_storage mdst;
929aa8f9f90SAlexander V. Chernikov 	int error;
930da187ddbSAlexander V. Chernikov 
931da187ddbSAlexander V. Chernikov 	NET_EPOCH_ASSERT();
932da187ddbSAlexander V. Chernikov 
933da187ddbSAlexander V. Chernikov 	rnh = get_rnh(fibnum, info);
934da187ddbSAlexander V. Chernikov 	if (rnh == NULL)
935da187ddbSAlexander V. Chernikov 		return (EAFNOSUPPORT);
936da187ddbSAlexander V. Chernikov 
937da187ddbSAlexander V. Chernikov 	bzero(rc, sizeof(struct rib_cmd_info));
938da187ddbSAlexander V. Chernikov 	rc->rc_cmd = RTM_DELETE;
939da187ddbSAlexander V. Chernikov 
940dedeec11SAlexander V. Chernikov 	dst = info->rti_info[RTAX_DST];
941aa8f9f90SAlexander V. Chernikov 	netmask = info->rti_info[RTAX_NETMASK];
942aa8f9f90SAlexander V. Chernikov 
943aa8f9f90SAlexander V. Chernikov 	if (netmask != NULL) {
944aa8f9f90SAlexander V. Chernikov 		/* Ensure @dst is always properly masked */
945dedeec11SAlexander V. Chernikov 		if (dst->sa_len > sizeof(mdst)) {
9466fa8ed43SAlexander V. Chernikov 			FIB_RH_LOG(LOG_DEBUG, rnh, "error: dst->sa_len too large");
947aa8f9f90SAlexander V. Chernikov 			return (EINVAL);
9486fa8ed43SAlexander V. Chernikov 		}
949dedeec11SAlexander V. Chernikov 		rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask);
950dedeec11SAlexander V. Chernikov 		dst = (struct sockaddr *)&mdst;
951aa8f9f90SAlexander V. Chernikov 	}
9520d60e88bSAlexander V. Chernikov 
953dedeec11SAlexander V. Chernikov 	rib_filter_f_t *filter_func = NULL;
954dedeec11SAlexander V. Chernikov 	void *filter_arg = NULL;
955dedeec11SAlexander V. Chernikov 	struct gw_filter_data gwd = { .gw = info->rti_info[RTAX_GATEWAY] };
9560d60e88bSAlexander V. Chernikov 
957dedeec11SAlexander V. Chernikov 	if (info->rti_filter != NULL) {
958dedeec11SAlexander V. Chernikov 		filter_func = info->rti_filter;
959dedeec11SAlexander V. Chernikov 		filter_arg = info->rti_filterdata;
960dedeec11SAlexander V. Chernikov 	} else if (gwd.gw != NULL) {
961730bfa28SAlexander V. Chernikov 		filter_func = match_gw_one;
962dedeec11SAlexander V. Chernikov 		filter_arg = &gwd;
963dedeec11SAlexander V. Chernikov 	}
964dedeec11SAlexander V. Chernikov 
965dedeec11SAlexander V. Chernikov 	int prio = get_prio_from_info(info);
966dedeec11SAlexander V. Chernikov 
967dedeec11SAlexander V. Chernikov 	RIB_WLOCK(rnh);
968dedeec11SAlexander V. Chernikov 	struct route_nhop_data rnd;
969dedeec11SAlexander V. Chernikov 	struct rtentry *rt = lookup_prefix_bysa(rnh, dst, netmask, &rnd);
970dedeec11SAlexander V. Chernikov 	if (rt != NULL) {
971dedeec11SAlexander V. Chernikov 		error = rt_delete_conditional(rnh, rt, prio, filter_func,
972dedeec11SAlexander V. Chernikov 		    filter_arg, rc);
973dedeec11SAlexander V. Chernikov 	} else
974dedeec11SAlexander V. Chernikov 		error = ESRCH;
975dedeec11SAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
976aa8f9f90SAlexander V. Chernikov 
9770d60e88bSAlexander V. Chernikov 	if (error != 0)
978aa8f9f90SAlexander V. Chernikov 		return (error);
9790d60e88bSAlexander V. Chernikov 
9800d60e88bSAlexander V. Chernikov 	rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
9810d60e88bSAlexander V. Chernikov 
9820d60e88bSAlexander V. Chernikov 	if (rc->rc_cmd == RTM_DELETE)
9835c4d2252SAlexander V. Chernikov 		rt_free(rc->rc_rt);
9840d60e88bSAlexander V. Chernikov #ifdef ROUTE_MPATH
9850d60e88bSAlexander V. Chernikov 	else {
9860d60e88bSAlexander V. Chernikov 		/*
9870d60e88bSAlexander V. Chernikov 		 * Deleting 1 path may result in RTM_CHANGE to
9880d60e88bSAlexander V. Chernikov 		 * a different mpath group/nhop.
9890d60e88bSAlexander V. Chernikov 		 * Free old mpath group.
9900d60e88bSAlexander V. Chernikov 		 */
9910d60e88bSAlexander V. Chernikov 		nhop_free_any(rc->rc_nh_old);
9920d60e88bSAlexander V. Chernikov 	}
9930d60e88bSAlexander V. Chernikov #endif
9940d60e88bSAlexander V. Chernikov 
9950d60e88bSAlexander V. Chernikov 	return (0);
996da187ddbSAlexander V. Chernikov }
997da187ddbSAlexander V. Chernikov 
998da187ddbSAlexander V. Chernikov /*
999dedeec11SAlexander V. Chernikov  * Conditionally unlinks rtentry paths from @rnh matching @cb.
1000aa8f9f90SAlexander V. Chernikov  * Returns 0 on success with operation result stored in @rc.
1001aa8f9f90SAlexander V. Chernikov  * On error, returns:
1002dedeec11SAlexander V. Chernikov  * ESRCH - if prefix was not found or filter function failed to match
1003aa8f9f90SAlexander V. Chernikov  * EADDRINUSE - if trying to delete higher priority route.
10044d2c2509SAlexander V. Chernikov  */
1005aa8f9f90SAlexander V. Chernikov static int
1006dedeec11SAlexander V. Chernikov rt_delete_conditional(struct rib_head *rnh, struct rtentry *rt,
1007dedeec11SAlexander V. Chernikov     int prio, rib_filter_f_t *cb, void *cbdata, struct rib_cmd_info *rc)
10084d2c2509SAlexander V. Chernikov {
1009dedeec11SAlexander V. Chernikov 	struct nhop_object *nh = rt->rt_nhop;
10104d2c2509SAlexander V. Chernikov 
1011fedeb08bSAlexander V. Chernikov #ifdef ROUTE_MPATH
1012fedeb08bSAlexander V. Chernikov 	if (NH_IS_NHGRP(nh)) {
1013dedeec11SAlexander V. Chernikov 		struct nhgrp_object *nhg = (struct nhgrp_object *)nh;
1014685866bbSAlexander V. Chernikov 		struct route_nhop_data rnd;
1015dedeec11SAlexander V. Chernikov 		int error;
1016dedeec11SAlexander V. Chernikov 
1017dedeec11SAlexander V. Chernikov 		if (cb == NULL)
1018dedeec11SAlexander V. Chernikov 			return (ESRCH);
1019dedeec11SAlexander V. Chernikov 		error = nhgrp_get_filtered_group(rnh, rt, nhg, cb, cbdata, &rnd);
1020dedeec11SAlexander V. Chernikov 		if (error == 0) {
1021dedeec11SAlexander V. Chernikov 			if (rnd.rnd_nhgrp == nhg) {
1022dedeec11SAlexander V. Chernikov 				/* No match, unreference new group and return. */
1023dedeec11SAlexander V. Chernikov 				nhop_free_any(rnd.rnd_nhop);
1024dedeec11SAlexander V. Chernikov 				return (ESRCH);
1025dedeec11SAlexander V. Chernikov 			}
1026dedeec11SAlexander V. Chernikov 			error = change_route(rnh, rt, &rnd, rc);
1027dedeec11SAlexander V. Chernikov 		}
1028fedeb08bSAlexander V. Chernikov 		return (error);
1029fedeb08bSAlexander V. Chernikov 	}
1030fedeb08bSAlexander V. Chernikov #endif
1031dedeec11SAlexander V. Chernikov 	if (cb != NULL && !cb(rt, nh, cbdata))
1032dedeec11SAlexander V. Chernikov 		return (ESRCH);
10334d2c2509SAlexander V. Chernikov 
1034dedeec11SAlexander V. Chernikov 	if (prio < nhop_get_prio(nh))
1035aa8f9f90SAlexander V. Chernikov 		return (EADDRINUSE);
10364d2c2509SAlexander V. Chernikov 
10370d60e88bSAlexander V. Chernikov 	return (delete_route(rnh, rt, rc));
10384d2c2509SAlexander V. Chernikov }
10394d2c2509SAlexander V. Chernikov 
1040da187ddbSAlexander V. Chernikov int
1041da187ddbSAlexander V. Chernikov rib_change_route(uint32_t fibnum, struct rt_addrinfo *info,
1042da187ddbSAlexander V. Chernikov     struct rib_cmd_info *rc)
1043da187ddbSAlexander V. Chernikov {
1044b122304fSAlexander V. Chernikov 	RIB_RLOCK_TRACKER;
1045b122304fSAlexander V. Chernikov 	struct route_nhop_data rnd_orig;
1046da187ddbSAlexander V. Chernikov 	struct rib_head *rnh;
1047b122304fSAlexander V. Chernikov 	struct rtentry *rt;
1048b122304fSAlexander V. Chernikov 	int error;
1049da187ddbSAlexander V. Chernikov 
1050da187ddbSAlexander V. Chernikov 	NET_EPOCH_ASSERT();
1051da187ddbSAlexander V. Chernikov 
1052da187ddbSAlexander V. Chernikov 	rnh = get_rnh(fibnum, info);
1053da187ddbSAlexander V. Chernikov 	if (rnh == NULL)
1054da187ddbSAlexander V. Chernikov 		return (EAFNOSUPPORT);
1055da187ddbSAlexander V. Chernikov 
1056da187ddbSAlexander V. Chernikov 	bzero(rc, sizeof(struct rib_cmd_info));
1057da187ddbSAlexander V. Chernikov 	rc->rc_cmd = RTM_CHANGE;
1058da187ddbSAlexander V. Chernikov 
1059b122304fSAlexander V. Chernikov 	/* Check if updated gateway exists */
1060b122304fSAlexander V. Chernikov 	if ((info->rti_flags & RTF_GATEWAY) &&
10611b95005eSAlexander V. Chernikov 	    (info->rti_info[RTAX_GATEWAY] == NULL)) {
10621b95005eSAlexander V. Chernikov 
10631b95005eSAlexander V. Chernikov 		/*
10641b95005eSAlexander V. Chernikov 		 * route(8) adds RTF_GATEWAY flag if -interface is not set.
10651b95005eSAlexander V. Chernikov 		 * Remove RTF_GATEWAY to enforce consistency and maintain
10661b95005eSAlexander V. Chernikov 		 * compatibility..
10671b95005eSAlexander V. Chernikov 		 */
10681b95005eSAlexander V. Chernikov 		info->rti_flags &= ~RTF_GATEWAY;
10691b95005eSAlexander V. Chernikov 	}
1070da187ddbSAlexander V. Chernikov 
1071b122304fSAlexander V. Chernikov 	/*
1072b122304fSAlexander V. Chernikov 	 * route change is done in multiple steps, with dropping and
1073b122304fSAlexander V. Chernikov 	 * reacquiring lock. In the situations with multiple processes
1074b122304fSAlexander V. Chernikov 	 * changes the same route in can lead to the case when route
1075b122304fSAlexander V. Chernikov 	 * is changed between the steps. Address it by retrying the operation
1076b122304fSAlexander V. Chernikov 	 * multiple times before failing.
1077b122304fSAlexander V. Chernikov 	 */
10784d2c2509SAlexander V. Chernikov 
10794d2c2509SAlexander V. Chernikov 	RIB_RLOCK(rnh);
10804d2c2509SAlexander V. Chernikov 	rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
10814d2c2509SAlexander V. Chernikov 	    info->rti_info[RTAX_NETMASK], &rnh->head);
10824d2c2509SAlexander V. Chernikov 
10834d2c2509SAlexander V. Chernikov 	if (rt == NULL) {
10844d2c2509SAlexander V. Chernikov 		RIB_RUNLOCK(rnh);
10854d2c2509SAlexander V. Chernikov 		return (ESRCH);
10864d2c2509SAlexander V. Chernikov 	}
10874d2c2509SAlexander V. Chernikov 
1088b122304fSAlexander V. Chernikov 	rnd_orig.rnd_nhop = rt->rt_nhop;
1089b122304fSAlexander V. Chernikov 	rnd_orig.rnd_weight = rt->rt_weight;
10904d2c2509SAlexander V. Chernikov 
10914d2c2509SAlexander V. Chernikov 	RIB_RUNLOCK(rnh);
10924d2c2509SAlexander V. Chernikov 
1093b122304fSAlexander V. Chernikov 	for (int i = 0; i < RIB_MAX_RETRIES; i++) {
10940d60e88bSAlexander V. Chernikov 		error = change_route_byinfo(rnh, rt, info, &rnd_orig, rc);
1095b122304fSAlexander V. Chernikov 		if (error != EAGAIN)
1096b122304fSAlexander V. Chernikov 			break;
1097b122304fSAlexander V. Chernikov 	}
1098b122304fSAlexander V. Chernikov 
1099b122304fSAlexander V. Chernikov 	return (error);
1100b122304fSAlexander V. Chernikov }
1101b122304fSAlexander V. Chernikov 
1102b122304fSAlexander V. Chernikov static int
1103fedeb08bSAlexander V. Chernikov change_nhop(struct rib_head *rnh, struct rt_addrinfo *info,
1104fedeb08bSAlexander V. Chernikov     struct nhop_object *nh_orig, struct nhop_object **nh_new)
1105b122304fSAlexander V. Chernikov {
1106fedeb08bSAlexander V. Chernikov 	int error;
11074d2c2509SAlexander V. Chernikov 
11084d2c2509SAlexander V. Chernikov 	/*
11094d2c2509SAlexander V. Chernikov 	 * New gateway could require new ifaddr, ifp;
11104d2c2509SAlexander V. Chernikov 	 * flags may also be different; ifp may be specified
11114d2c2509SAlexander V. Chernikov 	 * by ll sockaddr when protocol address is ambiguous
11124d2c2509SAlexander V. Chernikov 	 */
11134d2c2509SAlexander V. Chernikov 	if (((nh_orig->nh_flags & NHF_GATEWAY) &&
11144d2c2509SAlexander V. Chernikov 	    info->rti_info[RTAX_GATEWAY] != NULL) ||
11154d2c2509SAlexander V. Chernikov 	    info->rti_info[RTAX_IFP] != NULL ||
11164d2c2509SAlexander V. Chernikov 	    (info->rti_info[RTAX_IFA] != NULL &&
11174d2c2509SAlexander V. Chernikov 	     !sa_equal(info->rti_info[RTAX_IFA], nh_orig->nh_ifa->ifa_addr))) {
11184d2c2509SAlexander V. Chernikov 		error = rt_getifa_fib(info, rnh->rib_fibnum);
11194d2c2509SAlexander V. Chernikov 
11204d2c2509SAlexander V. Chernikov 		if (error != 0) {
11214d2c2509SAlexander V. Chernikov 			info->rti_ifa = NULL;
11224d2c2509SAlexander V. Chernikov 			return (error);
11234d2c2509SAlexander V. Chernikov 		}
11244d2c2509SAlexander V. Chernikov 	}
11254d2c2509SAlexander V. Chernikov 
1126fedeb08bSAlexander V. Chernikov 	error = nhop_create_from_nhop(rnh, nh_orig, info, nh_new);
11274d2c2509SAlexander V. Chernikov 	info->rti_ifa = NULL;
1128fedeb08bSAlexander V. Chernikov 
1129fedeb08bSAlexander V. Chernikov 	return (error);
1130fedeb08bSAlexander V. Chernikov }
1131fedeb08bSAlexander V. Chernikov 
1132fedeb08bSAlexander V. Chernikov #ifdef ROUTE_MPATH
1133fedeb08bSAlexander V. Chernikov static int
11340d60e88bSAlexander V. Chernikov change_mpath_route(struct rib_head *rnh, struct rtentry *rt,
11350d60e88bSAlexander V. Chernikov     struct rt_addrinfo *info, struct route_nhop_data *rnd_orig,
11360d60e88bSAlexander V. Chernikov     struct rib_cmd_info *rc)
1137fedeb08bSAlexander V. Chernikov {
1138c38da70cSAlexander V. Chernikov 	int error = 0, found_idx = 0;
1139c38da70cSAlexander V. Chernikov 	struct nhop_object *nh_orig = NULL, *nh_new;
1140ae6bfd12SAlexander V. Chernikov 	struct route_nhop_data rnd_new = {};
1141ae6bfd12SAlexander V. Chernikov 	const struct weightened_nhop *wn = NULL;
1142ae6bfd12SAlexander V. Chernikov 	struct weightened_nhop *wn_new;
1143fedeb08bSAlexander V. Chernikov 	uint32_t num_nhops;
1144fedeb08bSAlexander V. Chernikov 
1145c38da70cSAlexander V. Chernikov 	wn = nhgrp_get_nhops(rnd_orig->rnd_nhgrp, &num_nhops);
1146fedeb08bSAlexander V. Chernikov 	for (int i = 0; i < num_nhops; i++) {
1147c38da70cSAlexander V. Chernikov 		if (check_info_match_nhop(info, NULL, wn[i].nh) == 0) {
1148fedeb08bSAlexander V. Chernikov 			nh_orig = wn[i].nh;
1149c38da70cSAlexander V. Chernikov 			found_idx = i;
1150fedeb08bSAlexander V. Chernikov 			break;
1151fedeb08bSAlexander V. Chernikov 		}
1152fedeb08bSAlexander V. Chernikov 	}
1153fedeb08bSAlexander V. Chernikov 
1154fedeb08bSAlexander V. Chernikov 	if (nh_orig == NULL)
1155fedeb08bSAlexander V. Chernikov 		return (ESRCH);
1156fedeb08bSAlexander V. Chernikov 
1157fedeb08bSAlexander V. Chernikov 	error = change_nhop(rnh, info, nh_orig, &nh_new);
11584d2c2509SAlexander V. Chernikov 	if (error != 0)
11594d2c2509SAlexander V. Chernikov 		return (error);
11604d2c2509SAlexander V. Chernikov 
1161fedeb08bSAlexander V. Chernikov 	wn_new = mallocarray(num_nhops, sizeof(struct weightened_nhop),
1162fedeb08bSAlexander V. Chernikov 	    M_TEMP, M_NOWAIT | M_ZERO);
1163fedeb08bSAlexander V. Chernikov 	if (wn_new == NULL) {
1164fedeb08bSAlexander V. Chernikov 		nhop_free(nh_new);
1165fedeb08bSAlexander V. Chernikov 		return (EAGAIN);
1166fedeb08bSAlexander V. Chernikov 	}
11674d2c2509SAlexander V. Chernikov 
1168fedeb08bSAlexander V. Chernikov 	memcpy(wn_new, wn, num_nhops * sizeof(struct weightened_nhop));
1169c38da70cSAlexander V. Chernikov 	wn_new[found_idx].nh = nh_new;
1170c38da70cSAlexander V. Chernikov 	wn_new[found_idx].weight = get_info_weight(info, wn[found_idx].weight);
1171fedeb08bSAlexander V. Chernikov 
1172db4ca190SAlexander V. Chernikov 	error = nhgrp_get_group(rnh, wn_new, num_nhops, 0, &rnd_new.rnd_nhgrp);
1173fedeb08bSAlexander V. Chernikov 	nhop_free(nh_new);
1174fedeb08bSAlexander V. Chernikov 	free(wn_new, M_TEMP);
1175fedeb08bSAlexander V. Chernikov 
1176fedeb08bSAlexander V. Chernikov 	if (error != 0)
1177fedeb08bSAlexander V. Chernikov 		return (error);
1178fedeb08bSAlexander V. Chernikov 
11790d60e88bSAlexander V. Chernikov 	error = change_route_conditional(rnh, rt, rnd_orig, &rnd_new, rc);
1180fedeb08bSAlexander V. Chernikov 
1181fedeb08bSAlexander V. Chernikov 	return (error);
1182fedeb08bSAlexander V. Chernikov }
1183fedeb08bSAlexander V. Chernikov #endif
1184fedeb08bSAlexander V. Chernikov 
1185fedeb08bSAlexander V. Chernikov static int
11860d60e88bSAlexander V. Chernikov change_route_byinfo(struct rib_head *rnh, struct rtentry *rt,
11870d60e88bSAlexander V. Chernikov     struct rt_addrinfo *info, struct route_nhop_data *rnd_orig,
11880d60e88bSAlexander V. Chernikov     struct rib_cmd_info *rc)
1189fedeb08bSAlexander V. Chernikov {
1190fedeb08bSAlexander V. Chernikov 	int error = 0;
11915de5b5a3SWarner Losh 	struct nhop_object *nh_orig;
1192fedeb08bSAlexander V. Chernikov 	struct route_nhop_data rnd_new;
1193fedeb08bSAlexander V. Chernikov 
1194fedeb08bSAlexander V. Chernikov 	nh_orig = rnd_orig->rnd_nhop;
1195fedeb08bSAlexander V. Chernikov 	if (nh_orig == NULL)
1196fedeb08bSAlexander V. Chernikov 		return (ESRCH);
1197fedeb08bSAlexander V. Chernikov 
1198fedeb08bSAlexander V. Chernikov #ifdef ROUTE_MPATH
1199fedeb08bSAlexander V. Chernikov 	if (NH_IS_NHGRP(nh_orig))
12000d60e88bSAlexander V. Chernikov 		return (change_mpath_route(rnh, rt, info, rnd_orig, rc));
1201fedeb08bSAlexander V. Chernikov #endif
1202fedeb08bSAlexander V. Chernikov 
1203fedeb08bSAlexander V. Chernikov 	rnd_new.rnd_weight = get_info_weight(info, rnd_orig->rnd_weight);
1204fedeb08bSAlexander V. Chernikov 	error = change_nhop(rnh, info, nh_orig, &rnd_new.rnd_nhop);
1205fedeb08bSAlexander V. Chernikov 	if (error != 0)
1206fedeb08bSAlexander V. Chernikov 		return (error);
12070d60e88bSAlexander V. Chernikov 	error = change_route_conditional(rnh, rt, rnd_orig, &rnd_new, rc);
12084d2c2509SAlexander V. Chernikov 
1209b122304fSAlexander V. Chernikov 	return (error);
12104d2c2509SAlexander V. Chernikov }
12114d2c2509SAlexander V. Chernikov 
1212b122304fSAlexander V. Chernikov /*
1213b122304fSAlexander V. Chernikov  * Insert @rt with nhop data from @rnd_new to @rnh.
1214aa8f9f90SAlexander V. Chernikov  * Returns 0 on success and stores operation results in @rc.
1215b122304fSAlexander V. Chernikov  */
1216b122304fSAlexander V. Chernikov static int
12170d60e88bSAlexander V. Chernikov add_route(struct rib_head *rnh, struct rtentry *rt,
121829029b06SAlexander V. Chernikov     struct route_nhop_data *rnd, struct rib_cmd_info *rc)
1219b122304fSAlexander V. Chernikov {
1220b122304fSAlexander V. Chernikov 	struct radix_node *rn;
12214d2c2509SAlexander V. Chernikov 
1222b122304fSAlexander V. Chernikov 	RIB_WLOCK_ASSERT(rnh);
12234d2c2509SAlexander V. Chernikov 
1224b122304fSAlexander V. Chernikov 	rt->rt_nhop = rnd->rnd_nhop;
1225b122304fSAlexander V. Chernikov 	rt->rt_weight = rnd->rnd_weight;
122629029b06SAlexander V. Chernikov 	rn = rnh->rnh_addaddr(rt_key(rt), rt_mask_const(rt), &rnh->head, rt->rt_nodes);
1227b122304fSAlexander V. Chernikov 
1228b122304fSAlexander V. Chernikov 	if (rn != NULL) {
12292717e958SAlexander V. Chernikov 		if (!NH_IS_NHGRP(rnd->rnd_nhop) && nhop_get_expire(rnd->rnd_nhop))
12302717e958SAlexander V. Chernikov 			tmproutes_update(rnh, rt, rnd->rnd_nhop);
12314d2c2509SAlexander V. Chernikov 
1232da187ddbSAlexander V. Chernikov 		/* Finalize notification */
123333cb3cb2SAlexander V. Chernikov 		rib_bump_gen(rnh);
123498d5c4e5SAlexander V. Chernikov 		rnh->rnh_prefixes++;
1235592d300eSAlexander V. Chernikov 
1236b122304fSAlexander V. Chernikov 		rc->rc_cmd = RTM_ADD;
1237da187ddbSAlexander V. Chernikov 		rc->rc_rt = rt;
1238b122304fSAlexander V. Chernikov 		rc->rc_nh_old = NULL;
1239b122304fSAlexander V. Chernikov 		rc->rc_nh_new = rnd->rnd_nhop;
1240b122304fSAlexander V. Chernikov 		rc->rc_nh_weight = rnd->rnd_weight;
12414d2c2509SAlexander V. Chernikov 
1242da187ddbSAlexander V. Chernikov 		rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
12430d60e88bSAlexander V. Chernikov 		return (0);
1244b122304fSAlexander V. Chernikov 	}
1245da187ddbSAlexander V. Chernikov 
12460d60e88bSAlexander V. Chernikov 	/* Existing route or memory allocation failure. */
12470d60e88bSAlexander V. Chernikov 	return (EEXIST);
1248b122304fSAlexander V. Chernikov }
12494d2c2509SAlexander V. Chernikov 
1250b122304fSAlexander V. Chernikov /*
12510d60e88bSAlexander V. Chernikov  * Unconditionally deletes @rt from @rnh.
1252b122304fSAlexander V. Chernikov  */
12530d60e88bSAlexander V. Chernikov static int
12540d60e88bSAlexander V. Chernikov delete_route(struct rib_head *rnh, struct rtentry *rt, struct rib_cmd_info *rc)
1255b122304fSAlexander V. Chernikov {
1256b122304fSAlexander V. Chernikov 	RIB_WLOCK_ASSERT(rnh);
1257b122304fSAlexander V. Chernikov 
1258b122304fSAlexander V. Chernikov 	/* Route deletion requested. */
1259b122304fSAlexander V. Chernikov 	struct radix_node *rn;
1260b122304fSAlexander V. Chernikov 
126129029b06SAlexander V. Chernikov 	rn = rnh->rnh_deladdr(rt_key_const(rt), rt_mask_const(rt), &rnh->head);
1262b122304fSAlexander V. Chernikov 	if (rn == NULL)
1263b122304fSAlexander V. Chernikov 		return (ESRCH);
1264fedeb08bSAlexander V. Chernikov 	rt = RNTORT(rn);
1265fedeb08bSAlexander V. Chernikov 	rt->rte_flags &= ~RTF_UP;
12660d60e88bSAlexander V. Chernikov 
12670d60e88bSAlexander V. Chernikov 	rib_bump_gen(rnh);
12680d60e88bSAlexander V. Chernikov 	rnh->rnh_prefixes--;
12690d60e88bSAlexander V. Chernikov 
12700d60e88bSAlexander V. Chernikov 	rc->rc_cmd = RTM_DELETE;
12710d60e88bSAlexander V. Chernikov 	rc->rc_rt = rt;
12720d60e88bSAlexander V. Chernikov 	rc->rc_nh_old = rt->rt_nhop;
12730d60e88bSAlexander V. Chernikov 	rc->rc_nh_new = NULL;
12740d60e88bSAlexander V. Chernikov 	rc->rc_nh_weight = rt->rt_weight;
12750d60e88bSAlexander V. Chernikov 
12760d60e88bSAlexander V. Chernikov 	rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
12770d60e88bSAlexander V. Chernikov 
12780d60e88bSAlexander V. Chernikov 	return (0);
1279b122304fSAlexander V. Chernikov }
1280b122304fSAlexander V. Chernikov 
12810d60e88bSAlexander V. Chernikov /*
12820d60e88bSAlexander V. Chernikov  * Switch @rt nhop/weigh to the ones specified in @rnd.
12830d60e88bSAlexander V. Chernikov  * Returns 0 on success.
12840d60e88bSAlexander V. Chernikov  */
12850d60e88bSAlexander V. Chernikov int
12860d60e88bSAlexander V. Chernikov change_route(struct rib_head *rnh, struct rtentry *rt,
12870d60e88bSAlexander V. Chernikov     struct route_nhop_data *rnd, struct rib_cmd_info *rc)
12880d60e88bSAlexander V. Chernikov {
12890d60e88bSAlexander V. Chernikov 	struct nhop_object *nh_orig;
12900d60e88bSAlexander V. Chernikov 
12910d60e88bSAlexander V. Chernikov 	RIB_WLOCK_ASSERT(rnh);
12920d60e88bSAlexander V. Chernikov 
12930d60e88bSAlexander V. Chernikov 	nh_orig = rt->rt_nhop;
12940d60e88bSAlexander V. Chernikov 
12950d60e88bSAlexander V. Chernikov 	if (rnd->rnd_nhop == NULL)
12960d60e88bSAlexander V. Chernikov 		return (delete_route(rnh, rt, rc));
12970d60e88bSAlexander V. Chernikov 
12980d60e88bSAlexander V. Chernikov 	/* Changing nexthop & weight to a new one */
12990d60e88bSAlexander V. Chernikov 	rt->rt_nhop = rnd->rnd_nhop;
13000d60e88bSAlexander V. Chernikov 	rt->rt_weight = rnd->rnd_weight;
13010d60e88bSAlexander V. Chernikov 	if (!NH_IS_NHGRP(rnd->rnd_nhop) && nhop_get_expire(rnd->rnd_nhop))
13020d60e88bSAlexander V. Chernikov 		tmproutes_update(rnh, rt, rnd->rnd_nhop);
13030d60e88bSAlexander V. Chernikov 
1304b122304fSAlexander V. Chernikov 	/* Finalize notification */
130533cb3cb2SAlexander V. Chernikov 	rib_bump_gen(rnh);
13060d60e88bSAlexander V. Chernikov 	rc->rc_cmd = RTM_CHANGE;
1307b122304fSAlexander V. Chernikov 	rc->rc_rt = rt;
1308b122304fSAlexander V. Chernikov 	rc->rc_nh_old = nh_orig;
1309b122304fSAlexander V. Chernikov 	rc->rc_nh_new = rnd->rnd_nhop;
1310b122304fSAlexander V. Chernikov 	rc->rc_nh_weight = rnd->rnd_weight;
1311b122304fSAlexander V. Chernikov 
1312b122304fSAlexander V. Chernikov 	rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
13134d2c2509SAlexander V. Chernikov 
13144d2c2509SAlexander V. Chernikov 	return (0);
13154d2c2509SAlexander V. Chernikov }
13164d2c2509SAlexander V. Chernikov 
1317b122304fSAlexander V. Chernikov /*
1318b122304fSAlexander V. Chernikov  * Conditionally update route nhop/weight IFF data in @nhd_orig is
1319b122304fSAlexander V. Chernikov  *  consistent with the current route data.
1320b122304fSAlexander V. Chernikov  * Nexthop in @nhd_new is consumed.
1321b122304fSAlexander V. Chernikov  */
1322b122304fSAlexander V. Chernikov int
1323b122304fSAlexander V. Chernikov change_route_conditional(struct rib_head *rnh, struct rtentry *rt,
13240d60e88bSAlexander V. Chernikov     struct route_nhop_data *rnd_orig, struct route_nhop_data *rnd_new,
13250d60e88bSAlexander V. Chernikov     struct rib_cmd_info *rc)
13264d2c2509SAlexander V. Chernikov {
1327b122304fSAlexander V. Chernikov 	struct rtentry *rt_new;
1328b122304fSAlexander V. Chernikov 	int error = 0;
13294d2c2509SAlexander V. Chernikov 
1330578a99c9SAlexander V. Chernikov 	IF_DEBUG_LEVEL(LOG_DEBUG2) {
1331c38da70cSAlexander V. Chernikov 		char buf_old[NHOP_PRINT_BUFSIZE], buf_new[NHOP_PRINT_BUFSIZE];
1332c38da70cSAlexander V. Chernikov 		nhop_print_buf_any(rnd_orig->rnd_nhop, buf_old, NHOP_PRINT_BUFSIZE);
1333c38da70cSAlexander V. Chernikov 		nhop_print_buf_any(rnd_new->rnd_nhop, buf_new, NHOP_PRINT_BUFSIZE);
1334c38da70cSAlexander V. Chernikov 		FIB_LOG(LOG_DEBUG2, rnh->rib_fibnum, rnh->rib_family,
1335c38da70cSAlexander V. Chernikov 		    "trying change %s -> %s", buf_old, buf_new);
1336c38da70cSAlexander V. Chernikov 	}
1337b122304fSAlexander V. Chernikov 	RIB_WLOCK(rnh);
1338b122304fSAlexander V. Chernikov 
13390d60e88bSAlexander V. Chernikov 	struct route_nhop_data rnd;
13400d60e88bSAlexander V. Chernikov 	rt_new = lookup_prefix_rt(rnh, rt, &rnd);
1341b122304fSAlexander V. Chernikov 
1342b122304fSAlexander V. Chernikov 	if (rt_new == NULL) {
1343b122304fSAlexander V. Chernikov 		if (rnd_orig->rnd_nhop == NULL)
13440d60e88bSAlexander V. Chernikov 			error = add_route(rnh, rt, rnd_new, rc);
1345b122304fSAlexander V. Chernikov 		else {
1346b122304fSAlexander V. Chernikov 			/*
1347b122304fSAlexander V. Chernikov 			 * Prefix does not exist, which was not our assumption.
1348b122304fSAlexander V. Chernikov 			 * Update @rnd_orig with the new data and return
1349b122304fSAlexander V. Chernikov 			 */
1350b122304fSAlexander V. Chernikov 			rnd_orig->rnd_nhop = NULL;
1351b122304fSAlexander V. Chernikov 			rnd_orig->rnd_weight = 0;
1352b122304fSAlexander V. Chernikov 			error = EAGAIN;
1353b122304fSAlexander V. Chernikov 		}
1354b122304fSAlexander V. Chernikov 	} else {
1355b122304fSAlexander V. Chernikov 		/* Prefix exists, try to update */
1356b122304fSAlexander V. Chernikov 		if (rnd_orig->rnd_nhop == rt_new->rt_nhop) {
13574d2c2509SAlexander V. Chernikov 			/*
1358b122304fSAlexander V. Chernikov 			 * Nhop/mpath group hasn't changed. Flip
1359b122304fSAlexander V. Chernikov 			 * to the new precalculated one and return
13604d2c2509SAlexander V. Chernikov 			 */
13610d60e88bSAlexander V. Chernikov 			error = change_route(rnh, rt_new, rnd_new, rc);
1362b122304fSAlexander V. Chernikov 		} else {
1363b122304fSAlexander V. Chernikov 			/* Update and retry */
1364b122304fSAlexander V. Chernikov 			rnd_orig->rnd_nhop = rt_new->rt_nhop;
1365b122304fSAlexander V. Chernikov 			rnd_orig->rnd_weight = rt_new->rt_weight;
1366b122304fSAlexander V. Chernikov 			error = EAGAIN;
1367b122304fSAlexander V. Chernikov 		}
1368b122304fSAlexander V. Chernikov 	}
1369b122304fSAlexander V. Chernikov 
1370b122304fSAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
1371b122304fSAlexander V. Chernikov 
1372b122304fSAlexander V. Chernikov 	if (error == 0) {
1373b122304fSAlexander V. Chernikov 		rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
1374b122304fSAlexander V. Chernikov 
1375b122304fSAlexander V. Chernikov 		if (rnd_orig->rnd_nhop != NULL)
1376b122304fSAlexander V. Chernikov 			nhop_free_any(rnd_orig->rnd_nhop);
1377b122304fSAlexander V. Chernikov 
1378b122304fSAlexander V. Chernikov 	} else {
1379b122304fSAlexander V. Chernikov 		if (rnd_new->rnd_nhop != NULL)
1380b122304fSAlexander V. Chernikov 			nhop_free_any(rnd_new->rnd_nhop);
13814d2c2509SAlexander V. Chernikov 	}
13824d2c2509SAlexander V. Chernikov 
13834d2c2509SAlexander V. Chernikov 	return (error);
13844d2c2509SAlexander V. Chernikov }
13854d2c2509SAlexander V. Chernikov 
1386a287a973SAlexander V. Chernikov /*
1387a287a973SAlexander V. Chernikov  * Performs modification of routing table specificed by @action.
1388a287a973SAlexander V. Chernikov  * Table is specified by @fibnum and sa_family in @info->rti_info[RTAX_DST].
1389a287a973SAlexander V. Chernikov  * Needs to be run in network epoch.
1390a287a973SAlexander V. Chernikov  *
1391a287a973SAlexander V. Chernikov  * Returns 0 on success and fills in @rc with action result.
1392a287a973SAlexander V. Chernikov  */
1393a287a973SAlexander V. Chernikov int
1394a287a973SAlexander V. Chernikov rib_action(uint32_t fibnum, int action, struct rt_addrinfo *info,
1395a287a973SAlexander V. Chernikov     struct rib_cmd_info *rc)
1396a287a973SAlexander V. Chernikov {
1397a287a973SAlexander V. Chernikov 	int error;
1398a287a973SAlexander V. Chernikov 
1399a287a973SAlexander V. Chernikov 	switch (action) {
1400a287a973SAlexander V. Chernikov 	case RTM_ADD:
1401a287a973SAlexander V. Chernikov 		error = rib_add_route(fibnum, info, rc);
1402a287a973SAlexander V. Chernikov 		break;
1403a287a973SAlexander V. Chernikov 	case RTM_DELETE:
1404a287a973SAlexander V. Chernikov 		error = rib_del_route(fibnum, info, rc);
1405a287a973SAlexander V. Chernikov 		break;
1406a287a973SAlexander V. Chernikov 	case RTM_CHANGE:
1407a287a973SAlexander V. Chernikov 		error = rib_change_route(fibnum, info, rc);
1408a287a973SAlexander V. Chernikov 		break;
1409a287a973SAlexander V. Chernikov 	default:
1410a287a973SAlexander V. Chernikov 		error = ENOTSUP;
1411a287a973SAlexander V. Chernikov 	}
1412a287a973SAlexander V. Chernikov 
1413a287a973SAlexander V. Chernikov 	return (error);
1414a287a973SAlexander V. Chernikov }
1415a287a973SAlexander V. Chernikov 
14164d2c2509SAlexander V. Chernikov struct rt_delinfo
14174d2c2509SAlexander V. Chernikov {
14184d2c2509SAlexander V. Chernikov 	struct rib_head *rnh;
14194d2c2509SAlexander V. Chernikov 	struct rtentry *head;
1420dedeec11SAlexander V. Chernikov 	rib_filter_f_t *filter_f;
1421dedeec11SAlexander V. Chernikov 	void *filter_arg;
1422dedeec11SAlexander V. Chernikov 	int prio;
142341e66f4eSAlexander V. Chernikov 	struct rib_cmd_info rc;
14244d2c2509SAlexander V. Chernikov };
14254d2c2509SAlexander V. Chernikov 
14264d2c2509SAlexander V. Chernikov /*
1427dedeec11SAlexander V. Chernikov  * Conditionally unlinks rtenties or paths from radix tree based
1428dedeec11SAlexander V. Chernikov  * on the callback data passed in @arg.
14294d2c2509SAlexander V. Chernikov  */
14304d2c2509SAlexander V. Chernikov static int
14314d2c2509SAlexander V. Chernikov rt_checkdelroute(struct radix_node *rn, void *arg)
14324d2c2509SAlexander V. Chernikov {
1433dedeec11SAlexander V. Chernikov 	struct rt_delinfo *di = (struct rt_delinfo *)arg;
1434dedeec11SAlexander V. Chernikov 	struct rtentry *rt = (struct rtentry *)rn;
14354d2c2509SAlexander V. Chernikov 
1436dedeec11SAlexander V. Chernikov 	if (rt_delete_conditional(di->rnh, rt, di->prio,
1437dedeec11SAlexander V. Chernikov 	    di->filter_f, di->filter_arg, &di->rc) != 0)
143866f13856SAlexander V. Chernikov 		return (0);
14394d2c2509SAlexander V. Chernikov 
1440aa8f9f90SAlexander V. Chernikov 	/*
1441aa8f9f90SAlexander V. Chernikov 	 * Add deleted rtentries to the list to GC them
1442aa8f9f90SAlexander V. Chernikov 	 *  after dropping the lock.
1443aa8f9f90SAlexander V. Chernikov 	 *
1444aa8f9f90SAlexander V. Chernikov 	 * XXX: Delayed notifications not implemented
1445aa8f9f90SAlexander V. Chernikov 	 *  for nexthop updates.
1446aa8f9f90SAlexander V. Chernikov 	 */
144766f13856SAlexander V. Chernikov 	if (di->rc.rc_cmd == RTM_DELETE) {
144841e66f4eSAlexander V. Chernikov 		/* Add to the list and return */
14494d2c2509SAlexander V. Chernikov 		rt->rt_chain = di->head;
14504d2c2509SAlexander V. Chernikov 		di->head = rt;
145166f13856SAlexander V. Chernikov #ifdef ROUTE_MPATH
145266f13856SAlexander V. Chernikov 	} else {
145366f13856SAlexander V. Chernikov 		/*
1454dedeec11SAlexander V. Chernikov 		 * RTM_CHANGE to a different nexthop or nexthop group.
145566f13856SAlexander V. Chernikov 		 * Free old multipath group.
145666f13856SAlexander V. Chernikov 		 */
145766f13856SAlexander V. Chernikov 		nhop_free_any(di->rc.rc_nh_old);
145866f13856SAlexander V. Chernikov #endif
145924cd2796SAlexander V. Chernikov 	}
14604d2c2509SAlexander V. Chernikov 
14614d2c2509SAlexander V. Chernikov 	return (0);
14624d2c2509SAlexander V. Chernikov }
14634d2c2509SAlexander V. Chernikov 
14644d2c2509SAlexander V. Chernikov /*
14654d2c2509SAlexander V. Chernikov  * Iterates over a routing table specified by @fibnum and @family and
14664d2c2509SAlexander V. Chernikov  *  deletes elements marked by @filter_f.
14674d2c2509SAlexander V. Chernikov  * @fibnum: rtable id
14684d2c2509SAlexander V. Chernikov  * @family: AF_ address family
14694d2c2509SAlexander V. Chernikov  * @filter_f: function returning non-zero value for items to delete
14704d2c2509SAlexander V. Chernikov  * @arg: data to pass to the @filter_f function
14714d2c2509SAlexander V. Chernikov  * @report: true if rtsock notification is needed.
14724d2c2509SAlexander V. Chernikov  */
14734d2c2509SAlexander V. Chernikov void
1474dedeec11SAlexander V. Chernikov rib_walk_del(u_int fibnum, int family, rib_filter_f_t *filter_f, void *filter_arg,
1475dedeec11SAlexander V. Chernikov     bool report)
14764d2c2509SAlexander V. Chernikov {
14774d2c2509SAlexander V. Chernikov 	struct rib_head *rnh;
14784d2c2509SAlexander V. Chernikov 	struct rtentry *rt;
1479fedeb08bSAlexander V. Chernikov 	struct nhop_object *nh;
148041e66f4eSAlexander V. Chernikov 	struct epoch_tracker et;
14814d2c2509SAlexander V. Chernikov 
14824d2c2509SAlexander V. Chernikov 	rnh = rt_tables_get_rnh(fibnum, family);
14834d2c2509SAlexander V. Chernikov 	if (rnh == NULL)
14844d2c2509SAlexander V. Chernikov 		return;
14854d2c2509SAlexander V. Chernikov 
1486dedeec11SAlexander V. Chernikov 	struct rt_delinfo di = {
1487dedeec11SAlexander V. Chernikov 		.rnh = rnh,
1488dedeec11SAlexander V. Chernikov 		.filter_f = filter_f,
1489dedeec11SAlexander V. Chernikov 		.filter_arg = filter_arg,
1490dedeec11SAlexander V. Chernikov 		.prio = NH_PRIORITY_NORMAL,
1491dedeec11SAlexander V. Chernikov 	};
149241e66f4eSAlexander V. Chernikov 
149341e66f4eSAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
14944d2c2509SAlexander V. Chernikov 
14954d2c2509SAlexander V. Chernikov 	RIB_WLOCK(rnh);
14964d2c2509SAlexander V. Chernikov 	rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di);
14974d2c2509SAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
14984d2c2509SAlexander V. Chernikov 
14994d2c2509SAlexander V. Chernikov 	/* We might have something to reclaim. */
1500aa8f9f90SAlexander V. Chernikov 	bzero(&di.rc, sizeof(di.rc));
1501aa8f9f90SAlexander V. Chernikov 	di.rc.rc_cmd = RTM_DELETE;
15024d2c2509SAlexander V. Chernikov 	while (di.head != NULL) {
15034d2c2509SAlexander V. Chernikov 		rt = di.head;
15044d2c2509SAlexander V. Chernikov 		di.head = rt->rt_chain;
15054d2c2509SAlexander V. Chernikov 		rt->rt_chain = NULL;
1506fedeb08bSAlexander V. Chernikov 		nh = rt->rt_nhop;
15074d2c2509SAlexander V. Chernikov 
150841e66f4eSAlexander V. Chernikov 		di.rc.rc_rt = rt;
1509fedeb08bSAlexander V. Chernikov 		di.rc.rc_nh_old = nh;
151041e66f4eSAlexander V. Chernikov 		rib_notify(rnh, RIB_NOTIFY_DELAYED, &di.rc);
151141e66f4eSAlexander V. Chernikov 
1512fedeb08bSAlexander V. Chernikov 		if (report) {
1513fedeb08bSAlexander V. Chernikov #ifdef ROUTE_MPATH
1514fedeb08bSAlexander V. Chernikov 			struct nhgrp_object *nhg;
1515ae6bfd12SAlexander V. Chernikov 			const struct weightened_nhop *wn;
1516fedeb08bSAlexander V. Chernikov 			uint32_t num_nhops;
1517fedeb08bSAlexander V. Chernikov 			if (NH_IS_NHGRP(nh)) {
1518fedeb08bSAlexander V. Chernikov 				nhg = (struct nhgrp_object *)nh;
1519fedeb08bSAlexander V. Chernikov 				wn = nhgrp_get_nhops(nhg, &num_nhops);
1520fedeb08bSAlexander V. Chernikov 				for (int i = 0; i < num_nhops; i++)
1521d68cf57bSAlexander V. Chernikov 					rt_routemsg(RTM_DELETE, rt, wn[i].nh, fibnum);
1522fedeb08bSAlexander V. Chernikov 			} else
1523fedeb08bSAlexander V. Chernikov #endif
1524d68cf57bSAlexander V. Chernikov 			rt_routemsg(RTM_DELETE, rt, nh, fibnum);
1525fedeb08bSAlexander V. Chernikov 		}
15265c4d2252SAlexander V. Chernikov 		rt_free(rt);
15274d2c2509SAlexander V. Chernikov 	}
152841e66f4eSAlexander V. Chernikov 
152941e66f4eSAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
15304d2c2509SAlexander V. Chernikov }
15314d2c2509SAlexander V. Chernikov 
1532b1d63265SAlexander V. Chernikov static int
1533b1d63265SAlexander V. Chernikov rt_delete_unconditional(struct radix_node *rn, void *arg)
1534b1d63265SAlexander V. Chernikov {
1535b1d63265SAlexander V. Chernikov 	struct rtentry *rt = RNTORT(rn);
1536b1d63265SAlexander V. Chernikov 	struct rib_head *rnh = (struct rib_head *)arg;
1537b1d63265SAlexander V. Chernikov 
1538b1d63265SAlexander V. Chernikov 	rn = rnh->rnh_deladdr(rt_key(rt), rt_mask(rt), &rnh->head);
1539b1d63265SAlexander V. Chernikov 	if (RNTORT(rn) == rt)
15405c4d2252SAlexander V. Chernikov 		rt_free(rt);
1541b1d63265SAlexander V. Chernikov 
1542b1d63265SAlexander V. Chernikov 	return (0);
1543b1d63265SAlexander V. Chernikov }
1544b1d63265SAlexander V. Chernikov 
1545b1d63265SAlexander V. Chernikov /*
1546b1d63265SAlexander V. Chernikov  * Removes all routes from the routing table without executing notifications.
1547b1d63265SAlexander V. Chernikov  * rtentres will be removed after the end of a current epoch.
1548b1d63265SAlexander V. Chernikov  */
1549b1d63265SAlexander V. Chernikov static void
1550b1d63265SAlexander V. Chernikov rib_flush_routes(struct rib_head *rnh)
1551b1d63265SAlexander V. Chernikov {
1552b1d63265SAlexander V. Chernikov 	RIB_WLOCK(rnh);
1553b1d63265SAlexander V. Chernikov 	rnh->rnh_walktree(&rnh->head, rt_delete_unconditional, rnh);
1554b1d63265SAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
1555b1d63265SAlexander V. Chernikov }
1556b1d63265SAlexander V. Chernikov 
1557b1d63265SAlexander V. Chernikov void
1558b1d63265SAlexander V. Chernikov rib_flush_routes_family(int family)
1559b1d63265SAlexander V. Chernikov {
1560b1d63265SAlexander V. Chernikov 	struct rib_head *rnh;
1561b1d63265SAlexander V. Chernikov 
1562b1d63265SAlexander V. Chernikov 	for (uint32_t fibnum = 0; fibnum < rt_numfibs; fibnum++) {
1563b1d63265SAlexander V. Chernikov 		if ((rnh = rt_tables_get_rnh(fibnum, family)) != NULL)
1564b1d63265SAlexander V. Chernikov 			rib_flush_routes(rnh);
1565b1d63265SAlexander V. Chernikov 	}
1566b1d63265SAlexander V. Chernikov }
1567b1d63265SAlexander V. Chernikov 
156863f7f392SAlexander V. Chernikov const char *
156963f7f392SAlexander V. Chernikov rib_print_family(int family)
157063f7f392SAlexander V. Chernikov {
157163f7f392SAlexander V. Chernikov 	switch (family) {
157263f7f392SAlexander V. Chernikov 	case AF_INET:
157363f7f392SAlexander V. Chernikov 		return ("inet");
157463f7f392SAlexander V. Chernikov 	case AF_INET6:
157563f7f392SAlexander V. Chernikov 		return ("inet6");
157663f7f392SAlexander V. Chernikov 	case AF_LINK:
157763f7f392SAlexander V. Chernikov 		return ("link");
157863f7f392SAlexander V. Chernikov 	}
157963f7f392SAlexander V. Chernikov 	return ("unknown");
158063f7f392SAlexander V. Chernikov }
1581fe05d1ddSAlexander V. Chernikov 
1582