1c398230bSWarner Losh /*- 2df8bae1dSRodney W. Grimes * Copyright (c) 1980, 1986, 1991, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 14df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 15df8bae1dSRodney W. Grimes * without specific prior written permission. 16df8bae1dSRodney W. Grimes * 17df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27df8bae1dSRodney W. Grimes * SUCH DAMAGE. 28df8bae1dSRodney W. Grimes * 2942e9e16dSRuslan Ermilov * @(#)route.c 8.3.1.1 (Berkeley) 2/23/95 30c3aac50fSPeter Wemm * $FreeBSD$ 31df8bae1dSRodney W. Grimes */ 328b07e49aSJulian Elischer /************************************************************************ 338b07e49aSJulian Elischer * Note: In this file a 'fib' is a "forwarding information base" * 348b07e49aSJulian Elischer * Which is the new name for an in kernel routing (next hop) table. * 358b07e49aSJulian Elischer ***********************************************************************/ 36df8bae1dSRodney W. Grimes 371d5e9e22SEivind Eklund #include "opt_inet.h" 38096f2786SBjoern A. Zeeb #include "opt_inet6.h" 398b07e49aSJulian Elischer #include "opt_route.h" 404cbac30bSAlexander V. Chernikov #include "opt_sctp.h" 414bd49128SPeter Wemm #include "opt_mrouting.h" 42e440aed9SQing Li #include "opt_mpath.h" 434bd49128SPeter Wemm 44df8bae1dSRodney W. Grimes #include <sys/param.h> 45df8bae1dSRodney W. Grimes #include <sys/systm.h> 464d1d4912SBruce Evans #include <sys/malloc.h> 47df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 48df8bae1dSRodney W. Grimes #include <sys/socket.h> 498b07e49aSJulian Elischer #include <sys/sysctl.h> 503120b9d4SKip Macy #include <sys/syslog.h> 518b07e49aSJulian Elischer #include <sys/sysproto.h> 528b07e49aSJulian Elischer #include <sys/proc.h> 53df8bae1dSRodney W. Grimes #include <sys/domain.h> 54cb64988fSLuoqi Chen #include <sys/kernel.h> 55df8bae1dSRodney W. Grimes 56df8bae1dSRodney W. Grimes #include <net/if.h> 5776039bc8SGleb Smirnoff #include <net/if_var.h> 586e6b3f7cSQing Li #include <net/if_dl.h> 59df8bae1dSRodney W. Grimes #include <net/route.h> 60530c0060SRobert Watson #include <net/vnet.h> 61e5c610d6SQing Li #include <net/flowtable.h> 62df8bae1dSRodney W. Grimes 63e440aed9SQing Li #ifdef RADIX_MPATH 64e440aed9SQing Li #include <net/radix_mpath.h> 65e440aed9SQing Li #endif 66e440aed9SQing Li 67df8bae1dSRodney W. Grimes #include <netinet/in.h> 68b5e8ce9fSBruce Evans #include <netinet/ip_mroute.h> 69df8bae1dSRodney W. Grimes 702dc1d581SAndre Oppermann #include <vm/uma.h> 712dc1d581SAndre Oppermann 724871fc4aSJulian Elischer #define RT_MAXFIBS UINT16_MAX 73bfca216eSBjoern A. Zeeb 74bfca216eSBjoern A. Zeeb /* Kernel config default option. */ 75bfca216eSBjoern A. Zeeb #ifdef ROUTETABLES 76bfca216eSBjoern A. Zeeb #if ROUTETABLES <= 0 77bfca216eSBjoern A. Zeeb #error "ROUTETABLES defined too low" 78bfca216eSBjoern A. Zeeb #endif 79bfca216eSBjoern A. Zeeb #if ROUTETABLES > RT_MAXFIBS 80bfca216eSBjoern A. Zeeb #error "ROUTETABLES defined too big" 81bfca216eSBjoern A. Zeeb #endif 82bfca216eSBjoern A. Zeeb #define RT_NUMFIBS ROUTETABLES 83bfca216eSBjoern A. Zeeb #endif /* ROUTETABLES */ 84bfca216eSBjoern A. Zeeb /* Initialize to default if not otherwise set. */ 85bfca216eSBjoern A. Zeeb #ifndef RT_NUMFIBS 86bfca216eSBjoern A. Zeeb #define RT_NUMFIBS 1 87bfca216eSBjoern A. Zeeb #endif 88bfca216eSBjoern A. Zeeb 894cbac30bSAlexander V. Chernikov #if defined(INET) || defined(INET6) 904cbac30bSAlexander V. Chernikov #ifdef SCTP 914cbac30bSAlexander V. Chernikov extern void sctp_addr_change(struct ifaddr *ifa, int cmd); 924cbac30bSAlexander V. Chernikov #endif /* SCTP */ 934cbac30bSAlexander V. Chernikov #endif 944cbac30bSAlexander V. Chernikov 954cbac30bSAlexander V. Chernikov 964871fc4aSJulian Elischer /* This is read-only.. */ 978b07e49aSJulian Elischer u_int rt_numfibs = RT_NUMFIBS; 98af3b2549SHans Petter Selasky SYSCTL_UINT(_net, OID_AUTO, fibs, CTLFLAG_RDTUN, &rt_numfibs, 0, ""); 998b07e49aSJulian Elischer 10066e8505fSJulian Elischer /* 10166e8505fSJulian Elischer * By default add routes to all fibs for new interfaces. 10266e8505fSJulian Elischer * Once this is set to 0 then only allocate routes on interface 10366e8505fSJulian Elischer * changes for the FIB of the caller when adding a new set of addresses 10466e8505fSJulian Elischer * to an interface. XXX this is a shotgun aproach to a problem that needs 10566e8505fSJulian Elischer * a more fine grained solution.. that will come. 106a8498625SBjoern A. Zeeb * XXX also has the problems getting the FIB from curthread which will not 107a8498625SBjoern A. Zeeb * always work given the fib can be overridden and prefixes can be added 108a8498625SBjoern A. Zeeb * from the network stack context. 10966e8505fSJulian Elischer */ 110ee0bd4b9SHiroki Sato VNET_DEFINE(u_int, rt_add_addr_allfibs) = 1; 111ee0bd4b9SHiroki Sato SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, 112ee0bd4b9SHiroki Sato &VNET_NAME(rt_add_addr_allfibs), 0, ""); 11366e8505fSJulian Elischer 114eddfbb76SRobert Watson VNET_DEFINE(struct rtstat, rtstat); 1151e77c105SRobert Watson #define V_rtstat VNET(rtstat) 116b58ea5f3SBjoern A. Zeeb 11782cea7e6SBjoern A. Zeeb VNET_DEFINE(struct radix_node_head *, rt_tables); 11882cea7e6SBjoern A. Zeeb #define V_rt_tables VNET(rt_tables) 11982cea7e6SBjoern A. Zeeb 12082cea7e6SBjoern A. Zeeb VNET_DEFINE(int, rttrash); /* routes not in table but not freed */ 12182cea7e6SBjoern A. Zeeb #define V_rttrash VNET(rttrash) 12282cea7e6SBjoern A. Zeeb 123bfe1aba4SMarko Zec 124d6941ce9SLuigi Rizzo /* 125d6941ce9SLuigi Rizzo * Convert a 'struct radix_node *' to a 'struct rtentry *'. 126d6941ce9SLuigi Rizzo * The operation can be done safely (in this code) because a 127d6941ce9SLuigi Rizzo * 'struct rtentry' starts with two 'struct radix_node''s, the first 128d6941ce9SLuigi Rizzo * one representing leaf nodes in the routing tree, which is 129d6941ce9SLuigi Rizzo * what the code in radix.c passes us as a 'struct radix_node'. 130d6941ce9SLuigi Rizzo * 131d6941ce9SLuigi Rizzo * But because there are a lot of assumptions in this conversion, 132d6941ce9SLuigi Rizzo * do not cast explicitly, but always use the macro below. 133d6941ce9SLuigi Rizzo */ 134d6941ce9SLuigi Rizzo #define RNTORT(p) ((struct rtentry *)(p)) 135d6941ce9SLuigi Rizzo 1363e288e62SDimitry Andric static VNET_DEFINE(uma_zone_t, rtzone); /* Routing table UMA zone. */ 13782cea7e6SBjoern A. Zeeb #define V_rtzone VNET(rtzone) 13882cea7e6SBjoern A. Zeeb 139c77462ddSAlexander V. Chernikov static int rtrequest1_fib_change(struct radix_node_head *, struct rt_addrinfo *, 140c77462ddSAlexander V. Chernikov struct rtentry **, u_int); 1410fb9298dSAlexander V. Chernikov static void rt_setmetrics(const struct rt_addrinfo *, struct rtentry *); 142*4bdf0b6aSAlexander V. Chernikov static int rt_ifdelroute(struct rtentry *rt, void *arg); 143c77462ddSAlexander V. Chernikov 1447f948f12SAlexander V. Chernikov struct if_mtuinfo 1457f948f12SAlexander V. Chernikov { 1467f948f12SAlexander V. Chernikov struct ifnet *ifp; 1477f948f12SAlexander V. Chernikov int mtu; 1487f948f12SAlexander V. Chernikov }; 1497f948f12SAlexander V. Chernikov 1507f948f12SAlexander V. Chernikov static int if_updatemtu_cb(struct radix_node *, void *); 1517f948f12SAlexander V. Chernikov 1528b07e49aSJulian Elischer /* 1538b07e49aSJulian Elischer * handler for net.my_fibnum 1548b07e49aSJulian Elischer */ 1558b07e49aSJulian Elischer static int 1568b07e49aSJulian Elischer sysctl_my_fibnum(SYSCTL_HANDLER_ARGS) 157df8bae1dSRodney W. Grimes { 1588b07e49aSJulian Elischer int fibnum; 1598b07e49aSJulian Elischer int error; 1608b07e49aSJulian Elischer 1618b07e49aSJulian Elischer fibnum = curthread->td_proc->p_fibnum; 1628b07e49aSJulian Elischer error = sysctl_handle_int(oidp, &fibnum, 0, req); 1638b07e49aSJulian Elischer return (error); 164df8bae1dSRodney W. Grimes } 165df8bae1dSRodney W. Grimes 1668b07e49aSJulian Elischer SYSCTL_PROC(_net, OID_AUTO, my_fibnum, CTLTYPE_INT|CTLFLAG_RD, 1678b07e49aSJulian Elischer NULL, 0, &sysctl_my_fibnum, "I", "default FIB of caller"); 1682dc1d581SAndre Oppermann 169c2c2a7c1SBjoern A. Zeeb static __inline struct radix_node_head ** 170c2c2a7c1SBjoern A. Zeeb rt_tables_get_rnh_ptr(int table, int fam) 171c2c2a7c1SBjoern A. Zeeb { 172c2c2a7c1SBjoern A. Zeeb struct radix_node_head **rnh; 173c2c2a7c1SBjoern A. Zeeb 174c2c2a7c1SBjoern A. Zeeb KASSERT(table >= 0 && table < rt_numfibs, ("%s: table out of bounds.", 175c2c2a7c1SBjoern A. Zeeb __func__)); 176c2c2a7c1SBjoern A. Zeeb KASSERT(fam >= 0 && fam < (AF_MAX+1), ("%s: fam out of bounds.", 177c2c2a7c1SBjoern A. Zeeb __func__)); 178c2c2a7c1SBjoern A. Zeeb 179c2c2a7c1SBjoern A. Zeeb /* rnh is [fib=0][af=0]. */ 180c2c2a7c1SBjoern A. Zeeb rnh = (struct radix_node_head **)V_rt_tables; 181c2c2a7c1SBjoern A. Zeeb /* Get the offset to the requested table and fam. */ 182c2c2a7c1SBjoern A. Zeeb rnh += table * (AF_MAX+1) + fam; 183c2c2a7c1SBjoern A. Zeeb 184c2c2a7c1SBjoern A. Zeeb return (rnh); 185c2c2a7c1SBjoern A. Zeeb } 186c2c2a7c1SBjoern A. Zeeb 187c2c2a7c1SBjoern A. Zeeb struct radix_node_head * 188c2c2a7c1SBjoern A. Zeeb rt_tables_get_rnh(int table, int fam) 189c2c2a7c1SBjoern A. Zeeb { 190c2c2a7c1SBjoern A. Zeeb 191c2c2a7c1SBjoern A. Zeeb return (*rt_tables_get_rnh_ptr(table, fam)); 192c2c2a7c1SBjoern A. Zeeb } 193c2c2a7c1SBjoern A. Zeeb 194d0728d71SRobert Watson /* 195d0728d71SRobert Watson * route initialization must occur before ip6_init2(), which happenas at 196d0728d71SRobert Watson * SI_ORDER_MIDDLE. 197d0728d71SRobert Watson */ 1982eb5613fSLuigi Rizzo static void 1992eb5613fSLuigi Rizzo route_init(void) 200df8bae1dSRodney W. Grimes { 2018b07e49aSJulian Elischer 2026f95a5ebSJulian Elischer /* whack the tunable ints into line. */ 2038b07e49aSJulian Elischer if (rt_numfibs > RT_MAXFIBS) 2048b07e49aSJulian Elischer rt_numfibs = RT_MAXFIBS; 2058b07e49aSJulian Elischer if (rt_numfibs == 0) 2068b07e49aSJulian Elischer rt_numfibs = 1; 2071ed81b73SMarko Zec } 208d0728d71SRobert Watson SYSINIT(route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, route_init, 0); 2091ed81b73SMarko Zec 210e3a7aa6fSGleb Smirnoff static int 211e3a7aa6fSGleb Smirnoff rtentry_zinit(void *mem, int size, int how) 212e3a7aa6fSGleb Smirnoff { 213e3a7aa6fSGleb Smirnoff struct rtentry *rt = mem; 214e3a7aa6fSGleb Smirnoff 215e3a7aa6fSGleb Smirnoff rt->rt_pksent = counter_u64_alloc(how); 216e3a7aa6fSGleb Smirnoff if (rt->rt_pksent == NULL) 217e3a7aa6fSGleb Smirnoff return (ENOMEM); 218e3a7aa6fSGleb Smirnoff 219e3a7aa6fSGleb Smirnoff RT_LOCK_INIT(rt); 220e3a7aa6fSGleb Smirnoff 221e3a7aa6fSGleb Smirnoff return (0); 222e3a7aa6fSGleb Smirnoff } 223e3a7aa6fSGleb Smirnoff 224e3a7aa6fSGleb Smirnoff static void 225e3a7aa6fSGleb Smirnoff rtentry_zfini(void *mem, int size) 226e3a7aa6fSGleb Smirnoff { 227e3a7aa6fSGleb Smirnoff struct rtentry *rt = mem; 228e3a7aa6fSGleb Smirnoff 229e3a7aa6fSGleb Smirnoff RT_LOCK_DESTROY(rt); 230e3a7aa6fSGleb Smirnoff counter_u64_free(rt->rt_pksent); 231e3a7aa6fSGleb Smirnoff } 232e3a7aa6fSGleb Smirnoff 233e3a7aa6fSGleb Smirnoff static int 234e3a7aa6fSGleb Smirnoff rtentry_ctor(void *mem, int size, void *arg, int how) 235e3a7aa6fSGleb Smirnoff { 236e3a7aa6fSGleb Smirnoff struct rtentry *rt = mem; 237e3a7aa6fSGleb Smirnoff 238e3a7aa6fSGleb Smirnoff bzero(rt, offsetof(struct rtentry, rt_endzero)); 239e3a7aa6fSGleb Smirnoff counter_u64_zero(rt->rt_pksent); 240e3a7aa6fSGleb Smirnoff 241e3a7aa6fSGleb Smirnoff return (0); 242e3a7aa6fSGleb Smirnoff } 243e3a7aa6fSGleb Smirnoff 244d0728d71SRobert Watson static void 245256ea2abSGleb Smirnoff rtentry_dtor(void *mem, int size, void *arg) 246256ea2abSGleb Smirnoff { 247256ea2abSGleb Smirnoff struct rtentry *rt = mem; 248256ea2abSGleb Smirnoff 249256ea2abSGleb Smirnoff RT_UNLOCK_COND(rt); 250256ea2abSGleb Smirnoff } 251256ea2abSGleb Smirnoff 252256ea2abSGleb Smirnoff static void 253d0728d71SRobert Watson vnet_route_init(const void *unused __unused) 2541ed81b73SMarko Zec { 2551ed81b73SMarko Zec struct domain *dom; 256c2c2a7c1SBjoern A. Zeeb struct radix_node_head **rnh; 257c2c2a7c1SBjoern A. Zeeb int table; 2581ed81b73SMarko Zec int fam; 2591ed81b73SMarko Zec 260c2c2a7c1SBjoern A. Zeeb V_rt_tables = malloc(rt_numfibs * (AF_MAX+1) * 261c2c2a7c1SBjoern A. Zeeb sizeof(struct radix_node_head *), M_RTABLE, M_WAITOK|M_ZERO); 262c2c2a7c1SBjoern A. Zeeb 263e3a7aa6fSGleb Smirnoff V_rtzone = uma_zcreate("rtentry", sizeof(struct rtentry), 264256ea2abSGleb Smirnoff rtentry_ctor, rtentry_dtor, 265e3a7aa6fSGleb Smirnoff rtentry_zinit, rtentry_zfini, UMA_ALIGN_PTR, 0); 2668b07e49aSJulian Elischer for (dom = domains; dom; dom = dom->dom_next) { 267b680a383SBjoern A. Zeeb if (dom->dom_rtattach == NULL) 268b680a383SBjoern A. Zeeb continue; 269b680a383SBjoern A. Zeeb 2708b07e49aSJulian Elischer for (table = 0; table < rt_numfibs; table++) { 271b680a383SBjoern A. Zeeb fam = dom->dom_family; 272b680a383SBjoern A. Zeeb if (table != 0 && fam != AF_INET6 && fam != AF_INET) 273b680a383SBjoern A. Zeeb break; 274b680a383SBjoern A. Zeeb 275c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh_ptr(table, fam); 276c2c2a7c1SBjoern A. Zeeb if (rnh == NULL) 277c2c2a7c1SBjoern A. Zeeb panic("%s: rnh NULL", __func__); 278146a181fSAlexander V. Chernikov dom->dom_rtattach((void **)rnh, 0); 2798b07e49aSJulian Elischer } 2808b07e49aSJulian Elischer } 2818b07e49aSJulian Elischer } 282d0728d71SRobert Watson VNET_SYSINIT(vnet_route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FOURTH, 283d0728d71SRobert Watson vnet_route_init, 0); 2848b07e49aSJulian Elischer 285bc29160dSMarko Zec #ifdef VIMAGE 286d0728d71SRobert Watson static void 287d0728d71SRobert Watson vnet_route_uninit(const void *unused __unused) 288bc29160dSMarko Zec { 289bc29160dSMarko Zec int table; 290bc29160dSMarko Zec int fam; 291bc29160dSMarko Zec struct domain *dom; 292bc29160dSMarko Zec struct radix_node_head **rnh; 293bc29160dSMarko Zec 294bc29160dSMarko Zec for (dom = domains; dom; dom = dom->dom_next) { 295b680a383SBjoern A. Zeeb if (dom->dom_rtdetach == NULL) 296b680a383SBjoern A. Zeeb continue; 297b680a383SBjoern A. Zeeb 298bc29160dSMarko Zec for (table = 0; table < rt_numfibs; table++) { 299b680a383SBjoern A. Zeeb fam = dom->dom_family; 300b680a383SBjoern A. Zeeb 301b680a383SBjoern A. Zeeb if (table != 0 && fam != AF_INET6 && fam != AF_INET) 302b680a383SBjoern A. Zeeb break; 303b680a383SBjoern A. Zeeb 304bc29160dSMarko Zec rnh = rt_tables_get_rnh_ptr(table, fam); 305bc29160dSMarko Zec if (rnh == NULL) 306bc29160dSMarko Zec panic("%s: rnh NULL", __func__); 30757c3556bSAlexander V. Chernikov dom->dom_rtdetach((void **)rnh, 0); 308bc29160dSMarko Zec } 309bc29160dSMarko Zec } 3106274ce3eSCraig Rodrigues 3116274ce3eSCraig Rodrigues free(V_rt_tables, M_RTABLE); 3126274ce3eSCraig Rodrigues uma_zdestroy(V_rtzone); 313bc29160dSMarko Zec } 314d0728d71SRobert Watson VNET_SYSUNINIT(vnet_route_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, 315d0728d71SRobert Watson vnet_route_uninit, 0); 316bc29160dSMarko Zec #endif 317bc29160dSMarko Zec 3188b07e49aSJulian Elischer #ifndef _SYS_SYSPROTO_H_ 3198b07e49aSJulian Elischer struct setfib_args { 3208b07e49aSJulian Elischer int fibnum; 3218b07e49aSJulian Elischer }; 3228b07e49aSJulian Elischer #endif 3238b07e49aSJulian Elischer int 3248451d0ddSKip Macy sys_setfib(struct thread *td, struct setfib_args *uap) 3258b07e49aSJulian Elischer { 3268b07e49aSJulian Elischer if (uap->fibnum < 0 || uap->fibnum >= rt_numfibs) 3278b07e49aSJulian Elischer return EINVAL; 3288b07e49aSJulian Elischer td->td_proc->p_fibnum = uap->fibnum; 3298b07e49aSJulian Elischer return (0); 330df8bae1dSRodney W. Grimes } 331df8bae1dSRodney W. Grimes 332df8bae1dSRodney W. Grimes /* 333df8bae1dSRodney W. Grimes * Packet routing routines. 334df8bae1dSRodney W. Grimes */ 335df8bae1dSRodney W. Grimes void 336d1dd20beSSam Leffler rtalloc(struct route *ro) 337df8bae1dSRodney W. Grimes { 338a8498625SBjoern A. Zeeb 339a8498625SBjoern A. Zeeb rtalloc_ign_fib(ro, 0UL, RT_DEFAULT_FIB); 3408b07e49aSJulian Elischer } 3418b07e49aSJulian Elischer 3428b07e49aSJulian Elischer void 3438b07e49aSJulian Elischer rtalloc_fib(struct route *ro, u_int fibnum) 3448b07e49aSJulian Elischer { 3458b07e49aSJulian Elischer rtalloc_ign_fib(ro, 0UL, fibnum); 346df8bae1dSRodney W. Grimes } 347df8bae1dSRodney W. Grimes 348652082e6SGarrett Wollman void 349d1dd20beSSam Leffler rtalloc_ign(struct route *ro, u_long ignore) 350652082e6SGarrett Wollman { 35168f956b8SJohn Polstra struct rtentry *rt; 35268f956b8SJohn Polstra 35368f956b8SJohn Polstra if ((rt = ro->ro_rt) != NULL) { 35468f956b8SJohn Polstra if (rt->rt_ifp != NULL && rt->rt_flags & RTF_UP) 35568f956b8SJohn Polstra return; 35668f956b8SJohn Polstra RTFREE(rt); 35766810dd0SYoshinobu Inoue ro->ro_rt = NULL; 35868f956b8SJohn Polstra } 359a8498625SBjoern A. Zeeb ro->ro_rt = rtalloc1_fib(&ro->ro_dst, 1, ignore, RT_DEFAULT_FIB); 3608b07e49aSJulian Elischer if (ro->ro_rt) 3618b07e49aSJulian Elischer RT_UNLOCK(ro->ro_rt); 3628b07e49aSJulian Elischer } 3638b07e49aSJulian Elischer 3648b07e49aSJulian Elischer void 3658b07e49aSJulian Elischer rtalloc_ign_fib(struct route *ro, u_long ignore, u_int fibnum) 3668b07e49aSJulian Elischer { 3678b07e49aSJulian Elischer struct rtentry *rt; 3688b07e49aSJulian Elischer 3698b07e49aSJulian Elischer if ((rt = ro->ro_rt) != NULL) { 3708b07e49aSJulian Elischer if (rt->rt_ifp != NULL && rt->rt_flags & RTF_UP) 3718b07e49aSJulian Elischer return; 3728b07e49aSJulian Elischer RTFREE(rt); 3738b07e49aSJulian Elischer ro->ro_rt = NULL; 3748b07e49aSJulian Elischer } 3758b07e49aSJulian Elischer ro->ro_rt = rtalloc1_fib(&ro->ro_dst, 1, ignore, fibnum); 376d1dd20beSSam Leffler if (ro->ro_rt) 377d1dd20beSSam Leffler RT_UNLOCK(ro->ro_rt); 378652082e6SGarrett Wollman } 379652082e6SGarrett Wollman 380b0a76b88SJulian Elischer /* 381b0a76b88SJulian Elischer * Look up the route that matches the address given 382b0a76b88SJulian Elischer * Or, at least try.. Create a cloned route if needed. 383d1dd20beSSam Leffler * 384d1dd20beSSam Leffler * The returned route, if any, is locked. 385b0a76b88SJulian Elischer */ 386df8bae1dSRodney W. Grimes struct rtentry * 387d1dd20beSSam Leffler rtalloc1(struct sockaddr *dst, int report, u_long ignflags) 388df8bae1dSRodney W. Grimes { 389a8498625SBjoern A. Zeeb 390a8498625SBjoern A. Zeeb return (rtalloc1_fib(dst, report, ignflags, RT_DEFAULT_FIB)); 3918b07e49aSJulian Elischer } 3928b07e49aSJulian Elischer 3938b07e49aSJulian Elischer struct rtentry * 3948b07e49aSJulian Elischer rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, 3958b07e49aSJulian Elischer u_int fibnum) 3968b07e49aSJulian Elischer { 3978b07e49aSJulian Elischer struct radix_node_head *rnh; 398d1dd20beSSam Leffler struct radix_node *rn; 399d1dd20beSSam Leffler struct rtentry *newrt; 400df8bae1dSRodney W. Grimes struct rt_addrinfo info; 4016e6b3f7cSQing Li int err = 0, msgtype = RTM_MISS; 4023120b9d4SKip Macy int needlock; 403df8bae1dSRodney W. Grimes 4048b07e49aSJulian Elischer KASSERT((fibnum < rt_numfibs), ("rtalloc1_fib: bad fibnum")); 405c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 406e579f1c1SDmitry Chagin newrt = NULL; 407523e6002SDmitry Chagin if (rnh == NULL) 408523e6002SDmitry Chagin goto miss; 409523e6002SDmitry Chagin 410b0a76b88SJulian Elischer /* 411b0a76b88SJulian Elischer * Look up the address in the table for that Address Family 412b0a76b88SJulian Elischer */ 4133120b9d4SKip Macy needlock = !(ignflags & RTF_RNH_LOCKED); 4143120b9d4SKip Macy if (needlock) 4153120b9d4SKip Macy RADIX_NODE_HEAD_RLOCK(rnh); 4163120b9d4SKip Macy #ifdef INVARIANTS 4173120b9d4SKip Macy else 4189b5f5edeSGeorge V. Neville-Neil RADIX_NODE_HEAD_LOCK_ASSERT(rnh); 4193120b9d4SKip Macy #endif 4203120b9d4SKip Macy rn = rnh->rnh_matchaddr(dst, rnh); 4213120b9d4SKip Macy if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { 4226a873ef7SDmitry Chagin newrt = RNTORT(rn); 4233120b9d4SKip Macy RT_LOCK(newrt); 4243120b9d4SKip Macy RT_ADDREF(newrt); 4253120b9d4SKip Macy if (needlock) 4263120b9d4SKip Macy RADIX_NODE_HEAD_RUNLOCK(rnh); 4273120b9d4SKip Macy goto done; 4286e6b3f7cSQing Li 4296e6b3f7cSQing Li } else if (needlock) 4303120b9d4SKip Macy RADIX_NODE_HEAD_RUNLOCK(rnh); 4313120b9d4SKip Macy 432b0a76b88SJulian Elischer /* 433b0a76b88SJulian Elischer * Either we hit the root or couldn't find any match, 434b0a76b88SJulian Elischer * Which basically means 435b0a76b88SJulian Elischer * "caint get there frm here" 436b0a76b88SJulian Elischer */ 437956b0b65SJeffrey Hsu miss: 438523e6002SDmitry Chagin V_rtstat.rts_unreach++; 439523e6002SDmitry Chagin 4406e6b3f7cSQing Li if (report) { 441b0a76b88SJulian Elischer /* 442b0a76b88SJulian Elischer * If required, report the failure to the supervising 443b0a76b88SJulian Elischer * Authorities. 444b0a76b88SJulian Elischer * For a delete, this is not an error. (report == 0) 445b0a76b88SJulian Elischer */ 4466f5967c0SBruce Evans bzero(&info, sizeof(info)); 447df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 448528737fdSBjoern A. Zeeb rt_missmsg_fib(msgtype, &info, 0, err, fibnum); 449df8bae1dSRodney W. Grimes } 4503120b9d4SKip Macy done: 451d1dd20beSSam Leffler if (newrt) 452d1dd20beSSam Leffler RT_LOCK_ASSERT(newrt); 453df8bae1dSRodney W. Grimes return (newrt); 454df8bae1dSRodney W. Grimes } 455df8bae1dSRodney W. Grimes 456499676dfSJulian Elischer /* 457499676dfSJulian Elischer * Remove a reference count from an rtentry. 458499676dfSJulian Elischer * If the count gets low enough, take it out of the routing table 459499676dfSJulian Elischer */ 460df8bae1dSRodney W. Grimes void 461d1dd20beSSam Leffler rtfree(struct rtentry *rt) 462df8bae1dSRodney W. Grimes { 46385911824SLuigi Rizzo struct radix_node_head *rnh; 464df8bae1dSRodney W. Grimes 465a0c0e34bSGleb Smirnoff KASSERT(rt != NULL,("%s: NULL rt", __func__)); 466c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(rt->rt_fibnum, rt_key(rt)->sa_family); 467a0c0e34bSGleb Smirnoff KASSERT(rnh != NULL,("%s: NULL rnh", __func__)); 468499676dfSJulian Elischer 469d1dd20beSSam Leffler RT_LOCK_ASSERT(rt); 470d1dd20beSSam Leffler 471499676dfSJulian Elischer /* 472a0c0e34bSGleb Smirnoff * The callers should use RTFREE_LOCKED() or RTFREE(), so 473a0c0e34bSGleb Smirnoff * we should come here exactly with the last reference. 474499676dfSJulian Elischer */ 4757138d65cSSam Leffler RT_REMREF(rt); 476a0c0e34bSGleb Smirnoff if (rt->rt_refcnt > 0) { 477a42ea597SQing Li log(LOG_DEBUG, "%s: %p has %d refs\n", __func__, rt, rt->rt_refcnt); 478d1dd20beSSam Leffler goto done; 479a0c0e34bSGleb Smirnoff } 4809c63e9dbSSam Leffler 4819c63e9dbSSam Leffler /* 4829c63e9dbSSam Leffler * On last reference give the "close method" a chance 4839c63e9dbSSam Leffler * to cleanup private state. This also permits (for 4849c63e9dbSSam Leffler * IPv4 and IPv6) a chance to decide if the routing table 4859c63e9dbSSam Leffler * entry should be purged immediately or at a later time. 4869c63e9dbSSam Leffler * When an immediate purge is to happen the close routine 4879c63e9dbSSam Leffler * typically calls rtexpunge which clears the RTF_UP flag 4889c63e9dbSSam Leffler * on the entry so that the code below reclaims the storage. 4899c63e9dbSSam Leffler */ 490d1dd20beSSam Leffler if (rt->rt_refcnt == 0 && rnh->rnh_close) 4915c2dae8eSGarrett Wollman rnh->rnh_close((struct radix_node *)rt, rnh); 492499676dfSJulian Elischer 493499676dfSJulian Elischer /* 494499676dfSJulian Elischer * If we are no longer "up" (and ref == 0) 495499676dfSJulian Elischer * then we can free the resources associated 496499676dfSJulian Elischer * with the route. 497499676dfSJulian Elischer */ 498d1dd20beSSam Leffler if ((rt->rt_flags & RTF_UP) == 0) { 499df8bae1dSRodney W. Grimes if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 500df8bae1dSRodney W. Grimes panic("rtfree 2"); 501499676dfSJulian Elischer /* 502499676dfSJulian Elischer * the rtentry must have been removed from the routing table 503499676dfSJulian Elischer * so it is represented in rttrash.. remove that now. 504499676dfSJulian Elischer */ 505603724d3SBjoern A. Zeeb V_rttrash--; 506499676dfSJulian Elischer #ifdef DIAGNOSTIC 507df8bae1dSRodney W. Grimes if (rt->rt_refcnt < 0) { 508623ae52eSPoul-Henning Kamp printf("rtfree: %p not freed (neg refs)\n", rt); 509d1dd20beSSam Leffler goto done; 510df8bae1dSRodney W. Grimes } 511499676dfSJulian Elischer #endif 512499676dfSJulian Elischer /* 513499676dfSJulian Elischer * release references on items we hold them on.. 514499676dfSJulian Elischer * e.g other routes and ifaddrs. 515499676dfSJulian Elischer */ 51619fc74fbSJeffrey Hsu if (rt->rt_ifa) 5171099f828SRobert Watson ifa_free(rt->rt_ifa); 518499676dfSJulian Elischer /* 519499676dfSJulian Elischer * The key is separatly alloc'd so free it (see rt_setgate()). 520499676dfSJulian Elischer * This also frees the gateway, as they are always malloc'd 521499676dfSJulian Elischer * together. 522499676dfSJulian Elischer */ 5238b15f615SLuiz Otavio O Souza R_Free(rt_key(rt)); 524499676dfSJulian Elischer 525499676dfSJulian Elischer /* 526499676dfSJulian Elischer * and the rtentry itself of course 527499676dfSJulian Elischer */ 5281ed81b73SMarko Zec uma_zfree(V_rtzone, rt); 529d1dd20beSSam Leffler return; 530df8bae1dSRodney W. Grimes } 531d1dd20beSSam Leffler done: 532d1dd20beSSam Leffler RT_UNLOCK(rt); 533df8bae1dSRodney W. Grimes } 534df8bae1dSRodney W. Grimes 535df8bae1dSRodney W. Grimes 536df8bae1dSRodney W. Grimes /* 537df8bae1dSRodney W. Grimes * Force a routing table entry to the specified 538df8bae1dSRodney W. Grimes * destination to go through the given gateway. 539df8bae1dSRodney W. Grimes * Normally called as a result of a routing redirect 540df8bae1dSRodney W. Grimes * message from the network layer. 541df8bae1dSRodney W. Grimes */ 54226f9a767SRodney W. Grimes void 543d1dd20beSSam Leffler rtredirect(struct sockaddr *dst, 544d1dd20beSSam Leffler struct sockaddr *gateway, 545d1dd20beSSam Leffler struct sockaddr *netmask, 546d1dd20beSSam Leffler int flags, 547d1dd20beSSam Leffler struct sockaddr *src) 548df8bae1dSRodney W. Grimes { 549a8498625SBjoern A. Zeeb 550a8498625SBjoern A. Zeeb rtredirect_fib(dst, gateway, netmask, flags, src, RT_DEFAULT_FIB); 5518b07e49aSJulian Elischer } 5528b07e49aSJulian Elischer 5538b07e49aSJulian Elischer void 5548b07e49aSJulian Elischer rtredirect_fib(struct sockaddr *dst, 5558b07e49aSJulian Elischer struct sockaddr *gateway, 5568b07e49aSJulian Elischer struct sockaddr *netmask, 5578b07e49aSJulian Elischer int flags, 5588b07e49aSJulian Elischer struct sockaddr *src, 5598b07e49aSJulian Elischer u_int fibnum) 5608b07e49aSJulian Elischer { 5618e7e854cSKip Macy struct rtentry *rt, *rt0 = NULL; 562df8bae1dSRodney W. Grimes int error = 0; 56385911824SLuigi Rizzo short *stat = NULL; 564df8bae1dSRodney W. Grimes struct rt_addrinfo info; 565df8bae1dSRodney W. Grimes struct ifaddr *ifa; 566c2c2a7c1SBjoern A. Zeeb struct radix_node_head *rnh; 567c2c2a7c1SBjoern A. Zeeb 5688c0fec80SRobert Watson ifa = NULL; 569c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 570c2c2a7c1SBjoern A. Zeeb if (rnh == NULL) { 571c2c2a7c1SBjoern A. Zeeb error = EAFNOSUPPORT; 572c2c2a7c1SBjoern A. Zeeb goto out; 573c2c2a7c1SBjoern A. Zeeb } 574df8bae1dSRodney W. Grimes 575df8bae1dSRodney W. Grimes /* verify the gateway is directly reachable */ 5764f8585e0SAlan Somers if ((ifa = ifa_ifwithnet(gateway, 0, fibnum)) == NULL) { 577df8bae1dSRodney W. Grimes error = ENETUNREACH; 578df8bae1dSRodney W. Grimes goto out; 579df8bae1dSRodney W. Grimes } 5808b07e49aSJulian Elischer rt = rtalloc1_fib(dst, 0, 0UL, fibnum); /* NB: rt is locked */ 581df8bae1dSRodney W. Grimes /* 582df8bae1dSRodney W. Grimes * If the redirect isn't from our current router for this dst, 583df8bae1dSRodney W. Grimes * it's either old or wrong. If it redirects us to ourselves, 584df8bae1dSRodney W. Grimes * we have a routing loop, perhaps as a result of an interface 585df8bae1dSRodney W. Grimes * going down recently. 586df8bae1dSRodney W. Grimes */ 587df8bae1dSRodney W. Grimes if (!(flags & RTF_DONE) && rt && 588956b0b65SJeffrey Hsu (!sa_equal(src, rt->rt_gateway) || rt->rt_ifa != ifa)) 589df8bae1dSRodney W. Grimes error = EINVAL; 5908896f83aSRobert Watson else if (ifa_ifwithaddr_check(gateway)) 591df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 592df8bae1dSRodney W. Grimes if (error) 593df8bae1dSRodney W. Grimes goto done; 594df8bae1dSRodney W. Grimes /* 595df8bae1dSRodney W. Grimes * Create a new entry if we just got back a wildcard entry 5966bccea7cSRebecca Cran * or the lookup failed. This is necessary for hosts 597df8bae1dSRodney W. Grimes * which use routing redirects generated by smart gateways 598df8bae1dSRodney W. Grimes * to dynamically build the routing tables. 599df8bae1dSRodney W. Grimes */ 60085911824SLuigi Rizzo if (rt == NULL || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) 601df8bae1dSRodney W. Grimes goto create; 602df8bae1dSRodney W. Grimes /* 603df8bae1dSRodney W. Grimes * Don't listen to the redirect if it's 604df8bae1dSRodney W. Grimes * for a route to an interface. 605df8bae1dSRodney W. Grimes */ 606df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 607df8bae1dSRodney W. Grimes if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 608df8bae1dSRodney W. Grimes /* 609df8bae1dSRodney W. Grimes * Changing from route to net => route to host. 610df8bae1dSRodney W. Grimes * Create new route, rather than smashing route to net. 611df8bae1dSRodney W. Grimes */ 612df8bae1dSRodney W. Grimes create: 6138e7e854cSKip Macy rt0 = rt; 6148e7e854cSKip Macy rt = NULL; 6158e7e854cSKip Macy 616df8bae1dSRodney W. Grimes flags |= RTF_GATEWAY | RTF_DYNAMIC; 6178071913dSRuslan Ermilov bzero((caddr_t)&info, sizeof(info)); 6188071913dSRuslan Ermilov info.rti_info[RTAX_DST] = dst; 6198071913dSRuslan Ermilov info.rti_info[RTAX_GATEWAY] = gateway; 6208071913dSRuslan Ermilov info.rti_info[RTAX_NETMASK] = netmask; 6218071913dSRuslan Ermilov info.rti_ifa = ifa; 6228071913dSRuslan Ermilov info.rti_flags = flags; 6233120b9d4SKip Macy if (rt0 != NULL) 6243120b9d4SKip Macy RT_UNLOCK(rt0); /* drop lock to avoid LOR with RNH */ 6258b07e49aSJulian Elischer error = rtrequest1_fib(RTM_ADD, &info, &rt, fibnum); 626d1dd20beSSam Leffler if (rt != NULL) { 6274de5d90cSSam Leffler RT_LOCK(rt); 6283120b9d4SKip Macy if (rt0 != NULL) 62929910a5aSKip Macy EVENTHANDLER_INVOKE(route_redirect_event, rt0, rt, dst); 6308071913dSRuslan Ermilov flags = rt->rt_flags; 631d1dd20beSSam Leffler } 6323120b9d4SKip Macy if (rt0 != NULL) 6333120b9d4SKip Macy RTFREE(rt0); 6348e7e854cSKip Macy 635603724d3SBjoern A. Zeeb stat = &V_rtstat.rts_dynamic; 636df8bae1dSRodney W. Grimes } else { 6378e7e854cSKip Macy struct rtentry *gwrt; 6388e7e854cSKip Macy 639df8bae1dSRodney W. Grimes /* 640df8bae1dSRodney W. Grimes * Smash the current notion of the gateway to 641df8bae1dSRodney W. Grimes * this destination. Should check about netmask!!! 642df8bae1dSRodney W. Grimes */ 643df8bae1dSRodney W. Grimes rt->rt_flags |= RTF_MODIFIED; 644df8bae1dSRodney W. Grimes flags |= RTF_MODIFIED; 645603724d3SBjoern A. Zeeb stat = &V_rtstat.rts_newgateway; 646499676dfSJulian Elischer /* 647499676dfSJulian Elischer * add the key and gateway (in one malloc'd chunk). 648499676dfSJulian Elischer */ 6493120b9d4SKip Macy RT_UNLOCK(rt); 6503120b9d4SKip Macy RADIX_NODE_HEAD_LOCK(rnh); 6513120b9d4SKip Macy RT_LOCK(rt); 652df8bae1dSRodney W. Grimes rt_setgate(rt, rt_key(rt), gateway); 6533120b9d4SKip Macy gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED); 6543120b9d4SKip Macy RADIX_NODE_HEAD_UNLOCK(rnh); 65529910a5aSKip Macy EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst); 6568e7e854cSKip Macy RTFREE_LOCKED(gwrt); 657df8bae1dSRodney W. Grimes } 658df8bae1dSRodney W. Grimes } else 659df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 660df8bae1dSRodney W. Grimes done: 661d1dd20beSSam Leffler if (rt) 6621951e633SJohn Baldwin RTFREE_LOCKED(rt); 663df8bae1dSRodney W. Grimes out: 664df8bae1dSRodney W. Grimes if (error) 665603724d3SBjoern A. Zeeb V_rtstat.rts_badredirect++; 666df8bae1dSRodney W. Grimes else if (stat != NULL) 667df8bae1dSRodney W. Grimes (*stat)++; 668df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 669df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 670df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = gateway; 671df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = netmask; 672df8bae1dSRodney W. Grimes info.rti_info[RTAX_AUTHOR] = src; 673528737fdSBjoern A. Zeeb rt_missmsg_fib(RTM_REDIRECT, &info, flags, error, fibnum); 6748c0fec80SRobert Watson if (ifa != NULL) 6758c0fec80SRobert Watson ifa_free(ifa); 676df8bae1dSRodney W. Grimes } 677df8bae1dSRodney W. Grimes 6788b07e49aSJulian Elischer int 6798b07e49aSJulian Elischer rtioctl(u_long req, caddr_t data) 6808b07e49aSJulian Elischer { 681a8498625SBjoern A. Zeeb 682a8498625SBjoern A. Zeeb return (rtioctl_fib(req, data, RT_DEFAULT_FIB)); 6838b07e49aSJulian Elischer } 6848b07e49aSJulian Elischer 685df8bae1dSRodney W. Grimes /* 686df8bae1dSRodney W. Grimes * Routing table ioctl interface. 687df8bae1dSRodney W. Grimes */ 688df8bae1dSRodney W. Grimes int 6898b07e49aSJulian Elischer rtioctl_fib(u_long req, caddr_t data, u_int fibnum) 690df8bae1dSRodney W. Grimes { 6915090559bSChristian S.J. Peron 6925090559bSChristian S.J. Peron /* 6935090559bSChristian S.J. Peron * If more ioctl commands are added here, make sure the proper 6945090559bSChristian S.J. Peron * super-user checks are being performed because it is possible for 6955090559bSChristian S.J. Peron * prison-root to make it this far if raw sockets have been enabled 6965090559bSChristian S.J. Peron * in jails. 6975090559bSChristian S.J. Peron */ 698623ae52eSPoul-Henning Kamp #ifdef INET 699f0068c4aSGarrett Wollman /* Multicast goop, grrr... */ 7008b07e49aSJulian Elischer return mrt_ioctl ? mrt_ioctl(req, data, fibnum) : EOPNOTSUPP; 701623ae52eSPoul-Henning Kamp #else /* INET */ 702623ae52eSPoul-Henning Kamp return ENXIO; 703623ae52eSPoul-Henning Kamp #endif /* INET */ 704df8bae1dSRodney W. Grimes } 705df8bae1dSRodney W. Grimes 706df8bae1dSRodney W. Grimes struct ifaddr * 7074f8585e0SAlan Somers ifa_ifwithroute(int flags, struct sockaddr *dst, struct sockaddr *gateway, 7088b07e49aSJulian Elischer u_int fibnum) 7098b07e49aSJulian Elischer { 710f59c6cb0SAlexander V. Chernikov struct ifaddr *ifa; 711e034e82cSQing Li int not_found = 0; 712d1dd20beSSam Leffler 713df8bae1dSRodney W. Grimes if ((flags & RTF_GATEWAY) == 0) { 714df8bae1dSRodney W. Grimes /* 715df8bae1dSRodney W. Grimes * If we are adding a route to an interface, 716df8bae1dSRodney W. Grimes * and the interface is a pt to pt link 717df8bae1dSRodney W. Grimes * we should search for the destination 718df8bae1dSRodney W. Grimes * as our clue to the interface. Otherwise 719df8bae1dSRodney W. Grimes * we can use the local address. 720df8bae1dSRodney W. Grimes */ 72185911824SLuigi Rizzo ifa = NULL; 72285911824SLuigi Rizzo if (flags & RTF_HOST) 7234f8585e0SAlan Somers ifa = ifa_ifwithdstaddr(dst, fibnum); 72485911824SLuigi Rizzo if (ifa == NULL) 725df8bae1dSRodney W. Grimes ifa = ifa_ifwithaddr(gateway); 726df8bae1dSRodney W. Grimes } else { 727df8bae1dSRodney W. Grimes /* 728df8bae1dSRodney W. Grimes * If we are adding a route to a remote net 729df8bae1dSRodney W. Grimes * or host, the gateway may still be on the 730df8bae1dSRodney W. Grimes * other end of a pt to pt link. 731df8bae1dSRodney W. Grimes */ 7324f8585e0SAlan Somers ifa = ifa_ifwithdstaddr(gateway, fibnum); 733df8bae1dSRodney W. Grimes } 73485911824SLuigi Rizzo if (ifa == NULL) 7354f8585e0SAlan Somers ifa = ifa_ifwithnet(gateway, 0, fibnum); 73685911824SLuigi Rizzo if (ifa == NULL) { 7379b20205dSKip Macy struct rtentry *rt = rtalloc1_fib(gateway, 0, RTF_RNH_LOCKED, fibnum); 73885911824SLuigi Rizzo if (rt == NULL) 73985911824SLuigi Rizzo return (NULL); 740e034e82cSQing Li /* 741e034e82cSQing Li * dismiss a gateway that is reachable only 742e034e82cSQing Li * through the default router 743e034e82cSQing Li */ 744e034e82cSQing Li switch (gateway->sa_family) { 745e034e82cSQing Li case AF_INET: 746e034e82cSQing Li if (satosin(rt_key(rt))->sin_addr.s_addr == INADDR_ANY) 747e034e82cSQing Li not_found = 1; 748e034e82cSQing Li break; 749e034e82cSQing Li case AF_INET6: 750e034e82cSQing Li if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(rt))->sin6_addr)) 751e034e82cSQing Li not_found = 1; 752e034e82cSQing Li break; 753e034e82cSQing Li default: 754e034e82cSQing Li break; 755e034e82cSQing Li } 7568c0fec80SRobert Watson if (!not_found && rt->rt_ifa != NULL) { 7578c0fec80SRobert Watson ifa = rt->rt_ifa; 7588c0fec80SRobert Watson ifa_ref(ifa); 7598c0fec80SRobert Watson } 7607138d65cSSam Leffler RT_REMREF(rt); 761d1dd20beSSam Leffler RT_UNLOCK(rt); 7628c0fec80SRobert Watson if (not_found || ifa == NULL) 76385911824SLuigi Rizzo return (NULL); 764df8bae1dSRodney W. Grimes } 765df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family != dst->sa_family) { 766df8bae1dSRodney W. Grimes struct ifaddr *oifa = ifa; 767df8bae1dSRodney W. Grimes ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); 76885911824SLuigi Rizzo if (ifa == NULL) 769df8bae1dSRodney W. Grimes ifa = oifa; 7708c0fec80SRobert Watson else 7718c0fec80SRobert Watson ifa_free(oifa); 772df8bae1dSRodney W. Grimes } 773df8bae1dSRodney W. Grimes return (ifa); 774df8bae1dSRodney W. Grimes } 775df8bae1dSRodney W. Grimes 776b0a76b88SJulian Elischer /* 777b0a76b88SJulian Elischer * Do appropriate manipulations of a routing tree given 778b0a76b88SJulian Elischer * all the bits of info needed 779b0a76b88SJulian Elischer */ 780df8bae1dSRodney W. Grimes int 781d1dd20beSSam Leffler rtrequest(int req, 782d1dd20beSSam Leffler struct sockaddr *dst, 783d1dd20beSSam Leffler struct sockaddr *gateway, 784d1dd20beSSam Leffler struct sockaddr *netmask, 785d1dd20beSSam Leffler int flags, 786d1dd20beSSam Leffler struct rtentry **ret_nrt) 787df8bae1dSRodney W. Grimes { 788a8498625SBjoern A. Zeeb 789a8498625SBjoern A. Zeeb return (rtrequest_fib(req, dst, gateway, netmask, flags, ret_nrt, 790a8498625SBjoern A. Zeeb RT_DEFAULT_FIB)); 7918b07e49aSJulian Elischer } 7928b07e49aSJulian Elischer 7938b07e49aSJulian Elischer int 7948b07e49aSJulian Elischer rtrequest_fib(int req, 7958b07e49aSJulian Elischer struct sockaddr *dst, 7968b07e49aSJulian Elischer struct sockaddr *gateway, 7978b07e49aSJulian Elischer struct sockaddr *netmask, 7988b07e49aSJulian Elischer int flags, 7998b07e49aSJulian Elischer struct rtentry **ret_nrt, 8008b07e49aSJulian Elischer u_int fibnum) 8018b07e49aSJulian Elischer { 8028071913dSRuslan Ermilov struct rt_addrinfo info; 8038071913dSRuslan Ermilov 804ac4a76ebSBjoern A. Zeeb if (dst->sa_len == 0) 805ac4a76ebSBjoern A. Zeeb return(EINVAL); 806ac4a76ebSBjoern A. Zeeb 8078071913dSRuslan Ermilov bzero((caddr_t)&info, sizeof(info)); 8088071913dSRuslan Ermilov info.rti_flags = flags; 8098071913dSRuslan Ermilov info.rti_info[RTAX_DST] = dst; 8108071913dSRuslan Ermilov info.rti_info[RTAX_GATEWAY] = gateway; 8118071913dSRuslan Ermilov info.rti_info[RTAX_NETMASK] = netmask; 8128b07e49aSJulian Elischer return rtrequest1_fib(req, &info, ret_nrt, fibnum); 8138071913dSRuslan Ermilov } 8148071913dSRuslan Ermilov 815*4bdf0b6aSAlexander V. Chernikov 816*4bdf0b6aSAlexander V. Chernikov void 817*4bdf0b6aSAlexander V. Chernikov rt_foreach_fib(int af, rt_setwarg_t *setwa_f, rt_walktree_f_t *wa_f, void *arg) 818*4bdf0b6aSAlexander V. Chernikov { 819*4bdf0b6aSAlexander V. Chernikov struct radix_node_head *rnh; 820*4bdf0b6aSAlexander V. Chernikov uint32_t fibnum; 821*4bdf0b6aSAlexander V. Chernikov int i; 822*4bdf0b6aSAlexander V. Chernikov 823*4bdf0b6aSAlexander V. Chernikov for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { 824*4bdf0b6aSAlexander V. Chernikov /* Do we want some specific family? */ 825*4bdf0b6aSAlexander V. Chernikov if (af != AF_UNSPEC) { 826*4bdf0b6aSAlexander V. Chernikov rnh = rt_tables_get_rnh(fibnum, af); 827*4bdf0b6aSAlexander V. Chernikov if (rnh == NULL) 828*4bdf0b6aSAlexander V. Chernikov continue; 829*4bdf0b6aSAlexander V. Chernikov if (setwa_f != NULL) 830*4bdf0b6aSAlexander V. Chernikov setwa_f(rnh, fibnum, i, arg); 831*4bdf0b6aSAlexander V. Chernikov 832*4bdf0b6aSAlexander V. Chernikov RADIX_NODE_HEAD_LOCK(rnh); 833*4bdf0b6aSAlexander V. Chernikov rnh->rnh_walktree(rnh, (walktree_f_t *)wa_f, arg); 834*4bdf0b6aSAlexander V. Chernikov RADIX_NODE_HEAD_UNLOCK(rnh); 835*4bdf0b6aSAlexander V. Chernikov continue; 836*4bdf0b6aSAlexander V. Chernikov } 837*4bdf0b6aSAlexander V. Chernikov 838*4bdf0b6aSAlexander V. Chernikov for (i = 1; i <= AF_MAX; i++) { 839*4bdf0b6aSAlexander V. Chernikov rnh = rt_tables_get_rnh(fibnum, i); 840*4bdf0b6aSAlexander V. Chernikov if (rnh == NULL) 841*4bdf0b6aSAlexander V. Chernikov continue; 842*4bdf0b6aSAlexander V. Chernikov if (setwa_f != NULL) 843*4bdf0b6aSAlexander V. Chernikov setwa_f(rnh, fibnum, i, arg); 844*4bdf0b6aSAlexander V. Chernikov 845*4bdf0b6aSAlexander V. Chernikov RADIX_NODE_HEAD_LOCK(rnh); 846*4bdf0b6aSAlexander V. Chernikov rnh->rnh_walktree(rnh, (walktree_f_t *)wa_f, arg); 847*4bdf0b6aSAlexander V. Chernikov RADIX_NODE_HEAD_UNLOCK(rnh); 848*4bdf0b6aSAlexander V. Chernikov } 849*4bdf0b6aSAlexander V. Chernikov } 850*4bdf0b6aSAlexander V. Chernikov } 851*4bdf0b6aSAlexander V. Chernikov 852*4bdf0b6aSAlexander V. Chernikov /* 853*4bdf0b6aSAlexander V. Chernikov * Delete Routes for a Network Interface 854*4bdf0b6aSAlexander V. Chernikov * 855*4bdf0b6aSAlexander V. Chernikov * Called for each routing entry via the rnh->rnh_walktree() call above 856*4bdf0b6aSAlexander V. Chernikov * to delete all route entries referencing a detaching network interface. 857*4bdf0b6aSAlexander V. Chernikov * 858*4bdf0b6aSAlexander V. Chernikov * Arguments: 859*4bdf0b6aSAlexander V. Chernikov * rt pointer to rtentry 860*4bdf0b6aSAlexander V. Chernikov * arg argument passed to rnh->rnh_walktree() - detaching interface 861*4bdf0b6aSAlexander V. Chernikov * 862*4bdf0b6aSAlexander V. Chernikov * Returns: 863*4bdf0b6aSAlexander V. Chernikov * 0 successful 864*4bdf0b6aSAlexander V. Chernikov * errno failed - reason indicated 865*4bdf0b6aSAlexander V. Chernikov */ 866*4bdf0b6aSAlexander V. Chernikov static int 867*4bdf0b6aSAlexander V. Chernikov rt_ifdelroute(struct rtentry *rt, void *arg) 868*4bdf0b6aSAlexander V. Chernikov { 869*4bdf0b6aSAlexander V. Chernikov struct ifnet *ifp = arg; 870*4bdf0b6aSAlexander V. Chernikov int err; 871*4bdf0b6aSAlexander V. Chernikov 872*4bdf0b6aSAlexander V. Chernikov if (rt->rt_ifp != ifp) 873*4bdf0b6aSAlexander V. Chernikov return (0); 874*4bdf0b6aSAlexander V. Chernikov 875*4bdf0b6aSAlexander V. Chernikov /* 876*4bdf0b6aSAlexander V. Chernikov * Protect (sorta) against walktree recursion problems 877*4bdf0b6aSAlexander V. Chernikov * with cloned routes 878*4bdf0b6aSAlexander V. Chernikov */ 879*4bdf0b6aSAlexander V. Chernikov if ((rt->rt_flags & RTF_UP) == 0) 880*4bdf0b6aSAlexander V. Chernikov return (0); 881*4bdf0b6aSAlexander V. Chernikov 882*4bdf0b6aSAlexander V. Chernikov err = rtrequest_fib(RTM_DELETE, rt_key(rt), rt->rt_gateway, 883*4bdf0b6aSAlexander V. Chernikov rt_mask(rt), 884*4bdf0b6aSAlexander V. Chernikov rt->rt_flags | RTF_RNH_LOCKED | RTF_PINNED, 885*4bdf0b6aSAlexander V. Chernikov (struct rtentry **) NULL, rt->rt_fibnum); 886*4bdf0b6aSAlexander V. Chernikov if (err != 0) 887*4bdf0b6aSAlexander V. Chernikov log(LOG_WARNING, "rt_ifdelroute: error %d\n", err); 888*4bdf0b6aSAlexander V. Chernikov 889*4bdf0b6aSAlexander V. Chernikov return (0); 890*4bdf0b6aSAlexander V. Chernikov } 891*4bdf0b6aSAlexander V. Chernikov 892*4bdf0b6aSAlexander V. Chernikov /* 893*4bdf0b6aSAlexander V. Chernikov * Delete all remaining routes using this interface 894*4bdf0b6aSAlexander V. Chernikov * Unfortuneatly the only way to do this is to slog through 895*4bdf0b6aSAlexander V. Chernikov * the entire routing table looking for routes which point 896*4bdf0b6aSAlexander V. Chernikov * to this interface...oh well... 897*4bdf0b6aSAlexander V. Chernikov */ 898*4bdf0b6aSAlexander V. Chernikov void 899*4bdf0b6aSAlexander V. Chernikov rt_flushifroutes(struct ifnet *ifp) 900*4bdf0b6aSAlexander V. Chernikov { 901*4bdf0b6aSAlexander V. Chernikov 902*4bdf0b6aSAlexander V. Chernikov rt_foreach_fib(AF_UNSPEC, NULL, rt_ifdelroute, ifp); 903*4bdf0b6aSAlexander V. Chernikov } 904*4bdf0b6aSAlexander V. Chernikov 9058071913dSRuslan Ermilov /* 9068071913dSRuslan Ermilov * These (questionable) definitions of apparent local variables apply 9078071913dSRuslan Ermilov * to the next two functions. XXXXXX!!! 9088071913dSRuslan Ermilov */ 9098071913dSRuslan Ermilov #define dst info->rti_info[RTAX_DST] 9108071913dSRuslan Ermilov #define gateway info->rti_info[RTAX_GATEWAY] 9118071913dSRuslan Ermilov #define netmask info->rti_info[RTAX_NETMASK] 9128071913dSRuslan Ermilov #define ifaaddr info->rti_info[RTAX_IFA] 9138071913dSRuslan Ermilov #define ifpaddr info->rti_info[RTAX_IFP] 9148071913dSRuslan Ermilov #define flags info->rti_flags 9158071913dSRuslan Ermilov 9168071913dSRuslan Ermilov int 917d1dd20beSSam Leffler rt_getifa(struct rt_addrinfo *info) 9188071913dSRuslan Ermilov { 919a8498625SBjoern A. Zeeb 920a8498625SBjoern A. Zeeb return (rt_getifa_fib(info, RT_DEFAULT_FIB)); 9218b07e49aSJulian Elischer } 9228b07e49aSJulian Elischer 9238c0fec80SRobert Watson /* 9248c0fec80SRobert Watson * Look up rt_addrinfo for a specific fib. Note that if rti_ifa is defined, 9258c0fec80SRobert Watson * it will be referenced so the caller must free it. 9268c0fec80SRobert Watson */ 9278b07e49aSJulian Elischer int 9288b07e49aSJulian Elischer rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum) 9298b07e49aSJulian Elischer { 9308071913dSRuslan Ermilov struct ifaddr *ifa; 9318071913dSRuslan Ermilov int error = 0; 9328071913dSRuslan Ermilov 9338071913dSRuslan Ermilov /* 9348071913dSRuslan Ermilov * ifp may be specified by sockaddr_dl 9358071913dSRuslan Ermilov * when protocol address is ambiguous. 9368071913dSRuslan Ermilov */ 9378071913dSRuslan Ermilov if (info->rti_ifp == NULL && ifpaddr != NULL && 9388071913dSRuslan Ermilov ifpaddr->sa_family == AF_LINK && 9394f8585e0SAlan Somers (ifa = ifa_ifwithnet(ifpaddr, 0, fibnum)) != NULL) { 9408071913dSRuslan Ermilov info->rti_ifp = ifa->ifa_ifp; 9418c0fec80SRobert Watson ifa_free(ifa); 9428c0fec80SRobert Watson } 9438071913dSRuslan Ermilov if (info->rti_ifa == NULL && ifaaddr != NULL) 9448071913dSRuslan Ermilov info->rti_ifa = ifa_ifwithaddr(ifaaddr); 9458071913dSRuslan Ermilov if (info->rti_ifa == NULL) { 9468071913dSRuslan Ermilov struct sockaddr *sa; 9478071913dSRuslan Ermilov 9488071913dSRuslan Ermilov sa = ifaaddr != NULL ? ifaaddr : 9498071913dSRuslan Ermilov (gateway != NULL ? gateway : dst); 9508071913dSRuslan Ermilov if (sa != NULL && info->rti_ifp != NULL) 9518071913dSRuslan Ermilov info->rti_ifa = ifaof_ifpforaddr(sa, info->rti_ifp); 9528071913dSRuslan Ermilov else if (dst != NULL && gateway != NULL) 9534f8585e0SAlan Somers info->rti_ifa = ifa_ifwithroute(flags, dst, gateway, 9548b07e49aSJulian Elischer fibnum); 9558071913dSRuslan Ermilov else if (sa != NULL) 9564f8585e0SAlan Somers info->rti_ifa = ifa_ifwithroute(flags, sa, sa, 9578b07e49aSJulian Elischer fibnum); 9588071913dSRuslan Ermilov } 9598071913dSRuslan Ermilov if ((ifa = info->rti_ifa) != NULL) { 9608071913dSRuslan Ermilov if (info->rti_ifp == NULL) 9618071913dSRuslan Ermilov info->rti_ifp = ifa->ifa_ifp; 9628071913dSRuslan Ermilov } else 9638071913dSRuslan Ermilov error = ENETUNREACH; 9648071913dSRuslan Ermilov return (error); 9658071913dSRuslan Ermilov } 9668071913dSRuslan Ermilov 9679c63e9dbSSam Leffler /* 9689c63e9dbSSam Leffler * Expunges references to a route that's about to be reclaimed. 9699c63e9dbSSam Leffler * The route must be locked. 9709c63e9dbSSam Leffler */ 9719c63e9dbSSam Leffler int 972b980262eSAlexander V. Chernikov rt_expunge(struct radix_node_head *rnh, struct rtentry *rt) 9739c63e9dbSSam Leffler { 974c7ea0aa6SQing Li #if !defined(RADIX_MPATH) 9759c63e9dbSSam Leffler struct radix_node *rn; 976c7ea0aa6SQing Li #else 977c7ea0aa6SQing Li struct rt_addrinfo info; 978c7ea0aa6SQing Li int fib; 979c7ea0aa6SQing Li struct rtentry *rt0; 980c7ea0aa6SQing Li #endif 9819c63e9dbSSam Leffler struct ifaddr *ifa; 9829c63e9dbSSam Leffler int error = 0; 9839c63e9dbSSam Leffler 9849c63e9dbSSam Leffler RT_LOCK_ASSERT(rt); 9853120b9d4SKip Macy RADIX_NODE_HEAD_LOCK_ASSERT(rnh); 986c7ea0aa6SQing Li 987c7ea0aa6SQing Li #ifdef RADIX_MPATH 988c7ea0aa6SQing Li fib = rt->rt_fibnum; 989c7ea0aa6SQing Li bzero(&info, sizeof(info)); 990c7ea0aa6SQing Li info.rti_ifp = rt->rt_ifp; 991c7ea0aa6SQing Li info.rti_flags = RTF_RNH_LOCKED; 992c7ea0aa6SQing Li info.rti_info[RTAX_DST] = rt_key(rt); 993c7ea0aa6SQing Li info.rti_info[RTAX_GATEWAY] = rt->rt_ifa->ifa_addr; 994c7ea0aa6SQing Li 995c7ea0aa6SQing Li RT_UNLOCK(rt); 996c7ea0aa6SQing Li error = rtrequest1_fib(RTM_DELETE, &info, &rt0, fib); 997c7ea0aa6SQing Li 998c7ea0aa6SQing Li if (error == 0 && rt0 != NULL) { 999c7ea0aa6SQing Li rt = rt0; 1000c7ea0aa6SQing Li RT_LOCK(rt); 1001c7ea0aa6SQing Li } else if (error != 0) { 1002c7ea0aa6SQing Li RT_LOCK(rt); 1003c7ea0aa6SQing Li return (error); 1004c7ea0aa6SQing Li } 1005c7ea0aa6SQing Li #else 10069c63e9dbSSam Leffler /* 10079c63e9dbSSam Leffler * Remove the item from the tree; it should be there, 10089c63e9dbSSam Leffler * but when callers invoke us blindly it may not (sigh). 10099c63e9dbSSam Leffler */ 10109c63e9dbSSam Leffler rn = rnh->rnh_deladdr(rt_key(rt), rt_mask(rt), rnh); 101185911824SLuigi Rizzo if (rn == NULL) { 10129c63e9dbSSam Leffler error = ESRCH; 10139c63e9dbSSam Leffler goto bad; 10149c63e9dbSSam Leffler } 10159c63e9dbSSam Leffler KASSERT((rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) == 0, 10169c63e9dbSSam Leffler ("unexpected flags 0x%x", rn->rn_flags)); 1017d6941ce9SLuigi Rizzo KASSERT(rt == RNTORT(rn), 10189c63e9dbSSam Leffler ("lookup mismatch, rt %p rn %p", rt, rn)); 1019c7ea0aa6SQing Li #endif /* RADIX_MPATH */ 10209c63e9dbSSam Leffler 10219c63e9dbSSam Leffler rt->rt_flags &= ~RTF_UP; 10229c63e9dbSSam Leffler 10239c63e9dbSSam Leffler /* 10249c63e9dbSSam Leffler * Give the protocol a chance to keep things in sync. 10259c63e9dbSSam Leffler */ 10269c63e9dbSSam Leffler if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) { 10279c63e9dbSSam Leffler struct rt_addrinfo info; 10289c63e9dbSSam Leffler 10299c63e9dbSSam Leffler bzero((caddr_t)&info, sizeof(info)); 10309c63e9dbSSam Leffler info.rti_flags = rt->rt_flags; 10319c63e9dbSSam Leffler info.rti_info[RTAX_DST] = rt_key(rt); 10329c63e9dbSSam Leffler info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 10339c63e9dbSSam Leffler info.rti_info[RTAX_NETMASK] = rt_mask(rt); 10349c63e9dbSSam Leffler ifa->ifa_rtrequest(RTM_DELETE, rt, &info); 10359c63e9dbSSam Leffler } 10369c63e9dbSSam Leffler 10379c63e9dbSSam Leffler /* 10389c63e9dbSSam Leffler * one more rtentry floating around that is not 10399c63e9dbSSam Leffler * linked to the routing table. 10409c63e9dbSSam Leffler */ 1041603724d3SBjoern A. Zeeb V_rttrash++; 1042c7ea0aa6SQing Li #if !defined(RADIX_MPATH) 10439c63e9dbSSam Leffler bad: 1044c7ea0aa6SQing Li #endif 10459c63e9dbSSam Leffler return (error); 10469c63e9dbSSam Leffler } 10479c63e9dbSSam Leffler 10487f948f12SAlexander V. Chernikov static int 10497f948f12SAlexander V. Chernikov if_updatemtu_cb(struct radix_node *rn, void *arg) 10507f948f12SAlexander V. Chernikov { 10517f948f12SAlexander V. Chernikov struct rtentry *rt; 10527f948f12SAlexander V. Chernikov struct if_mtuinfo *ifmtu; 10537f948f12SAlexander V. Chernikov 10547f948f12SAlexander V. Chernikov rt = (struct rtentry *)rn; 10557f948f12SAlexander V. Chernikov ifmtu = (struct if_mtuinfo *)arg; 10567f948f12SAlexander V. Chernikov 10577f948f12SAlexander V. Chernikov if (rt->rt_ifp != ifmtu->ifp) 10587f948f12SAlexander V. Chernikov return (0); 10597f948f12SAlexander V. Chernikov 10607f948f12SAlexander V. Chernikov if (rt->rt_mtu >= ifmtu->mtu) { 10617f948f12SAlexander V. Chernikov /* We have to decrease mtu regardless of flags */ 10627f948f12SAlexander V. Chernikov rt->rt_mtu = ifmtu->mtu; 10637f948f12SAlexander V. Chernikov return (0); 10647f948f12SAlexander V. Chernikov } 10657f948f12SAlexander V. Chernikov 10667f948f12SAlexander V. Chernikov /* 10677f948f12SAlexander V. Chernikov * New MTU is bigger. Check if are allowed to alter it 10687f948f12SAlexander V. Chernikov */ 10697f948f12SAlexander V. Chernikov if ((rt->rt_flags & (RTF_FIXEDMTU | RTF_GATEWAY | RTF_HOST)) != 0) { 10707f948f12SAlexander V. Chernikov 10717f948f12SAlexander V. Chernikov /* 10727f948f12SAlexander V. Chernikov * Skip routes with user-supplied MTU and 10737f948f12SAlexander V. Chernikov * non-interface routes 10747f948f12SAlexander V. Chernikov */ 10757f948f12SAlexander V. Chernikov return (0); 10767f948f12SAlexander V. Chernikov } 10777f948f12SAlexander V. Chernikov 10787f948f12SAlexander V. Chernikov /* We are safe to update route MTU */ 10797f948f12SAlexander V. Chernikov rt->rt_mtu = ifmtu->mtu; 10807f948f12SAlexander V. Chernikov 10817f948f12SAlexander V. Chernikov return (0); 10827f948f12SAlexander V. Chernikov } 10837f948f12SAlexander V. Chernikov 10847f948f12SAlexander V. Chernikov void 10857f948f12SAlexander V. Chernikov rt_updatemtu(struct ifnet *ifp) 10867f948f12SAlexander V. Chernikov { 10877f948f12SAlexander V. Chernikov struct if_mtuinfo ifmtu; 10887f948f12SAlexander V. Chernikov struct radix_node_head *rnh; 10897f948f12SAlexander V. Chernikov int i, j; 10907f948f12SAlexander V. Chernikov 10917f948f12SAlexander V. Chernikov ifmtu.ifp = ifp; 10927f948f12SAlexander V. Chernikov 10937f948f12SAlexander V. Chernikov /* 10947f948f12SAlexander V. Chernikov * Try to update rt_mtu for all routes using this interface 10957f948f12SAlexander V. Chernikov * Unfortunately the only way to do this is to traverse all 10967f948f12SAlexander V. Chernikov * routing tables in all fibs/domains. 10977f948f12SAlexander V. Chernikov */ 10987f948f12SAlexander V. Chernikov for (i = 1; i <= AF_MAX; i++) { 10997f948f12SAlexander V. Chernikov ifmtu.mtu = if_getmtu_family(ifp, i); 11007f948f12SAlexander V. Chernikov for (j = 0; j < rt_numfibs; j++) { 11017f948f12SAlexander V. Chernikov rnh = rt_tables_get_rnh(j, i); 11027f948f12SAlexander V. Chernikov if (rnh == NULL) 11037f948f12SAlexander V. Chernikov continue; 11047f948f12SAlexander V. Chernikov RADIX_NODE_HEAD_LOCK(rnh); 11057f948f12SAlexander V. Chernikov rnh->rnh_walktree(rnh, if_updatemtu_cb, &ifmtu); 11067f948f12SAlexander V. Chernikov RADIX_NODE_HEAD_UNLOCK(rnh); 11077f948f12SAlexander V. Chernikov } 11087f948f12SAlexander V. Chernikov } 11097f948f12SAlexander V. Chernikov } 11107f948f12SAlexander V. Chernikov 11117f948f12SAlexander V. Chernikov 11125a2f4cbdSAlexander V. Chernikov #if 0 11135a2f4cbdSAlexander V. Chernikov int p_sockaddr(char *buf, int buflen, struct sockaddr *s); 11145a2f4cbdSAlexander V. Chernikov int rt_print(char *buf, int buflen, struct rtentry *rt); 11155a2f4cbdSAlexander V. Chernikov 11165a2f4cbdSAlexander V. Chernikov int 11175a2f4cbdSAlexander V. Chernikov p_sockaddr(char *buf, int buflen, struct sockaddr *s) 11185a2f4cbdSAlexander V. Chernikov { 11195a2f4cbdSAlexander V. Chernikov void *paddr = NULL; 11205a2f4cbdSAlexander V. Chernikov 11215a2f4cbdSAlexander V. Chernikov switch (s->sa_family) { 11225a2f4cbdSAlexander V. Chernikov case AF_INET: 11235a2f4cbdSAlexander V. Chernikov paddr = &((struct sockaddr_in *)s)->sin_addr; 11245a2f4cbdSAlexander V. Chernikov break; 11255a2f4cbdSAlexander V. Chernikov case AF_INET6: 11265a2f4cbdSAlexander V. Chernikov paddr = &((struct sockaddr_in6 *)s)->sin6_addr; 11275a2f4cbdSAlexander V. Chernikov break; 11285a2f4cbdSAlexander V. Chernikov } 11295a2f4cbdSAlexander V. Chernikov 11305a2f4cbdSAlexander V. Chernikov if (paddr == NULL) 11315a2f4cbdSAlexander V. Chernikov return (0); 11325a2f4cbdSAlexander V. Chernikov 11335a2f4cbdSAlexander V. Chernikov if (inet_ntop(s->sa_family, paddr, buf, buflen) == NULL) 11345a2f4cbdSAlexander V. Chernikov return (0); 11355a2f4cbdSAlexander V. Chernikov 11365a2f4cbdSAlexander V. Chernikov return (strlen(buf)); 11375a2f4cbdSAlexander V. Chernikov } 11385a2f4cbdSAlexander V. Chernikov 11395a2f4cbdSAlexander V. Chernikov int 11405a2f4cbdSAlexander V. Chernikov rt_print(char *buf, int buflen, struct rtentry *rt) 11415a2f4cbdSAlexander V. Chernikov { 11425a2f4cbdSAlexander V. Chernikov struct sockaddr *addr, *mask; 11435a2f4cbdSAlexander V. Chernikov int i = 0; 11445a2f4cbdSAlexander V. Chernikov 11455a2f4cbdSAlexander V. Chernikov addr = rt_key(rt); 11465a2f4cbdSAlexander V. Chernikov mask = rt_mask(rt); 11475a2f4cbdSAlexander V. Chernikov 11485a2f4cbdSAlexander V. Chernikov i = p_sockaddr(buf, buflen, addr); 11495a2f4cbdSAlexander V. Chernikov if (!(rt->rt_flags & RTF_HOST)) { 11505a2f4cbdSAlexander V. Chernikov buf[i++] = '/'; 11515a2f4cbdSAlexander V. Chernikov i += p_sockaddr(buf + i, buflen - i, mask); 11525a2f4cbdSAlexander V. Chernikov } 11535a2f4cbdSAlexander V. Chernikov 11545a2f4cbdSAlexander V. Chernikov if (rt->rt_flags & RTF_GATEWAY) { 11555a2f4cbdSAlexander V. Chernikov buf[i++] = '>'; 11565a2f4cbdSAlexander V. Chernikov i += p_sockaddr(buf + i, buflen - i, rt->rt_gateway); 11575a2f4cbdSAlexander V. Chernikov } 11585a2f4cbdSAlexander V. Chernikov 11595a2f4cbdSAlexander V. Chernikov return (i); 11605a2f4cbdSAlexander V. Chernikov } 11615a2f4cbdSAlexander V. Chernikov #endif 11625a2f4cbdSAlexander V. Chernikov 1163427ac07fSKip Macy #ifdef RADIX_MPATH 1164427ac07fSKip Macy static int 1165427ac07fSKip Macy rn_mpath_update(int req, struct rt_addrinfo *info, 1166427ac07fSKip Macy struct radix_node_head *rnh, struct rtentry **ret_nrt) 1167427ac07fSKip Macy { 1168427ac07fSKip Macy /* 1169427ac07fSKip Macy * if we got multipath routes, we require users to specify 1170427ac07fSKip Macy * a matching RTAX_GATEWAY. 1171427ac07fSKip Macy */ 1172427ac07fSKip Macy struct rtentry *rt, *rto = NULL; 1173f59c6cb0SAlexander V. Chernikov struct radix_node *rn; 1174427ac07fSKip Macy int error = 0; 1175427ac07fSKip Macy 11765a2f4cbdSAlexander V. Chernikov rn = rnh->rnh_lookup(dst, netmask, rnh); 1177427ac07fSKip Macy if (rn == NULL) 1178427ac07fSKip Macy return (ESRCH); 1179427ac07fSKip Macy rto = rt = RNTORT(rn); 11805a2f4cbdSAlexander V. Chernikov 1181427ac07fSKip Macy rt = rt_mpath_matchgate(rt, gateway); 1182427ac07fSKip Macy if (rt == NULL) 1183427ac07fSKip Macy return (ESRCH); 1184427ac07fSKip Macy /* 1185427ac07fSKip Macy * this is the first entry in the chain 1186427ac07fSKip Macy */ 1187427ac07fSKip Macy if (rto == rt) { 1188427ac07fSKip Macy rn = rn_mpath_next((struct radix_node *)rt); 1189427ac07fSKip Macy /* 1190427ac07fSKip Macy * there is another entry, now it's active 1191427ac07fSKip Macy */ 1192427ac07fSKip Macy if (rn) { 1193427ac07fSKip Macy rto = RNTORT(rn); 1194427ac07fSKip Macy RT_LOCK(rto); 1195427ac07fSKip Macy rto->rt_flags |= RTF_UP; 1196427ac07fSKip Macy RT_UNLOCK(rto); 1197427ac07fSKip Macy } else if (rt->rt_flags & RTF_GATEWAY) { 1198427ac07fSKip Macy /* 1199427ac07fSKip Macy * For gateway routes, we need to 1200427ac07fSKip Macy * make sure that we we are deleting 1201427ac07fSKip Macy * the correct gateway. 1202427ac07fSKip Macy * rt_mpath_matchgate() does not 1203427ac07fSKip Macy * check the case when there is only 1204427ac07fSKip Macy * one route in the chain. 1205427ac07fSKip Macy */ 1206427ac07fSKip Macy if (gateway && 1207427ac07fSKip Macy (rt->rt_gateway->sa_len != gateway->sa_len || 1208427ac07fSKip Macy memcmp(rt->rt_gateway, gateway, gateway->sa_len))) 1209427ac07fSKip Macy error = ESRCH; 12106a7bff2cSKip Macy else { 12116a7bff2cSKip Macy /* 12126a7bff2cSKip Macy * remove from tree before returning it 12136a7bff2cSKip Macy * to the caller 12146a7bff2cSKip Macy */ 12156a7bff2cSKip Macy rn = rnh->rnh_deladdr(dst, netmask, rnh); 12166a7bff2cSKip Macy KASSERT(rt == RNTORT(rn), ("radix node disappeared")); 12176a7bff2cSKip Macy goto gwdelete; 12186a7bff2cSKip Macy } 12196a7bff2cSKip Macy 1220427ac07fSKip Macy } 1221427ac07fSKip Macy /* 1222427ac07fSKip Macy * use the normal delete code to remove 1223427ac07fSKip Macy * the first entry 1224427ac07fSKip Macy */ 1225427ac07fSKip Macy if (req != RTM_DELETE) 1226427ac07fSKip Macy goto nondelete; 1227427ac07fSKip Macy 1228427ac07fSKip Macy error = ENOENT; 1229427ac07fSKip Macy goto done; 1230427ac07fSKip Macy } 1231427ac07fSKip Macy 1232427ac07fSKip Macy /* 1233427ac07fSKip Macy * if the entry is 2nd and on up 1234427ac07fSKip Macy */ 1235427ac07fSKip Macy if ((req == RTM_DELETE) && !rt_mpath_deldup(rto, rt)) 1236427ac07fSKip Macy panic ("rtrequest1: rt_mpath_deldup"); 12376a7bff2cSKip Macy gwdelete: 1238427ac07fSKip Macy RT_LOCK(rt); 1239427ac07fSKip Macy RT_ADDREF(rt); 1240427ac07fSKip Macy if (req == RTM_DELETE) { 1241427ac07fSKip Macy rt->rt_flags &= ~RTF_UP; 1242427ac07fSKip Macy /* 1243427ac07fSKip Macy * One more rtentry floating around that is not 1244427ac07fSKip Macy * linked to the routing table. rttrash will be decremented 1245427ac07fSKip Macy * when RTFREE(rt) is eventually called. 1246427ac07fSKip Macy */ 1247427ac07fSKip Macy V_rttrash++; 1248427ac07fSKip Macy } 1249427ac07fSKip Macy 1250427ac07fSKip Macy nondelete: 1251427ac07fSKip Macy if (req != RTM_DELETE) 1252427ac07fSKip Macy panic("unrecognized request %d", req); 1253427ac07fSKip Macy 1254427ac07fSKip Macy 1255427ac07fSKip Macy /* 1256427ac07fSKip Macy * If the caller wants it, then it can have it, 1257427ac07fSKip Macy * but it's up to it to free the rtentry as we won't be 1258427ac07fSKip Macy * doing it. 1259427ac07fSKip Macy */ 1260427ac07fSKip Macy if (ret_nrt) { 1261427ac07fSKip Macy *ret_nrt = rt; 1262427ac07fSKip Macy RT_UNLOCK(rt); 1263427ac07fSKip Macy } else 1264427ac07fSKip Macy RTFREE_LOCKED(rt); 1265427ac07fSKip Macy done: 1266427ac07fSKip Macy return (error); 1267427ac07fSKip Macy } 1268427ac07fSKip Macy #endif 1269427ac07fSKip Macy 12708071913dSRuslan Ermilov int 12718b07e49aSJulian Elischer rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, 12728b07e49aSJulian Elischer u_int fibnum) 12738b07e49aSJulian Elischer { 12743120b9d4SKip Macy int error = 0, needlock = 0; 1275f59c6cb0SAlexander V. Chernikov struct rtentry *rt; 1276e5c610d6SQing Li #ifdef FLOWTABLE 1277f59c6cb0SAlexander V. Chernikov struct rtentry *rt0; 1278e5c610d6SQing Li #endif 1279f59c6cb0SAlexander V. Chernikov struct radix_node *rn; 1280f59c6cb0SAlexander V. Chernikov struct radix_node_head *rnh; 1281df8bae1dSRodney W. Grimes struct ifaddr *ifa; 1282df8bae1dSRodney W. Grimes struct sockaddr *ndst; 128346a70de2SQing Li struct sockaddr_storage mdst; 1284df8bae1dSRodney W. Grimes #define senderr(x) { error = x ; goto bad; } 1285df8bae1dSRodney W. Grimes 12868b07e49aSJulian Elischer KASSERT((fibnum < rt_numfibs), ("rtrequest1_fib: bad fibnum")); 1287b680a383SBjoern A. Zeeb switch (dst->sa_family) { 1288b680a383SBjoern A. Zeeb case AF_INET6: 1289b680a383SBjoern A. Zeeb case AF_INET: 1290b680a383SBjoern A. Zeeb /* We support multiple FIBs. */ 1291b680a383SBjoern A. Zeeb break; 1292b680a383SBjoern A. Zeeb default: 1293b680a383SBjoern A. Zeeb fibnum = RT_DEFAULT_FIB; 1294b680a383SBjoern A. Zeeb break; 1295b680a383SBjoern A. Zeeb } 1296b680a383SBjoern A. Zeeb 1297b0a76b88SJulian Elischer /* 1298b0a76b88SJulian Elischer * Find the correct routing tree to use for this Address Family 1299b0a76b88SJulian Elischer */ 1300c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 130185911824SLuigi Rizzo if (rnh == NULL) 1302983985c1SJeffrey Hsu return (EAFNOSUPPORT); 13033120b9d4SKip Macy needlock = ((flags & RTF_RNH_LOCKED) == 0); 13043120b9d4SKip Macy flags &= ~RTF_RNH_LOCKED; 13053120b9d4SKip Macy if (needlock) 1306956b0b65SJeffrey Hsu RADIX_NODE_HEAD_LOCK(rnh); 1307c96b8224SKip Macy else 1308c96b8224SKip Macy RADIX_NODE_HEAD_LOCK_ASSERT(rnh); 1309b0a76b88SJulian Elischer /* 1310b0a76b88SJulian Elischer * If we are adding a host route then we don't want to put 131166953138SRuslan Ermilov * a netmask in the tree, nor do we want to clone it. 1312b0a76b88SJulian Elischer */ 13136e6b3f7cSQing Li if (flags & RTF_HOST) 131485911824SLuigi Rizzo netmask = NULL; 13156e6b3f7cSQing Li 1316df8bae1dSRodney W. Grimes switch (req) { 1317df8bae1dSRodney W. Grimes case RTM_DELETE: 131846a70de2SQing Li if (netmask) { 131946a70de2SQing Li rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask); 132046a70de2SQing Li dst = (struct sockaddr *)&mdst; 132146a70de2SQing Li } 1322e440aed9SQing Li #ifdef RADIX_MPATH 1323e440aed9SQing Li if (rn_mpath_capable(rnh)) { 1324427ac07fSKip Macy error = rn_mpath_update(req, info, rnh, ret_nrt); 1325e440aed9SQing Li /* 1326427ac07fSKip Macy * "bad" holds true for the success case 1327427ac07fSKip Macy * as well 1328e440aed9SQing Li */ 1329427ac07fSKip Macy if (error != ENOENT) 1330427ac07fSKip Macy goto bad; 1331c7ea0aa6SQing Li error = 0; 1332e440aed9SQing Li } 1333ea9cd9f2SBjoern A. Zeeb #endif 13343034f43fSAlexander V. Chernikov if ((flags & RTF_PINNED) == 0) { 13353034f43fSAlexander V. Chernikov /* Check if target route can be deleted */ 13363034f43fSAlexander V. Chernikov rt = (struct rtentry *)rnh->rnh_lookup(dst, 13373034f43fSAlexander V. Chernikov netmask, rnh); 13383034f43fSAlexander V. Chernikov if ((rt != NULL) && (rt->rt_flags & RTF_PINNED)) 13393034f43fSAlexander V. Chernikov senderr(EADDRINUSE); 13403034f43fSAlexander V. Chernikov } 13413034f43fSAlexander V. Chernikov 1342b0a76b88SJulian Elischer /* 1343b0a76b88SJulian Elischer * Remove the item from the tree and return it. 1344b0a76b88SJulian Elischer * Complain if it is not there and do no more processing. 1345b0a76b88SJulian Elischer */ 1346d1dd20beSSam Leffler rn = rnh->rnh_deladdr(dst, netmask, rnh); 134785911824SLuigi Rizzo if (rn == NULL) 1348df8bae1dSRodney W. Grimes senderr(ESRCH); 1349df8bae1dSRodney W. Grimes if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 1350df8bae1dSRodney W. Grimes panic ("rtrequest delete"); 1351d6941ce9SLuigi Rizzo rt = RNTORT(rn); 1352d1dd20beSSam Leffler RT_LOCK(rt); 13537138d65cSSam Leffler RT_ADDREF(rt); 135471eba915SRuslan Ermilov rt->rt_flags &= ~RTF_UP; 1355c2bed6a3SGarrett Wollman 1356c2bed6a3SGarrett Wollman /* 1357499676dfSJulian Elischer * give the protocol a chance to keep things in sync. 1358b0a76b88SJulian Elischer */ 1359df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) 13608071913dSRuslan Ermilov ifa->ifa_rtrequest(RTM_DELETE, rt, info); 1361499676dfSJulian Elischer 1362b0a76b88SJulian Elischer /* 1363d6941ce9SLuigi Rizzo * One more rtentry floating around that is not 1364d6941ce9SLuigi Rizzo * linked to the routing table. rttrash will be decremented 1365d6941ce9SLuigi Rizzo * when RTFREE(rt) is eventually called. 1366499676dfSJulian Elischer */ 1367603724d3SBjoern A. Zeeb V_rttrash++; 1368499676dfSJulian Elischer 1369499676dfSJulian Elischer /* 1370499676dfSJulian Elischer * If the caller wants it, then it can have it, 1371499676dfSJulian Elischer * but it's up to it to free the rtentry as we won't be 1372499676dfSJulian Elischer * doing it. 1373b0a76b88SJulian Elischer */ 1374d1dd20beSSam Leffler if (ret_nrt) { 1375df8bae1dSRodney W. Grimes *ret_nrt = rt; 1376d1dd20beSSam Leffler RT_UNLOCK(rt); 1377d1dd20beSSam Leffler } else 1378d1dd20beSSam Leffler RTFREE_LOCKED(rt); 1379df8bae1dSRodney W. Grimes break; 1380df8bae1dSRodney W. Grimes case RTM_RESOLVE: 13816e6b3f7cSQing Li /* 13826e6b3f7cSQing Li * resolve was only used for route cloning 13836e6b3f7cSQing Li * here for compat 13846e6b3f7cSQing Li */ 13856e6b3f7cSQing Li break; 1386df8bae1dSRodney W. Grimes case RTM_ADD: 13875df72964SGarrett Wollman if ((flags & RTF_GATEWAY) && !gateway) 138816a2e0a6SQing Li senderr(EINVAL); 138916a2e0a6SQing Li if (dst && gateway && (dst->sa_family != gateway->sa_family) && 139016a2e0a6SQing Li (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK)) 139116a2e0a6SQing Li senderr(EINVAL); 13925df72964SGarrett Wollman 13938c0fec80SRobert Watson if (info->rti_ifa == NULL) { 13948c0fec80SRobert Watson error = rt_getifa_fib(info, fibnum); 13958c0fec80SRobert Watson if (error) 13968071913dSRuslan Ermilov senderr(error); 13978c0fec80SRobert Watson } else 13988c0fec80SRobert Watson ifa_ref(info->rti_ifa); 13998071913dSRuslan Ermilov ifa = info->rti_ifa; 1400e3a7aa6fSGleb Smirnoff rt = uma_zalloc(V_rtzone, M_NOWAIT); 14018c0fec80SRobert Watson if (rt == NULL) { 14028c0fec80SRobert Watson ifa_free(ifa); 1403df8bae1dSRodney W. Grimes senderr(ENOBUFS); 14048c0fec80SRobert Watson } 1405df8bae1dSRodney W. Grimes rt->rt_flags = RTF_UP | flags; 14068b07e49aSJulian Elischer rt->rt_fibnum = fibnum; 1407499676dfSJulian Elischer /* 1408a8498625SBjoern A. Zeeb * Add the gateway. Possibly re-malloc-ing the storage for it. 1409499676dfSJulian Elischer */ 1410d1dd20beSSam Leffler RT_LOCK(rt); 1411831a80b0SMatthew Dillon if ((error = rt_setgate(rt, dst, gateway)) != 0) { 14128c0fec80SRobert Watson ifa_free(ifa); 14131ed81b73SMarko Zec uma_zfree(V_rtzone, rt); 1414704b0666SBill Fenner senderr(error); 1415df8bae1dSRodney W. Grimes } 1416499676dfSJulian Elischer 1417499676dfSJulian Elischer /* 1418499676dfSJulian Elischer * point to the (possibly newly malloc'd) dest address. 1419499676dfSJulian Elischer */ 1420d1dd20beSSam Leffler ndst = (struct sockaddr *)rt_key(rt); 1421499676dfSJulian Elischer 1422499676dfSJulian Elischer /* 1423499676dfSJulian Elischer * make sure it contains the value we want (masked if needed). 1424499676dfSJulian Elischer */ 1425df8bae1dSRodney W. Grimes if (netmask) { 1426df8bae1dSRodney W. Grimes rt_maskedcopy(dst, ndst, netmask); 1427df8bae1dSRodney W. Grimes } else 14281838a647SLuigi Rizzo bcopy(dst, ndst, dst->sa_len); 14298e718bb4SGarrett Wollman 14308e718bb4SGarrett Wollman /* 14318c0fec80SRobert Watson * We use the ifa reference returned by rt_getifa_fib(). 14328e718bb4SGarrett Wollman * This moved from below so that rnh->rnh_addaddr() can 1433499676dfSJulian Elischer * examine the ifa and ifa->ifa_ifp if it so desires. 14348e718bb4SGarrett Wollman */ 14358e718bb4SGarrett Wollman rt->rt_ifa = ifa; 14368e718bb4SGarrett Wollman rt->rt_ifp = ifa->ifa_ifp; 1437e3a7aa6fSGleb Smirnoff rt->rt_weight = 1; 14388e718bb4SGarrett Wollman 14391a75e3b2SAlexander V. Chernikov rt_setmetrics(info, rt); 14401a75e3b2SAlexander V. Chernikov 1441e440aed9SQing Li #ifdef RADIX_MPATH 1442e440aed9SQing Li /* do not permit exactly the same dst/mask/gw pair */ 1443e440aed9SQing Li if (rn_mpath_capable(rnh) && 1444e440aed9SQing Li rt_mpath_conflict(rnh, rt, netmask)) { 14451099f828SRobert Watson ifa_free(rt->rt_ifa); 14468b15f615SLuiz Otavio O Souza R_Free(rt_key(rt)); 14471ed81b73SMarko Zec uma_zfree(V_rtzone, rt); 1448e440aed9SQing Li senderr(EEXIST); 1449e440aed9SQing Li } 1450e440aed9SQing Li #endif 1451e440aed9SQing Li 1452e5c610d6SQing Li #ifdef FLOWTABLE 1453e5c610d6SQing Li rt0 = NULL; 1454096f2786SBjoern A. Zeeb /* "flow-table" only supports IPv6 and IPv4 at the moment. */ 1455096f2786SBjoern A. Zeeb switch (dst->sa_family) { 1456096f2786SBjoern A. Zeeb #ifdef INET6 1457096f2786SBjoern A. Zeeb case AF_INET6: 1458096f2786SBjoern A. Zeeb #endif 1459db44ff40SBjoern A. Zeeb #ifdef INET 1460096f2786SBjoern A. Zeeb case AF_INET: 1461096f2786SBjoern A. Zeeb #endif 1462096f2786SBjoern A. Zeeb #if defined(INET6) || defined(INET) 1463e5c610d6SQing Li rn = rnh->rnh_matchaddr(dst, rnh); 1464e5c610d6SQing Li if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { 1465e5c610d6SQing Li struct sockaddr *mask; 1466e5c610d6SQing Li u_char *m, *n; 1467e5c610d6SQing Li int len; 1468e5c610d6SQing Li 1469e5c610d6SQing Li /* 1470e5c610d6SQing Li * compare mask to see if the new route is 1471e5c610d6SQing Li * more specific than the existing one 1472e5c610d6SQing Li */ 1473e5c610d6SQing Li rt0 = RNTORT(rn); 1474e5c610d6SQing Li RT_LOCK(rt0); 1475e5c610d6SQing Li RT_ADDREF(rt0); 1476e5c610d6SQing Li RT_UNLOCK(rt0); 1477e5c610d6SQing Li /* 1478e5c610d6SQing Li * A host route is already present, so 1479e5c610d6SQing Li * leave the flow-table entries as is. 1480e5c610d6SQing Li */ 1481e5c610d6SQing Li if (rt0->rt_flags & RTF_HOST) { 1482e5c610d6SQing Li RTFREE(rt0); 1483e5c610d6SQing Li rt0 = NULL; 1484e5c610d6SQing Li } else if (!(flags & RTF_HOST) && netmask) { 1485e5c610d6SQing Li mask = rt_mask(rt0); 1486e5c610d6SQing Li len = mask->sa_len; 1487e5c610d6SQing Li m = (u_char *)mask; 1488e5c610d6SQing Li n = (u_char *)netmask; 1489e5c610d6SQing Li while (len-- > 0) { 1490e5c610d6SQing Li if (*n != *m) 1491e5c610d6SQing Li break; 1492e5c610d6SQing Li n++; 1493e5c610d6SQing Li m++; 1494e5c610d6SQing Li } 1495e5c610d6SQing Li if (len == 0 || (*n < *m)) { 1496e5c610d6SQing Li RTFREE(rt0); 1497e5c610d6SQing Li rt0 = NULL; 1498e5c610d6SQing Li } 1499e5c610d6SQing Li } 1500e5c610d6SQing Li } 1501096f2786SBjoern A. Zeeb #endif/* INET6 || INET */ 1502e5c610d6SQing Li } 1503096f2786SBjoern A. Zeeb #endif /* FLOWTABLE */ 1504e5c610d6SQing Li 1505d1dd20beSSam Leffler /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ 1506d1dd20beSSam Leffler rn = rnh->rnh_addaddr(ndst, netmask, rnh, rt->rt_nodes); 1507499676dfSJulian Elischer /* 1508499676dfSJulian Elischer * If it still failed to go into the tree, 1509499676dfSJulian Elischer * then un-make it (this should be a function) 1510499676dfSJulian Elischer */ 151185911824SLuigi Rizzo if (rn == NULL) { 15121099f828SRobert Watson ifa_free(rt->rt_ifa); 15138b15f615SLuiz Otavio O Souza R_Free(rt_key(rt)); 15141ed81b73SMarko Zec uma_zfree(V_rtzone, rt); 1515e5c610d6SQing Li #ifdef FLOWTABLE 1516e5c610d6SQing Li if (rt0 != NULL) 1517e5c610d6SQing Li RTFREE(rt0); 1518e5c610d6SQing Li #endif 1519df8bae1dSRodney W. Grimes senderr(EEXIST); 1520df8bae1dSRodney W. Grimes } 1521e5c610d6SQing Li #ifdef FLOWTABLE 1522e5c610d6SQing Li else if (rt0 != NULL) { 15235d6d7e75SGleb Smirnoff flowtable_route_flush(dst->sa_family, rt0); 1524e5c610d6SQing Li RTFREE(rt0); 1525e5c610d6SQing Li } 1526e5c610d6SQing Li #endif 1527499676dfSJulian Elischer 1528499676dfSJulian Elischer /* 1529a0c0e34bSGleb Smirnoff * If this protocol has something to add to this then 1530499676dfSJulian Elischer * allow it to do that as well. 1531499676dfSJulian Elischer */ 1532df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 15338071913dSRuslan Ermilov ifa->ifa_rtrequest(req, rt, info); 1534499676dfSJulian Elischer 1535cd02a0b7SGarrett Wollman /* 1536499676dfSJulian Elischer * actually return a resultant rtentry and 1537499676dfSJulian Elischer * give the caller a single reference. 1538499676dfSJulian Elischer */ 1539df8bae1dSRodney W. Grimes if (ret_nrt) { 1540df8bae1dSRodney W. Grimes *ret_nrt = rt; 15417138d65cSSam Leffler RT_ADDREF(rt); 1542df8bae1dSRodney W. Grimes } 1543d1dd20beSSam Leffler RT_UNLOCK(rt); 1544df8bae1dSRodney W. Grimes break; 1545c77462ddSAlexander V. Chernikov case RTM_CHANGE: 1546c77462ddSAlexander V. Chernikov error = rtrequest1_fib_change(rnh, info, ret_nrt, fibnum); 1547c77462ddSAlexander V. Chernikov break; 15488071913dSRuslan Ermilov default: 15498071913dSRuslan Ermilov error = EOPNOTSUPP; 1550df8bae1dSRodney W. Grimes } 1551df8bae1dSRodney W. Grimes bad: 15523120b9d4SKip Macy if (needlock) 1553956b0b65SJeffrey Hsu RADIX_NODE_HEAD_UNLOCK(rnh); 1554df8bae1dSRodney W. Grimes return (error); 1555d1dd20beSSam Leffler #undef senderr 1556d1dd20beSSam Leffler } 1557d1dd20beSSam Leffler 15588071913dSRuslan Ermilov #undef dst 15598071913dSRuslan Ermilov #undef gateway 15608071913dSRuslan Ermilov #undef netmask 15618071913dSRuslan Ermilov #undef ifaaddr 15628071913dSRuslan Ermilov #undef ifpaddr 15638071913dSRuslan Ermilov #undef flags 1564df8bae1dSRodney W. Grimes 1565c77462ddSAlexander V. Chernikov static int 1566c77462ddSAlexander V. Chernikov rtrequest1_fib_change(struct radix_node_head *rnh, struct rt_addrinfo *info, 1567c77462ddSAlexander V. Chernikov struct rtentry **ret_nrt, u_int fibnum) 1568c77462ddSAlexander V. Chernikov { 1569c77462ddSAlexander V. Chernikov struct rtentry *rt = NULL; 1570c77462ddSAlexander V. Chernikov int error = 0; 1571c77462ddSAlexander V. Chernikov int free_ifa = 0; 15721a75e3b2SAlexander V. Chernikov int family, mtu; 15737f948f12SAlexander V. Chernikov struct if_mtuinfo ifmtu; 1574c77462ddSAlexander V. Chernikov 1575c77462ddSAlexander V. Chernikov rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST], 1576c77462ddSAlexander V. Chernikov info->rti_info[RTAX_NETMASK], rnh); 1577c77462ddSAlexander V. Chernikov 1578c77462ddSAlexander V. Chernikov if (rt == NULL) 1579c77462ddSAlexander V. Chernikov return (ESRCH); 1580c77462ddSAlexander V. Chernikov 1581c77462ddSAlexander V. Chernikov #ifdef RADIX_MPATH 1582c77462ddSAlexander V. Chernikov /* 1583c77462ddSAlexander V. Chernikov * If we got multipath routes, 1584c77462ddSAlexander V. Chernikov * we require users to specify a matching RTAX_GATEWAY. 1585c77462ddSAlexander V. Chernikov */ 1586c77462ddSAlexander V. Chernikov if (rn_mpath_capable(rnh)) { 1587c77462ddSAlexander V. Chernikov rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]); 1588c77462ddSAlexander V. Chernikov if (rt == NULL) 1589c77462ddSAlexander V. Chernikov return (ESRCH); 1590c77462ddSAlexander V. Chernikov } 1591c77462ddSAlexander V. Chernikov #endif 1592c77462ddSAlexander V. Chernikov 1593c77462ddSAlexander V. Chernikov RT_LOCK(rt); 1594c77462ddSAlexander V. Chernikov 15951a75e3b2SAlexander V. Chernikov rt_setmetrics(info, rt); 15961a75e3b2SAlexander V. Chernikov 1597c77462ddSAlexander V. Chernikov /* 1598c77462ddSAlexander V. Chernikov * New gateway could require new ifaddr, ifp; 1599c77462ddSAlexander V. Chernikov * flags may also be different; ifp may be specified 1600c77462ddSAlexander V. Chernikov * by ll sockaddr when protocol address is ambiguous 1601c77462ddSAlexander V. Chernikov */ 1602c77462ddSAlexander V. Chernikov if (((rt->rt_flags & RTF_GATEWAY) && 1603c77462ddSAlexander V. Chernikov info->rti_info[RTAX_GATEWAY] != NULL) || 1604c77462ddSAlexander V. Chernikov info->rti_info[RTAX_IFP] != NULL || 1605c77462ddSAlexander V. Chernikov (info->rti_info[RTAX_IFA] != NULL && 1606c77462ddSAlexander V. Chernikov !sa_equal(info->rti_info[RTAX_IFA], rt->rt_ifa->ifa_addr))) { 1607c77462ddSAlexander V. Chernikov 1608c77462ddSAlexander V. Chernikov error = rt_getifa_fib(info, fibnum); 1609c77462ddSAlexander V. Chernikov if (info->rti_ifa != NULL) 1610c77462ddSAlexander V. Chernikov free_ifa = 1; 1611c77462ddSAlexander V. Chernikov 1612c77462ddSAlexander V. Chernikov if (error != 0) 1613a713ee5cSAlexander V. Chernikov goto bad; 1614c77462ddSAlexander V. Chernikov } 1615c77462ddSAlexander V. Chernikov 1616c77462ddSAlexander V. Chernikov /* Check if outgoing interface has changed */ 1617c77462ddSAlexander V. Chernikov if (info->rti_ifa != NULL && info->rti_ifa != rt->rt_ifa && 1618c77462ddSAlexander V. Chernikov rt->rt_ifa != NULL && rt->rt_ifa->ifa_rtrequest != NULL) { 1619c77462ddSAlexander V. Chernikov rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, info); 1620c77462ddSAlexander V. Chernikov ifa_free(rt->rt_ifa); 1621c77462ddSAlexander V. Chernikov } 1622c77462ddSAlexander V. Chernikov /* Update gateway address */ 1623c77462ddSAlexander V. Chernikov if (info->rti_info[RTAX_GATEWAY] != NULL) { 1624c77462ddSAlexander V. Chernikov error = rt_setgate(rt, rt_key(rt), info->rti_info[RTAX_GATEWAY]); 1625c77462ddSAlexander V. Chernikov if (error != 0) 1626a713ee5cSAlexander V. Chernikov goto bad; 1627c77462ddSAlexander V. Chernikov 1628c77462ddSAlexander V. Chernikov rt->rt_flags &= ~RTF_GATEWAY; 1629c77462ddSAlexander V. Chernikov rt->rt_flags |= (RTF_GATEWAY & info->rti_flags); 1630c77462ddSAlexander V. Chernikov } 1631c77462ddSAlexander V. Chernikov 1632c77462ddSAlexander V. Chernikov if (info->rti_ifa != NULL && info->rti_ifa != rt->rt_ifa) { 1633c77462ddSAlexander V. Chernikov ifa_ref(info->rti_ifa); 1634c77462ddSAlexander V. Chernikov rt->rt_ifa = info->rti_ifa; 1635c77462ddSAlexander V. Chernikov rt->rt_ifp = info->rti_ifp; 1636c77462ddSAlexander V. Chernikov } 1637c77462ddSAlexander V. Chernikov /* Allow some flags to be toggled on change. */ 1638c77462ddSAlexander V. Chernikov rt->rt_flags &= ~RTF_FMASK; 1639c77462ddSAlexander V. Chernikov rt->rt_flags |= info->rti_flags & RTF_FMASK; 1640c77462ddSAlexander V. Chernikov 1641c77462ddSAlexander V. Chernikov if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest != NULL) 1642c77462ddSAlexander V. Chernikov rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, info); 1643c77462ddSAlexander V. Chernikov 16447f948f12SAlexander V. Chernikov /* Alter route MTU if necessary */ 16451a75e3b2SAlexander V. Chernikov if (rt->rt_ifp != NULL) { 16461a75e3b2SAlexander V. Chernikov family = info->rti_info[RTAX_DST]->sa_family; 16471a75e3b2SAlexander V. Chernikov mtu = if_getmtu_family(rt->rt_ifp, family); 16487f948f12SAlexander V. Chernikov /* Set default MTU */ 16497f948f12SAlexander V. Chernikov if (rt->rt_mtu == 0) 16501a75e3b2SAlexander V. Chernikov rt->rt_mtu = mtu; 16517f948f12SAlexander V. Chernikov if (rt->rt_mtu != mtu) { 16527f948f12SAlexander V. Chernikov /* Check if we really need to update */ 16537f948f12SAlexander V. Chernikov ifmtu.ifp = rt->rt_ifp; 16547f948f12SAlexander V. Chernikov ifmtu.mtu = mtu; 16557f948f12SAlexander V. Chernikov if_updatemtu_cb(rt->rt_nodes, &ifmtu); 16567f948f12SAlexander V. Chernikov } 16571a75e3b2SAlexander V. Chernikov } 16580fb9298dSAlexander V. Chernikov 1659c77462ddSAlexander V. Chernikov if (ret_nrt) { 1660c77462ddSAlexander V. Chernikov *ret_nrt = rt; 1661c77462ddSAlexander V. Chernikov RT_ADDREF(rt); 1662c77462ddSAlexander V. Chernikov } 1663c77462ddSAlexander V. Chernikov bad: 1664c77462ddSAlexander V. Chernikov RT_UNLOCK(rt); 1665c77462ddSAlexander V. Chernikov if (free_ifa != 0) 1666c77462ddSAlexander V. Chernikov ifa_free(info->rti_ifa); 1667c77462ddSAlexander V. Chernikov return (error); 1668c77462ddSAlexander V. Chernikov } 1669c77462ddSAlexander V. Chernikov 16700fb9298dSAlexander V. Chernikov static void 16710fb9298dSAlexander V. Chernikov rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt) 16720fb9298dSAlexander V. Chernikov { 16730fb9298dSAlexander V. Chernikov 16747f948f12SAlexander V. Chernikov if (info->rti_mflags & RTV_MTU) { 16757f948f12SAlexander V. Chernikov if (info->rti_rmx->rmx_mtu != 0) { 16767f948f12SAlexander V. Chernikov 16777f948f12SAlexander V. Chernikov /* 16787f948f12SAlexander V. Chernikov * MTU was explicitly provided by user. 16797f948f12SAlexander V. Chernikov * Keep it. 16807f948f12SAlexander V. Chernikov */ 16817f948f12SAlexander V. Chernikov rt->rt_flags |= RTF_FIXEDMTU; 16827f948f12SAlexander V. Chernikov } else { 16837f948f12SAlexander V. Chernikov 16847f948f12SAlexander V. Chernikov /* 16857f948f12SAlexander V. Chernikov * User explicitly sets MTU to 0. 16867f948f12SAlexander V. Chernikov * Assume rollback to default. 16877f948f12SAlexander V. Chernikov */ 16887f948f12SAlexander V. Chernikov rt->rt_flags &= ~RTF_FIXEDMTU; 16897f948f12SAlexander V. Chernikov } 16900fb9298dSAlexander V. Chernikov rt->rt_mtu = info->rti_rmx->rmx_mtu; 16917f948f12SAlexander V. Chernikov } 16920fb9298dSAlexander V. Chernikov if (info->rti_mflags & RTV_WEIGHT) 16930fb9298dSAlexander V. Chernikov rt->rt_weight = info->rti_rmx->rmx_weight; 16940fb9298dSAlexander V. Chernikov /* Kernel -> userland timebase conversion. */ 16950fb9298dSAlexander V. Chernikov if (info->rti_mflags & RTV_EXPIRE) 16960fb9298dSAlexander V. Chernikov rt->rt_expire = info->rti_rmx->rmx_expire ? 16970fb9298dSAlexander V. Chernikov info->rti_rmx->rmx_expire - time_second + time_uptime : 0; 16980fb9298dSAlexander V. Chernikov } 16990fb9298dSAlexander V. Chernikov 1700df8bae1dSRodney W. Grimes int 1701d1dd20beSSam Leffler rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate) 1702df8bae1dSRodney W. Grimes { 1703d1dd20beSSam Leffler /* XXX dst may be overwritten, can we move this to below */ 17046e6b3f7cSQing Li int dlen = SA_SIZE(dst), glen = SA_SIZE(gate); 17056e6b3f7cSQing Li #ifdef INVARIANTS 1706c2c2a7c1SBjoern A. Zeeb struct radix_node_head *rnh; 1707c2c2a7c1SBjoern A. Zeeb 1708c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(rt->rt_fibnum, dst->sa_family); 17096e6b3f7cSQing Li #endif 1710d1dd20beSSam Leffler 1711d1dd20beSSam Leffler RT_LOCK_ASSERT(rt); 17123120b9d4SKip Macy RADIX_NODE_HEAD_LOCK_ASSERT(rnh); 1713df8bae1dSRodney W. Grimes 17141db1fffaSBill Fenner /* 171585911824SLuigi Rizzo * Prepare to store the gateway in rt->rt_gateway. 171685911824SLuigi Rizzo * Both dst and gateway are stored one after the other in the same 171785911824SLuigi Rizzo * malloc'd chunk. If we have room, we can reuse the old buffer, 171885911824SLuigi Rizzo * rt_gateway already points to the right place. 171985911824SLuigi Rizzo * Otherwise, malloc a new block and update the 'dst' address. 1720499676dfSJulian Elischer */ 172185911824SLuigi Rizzo if (rt->rt_gateway == NULL || glen > SA_SIZE(rt->rt_gateway)) { 172285911824SLuigi Rizzo caddr_t new; 172385911824SLuigi Rizzo 1724df8bae1dSRodney W. Grimes R_Malloc(new, caddr_t, dlen + glen); 172585911824SLuigi Rizzo if (new == NULL) 17261db1fffaSBill Fenner return ENOBUFS; 1727499676dfSJulian Elischer /* 172885911824SLuigi Rizzo * XXX note, we copy from *dst and not *rt_key(rt) because 172985911824SLuigi Rizzo * rt_setgate() can be called to initialize a newly 173085911824SLuigi Rizzo * allocated route entry, in which case rt_key(rt) == NULL 173185911824SLuigi Rizzo * (and also rt->rt_gateway == NULL). 173285911824SLuigi Rizzo * Free()/free() handle a NULL argument just fine. 1733499676dfSJulian Elischer */ 17341838a647SLuigi Rizzo bcopy(dst, new, dlen); 17358b15f615SLuiz Otavio O Souza R_Free(rt_key(rt)); /* free old block, if any */ 1736445e045bSAlexander Kabaev rt_key(rt) = (struct sockaddr *)new; 173785911824SLuigi Rizzo rt->rt_gateway = (struct sockaddr *)(new + dlen); 1738df8bae1dSRodney W. Grimes } 1739499676dfSJulian Elischer 1740499676dfSJulian Elischer /* 174185911824SLuigi Rizzo * Copy the new gateway value into the memory chunk. 174285911824SLuigi Rizzo */ 174385911824SLuigi Rizzo bcopy(gate, rt->rt_gateway, glen); 174485911824SLuigi Rizzo 17456e6b3f7cSQing Li return (0); 1746df8bae1dSRodney W. Grimes } 1747df8bae1dSRodney W. Grimes 1748c7ab6602SQing Li void 1749d1dd20beSSam Leffler rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) 1750df8bae1dSRodney W. Grimes { 1751f59c6cb0SAlexander V. Chernikov u_char *cp1 = (u_char *)src; 1752f59c6cb0SAlexander V. Chernikov u_char *cp2 = (u_char *)dst; 1753f59c6cb0SAlexander V. Chernikov u_char *cp3 = (u_char *)netmask; 1754df8bae1dSRodney W. Grimes u_char *cplim = cp2 + *cp3; 1755df8bae1dSRodney W. Grimes u_char *cplim2 = cp2 + *cp1; 1756df8bae1dSRodney W. Grimes 1757df8bae1dSRodney W. Grimes *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ 1758df8bae1dSRodney W. Grimes cp3 += 2; 1759df8bae1dSRodney W. Grimes if (cplim > cplim2) 1760df8bae1dSRodney W. Grimes cplim = cplim2; 1761df8bae1dSRodney W. Grimes while (cp2 < cplim) 1762df8bae1dSRodney W. Grimes *cp2++ = *cp1++ & *cp3++; 1763df8bae1dSRodney W. Grimes if (cp2 < cplim2) 1764df8bae1dSRodney W. Grimes bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); 1765df8bae1dSRodney W. Grimes } 1766df8bae1dSRodney W. Grimes 1767df8bae1dSRodney W. Grimes /* 1768df8bae1dSRodney W. Grimes * Set up a routing table entry, normally 1769df8bae1dSRodney W. Grimes * for an interface. 1770df8bae1dSRodney W. Grimes */ 17718b07e49aSJulian Elischer #define _SOCKADDR_TMPSIZE 128 /* Not too big.. kernel stack size is limited */ 17728b07e49aSJulian Elischer static inline int 17738b07e49aSJulian Elischer rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) 1774df8bae1dSRodney W. Grimes { 17755aca0b30SLuigi Rizzo struct sockaddr *dst; 17768071913dSRuslan Ermilov struct sockaddr *netmask; 177785911824SLuigi Rizzo struct rtentry *rt = NULL; 17788071913dSRuslan Ermilov struct rt_addrinfo info; 1779e440aed9SQing Li int error = 0; 17808b07e49aSJulian Elischer int startfib, endfib; 17818b07e49aSJulian Elischer char tempbuf[_SOCKADDR_TMPSIZE]; 17828b07e49aSJulian Elischer int didwork = 0; 17838b07e49aSJulian Elischer int a_failure = 0; 17846e6b3f7cSQing Li static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; 17853034f43fSAlexander V. Chernikov struct radix_node_head *rnh; 1786df8bae1dSRodney W. Grimes 17878071913dSRuslan Ermilov if (flags & RTF_HOST) { 17888071913dSRuslan Ermilov dst = ifa->ifa_dstaddr; 17898071913dSRuslan Ermilov netmask = NULL; 17908071913dSRuslan Ermilov } else { 17918071913dSRuslan Ermilov dst = ifa->ifa_addr; 17928071913dSRuslan Ermilov netmask = ifa->ifa_netmask; 17938071913dSRuslan Ermilov } 1794b3dd0771SBjoern A. Zeeb if (dst->sa_len == 0) 1795b3dd0771SBjoern A. Zeeb return(EINVAL); 1796b680a383SBjoern A. Zeeb switch (dst->sa_family) { 1797b680a383SBjoern A. Zeeb case AF_INET6: 1798b680a383SBjoern A. Zeeb case AF_INET: 1799b680a383SBjoern A. Zeeb /* We support multiple FIBs. */ 1800b680a383SBjoern A. Zeeb break; 1801b680a383SBjoern A. Zeeb default: 1802b680a383SBjoern A. Zeeb fibnum = RT_DEFAULT_FIB; 1803b680a383SBjoern A. Zeeb break; 1804b680a383SBjoern A. Zeeb } 18057d9b6df1SAlexander V. Chernikov if (fibnum == RT_ALL_FIBS) { 1806ee0bd4b9SHiroki Sato if (V_rt_add_addr_allfibs == 0 && cmd == (int)RTM_ADD) 18070489b891SAlan Somers startfib = endfib = ifa->ifa_ifp->if_fib; 1808ee0bd4b9SHiroki Sato else { 18098b07e49aSJulian Elischer startfib = 0; 18108b07e49aSJulian Elischer endfib = rt_numfibs - 1; 181166e8505fSJulian Elischer } 18128b07e49aSJulian Elischer } else { 18138b07e49aSJulian Elischer KASSERT((fibnum < rt_numfibs), ("rtinit1: bad fibnum")); 18148b07e49aSJulian Elischer startfib = fibnum; 18158b07e49aSJulian Elischer endfib = fibnum; 18168b07e49aSJulian Elischer } 1817ac4a76ebSBjoern A. Zeeb 1818b0a76b88SJulian Elischer /* 18198b07e49aSJulian Elischer * If it's a delete, check that if it exists, 18208b07e49aSJulian Elischer * it's on the correct interface or we might scrub 18218b07e49aSJulian Elischer * a route to another ifa which would 1822b0a76b88SJulian Elischer * be confusing at best and possibly worse. 1823b0a76b88SJulian Elischer */ 1824df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE) { 1825b0a76b88SJulian Elischer /* 1826b0a76b88SJulian Elischer * It's a delete, so it should already exist.. 1827b0a76b88SJulian Elischer * If it's a net, mask off the host bits 1828b0a76b88SJulian Elischer * (Assuming we have a mask) 18298b07e49aSJulian Elischer * XXX this is kinda inet specific.. 1830b0a76b88SJulian Elischer */ 18318071913dSRuslan Ermilov if (netmask != NULL) { 18328b07e49aSJulian Elischer rt_maskedcopy(dst, (struct sockaddr *)tempbuf, netmask); 18338b07e49aSJulian Elischer dst = (struct sockaddr *)tempbuf; 1834df8bae1dSRodney W. Grimes } 18358b07e49aSJulian Elischer } 18368b07e49aSJulian Elischer /* 18378b07e49aSJulian Elischer * Now go through all the requested tables (fibs) and do the 18388b07e49aSJulian Elischer * requested action. Realistically, this will either be fib 0 18398b07e49aSJulian Elischer * for protocols that don't do multiple tables or all the 1840a8498625SBjoern A. Zeeb * tables for those that do. 18418b07e49aSJulian Elischer */ 18428b07e49aSJulian Elischer for ( fibnum = startfib; fibnum <= endfib; fibnum++) { 18438b07e49aSJulian Elischer if (cmd == RTM_DELETE) { 18448b07e49aSJulian Elischer struct radix_node *rn; 1845b0a76b88SJulian Elischer /* 18468071913dSRuslan Ermilov * Look up an rtentry that is in the routing tree and 18478071913dSRuslan Ermilov * contains the correct info. 1848b0a76b88SJulian Elischer */ 1849c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 1850c2c2a7c1SBjoern A. Zeeb if (rnh == NULL) 18518b07e49aSJulian Elischer /* this table doesn't exist but others might */ 18528b07e49aSJulian Elischer continue; 185314126522SAlexander V. Chernikov RADIX_NODE_HEAD_RLOCK(rnh); 1854034c09ffSAlexander V. Chernikov rn = rnh->rnh_lookup(dst, netmask, rnh); 1855e440aed9SQing Li #ifdef RADIX_MPATH 1856e440aed9SQing Li if (rn_mpath_capable(rnh)) { 1857e440aed9SQing Li 1858e440aed9SQing Li if (rn == NULL) 1859e440aed9SQing Li error = ESRCH; 1860e440aed9SQing Li else { 1861e440aed9SQing Li rt = RNTORT(rn); 1862e440aed9SQing Li /* 18638b07e49aSJulian Elischer * for interface route the 18648b07e49aSJulian Elischer * rt->rt_gateway is sockaddr_intf 18658b07e49aSJulian Elischer * for cloning ARP entries, so 18668b07e49aSJulian Elischer * rt_mpath_matchgate must use the 18678b07e49aSJulian Elischer * interface address 1868e440aed9SQing Li */ 18698b07e49aSJulian Elischer rt = rt_mpath_matchgate(rt, 18708b07e49aSJulian Elischer ifa->ifa_addr); 1871034c09ffSAlexander V. Chernikov if (rt == NULL) 1872e440aed9SQing Li error = ESRCH; 1873e440aed9SQing Li } 1874e440aed9SQing Li } 1875e440aed9SQing Li #endif 18768b07e49aSJulian Elischer error = (rn == NULL || 18778071913dSRuslan Ermilov (rn->rn_flags & RNF_ROOT) || 18785a2f4cbdSAlexander V. Chernikov RNTORT(rn)->rt_ifa != ifa); 187914126522SAlexander V. Chernikov RADIX_NODE_HEAD_RUNLOCK(rnh); 1880956b0b65SJeffrey Hsu if (error) { 18818b07e49aSJulian Elischer /* this is only an error if bad on ALL tables */ 18828b07e49aSJulian Elischer continue; 1883df8bae1dSRodney W. Grimes } 1884b0a76b88SJulian Elischer } 1885b0a76b88SJulian Elischer /* 1886b0a76b88SJulian Elischer * Do the actual request 1887b0a76b88SJulian Elischer */ 18888071913dSRuslan Ermilov bzero((caddr_t)&info, sizeof(info)); 18898071913dSRuslan Ermilov info.rti_ifa = ifa; 18903034f43fSAlexander V. Chernikov info.rti_flags = flags | 18913034f43fSAlexander V. Chernikov (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED; 18928071913dSRuslan Ermilov info.rti_info[RTAX_DST] = dst; 18936e6b3f7cSQing Li /* 18946e6b3f7cSQing Li * doing this for compatibility reasons 18956e6b3f7cSQing Li */ 18966e6b3f7cSQing Li if (cmd == RTM_ADD) 18976e6b3f7cSQing Li info.rti_info[RTAX_GATEWAY] = 18986e6b3f7cSQing Li (struct sockaddr *)&null_sdl; 18996e6b3f7cSQing Li else 19008071913dSRuslan Ermilov info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; 19018071913dSRuslan Ermilov info.rti_info[RTAX_NETMASK] = netmask; 19028b07e49aSJulian Elischer error = rtrequest1_fib(cmd, &info, &rt, fibnum); 19033034f43fSAlexander V. Chernikov 19043034f43fSAlexander V. Chernikov if ((error == EEXIST) && (cmd == RTM_ADD)) { 19053034f43fSAlexander V. Chernikov /* 19063034f43fSAlexander V. Chernikov * Interface route addition failed. 19073034f43fSAlexander V. Chernikov * Atomically delete current prefix generating 19083034f43fSAlexander V. Chernikov * RTM_DELETE message, and retry adding 19093034f43fSAlexander V. Chernikov * interface prefix. 19103034f43fSAlexander V. Chernikov */ 19113034f43fSAlexander V. Chernikov rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 19123034f43fSAlexander V. Chernikov RADIX_NODE_HEAD_LOCK(rnh); 19133034f43fSAlexander V. Chernikov 19143034f43fSAlexander V. Chernikov /* Delete old prefix */ 19153034f43fSAlexander V. Chernikov info.rti_ifa = NULL; 19163034f43fSAlexander V. Chernikov info.rti_flags = RTF_RNH_LOCKED; 19173034f43fSAlexander V. Chernikov 1918d54455b0SAlexander V. Chernikov error = rtrequest1_fib(RTM_DELETE, &info, NULL, fibnum); 19193034f43fSAlexander V. Chernikov if (error == 0) { 19203034f43fSAlexander V. Chernikov info.rti_ifa = ifa; 19213034f43fSAlexander V. Chernikov info.rti_flags = flags | RTF_RNH_LOCKED | 19223034f43fSAlexander V. Chernikov (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED; 19233034f43fSAlexander V. Chernikov error = rtrequest1_fib(cmd, &info, &rt, fibnum); 19243034f43fSAlexander V. Chernikov } 19253034f43fSAlexander V. Chernikov 19263034f43fSAlexander V. Chernikov RADIX_NODE_HEAD_UNLOCK(rnh); 19273034f43fSAlexander V. Chernikov } 19283034f43fSAlexander V. Chernikov 19293034f43fSAlexander V. Chernikov 19305aca0b30SLuigi Rizzo if (error == 0 && rt != NULL) { 19318071913dSRuslan Ermilov /* 19326f99b44cSBrian Somers * notify any listening routing agents of the change 19338071913dSRuslan Ermilov */ 1934d1dd20beSSam Leffler RT_LOCK(rt); 1935e440aed9SQing Li #ifdef RADIX_MPATH 1936e440aed9SQing Li /* 1937e440aed9SQing Li * in case address alias finds the first address 19388d74af36SBjoern A. Zeeb * e.g. ifconfig bge0 192.0.2.246/24 19398d74af36SBjoern A. Zeeb * e.g. ifconfig bge0 192.0.2.247/24 19408d74af36SBjoern A. Zeeb * the address set in the route is 192.0.2.246 19418d74af36SBjoern A. Zeeb * so we need to replace it with 192.0.2.247 1942e440aed9SQing Li */ 19438b07e49aSJulian Elischer if (memcmp(rt->rt_ifa->ifa_addr, 19448b07e49aSJulian Elischer ifa->ifa_addr, ifa->ifa_addr->sa_len)) { 19451099f828SRobert Watson ifa_free(rt->rt_ifa); 19461099f828SRobert Watson ifa_ref(ifa); 1947e440aed9SQing Li rt->rt_ifp = ifa->ifa_ifp; 1948e440aed9SQing Li rt->rt_ifa = ifa; 1949e440aed9SQing Li } 1950e440aed9SQing Li #endif 19516e6b3f7cSQing Li /* 19526e6b3f7cSQing Li * doing this for compatibility reasons 19536e6b3f7cSQing Li */ 19546e6b3f7cSQing Li if (cmd == RTM_ADD) { 19556e6b3f7cSQing Li ((struct sockaddr_dl *)rt->rt_gateway)->sdl_type = 19566e6b3f7cSQing Li rt->rt_ifp->if_type; 19576e6b3f7cSQing Li ((struct sockaddr_dl *)rt->rt_gateway)->sdl_index = 19586e6b3f7cSQing Li rt->rt_ifp->if_index; 19596e6b3f7cSQing Li } 19607f279720SMichael Tuexen RT_ADDREF(rt); 19617f279720SMichael Tuexen RT_UNLOCK(rt); 1962528737fdSBjoern A. Zeeb rt_newaddrmsg_fib(cmd, ifa, error, rt, fibnum); 19637f279720SMichael Tuexen RT_LOCK(rt); 19647f279720SMichael Tuexen RT_REMREF(rt); 19658071913dSRuslan Ermilov if (cmd == RTM_DELETE) { 1966b0a76b88SJulian Elischer /* 19678b07e49aSJulian Elischer * If we are deleting, and we found an entry, 19688b07e49aSJulian Elischer * then it's been removed from the tree.. 19698b07e49aSJulian Elischer * now throw it away. 1970b0a76b88SJulian Elischer */ 1971d1dd20beSSam Leffler RTFREE_LOCKED(rt); 1972d1dd20beSSam Leffler } else { 1973d1dd20beSSam Leffler if (cmd == RTM_ADD) { 1974b0a76b88SJulian Elischer /* 19758b07e49aSJulian Elischer * We just wanted to add it.. 19768b07e49aSJulian Elischer * we don't actually need a reference. 1977b0a76b88SJulian Elischer */ 19787138d65cSSam Leffler RT_REMREF(rt); 1979df8bae1dSRodney W. Grimes } 1980d1dd20beSSam Leffler RT_UNLOCK(rt); 1981d1dd20beSSam Leffler } 19828b07e49aSJulian Elischer didwork = 1; 1983df8bae1dSRodney W. Grimes } 19848b07e49aSJulian Elischer if (error) 19858b07e49aSJulian Elischer a_failure = error; 19868b07e49aSJulian Elischer } 19878b07e49aSJulian Elischer if (cmd == RTM_DELETE) { 19888b07e49aSJulian Elischer if (didwork) { 19898b07e49aSJulian Elischer error = 0; 19908b07e49aSJulian Elischer } else { 19918b07e49aSJulian Elischer /* we only give an error if it wasn't in any table */ 19928b07e49aSJulian Elischer error = ((flags & RTF_HOST) ? 19938b07e49aSJulian Elischer EHOSTUNREACH : ENETUNREACH); 19948b07e49aSJulian Elischer } 19958b07e49aSJulian Elischer } else { 19968b07e49aSJulian Elischer if (a_failure) { 19978b07e49aSJulian Elischer /* return an error if any of them failed */ 19988b07e49aSJulian Elischer error = a_failure; 19998b07e49aSJulian Elischer } 20008b07e49aSJulian Elischer } 20013ec66d6cSDavid Greenman return (error); 20023ec66d6cSDavid Greenman } 2003cb64988fSLuoqi Chen 20048b07e49aSJulian Elischer /* 20058b07e49aSJulian Elischer * Set up a routing table entry, normally 20068b07e49aSJulian Elischer * for an interface. 20078b07e49aSJulian Elischer */ 20088b07e49aSJulian Elischer int 20098b07e49aSJulian Elischer rtinit(struct ifaddr *ifa, int cmd, int flags) 20108b07e49aSJulian Elischer { 20118b07e49aSJulian Elischer struct sockaddr *dst; 2012a8498625SBjoern A. Zeeb int fib = RT_DEFAULT_FIB; 20138b07e49aSJulian Elischer 20148b07e49aSJulian Elischer if (flags & RTF_HOST) { 20158b07e49aSJulian Elischer dst = ifa->ifa_dstaddr; 20168b07e49aSJulian Elischer } else { 20178b07e49aSJulian Elischer dst = ifa->ifa_addr; 20188b07e49aSJulian Elischer } 20198b07e49aSJulian Elischer 2020b680a383SBjoern A. Zeeb switch (dst->sa_family) { 2021b680a383SBjoern A. Zeeb case AF_INET6: 2022b680a383SBjoern A. Zeeb case AF_INET: 2023b680a383SBjoern A. Zeeb /* We do support multiple FIBs. */ 20247d9b6df1SAlexander V. Chernikov fib = RT_ALL_FIBS; 2025b680a383SBjoern A. Zeeb break; 2026b680a383SBjoern A. Zeeb } 20278b07e49aSJulian Elischer return (rtinit1(ifa, cmd, flags, fib)); 20288b07e49aSJulian Elischer } 20294cbac30bSAlexander V. Chernikov 20304cbac30bSAlexander V. Chernikov /* 20314cbac30bSAlexander V. Chernikov * Announce interface address arrival/withdraw 20324cbac30bSAlexander V. Chernikov * Returns 0 on success. 20334cbac30bSAlexander V. Chernikov */ 20344cbac30bSAlexander V. Chernikov int 20354cbac30bSAlexander V. Chernikov rt_addrmsg(int cmd, struct ifaddr *ifa, int fibnum) 20364cbac30bSAlexander V. Chernikov { 20374cbac30bSAlexander V. Chernikov 20384cbac30bSAlexander V. Chernikov KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, 2039d375edc9SAlexander V. Chernikov ("unexpected cmd %d", cmd)); 20404cbac30bSAlexander V. Chernikov 2041d375edc9SAlexander V. Chernikov KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), 2042d375edc9SAlexander V. Chernikov ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); 20434cbac30bSAlexander V. Chernikov 20447f946da0SMichael Tuexen #if defined(INET) || defined(INET6) 20457f946da0SMichael Tuexen #ifdef SCTP 20467f946da0SMichael Tuexen /* 20477f946da0SMichael Tuexen * notify the SCTP stack 20487f946da0SMichael Tuexen * this will only get called when an address is added/deleted 20497f946da0SMichael Tuexen * XXX pass the ifaddr struct instead if ifa->ifa_addr... 20507f946da0SMichael Tuexen */ 20517f946da0SMichael Tuexen sctp_addr_change(ifa, cmd); 20527f946da0SMichael Tuexen #endif /* SCTP */ 20537f946da0SMichael Tuexen #endif 20544cbac30bSAlexander V. Chernikov return (rtsock_addrmsg(cmd, ifa, fibnum)); 20554cbac30bSAlexander V. Chernikov } 20564cbac30bSAlexander V. Chernikov 20574cbac30bSAlexander V. Chernikov /* 2058d375edc9SAlexander V. Chernikov * Announce route addition/removal. 20594cbac30bSAlexander V. Chernikov * Users of this function MUST validate input data BEFORE calling. 20604cbac30bSAlexander V. Chernikov * However we have to be able to handle invalid data: 20614cbac30bSAlexander V. Chernikov * if some userland app sends us "invalid" route message (invalid mask, 2062d375edc9SAlexander V. Chernikov * no dst, wrong address families, etc...) we need to pass it back 20634cbac30bSAlexander V. Chernikov * to app (and any other rtsock consumers) with rtm_errno field set to 20644cbac30bSAlexander V. Chernikov * non-zero value. 20654cbac30bSAlexander V. Chernikov * Returns 0 on success. 20664cbac30bSAlexander V. Chernikov */ 20674cbac30bSAlexander V. Chernikov int 20684cbac30bSAlexander V. Chernikov rt_routemsg(int cmd, struct ifnet *ifp, int error, struct rtentry *rt, 20694cbac30bSAlexander V. Chernikov int fibnum) 20704cbac30bSAlexander V. Chernikov { 20714cbac30bSAlexander V. Chernikov 20724cbac30bSAlexander V. Chernikov KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, 2073d375edc9SAlexander V. Chernikov ("unexpected cmd %d", cmd)); 20744cbac30bSAlexander V. Chernikov 2075d375edc9SAlexander V. Chernikov KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), 2076d375edc9SAlexander V. Chernikov ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); 20774cbac30bSAlexander V. Chernikov 20784cbac30bSAlexander V. Chernikov KASSERT(rt_key(rt) != NULL, (":%s: rt_key must be supplied", __func__)); 20794cbac30bSAlexander V. Chernikov 20804cbac30bSAlexander V. Chernikov return (rtsock_routemsg(cmd, ifp, error, rt, fibnum)); 20814cbac30bSAlexander V. Chernikov } 20824cbac30bSAlexander V. Chernikov 20834cbac30bSAlexander V. Chernikov void 20844cbac30bSAlexander V. Chernikov rt_newaddrmsg(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt) 20854cbac30bSAlexander V. Chernikov { 20864cbac30bSAlexander V. Chernikov 20874cbac30bSAlexander V. Chernikov rt_newaddrmsg_fib(cmd, ifa, error, rt, RT_ALL_FIBS); 20884cbac30bSAlexander V. Chernikov } 20894cbac30bSAlexander V. Chernikov 20904cbac30bSAlexander V. Chernikov /* 20914cbac30bSAlexander V. Chernikov * This is called to generate messages from the routing socket 20924cbac30bSAlexander V. Chernikov * indicating a network interface has had addresses associated with it. 20934cbac30bSAlexander V. Chernikov */ 20944cbac30bSAlexander V. Chernikov void 20954cbac30bSAlexander V. Chernikov rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt, 20964cbac30bSAlexander V. Chernikov int fibnum) 20974cbac30bSAlexander V. Chernikov { 20984cbac30bSAlexander V. Chernikov 20994cbac30bSAlexander V. Chernikov KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, 21004cbac30bSAlexander V. Chernikov ("unexpected cmd %u", cmd)); 2101d375edc9SAlexander V. Chernikov KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), 2102d375edc9SAlexander V. Chernikov ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); 21034cbac30bSAlexander V. Chernikov 21044cbac30bSAlexander V. Chernikov if (cmd == RTM_ADD) { 21054cbac30bSAlexander V. Chernikov rt_addrmsg(cmd, ifa, fibnum); 21064cbac30bSAlexander V. Chernikov if (rt != NULL) 21074cbac30bSAlexander V. Chernikov rt_routemsg(cmd, ifa->ifa_ifp, error, rt, fibnum); 21084cbac30bSAlexander V. Chernikov } else { 21094cbac30bSAlexander V. Chernikov if (rt != NULL) 21104cbac30bSAlexander V. Chernikov rt_routemsg(cmd, ifa->ifa_ifp, error, rt, fibnum); 21114cbac30bSAlexander V. Chernikov rt_addrmsg(cmd, ifa, fibnum); 21124cbac30bSAlexander V. Chernikov } 21134cbac30bSAlexander V. Chernikov } 21144cbac30bSAlexander V. Chernikov 2115