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