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> 466e6b3f7cSQing Li #include <sys/syslog.h> 474d1d4912SBruce Evans #include <sys/malloc.h> 48df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 49df8bae1dSRodney W. Grimes #include <sys/socket.h> 508b07e49aSJulian Elischer #include <sys/sysctl.h> 513120b9d4SKip Macy #include <sys/syslog.h> 528b07e49aSJulian Elischer #include <sys/sysproto.h> 538b07e49aSJulian Elischer #include <sys/proc.h> 54df8bae1dSRodney W. Grimes #include <sys/domain.h> 55cb64988fSLuoqi Chen #include <sys/kernel.h> 56df8bae1dSRodney W. Grimes 57df8bae1dSRodney W. Grimes #include <net/if.h> 5876039bc8SGleb Smirnoff #include <net/if_var.h> 596e6b3f7cSQing Li #include <net/if_dl.h> 60df8bae1dSRodney W. Grimes #include <net/route.h> 61530c0060SRobert Watson #include <net/vnet.h> 62e5c610d6SQing Li #include <net/flowtable.h> 63df8bae1dSRodney W. Grimes 64e440aed9SQing Li #ifdef RADIX_MPATH 65e440aed9SQing Li #include <net/radix_mpath.h> 66e440aed9SQing Li #endif 67e440aed9SQing Li 68df8bae1dSRodney W. Grimes #include <netinet/in.h> 69b5e8ce9fSBruce Evans #include <netinet/ip_mroute.h> 70df8bae1dSRodney W. Grimes 712dc1d581SAndre Oppermann #include <vm/uma.h> 722dc1d581SAndre Oppermann 734871fc4aSJulian Elischer #define RT_MAXFIBS UINT16_MAX 74bfca216eSBjoern A. Zeeb 75bfca216eSBjoern A. Zeeb /* Kernel config default option. */ 76bfca216eSBjoern A. Zeeb #ifdef ROUTETABLES 77bfca216eSBjoern A. Zeeb #if ROUTETABLES <= 0 78bfca216eSBjoern A. Zeeb #error "ROUTETABLES defined too low" 79bfca216eSBjoern A. Zeeb #endif 80bfca216eSBjoern A. Zeeb #if ROUTETABLES > RT_MAXFIBS 81bfca216eSBjoern A. Zeeb #error "ROUTETABLES defined too big" 82bfca216eSBjoern A. Zeeb #endif 83bfca216eSBjoern A. Zeeb #define RT_NUMFIBS ROUTETABLES 84bfca216eSBjoern A. Zeeb #endif /* ROUTETABLES */ 85bfca216eSBjoern A. Zeeb /* Initialize to default if not otherwise set. */ 86bfca216eSBjoern A. Zeeb #ifndef RT_NUMFIBS 87bfca216eSBjoern A. Zeeb #define RT_NUMFIBS 1 88bfca216eSBjoern A. Zeeb #endif 89bfca216eSBjoern A. Zeeb 904cbac30bSAlexander V. Chernikov #if defined(INET) || defined(INET6) 914cbac30bSAlexander V. Chernikov #ifdef SCTP 924cbac30bSAlexander V. Chernikov extern void sctp_addr_change(struct ifaddr *ifa, int cmd); 934cbac30bSAlexander V. Chernikov #endif /* SCTP */ 944cbac30bSAlexander V. Chernikov #endif 954cbac30bSAlexander V. Chernikov 964cbac30bSAlexander V. Chernikov 974871fc4aSJulian Elischer /* This is read-only.. */ 988b07e49aSJulian Elischer u_int rt_numfibs = RT_NUMFIBS; 99af3b2549SHans Petter Selasky SYSCTL_UINT(_net, OID_AUTO, fibs, CTLFLAG_RDTUN, &rt_numfibs, 0, ""); 1008b07e49aSJulian Elischer 10166e8505fSJulian Elischer /* 10266e8505fSJulian Elischer * By default add routes to all fibs for new interfaces. 10366e8505fSJulian Elischer * Once this is set to 0 then only allocate routes on interface 10466e8505fSJulian Elischer * changes for the FIB of the caller when adding a new set of addresses 10566e8505fSJulian Elischer * to an interface. XXX this is a shotgun aproach to a problem that needs 10666e8505fSJulian Elischer * a more fine grained solution.. that will come. 107a8498625SBjoern A. Zeeb * XXX also has the problems getting the FIB from curthread which will not 108a8498625SBjoern A. Zeeb * always work given the fib can be overridden and prefixes can be added 109a8498625SBjoern A. Zeeb * from the network stack context. 11066e8505fSJulian Elischer */ 111ee0bd4b9SHiroki Sato VNET_DEFINE(u_int, rt_add_addr_allfibs) = 1; 112ee0bd4b9SHiroki Sato SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, 113ee0bd4b9SHiroki Sato &VNET_NAME(rt_add_addr_allfibs), 0, ""); 11466e8505fSJulian Elischer 115eddfbb76SRobert Watson VNET_DEFINE(struct rtstat, rtstat); 1161e77c105SRobert Watson #define V_rtstat VNET(rtstat) 117b58ea5f3SBjoern A. Zeeb 11882cea7e6SBjoern A. Zeeb VNET_DEFINE(struct radix_node_head *, rt_tables); 11982cea7e6SBjoern A. Zeeb #define V_rt_tables VNET(rt_tables) 12082cea7e6SBjoern A. Zeeb 12182cea7e6SBjoern A. Zeeb VNET_DEFINE(int, rttrash); /* routes not in table but not freed */ 12282cea7e6SBjoern A. Zeeb #define V_rttrash VNET(rttrash) 12382cea7e6SBjoern A. Zeeb 124bfe1aba4SMarko Zec 125d6941ce9SLuigi Rizzo /* 126d6941ce9SLuigi Rizzo * Convert a 'struct radix_node *' to a 'struct rtentry *'. 127d6941ce9SLuigi Rizzo * The operation can be done safely (in this code) because a 128d6941ce9SLuigi Rizzo * 'struct rtentry' starts with two 'struct radix_node''s, the first 129d6941ce9SLuigi Rizzo * one representing leaf nodes in the routing tree, which is 130d6941ce9SLuigi Rizzo * what the code in radix.c passes us as a 'struct radix_node'. 131d6941ce9SLuigi Rizzo * 132d6941ce9SLuigi Rizzo * But because there are a lot of assumptions in this conversion, 133d6941ce9SLuigi Rizzo * do not cast explicitly, but always use the macro below. 134d6941ce9SLuigi Rizzo */ 135d6941ce9SLuigi Rizzo #define RNTORT(p) ((struct rtentry *)(p)) 136d6941ce9SLuigi Rizzo 1373e288e62SDimitry Andric static VNET_DEFINE(uma_zone_t, rtzone); /* Routing table UMA zone. */ 13882cea7e6SBjoern A. Zeeb #define V_rtzone VNET(rtzone) 13982cea7e6SBjoern A. Zeeb 140c77462ddSAlexander V. Chernikov static int rtrequest1_fib_change(struct radix_node_head *, struct rt_addrinfo *, 141c77462ddSAlexander V. Chernikov struct rtentry **, u_int); 1420fb9298dSAlexander V. Chernikov static void rt_setmetrics(const struct rt_addrinfo *, struct rtentry *); 143c77462ddSAlexander V. Chernikov 144*7f948f12SAlexander V. Chernikov struct if_mtuinfo 145*7f948f12SAlexander V. Chernikov { 146*7f948f12SAlexander V. Chernikov struct ifnet *ifp; 147*7f948f12SAlexander V. Chernikov int mtu; 148*7f948f12SAlexander V. Chernikov }; 149*7f948f12SAlexander V. Chernikov 150*7f948f12SAlexander V. Chernikov static int if_updatemtu_cb(struct radix_node *, void *); 151*7f948f12SAlexander 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 */ 523df8bae1dSRodney W. Grimes 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 8158071913dSRuslan Ermilov /* 8168071913dSRuslan Ermilov * These (questionable) definitions of apparent local variables apply 8178071913dSRuslan Ermilov * to the next two functions. XXXXXX!!! 8188071913dSRuslan Ermilov */ 8198071913dSRuslan Ermilov #define dst info->rti_info[RTAX_DST] 8208071913dSRuslan Ermilov #define gateway info->rti_info[RTAX_GATEWAY] 8218071913dSRuslan Ermilov #define netmask info->rti_info[RTAX_NETMASK] 8228071913dSRuslan Ermilov #define ifaaddr info->rti_info[RTAX_IFA] 8238071913dSRuslan Ermilov #define ifpaddr info->rti_info[RTAX_IFP] 8248071913dSRuslan Ermilov #define flags info->rti_flags 8258071913dSRuslan Ermilov 8268071913dSRuslan Ermilov int 827d1dd20beSSam Leffler rt_getifa(struct rt_addrinfo *info) 8288071913dSRuslan Ermilov { 829a8498625SBjoern A. Zeeb 830a8498625SBjoern A. Zeeb return (rt_getifa_fib(info, RT_DEFAULT_FIB)); 8318b07e49aSJulian Elischer } 8328b07e49aSJulian Elischer 8338c0fec80SRobert Watson /* 8348c0fec80SRobert Watson * Look up rt_addrinfo for a specific fib. Note that if rti_ifa is defined, 8358c0fec80SRobert Watson * it will be referenced so the caller must free it. 8368c0fec80SRobert Watson */ 8378b07e49aSJulian Elischer int 8388b07e49aSJulian Elischer rt_getifa_fib(struct rt_addrinfo *info, u_int fibnum) 8398b07e49aSJulian Elischer { 8408071913dSRuslan Ermilov struct ifaddr *ifa; 8418071913dSRuslan Ermilov int error = 0; 8428071913dSRuslan Ermilov 8438071913dSRuslan Ermilov /* 8448071913dSRuslan Ermilov * ifp may be specified by sockaddr_dl 8458071913dSRuslan Ermilov * when protocol address is ambiguous. 8468071913dSRuslan Ermilov */ 8478071913dSRuslan Ermilov if (info->rti_ifp == NULL && ifpaddr != NULL && 8488071913dSRuslan Ermilov ifpaddr->sa_family == AF_LINK && 8494f8585e0SAlan Somers (ifa = ifa_ifwithnet(ifpaddr, 0, fibnum)) != NULL) { 8508071913dSRuslan Ermilov info->rti_ifp = ifa->ifa_ifp; 8518c0fec80SRobert Watson ifa_free(ifa); 8528c0fec80SRobert Watson } 8538071913dSRuslan Ermilov if (info->rti_ifa == NULL && ifaaddr != NULL) 8548071913dSRuslan Ermilov info->rti_ifa = ifa_ifwithaddr(ifaaddr); 8558071913dSRuslan Ermilov if (info->rti_ifa == NULL) { 8568071913dSRuslan Ermilov struct sockaddr *sa; 8578071913dSRuslan Ermilov 8588071913dSRuslan Ermilov sa = ifaaddr != NULL ? ifaaddr : 8598071913dSRuslan Ermilov (gateway != NULL ? gateway : dst); 8608071913dSRuslan Ermilov if (sa != NULL && info->rti_ifp != NULL) 8618071913dSRuslan Ermilov info->rti_ifa = ifaof_ifpforaddr(sa, info->rti_ifp); 8628071913dSRuslan Ermilov else if (dst != NULL && gateway != NULL) 8634f8585e0SAlan Somers info->rti_ifa = ifa_ifwithroute(flags, dst, gateway, 8648b07e49aSJulian Elischer fibnum); 8658071913dSRuslan Ermilov else if (sa != NULL) 8664f8585e0SAlan Somers info->rti_ifa = ifa_ifwithroute(flags, sa, sa, 8678b07e49aSJulian Elischer fibnum); 8688071913dSRuslan Ermilov } 8698071913dSRuslan Ermilov if ((ifa = info->rti_ifa) != NULL) { 8708071913dSRuslan Ermilov if (info->rti_ifp == NULL) 8718071913dSRuslan Ermilov info->rti_ifp = ifa->ifa_ifp; 8728071913dSRuslan Ermilov } else 8738071913dSRuslan Ermilov error = ENETUNREACH; 8748071913dSRuslan Ermilov return (error); 8758071913dSRuslan Ermilov } 8768071913dSRuslan Ermilov 8779c63e9dbSSam Leffler /* 8789c63e9dbSSam Leffler * Expunges references to a route that's about to be reclaimed. 8799c63e9dbSSam Leffler * The route must be locked. 8809c63e9dbSSam Leffler */ 8819c63e9dbSSam Leffler int 882b980262eSAlexander V. Chernikov rt_expunge(struct radix_node_head *rnh, struct rtentry *rt) 8839c63e9dbSSam Leffler { 884c7ea0aa6SQing Li #if !defined(RADIX_MPATH) 8859c63e9dbSSam Leffler struct radix_node *rn; 886c7ea0aa6SQing Li #else 887c7ea0aa6SQing Li struct rt_addrinfo info; 888c7ea0aa6SQing Li int fib; 889c7ea0aa6SQing Li struct rtentry *rt0; 890c7ea0aa6SQing Li #endif 8919c63e9dbSSam Leffler struct ifaddr *ifa; 8929c63e9dbSSam Leffler int error = 0; 8939c63e9dbSSam Leffler 8949c63e9dbSSam Leffler RT_LOCK_ASSERT(rt); 8953120b9d4SKip Macy RADIX_NODE_HEAD_LOCK_ASSERT(rnh); 896c7ea0aa6SQing Li 897c7ea0aa6SQing Li #ifdef RADIX_MPATH 898c7ea0aa6SQing Li fib = rt->rt_fibnum; 899c7ea0aa6SQing Li bzero(&info, sizeof(info)); 900c7ea0aa6SQing Li info.rti_ifp = rt->rt_ifp; 901c7ea0aa6SQing Li info.rti_flags = RTF_RNH_LOCKED; 902c7ea0aa6SQing Li info.rti_info[RTAX_DST] = rt_key(rt); 903c7ea0aa6SQing Li info.rti_info[RTAX_GATEWAY] = rt->rt_ifa->ifa_addr; 904c7ea0aa6SQing Li 905c7ea0aa6SQing Li RT_UNLOCK(rt); 906c7ea0aa6SQing Li error = rtrequest1_fib(RTM_DELETE, &info, &rt0, fib); 907c7ea0aa6SQing Li 908c7ea0aa6SQing Li if (error == 0 && rt0 != NULL) { 909c7ea0aa6SQing Li rt = rt0; 910c7ea0aa6SQing Li RT_LOCK(rt); 911c7ea0aa6SQing Li } else if (error != 0) { 912c7ea0aa6SQing Li RT_LOCK(rt); 913c7ea0aa6SQing Li return (error); 914c7ea0aa6SQing Li } 915c7ea0aa6SQing Li #else 9169c63e9dbSSam Leffler /* 9179c63e9dbSSam Leffler * Remove the item from the tree; it should be there, 9189c63e9dbSSam Leffler * but when callers invoke us blindly it may not (sigh). 9199c63e9dbSSam Leffler */ 9209c63e9dbSSam Leffler rn = rnh->rnh_deladdr(rt_key(rt), rt_mask(rt), rnh); 92185911824SLuigi Rizzo if (rn == NULL) { 9229c63e9dbSSam Leffler error = ESRCH; 9239c63e9dbSSam Leffler goto bad; 9249c63e9dbSSam Leffler } 9259c63e9dbSSam Leffler KASSERT((rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) == 0, 9269c63e9dbSSam Leffler ("unexpected flags 0x%x", rn->rn_flags)); 927d6941ce9SLuigi Rizzo KASSERT(rt == RNTORT(rn), 9289c63e9dbSSam Leffler ("lookup mismatch, rt %p rn %p", rt, rn)); 929c7ea0aa6SQing Li #endif /* RADIX_MPATH */ 9309c63e9dbSSam Leffler 9319c63e9dbSSam Leffler rt->rt_flags &= ~RTF_UP; 9329c63e9dbSSam Leffler 9339c63e9dbSSam Leffler /* 9349c63e9dbSSam Leffler * Give the protocol a chance to keep things in sync. 9359c63e9dbSSam Leffler */ 9369c63e9dbSSam Leffler if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) { 9379c63e9dbSSam Leffler struct rt_addrinfo info; 9389c63e9dbSSam Leffler 9399c63e9dbSSam Leffler bzero((caddr_t)&info, sizeof(info)); 9409c63e9dbSSam Leffler info.rti_flags = rt->rt_flags; 9419c63e9dbSSam Leffler info.rti_info[RTAX_DST] = rt_key(rt); 9429c63e9dbSSam Leffler info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 9439c63e9dbSSam Leffler info.rti_info[RTAX_NETMASK] = rt_mask(rt); 9449c63e9dbSSam Leffler ifa->ifa_rtrequest(RTM_DELETE, rt, &info); 9459c63e9dbSSam Leffler } 9469c63e9dbSSam Leffler 9479c63e9dbSSam Leffler /* 9489c63e9dbSSam Leffler * one more rtentry floating around that is not 9499c63e9dbSSam Leffler * linked to the routing table. 9509c63e9dbSSam Leffler */ 951603724d3SBjoern A. Zeeb V_rttrash++; 952c7ea0aa6SQing Li #if !defined(RADIX_MPATH) 9539c63e9dbSSam Leffler bad: 954c7ea0aa6SQing Li #endif 9559c63e9dbSSam Leffler return (error); 9569c63e9dbSSam Leffler } 9579c63e9dbSSam Leffler 958*7f948f12SAlexander V. Chernikov static int 959*7f948f12SAlexander V. Chernikov if_updatemtu_cb(struct radix_node *rn, void *arg) 960*7f948f12SAlexander V. Chernikov { 961*7f948f12SAlexander V. Chernikov struct rtentry *rt; 962*7f948f12SAlexander V. Chernikov struct if_mtuinfo *ifmtu; 963*7f948f12SAlexander V. Chernikov 964*7f948f12SAlexander V. Chernikov rt = (struct rtentry *)rn; 965*7f948f12SAlexander V. Chernikov ifmtu = (struct if_mtuinfo *)arg; 966*7f948f12SAlexander V. Chernikov 967*7f948f12SAlexander V. Chernikov if (rt->rt_ifp != ifmtu->ifp) 968*7f948f12SAlexander V. Chernikov return (0); 969*7f948f12SAlexander V. Chernikov 970*7f948f12SAlexander V. Chernikov if (rt->rt_mtu >= ifmtu->mtu) { 971*7f948f12SAlexander V. Chernikov /* We have to decrease mtu regardless of flags */ 972*7f948f12SAlexander V. Chernikov rt->rt_mtu = ifmtu->mtu; 973*7f948f12SAlexander V. Chernikov return (0); 974*7f948f12SAlexander V. Chernikov } 975*7f948f12SAlexander V. Chernikov 976*7f948f12SAlexander V. Chernikov /* 977*7f948f12SAlexander V. Chernikov * New MTU is bigger. Check if are allowed to alter it 978*7f948f12SAlexander V. Chernikov */ 979*7f948f12SAlexander V. Chernikov if ((rt->rt_flags & (RTF_FIXEDMTU | RTF_GATEWAY | RTF_HOST)) != 0) { 980*7f948f12SAlexander V. Chernikov 981*7f948f12SAlexander V. Chernikov /* 982*7f948f12SAlexander V. Chernikov * Skip routes with user-supplied MTU and 983*7f948f12SAlexander V. Chernikov * non-interface routes 984*7f948f12SAlexander V. Chernikov */ 985*7f948f12SAlexander V. Chernikov return (0); 986*7f948f12SAlexander V. Chernikov } 987*7f948f12SAlexander V. Chernikov 988*7f948f12SAlexander V. Chernikov /* We are safe to update route MTU */ 989*7f948f12SAlexander V. Chernikov rt->rt_mtu = ifmtu->mtu; 990*7f948f12SAlexander V. Chernikov 991*7f948f12SAlexander V. Chernikov return (0); 992*7f948f12SAlexander V. Chernikov } 993*7f948f12SAlexander V. Chernikov 994*7f948f12SAlexander V. Chernikov void 995*7f948f12SAlexander V. Chernikov rt_updatemtu(struct ifnet *ifp) 996*7f948f12SAlexander V. Chernikov { 997*7f948f12SAlexander V. Chernikov struct if_mtuinfo ifmtu; 998*7f948f12SAlexander V. Chernikov struct radix_node_head *rnh; 999*7f948f12SAlexander V. Chernikov int i, j; 1000*7f948f12SAlexander V. Chernikov 1001*7f948f12SAlexander V. Chernikov ifmtu.ifp = ifp; 1002*7f948f12SAlexander V. Chernikov 1003*7f948f12SAlexander V. Chernikov /* 1004*7f948f12SAlexander V. Chernikov * Try to update rt_mtu for all routes using this interface 1005*7f948f12SAlexander V. Chernikov * Unfortunately the only way to do this is to traverse all 1006*7f948f12SAlexander V. Chernikov * routing tables in all fibs/domains. 1007*7f948f12SAlexander V. Chernikov */ 1008*7f948f12SAlexander V. Chernikov for (i = 1; i <= AF_MAX; i++) { 1009*7f948f12SAlexander V. Chernikov ifmtu.mtu = if_getmtu_family(ifp, i); 1010*7f948f12SAlexander V. Chernikov for (j = 0; j < rt_numfibs; j++) { 1011*7f948f12SAlexander V. Chernikov rnh = rt_tables_get_rnh(j, i); 1012*7f948f12SAlexander V. Chernikov if (rnh == NULL) 1013*7f948f12SAlexander V. Chernikov continue; 1014*7f948f12SAlexander V. Chernikov RADIX_NODE_HEAD_LOCK(rnh); 1015*7f948f12SAlexander V. Chernikov rnh->rnh_walktree(rnh, if_updatemtu_cb, &ifmtu); 1016*7f948f12SAlexander V. Chernikov RADIX_NODE_HEAD_UNLOCK(rnh); 1017*7f948f12SAlexander V. Chernikov } 1018*7f948f12SAlexander V. Chernikov } 1019*7f948f12SAlexander V. Chernikov } 1020*7f948f12SAlexander V. Chernikov 1021*7f948f12SAlexander V. Chernikov 10225a2f4cbdSAlexander V. Chernikov #if 0 10235a2f4cbdSAlexander V. Chernikov int p_sockaddr(char *buf, int buflen, struct sockaddr *s); 10245a2f4cbdSAlexander V. Chernikov int rt_print(char *buf, int buflen, struct rtentry *rt); 10255a2f4cbdSAlexander V. Chernikov 10265a2f4cbdSAlexander V. Chernikov int 10275a2f4cbdSAlexander V. Chernikov p_sockaddr(char *buf, int buflen, struct sockaddr *s) 10285a2f4cbdSAlexander V. Chernikov { 10295a2f4cbdSAlexander V. Chernikov void *paddr = NULL; 10305a2f4cbdSAlexander V. Chernikov 10315a2f4cbdSAlexander V. Chernikov switch (s->sa_family) { 10325a2f4cbdSAlexander V. Chernikov case AF_INET: 10335a2f4cbdSAlexander V. Chernikov paddr = &((struct sockaddr_in *)s)->sin_addr; 10345a2f4cbdSAlexander V. Chernikov break; 10355a2f4cbdSAlexander V. Chernikov case AF_INET6: 10365a2f4cbdSAlexander V. Chernikov paddr = &((struct sockaddr_in6 *)s)->sin6_addr; 10375a2f4cbdSAlexander V. Chernikov break; 10385a2f4cbdSAlexander V. Chernikov } 10395a2f4cbdSAlexander V. Chernikov 10405a2f4cbdSAlexander V. Chernikov if (paddr == NULL) 10415a2f4cbdSAlexander V. Chernikov return (0); 10425a2f4cbdSAlexander V. Chernikov 10435a2f4cbdSAlexander V. Chernikov if (inet_ntop(s->sa_family, paddr, buf, buflen) == NULL) 10445a2f4cbdSAlexander V. Chernikov return (0); 10455a2f4cbdSAlexander V. Chernikov 10465a2f4cbdSAlexander V. Chernikov return (strlen(buf)); 10475a2f4cbdSAlexander V. Chernikov } 10485a2f4cbdSAlexander V. Chernikov 10495a2f4cbdSAlexander V. Chernikov int 10505a2f4cbdSAlexander V. Chernikov rt_print(char *buf, int buflen, struct rtentry *rt) 10515a2f4cbdSAlexander V. Chernikov { 10525a2f4cbdSAlexander V. Chernikov struct sockaddr *addr, *mask; 10535a2f4cbdSAlexander V. Chernikov int i = 0; 10545a2f4cbdSAlexander V. Chernikov 10555a2f4cbdSAlexander V. Chernikov addr = rt_key(rt); 10565a2f4cbdSAlexander V. Chernikov mask = rt_mask(rt); 10575a2f4cbdSAlexander V. Chernikov 10585a2f4cbdSAlexander V. Chernikov i = p_sockaddr(buf, buflen, addr); 10595a2f4cbdSAlexander V. Chernikov if (!(rt->rt_flags & RTF_HOST)) { 10605a2f4cbdSAlexander V. Chernikov buf[i++] = '/'; 10615a2f4cbdSAlexander V. Chernikov i += p_sockaddr(buf + i, buflen - i, mask); 10625a2f4cbdSAlexander V. Chernikov } 10635a2f4cbdSAlexander V. Chernikov 10645a2f4cbdSAlexander V. Chernikov if (rt->rt_flags & RTF_GATEWAY) { 10655a2f4cbdSAlexander V. Chernikov buf[i++] = '>'; 10665a2f4cbdSAlexander V. Chernikov i += p_sockaddr(buf + i, buflen - i, rt->rt_gateway); 10675a2f4cbdSAlexander V. Chernikov } 10685a2f4cbdSAlexander V. Chernikov 10695a2f4cbdSAlexander V. Chernikov return (i); 10705a2f4cbdSAlexander V. Chernikov } 10715a2f4cbdSAlexander V. Chernikov #endif 10725a2f4cbdSAlexander V. Chernikov 1073427ac07fSKip Macy #ifdef RADIX_MPATH 1074427ac07fSKip Macy static int 1075427ac07fSKip Macy rn_mpath_update(int req, struct rt_addrinfo *info, 1076427ac07fSKip Macy struct radix_node_head *rnh, struct rtentry **ret_nrt) 1077427ac07fSKip Macy { 1078427ac07fSKip Macy /* 1079427ac07fSKip Macy * if we got multipath routes, we require users to specify 1080427ac07fSKip Macy * a matching RTAX_GATEWAY. 1081427ac07fSKip Macy */ 1082427ac07fSKip Macy struct rtentry *rt, *rto = NULL; 1083f59c6cb0SAlexander V. Chernikov struct radix_node *rn; 1084427ac07fSKip Macy int error = 0; 1085427ac07fSKip Macy 10865a2f4cbdSAlexander V. Chernikov rn = rnh->rnh_lookup(dst, netmask, rnh); 1087427ac07fSKip Macy if (rn == NULL) 1088427ac07fSKip Macy return (ESRCH); 1089427ac07fSKip Macy rto = rt = RNTORT(rn); 10905a2f4cbdSAlexander V. Chernikov 1091427ac07fSKip Macy rt = rt_mpath_matchgate(rt, gateway); 1092427ac07fSKip Macy if (rt == NULL) 1093427ac07fSKip Macy return (ESRCH); 1094427ac07fSKip Macy /* 1095427ac07fSKip Macy * this is the first entry in the chain 1096427ac07fSKip Macy */ 1097427ac07fSKip Macy if (rto == rt) { 1098427ac07fSKip Macy rn = rn_mpath_next((struct radix_node *)rt); 1099427ac07fSKip Macy /* 1100427ac07fSKip Macy * there is another entry, now it's active 1101427ac07fSKip Macy */ 1102427ac07fSKip Macy if (rn) { 1103427ac07fSKip Macy rto = RNTORT(rn); 1104427ac07fSKip Macy RT_LOCK(rto); 1105427ac07fSKip Macy rto->rt_flags |= RTF_UP; 1106427ac07fSKip Macy RT_UNLOCK(rto); 1107427ac07fSKip Macy } else if (rt->rt_flags & RTF_GATEWAY) { 1108427ac07fSKip Macy /* 1109427ac07fSKip Macy * For gateway routes, we need to 1110427ac07fSKip Macy * make sure that we we are deleting 1111427ac07fSKip Macy * the correct gateway. 1112427ac07fSKip Macy * rt_mpath_matchgate() does not 1113427ac07fSKip Macy * check the case when there is only 1114427ac07fSKip Macy * one route in the chain. 1115427ac07fSKip Macy */ 1116427ac07fSKip Macy if (gateway && 1117427ac07fSKip Macy (rt->rt_gateway->sa_len != gateway->sa_len || 1118427ac07fSKip Macy memcmp(rt->rt_gateway, gateway, gateway->sa_len))) 1119427ac07fSKip Macy error = ESRCH; 11206a7bff2cSKip Macy else { 11216a7bff2cSKip Macy /* 11226a7bff2cSKip Macy * remove from tree before returning it 11236a7bff2cSKip Macy * to the caller 11246a7bff2cSKip Macy */ 11256a7bff2cSKip Macy rn = rnh->rnh_deladdr(dst, netmask, rnh); 11266a7bff2cSKip Macy KASSERT(rt == RNTORT(rn), ("radix node disappeared")); 11276a7bff2cSKip Macy goto gwdelete; 11286a7bff2cSKip Macy } 11296a7bff2cSKip Macy 1130427ac07fSKip Macy } 1131427ac07fSKip Macy /* 1132427ac07fSKip Macy * use the normal delete code to remove 1133427ac07fSKip Macy * the first entry 1134427ac07fSKip Macy */ 1135427ac07fSKip Macy if (req != RTM_DELETE) 1136427ac07fSKip Macy goto nondelete; 1137427ac07fSKip Macy 1138427ac07fSKip Macy error = ENOENT; 1139427ac07fSKip Macy goto done; 1140427ac07fSKip Macy } 1141427ac07fSKip Macy 1142427ac07fSKip Macy /* 1143427ac07fSKip Macy * if the entry is 2nd and on up 1144427ac07fSKip Macy */ 1145427ac07fSKip Macy if ((req == RTM_DELETE) && !rt_mpath_deldup(rto, rt)) 1146427ac07fSKip Macy panic ("rtrequest1: rt_mpath_deldup"); 11476a7bff2cSKip Macy gwdelete: 1148427ac07fSKip Macy RT_LOCK(rt); 1149427ac07fSKip Macy RT_ADDREF(rt); 1150427ac07fSKip Macy if (req == RTM_DELETE) { 1151427ac07fSKip Macy rt->rt_flags &= ~RTF_UP; 1152427ac07fSKip Macy /* 1153427ac07fSKip Macy * One more rtentry floating around that is not 1154427ac07fSKip Macy * linked to the routing table. rttrash will be decremented 1155427ac07fSKip Macy * when RTFREE(rt) is eventually called. 1156427ac07fSKip Macy */ 1157427ac07fSKip Macy V_rttrash++; 1158427ac07fSKip Macy } 1159427ac07fSKip Macy 1160427ac07fSKip Macy nondelete: 1161427ac07fSKip Macy if (req != RTM_DELETE) 1162427ac07fSKip Macy panic("unrecognized request %d", req); 1163427ac07fSKip Macy 1164427ac07fSKip Macy 1165427ac07fSKip Macy /* 1166427ac07fSKip Macy * If the caller wants it, then it can have it, 1167427ac07fSKip Macy * but it's up to it to free the rtentry as we won't be 1168427ac07fSKip Macy * doing it. 1169427ac07fSKip Macy */ 1170427ac07fSKip Macy if (ret_nrt) { 1171427ac07fSKip Macy *ret_nrt = rt; 1172427ac07fSKip Macy RT_UNLOCK(rt); 1173427ac07fSKip Macy } else 1174427ac07fSKip Macy RTFREE_LOCKED(rt); 1175427ac07fSKip Macy done: 1176427ac07fSKip Macy return (error); 1177427ac07fSKip Macy } 1178427ac07fSKip Macy #endif 1179427ac07fSKip Macy 11808071913dSRuslan Ermilov int 11818b07e49aSJulian Elischer rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, 11828b07e49aSJulian Elischer u_int fibnum) 11838b07e49aSJulian Elischer { 11843120b9d4SKip Macy int error = 0, needlock = 0; 1185f59c6cb0SAlexander V. Chernikov struct rtentry *rt; 1186e5c610d6SQing Li #ifdef FLOWTABLE 1187f59c6cb0SAlexander V. Chernikov struct rtentry *rt0; 1188e5c610d6SQing Li #endif 1189f59c6cb0SAlexander V. Chernikov struct radix_node *rn; 1190f59c6cb0SAlexander V. Chernikov struct radix_node_head *rnh; 1191df8bae1dSRodney W. Grimes struct ifaddr *ifa; 1192df8bae1dSRodney W. Grimes struct sockaddr *ndst; 119346a70de2SQing Li struct sockaddr_storage mdst; 1194df8bae1dSRodney W. Grimes #define senderr(x) { error = x ; goto bad; } 1195df8bae1dSRodney W. Grimes 11968b07e49aSJulian Elischer KASSERT((fibnum < rt_numfibs), ("rtrequest1_fib: bad fibnum")); 1197b680a383SBjoern A. Zeeb switch (dst->sa_family) { 1198b680a383SBjoern A. Zeeb case AF_INET6: 1199b680a383SBjoern A. Zeeb case AF_INET: 1200b680a383SBjoern A. Zeeb /* We support multiple FIBs. */ 1201b680a383SBjoern A. Zeeb break; 1202b680a383SBjoern A. Zeeb default: 1203b680a383SBjoern A. Zeeb fibnum = RT_DEFAULT_FIB; 1204b680a383SBjoern A. Zeeb break; 1205b680a383SBjoern A. Zeeb } 1206b680a383SBjoern A. Zeeb 1207b0a76b88SJulian Elischer /* 1208b0a76b88SJulian Elischer * Find the correct routing tree to use for this Address Family 1209b0a76b88SJulian Elischer */ 1210c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 121185911824SLuigi Rizzo if (rnh == NULL) 1212983985c1SJeffrey Hsu return (EAFNOSUPPORT); 12133120b9d4SKip Macy needlock = ((flags & RTF_RNH_LOCKED) == 0); 12143120b9d4SKip Macy flags &= ~RTF_RNH_LOCKED; 12153120b9d4SKip Macy if (needlock) 1216956b0b65SJeffrey Hsu RADIX_NODE_HEAD_LOCK(rnh); 1217c96b8224SKip Macy else 1218c96b8224SKip Macy RADIX_NODE_HEAD_LOCK_ASSERT(rnh); 1219b0a76b88SJulian Elischer /* 1220b0a76b88SJulian Elischer * If we are adding a host route then we don't want to put 122166953138SRuslan Ermilov * a netmask in the tree, nor do we want to clone it. 1222b0a76b88SJulian Elischer */ 12236e6b3f7cSQing Li if (flags & RTF_HOST) 122485911824SLuigi Rizzo netmask = NULL; 12256e6b3f7cSQing Li 1226df8bae1dSRodney W. Grimes switch (req) { 1227df8bae1dSRodney W. Grimes case RTM_DELETE: 122846a70de2SQing Li if (netmask) { 122946a70de2SQing Li rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask); 123046a70de2SQing Li dst = (struct sockaddr *)&mdst; 123146a70de2SQing Li } 1232e440aed9SQing Li #ifdef RADIX_MPATH 1233e440aed9SQing Li if (rn_mpath_capable(rnh)) { 1234427ac07fSKip Macy error = rn_mpath_update(req, info, rnh, ret_nrt); 1235e440aed9SQing Li /* 1236427ac07fSKip Macy * "bad" holds true for the success case 1237427ac07fSKip Macy * as well 1238e440aed9SQing Li */ 1239427ac07fSKip Macy if (error != ENOENT) 1240427ac07fSKip Macy goto bad; 1241c7ea0aa6SQing Li error = 0; 1242e440aed9SQing Li } 1243ea9cd9f2SBjoern A. Zeeb #endif 12443034f43fSAlexander V. Chernikov if ((flags & RTF_PINNED) == 0) { 12453034f43fSAlexander V. Chernikov /* Check if target route can be deleted */ 12463034f43fSAlexander V. Chernikov rt = (struct rtentry *)rnh->rnh_lookup(dst, 12473034f43fSAlexander V. Chernikov netmask, rnh); 12483034f43fSAlexander V. Chernikov if ((rt != NULL) && (rt->rt_flags & RTF_PINNED)) 12493034f43fSAlexander V. Chernikov senderr(EADDRINUSE); 12503034f43fSAlexander V. Chernikov } 12513034f43fSAlexander V. Chernikov 1252b0a76b88SJulian Elischer /* 1253b0a76b88SJulian Elischer * Remove the item from the tree and return it. 1254b0a76b88SJulian Elischer * Complain if it is not there and do no more processing. 1255b0a76b88SJulian Elischer */ 1256d1dd20beSSam Leffler rn = rnh->rnh_deladdr(dst, netmask, rnh); 125785911824SLuigi Rizzo if (rn == NULL) 1258df8bae1dSRodney W. Grimes senderr(ESRCH); 1259df8bae1dSRodney W. Grimes if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 1260df8bae1dSRodney W. Grimes panic ("rtrequest delete"); 1261d6941ce9SLuigi Rizzo rt = RNTORT(rn); 1262d1dd20beSSam Leffler RT_LOCK(rt); 12637138d65cSSam Leffler RT_ADDREF(rt); 126471eba915SRuslan Ermilov rt->rt_flags &= ~RTF_UP; 1265c2bed6a3SGarrett Wollman 1266c2bed6a3SGarrett Wollman /* 1267499676dfSJulian Elischer * give the protocol a chance to keep things in sync. 1268b0a76b88SJulian Elischer */ 1269df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) 12708071913dSRuslan Ermilov ifa->ifa_rtrequest(RTM_DELETE, rt, info); 1271499676dfSJulian Elischer 1272b0a76b88SJulian Elischer /* 1273d6941ce9SLuigi Rizzo * One more rtentry floating around that is not 1274d6941ce9SLuigi Rizzo * linked to the routing table. rttrash will be decremented 1275d6941ce9SLuigi Rizzo * when RTFREE(rt) is eventually called. 1276499676dfSJulian Elischer */ 1277603724d3SBjoern A. Zeeb V_rttrash++; 1278499676dfSJulian Elischer 1279499676dfSJulian Elischer /* 1280499676dfSJulian Elischer * If the caller wants it, then it can have it, 1281499676dfSJulian Elischer * but it's up to it to free the rtentry as we won't be 1282499676dfSJulian Elischer * doing it. 1283b0a76b88SJulian Elischer */ 1284d1dd20beSSam Leffler if (ret_nrt) { 1285df8bae1dSRodney W. Grimes *ret_nrt = rt; 1286d1dd20beSSam Leffler RT_UNLOCK(rt); 1287d1dd20beSSam Leffler } else 1288d1dd20beSSam Leffler RTFREE_LOCKED(rt); 1289df8bae1dSRodney W. Grimes break; 1290df8bae1dSRodney W. Grimes case RTM_RESOLVE: 12916e6b3f7cSQing Li /* 12926e6b3f7cSQing Li * resolve was only used for route cloning 12936e6b3f7cSQing Li * here for compat 12946e6b3f7cSQing Li */ 12956e6b3f7cSQing Li break; 1296df8bae1dSRodney W. Grimes case RTM_ADD: 12975df72964SGarrett Wollman if ((flags & RTF_GATEWAY) && !gateway) 129816a2e0a6SQing Li senderr(EINVAL); 129916a2e0a6SQing Li if (dst && gateway && (dst->sa_family != gateway->sa_family) && 130016a2e0a6SQing Li (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK)) 130116a2e0a6SQing Li senderr(EINVAL); 13025df72964SGarrett Wollman 13038c0fec80SRobert Watson if (info->rti_ifa == NULL) { 13048c0fec80SRobert Watson error = rt_getifa_fib(info, fibnum); 13058c0fec80SRobert Watson if (error) 13068071913dSRuslan Ermilov senderr(error); 13078c0fec80SRobert Watson } else 13088c0fec80SRobert Watson ifa_ref(info->rti_ifa); 13098071913dSRuslan Ermilov ifa = info->rti_ifa; 1310e3a7aa6fSGleb Smirnoff rt = uma_zalloc(V_rtzone, M_NOWAIT); 13118c0fec80SRobert Watson if (rt == NULL) { 13128c0fec80SRobert Watson ifa_free(ifa); 1313df8bae1dSRodney W. Grimes senderr(ENOBUFS); 13148c0fec80SRobert Watson } 1315df8bae1dSRodney W. Grimes rt->rt_flags = RTF_UP | flags; 13168b07e49aSJulian Elischer rt->rt_fibnum = fibnum; 1317499676dfSJulian Elischer /* 1318a8498625SBjoern A. Zeeb * Add the gateway. Possibly re-malloc-ing the storage for it. 1319499676dfSJulian Elischer */ 1320d1dd20beSSam Leffler RT_LOCK(rt); 1321831a80b0SMatthew Dillon if ((error = rt_setgate(rt, dst, gateway)) != 0) { 13228c0fec80SRobert Watson ifa_free(ifa); 13231ed81b73SMarko Zec uma_zfree(V_rtzone, rt); 1324704b0666SBill Fenner senderr(error); 1325df8bae1dSRodney W. Grimes } 1326499676dfSJulian Elischer 1327499676dfSJulian Elischer /* 1328499676dfSJulian Elischer * point to the (possibly newly malloc'd) dest address. 1329499676dfSJulian Elischer */ 1330d1dd20beSSam Leffler ndst = (struct sockaddr *)rt_key(rt); 1331499676dfSJulian Elischer 1332499676dfSJulian Elischer /* 1333499676dfSJulian Elischer * make sure it contains the value we want (masked if needed). 1334499676dfSJulian Elischer */ 1335df8bae1dSRodney W. Grimes if (netmask) { 1336df8bae1dSRodney W. Grimes rt_maskedcopy(dst, ndst, netmask); 1337df8bae1dSRodney W. Grimes } else 13381838a647SLuigi Rizzo bcopy(dst, ndst, dst->sa_len); 13398e718bb4SGarrett Wollman 13408e718bb4SGarrett Wollman /* 13418c0fec80SRobert Watson * We use the ifa reference returned by rt_getifa_fib(). 13428e718bb4SGarrett Wollman * This moved from below so that rnh->rnh_addaddr() can 1343499676dfSJulian Elischer * examine the ifa and ifa->ifa_ifp if it so desires. 13448e718bb4SGarrett Wollman */ 13458e718bb4SGarrett Wollman rt->rt_ifa = ifa; 13468e718bb4SGarrett Wollman rt->rt_ifp = ifa->ifa_ifp; 1347e3a7aa6fSGleb Smirnoff rt->rt_weight = 1; 13488e718bb4SGarrett Wollman 13491a75e3b2SAlexander V. Chernikov rt_setmetrics(info, rt); 13501a75e3b2SAlexander V. Chernikov 1351e440aed9SQing Li #ifdef RADIX_MPATH 1352e440aed9SQing Li /* do not permit exactly the same dst/mask/gw pair */ 1353e440aed9SQing Li if (rn_mpath_capable(rnh) && 1354e440aed9SQing Li rt_mpath_conflict(rnh, rt, netmask)) { 13551099f828SRobert Watson ifa_free(rt->rt_ifa); 1356e440aed9SQing Li Free(rt_key(rt)); 13571ed81b73SMarko Zec uma_zfree(V_rtzone, rt); 1358e440aed9SQing Li senderr(EEXIST); 1359e440aed9SQing Li } 1360e440aed9SQing Li #endif 1361e440aed9SQing Li 1362e5c610d6SQing Li #ifdef FLOWTABLE 1363e5c610d6SQing Li rt0 = NULL; 1364096f2786SBjoern A. Zeeb /* "flow-table" only supports IPv6 and IPv4 at the moment. */ 1365096f2786SBjoern A. Zeeb switch (dst->sa_family) { 1366096f2786SBjoern A. Zeeb #ifdef INET6 1367096f2786SBjoern A. Zeeb case AF_INET6: 1368096f2786SBjoern A. Zeeb #endif 1369db44ff40SBjoern A. Zeeb #ifdef INET 1370096f2786SBjoern A. Zeeb case AF_INET: 1371096f2786SBjoern A. Zeeb #endif 1372096f2786SBjoern A. Zeeb #if defined(INET6) || defined(INET) 1373e5c610d6SQing Li rn = rnh->rnh_matchaddr(dst, rnh); 1374e5c610d6SQing Li if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { 1375e5c610d6SQing Li struct sockaddr *mask; 1376e5c610d6SQing Li u_char *m, *n; 1377e5c610d6SQing Li int len; 1378e5c610d6SQing Li 1379e5c610d6SQing Li /* 1380e5c610d6SQing Li * compare mask to see if the new route is 1381e5c610d6SQing Li * more specific than the existing one 1382e5c610d6SQing Li */ 1383e5c610d6SQing Li rt0 = RNTORT(rn); 1384e5c610d6SQing Li RT_LOCK(rt0); 1385e5c610d6SQing Li RT_ADDREF(rt0); 1386e5c610d6SQing Li RT_UNLOCK(rt0); 1387e5c610d6SQing Li /* 1388e5c610d6SQing Li * A host route is already present, so 1389e5c610d6SQing Li * leave the flow-table entries as is. 1390e5c610d6SQing Li */ 1391e5c610d6SQing Li if (rt0->rt_flags & RTF_HOST) { 1392e5c610d6SQing Li RTFREE(rt0); 1393e5c610d6SQing Li rt0 = NULL; 1394e5c610d6SQing Li } else if (!(flags & RTF_HOST) && netmask) { 1395e5c610d6SQing Li mask = rt_mask(rt0); 1396e5c610d6SQing Li len = mask->sa_len; 1397e5c610d6SQing Li m = (u_char *)mask; 1398e5c610d6SQing Li n = (u_char *)netmask; 1399e5c610d6SQing Li while (len-- > 0) { 1400e5c610d6SQing Li if (*n != *m) 1401e5c610d6SQing Li break; 1402e5c610d6SQing Li n++; 1403e5c610d6SQing Li m++; 1404e5c610d6SQing Li } 1405e5c610d6SQing Li if (len == 0 || (*n < *m)) { 1406e5c610d6SQing Li RTFREE(rt0); 1407e5c610d6SQing Li rt0 = NULL; 1408e5c610d6SQing Li } 1409e5c610d6SQing Li } 1410e5c610d6SQing Li } 1411096f2786SBjoern A. Zeeb #endif/* INET6 || INET */ 1412e5c610d6SQing Li } 1413096f2786SBjoern A. Zeeb #endif /* FLOWTABLE */ 1414e5c610d6SQing Li 1415d1dd20beSSam Leffler /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ 1416d1dd20beSSam Leffler rn = rnh->rnh_addaddr(ndst, netmask, rnh, rt->rt_nodes); 1417499676dfSJulian Elischer /* 1418499676dfSJulian Elischer * If it still failed to go into the tree, 1419499676dfSJulian Elischer * then un-make it (this should be a function) 1420499676dfSJulian Elischer */ 142185911824SLuigi Rizzo if (rn == NULL) { 14221099f828SRobert Watson ifa_free(rt->rt_ifa); 1423df8bae1dSRodney W. Grimes Free(rt_key(rt)); 14241ed81b73SMarko Zec uma_zfree(V_rtzone, rt); 1425e5c610d6SQing Li #ifdef FLOWTABLE 1426e5c610d6SQing Li if (rt0 != NULL) 1427e5c610d6SQing Li RTFREE(rt0); 1428e5c610d6SQing Li #endif 1429df8bae1dSRodney W. Grimes senderr(EEXIST); 1430df8bae1dSRodney W. Grimes } 1431e5c610d6SQing Li #ifdef FLOWTABLE 1432e5c610d6SQing Li else if (rt0 != NULL) { 14335d6d7e75SGleb Smirnoff flowtable_route_flush(dst->sa_family, rt0); 1434e5c610d6SQing Li RTFREE(rt0); 1435e5c610d6SQing Li } 1436e5c610d6SQing Li #endif 1437499676dfSJulian Elischer 1438499676dfSJulian Elischer /* 1439a0c0e34bSGleb Smirnoff * If this protocol has something to add to this then 1440499676dfSJulian Elischer * allow it to do that as well. 1441499676dfSJulian Elischer */ 1442df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 14438071913dSRuslan Ermilov ifa->ifa_rtrequest(req, rt, info); 1444499676dfSJulian Elischer 1445cd02a0b7SGarrett Wollman /* 1446499676dfSJulian Elischer * actually return a resultant rtentry and 1447499676dfSJulian Elischer * give the caller a single reference. 1448499676dfSJulian Elischer */ 1449df8bae1dSRodney W. Grimes if (ret_nrt) { 1450df8bae1dSRodney W. Grimes *ret_nrt = rt; 14517138d65cSSam Leffler RT_ADDREF(rt); 1452df8bae1dSRodney W. Grimes } 1453d1dd20beSSam Leffler RT_UNLOCK(rt); 1454df8bae1dSRodney W. Grimes break; 1455c77462ddSAlexander V. Chernikov case RTM_CHANGE: 1456c77462ddSAlexander V. Chernikov error = rtrequest1_fib_change(rnh, info, ret_nrt, fibnum); 1457c77462ddSAlexander V. Chernikov break; 14588071913dSRuslan Ermilov default: 14598071913dSRuslan Ermilov error = EOPNOTSUPP; 1460df8bae1dSRodney W. Grimes } 1461df8bae1dSRodney W. Grimes bad: 14623120b9d4SKip Macy if (needlock) 1463956b0b65SJeffrey Hsu RADIX_NODE_HEAD_UNLOCK(rnh); 1464df8bae1dSRodney W. Grimes return (error); 1465d1dd20beSSam Leffler #undef senderr 1466d1dd20beSSam Leffler } 1467d1dd20beSSam Leffler 14688071913dSRuslan Ermilov #undef dst 14698071913dSRuslan Ermilov #undef gateway 14708071913dSRuslan Ermilov #undef netmask 14718071913dSRuslan Ermilov #undef ifaaddr 14728071913dSRuslan Ermilov #undef ifpaddr 14738071913dSRuslan Ermilov #undef flags 1474df8bae1dSRodney W. Grimes 1475c77462ddSAlexander V. Chernikov static int 1476c77462ddSAlexander V. Chernikov rtrequest1_fib_change(struct radix_node_head *rnh, struct rt_addrinfo *info, 1477c77462ddSAlexander V. Chernikov struct rtentry **ret_nrt, u_int fibnum) 1478c77462ddSAlexander V. Chernikov { 1479c77462ddSAlexander V. Chernikov struct rtentry *rt = NULL; 1480c77462ddSAlexander V. Chernikov int error = 0; 1481c77462ddSAlexander V. Chernikov int free_ifa = 0; 14821a75e3b2SAlexander V. Chernikov int family, mtu; 1483*7f948f12SAlexander V. Chernikov struct if_mtuinfo ifmtu; 1484c77462ddSAlexander V. Chernikov 1485c77462ddSAlexander V. Chernikov rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST], 1486c77462ddSAlexander V. Chernikov info->rti_info[RTAX_NETMASK], rnh); 1487c77462ddSAlexander V. Chernikov 1488c77462ddSAlexander V. Chernikov if (rt == NULL) 1489c77462ddSAlexander V. Chernikov return (ESRCH); 1490c77462ddSAlexander V. Chernikov 1491c77462ddSAlexander V. Chernikov #ifdef RADIX_MPATH 1492c77462ddSAlexander V. Chernikov /* 1493c77462ddSAlexander V. Chernikov * If we got multipath routes, 1494c77462ddSAlexander V. Chernikov * we require users to specify a matching RTAX_GATEWAY. 1495c77462ddSAlexander V. Chernikov */ 1496c77462ddSAlexander V. Chernikov if (rn_mpath_capable(rnh)) { 1497c77462ddSAlexander V. Chernikov rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]); 1498c77462ddSAlexander V. Chernikov if (rt == NULL) 1499c77462ddSAlexander V. Chernikov return (ESRCH); 1500c77462ddSAlexander V. Chernikov } 1501c77462ddSAlexander V. Chernikov #endif 1502c77462ddSAlexander V. Chernikov 1503c77462ddSAlexander V. Chernikov RT_LOCK(rt); 1504c77462ddSAlexander V. Chernikov 15051a75e3b2SAlexander V. Chernikov rt_setmetrics(info, rt); 15061a75e3b2SAlexander V. Chernikov 1507c77462ddSAlexander V. Chernikov /* 1508c77462ddSAlexander V. Chernikov * New gateway could require new ifaddr, ifp; 1509c77462ddSAlexander V. Chernikov * flags may also be different; ifp may be specified 1510c77462ddSAlexander V. Chernikov * by ll sockaddr when protocol address is ambiguous 1511c77462ddSAlexander V. Chernikov */ 1512c77462ddSAlexander V. Chernikov if (((rt->rt_flags & RTF_GATEWAY) && 1513c77462ddSAlexander V. Chernikov info->rti_info[RTAX_GATEWAY] != NULL) || 1514c77462ddSAlexander V. Chernikov info->rti_info[RTAX_IFP] != NULL || 1515c77462ddSAlexander V. Chernikov (info->rti_info[RTAX_IFA] != NULL && 1516c77462ddSAlexander V. Chernikov !sa_equal(info->rti_info[RTAX_IFA], rt->rt_ifa->ifa_addr))) { 1517c77462ddSAlexander V. Chernikov 1518c77462ddSAlexander V. Chernikov error = rt_getifa_fib(info, fibnum); 1519c77462ddSAlexander V. Chernikov if (info->rti_ifa != NULL) 1520c77462ddSAlexander V. Chernikov free_ifa = 1; 1521c77462ddSAlexander V. Chernikov 1522c77462ddSAlexander V. Chernikov if (error != 0) 1523a713ee5cSAlexander V. Chernikov goto bad; 1524c77462ddSAlexander V. Chernikov } 1525c77462ddSAlexander V. Chernikov 1526c77462ddSAlexander V. Chernikov /* Check if outgoing interface has changed */ 1527c77462ddSAlexander V. Chernikov if (info->rti_ifa != NULL && info->rti_ifa != rt->rt_ifa && 1528c77462ddSAlexander V. Chernikov rt->rt_ifa != NULL && rt->rt_ifa->ifa_rtrequest != NULL) { 1529c77462ddSAlexander V. Chernikov rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, info); 1530c77462ddSAlexander V. Chernikov ifa_free(rt->rt_ifa); 1531c77462ddSAlexander V. Chernikov } 1532c77462ddSAlexander V. Chernikov /* Update gateway address */ 1533c77462ddSAlexander V. Chernikov if (info->rti_info[RTAX_GATEWAY] != NULL) { 1534c77462ddSAlexander V. Chernikov error = rt_setgate(rt, rt_key(rt), info->rti_info[RTAX_GATEWAY]); 1535c77462ddSAlexander V. Chernikov if (error != 0) 1536a713ee5cSAlexander V. Chernikov goto bad; 1537c77462ddSAlexander V. Chernikov 1538c77462ddSAlexander V. Chernikov rt->rt_flags &= ~RTF_GATEWAY; 1539c77462ddSAlexander V. Chernikov rt->rt_flags |= (RTF_GATEWAY & info->rti_flags); 1540c77462ddSAlexander V. Chernikov } 1541c77462ddSAlexander V. Chernikov 1542c77462ddSAlexander V. Chernikov if (info->rti_ifa != NULL && info->rti_ifa != rt->rt_ifa) { 1543c77462ddSAlexander V. Chernikov ifa_ref(info->rti_ifa); 1544c77462ddSAlexander V. Chernikov rt->rt_ifa = info->rti_ifa; 1545c77462ddSAlexander V. Chernikov rt->rt_ifp = info->rti_ifp; 1546c77462ddSAlexander V. Chernikov } 1547c77462ddSAlexander V. Chernikov /* Allow some flags to be toggled on change. */ 1548c77462ddSAlexander V. Chernikov rt->rt_flags &= ~RTF_FMASK; 1549c77462ddSAlexander V. Chernikov rt->rt_flags |= info->rti_flags & RTF_FMASK; 1550c77462ddSAlexander V. Chernikov 1551c77462ddSAlexander V. Chernikov if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest != NULL) 1552c77462ddSAlexander V. Chernikov rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, info); 1553c77462ddSAlexander V. Chernikov 1554*7f948f12SAlexander V. Chernikov /* Alter route MTU if necessary */ 15551a75e3b2SAlexander V. Chernikov if (rt->rt_ifp != NULL) { 15561a75e3b2SAlexander V. Chernikov family = info->rti_info[RTAX_DST]->sa_family; 15571a75e3b2SAlexander V. Chernikov mtu = if_getmtu_family(rt->rt_ifp, family); 1558*7f948f12SAlexander V. Chernikov /* Set default MTU */ 1559*7f948f12SAlexander V. Chernikov if (rt->rt_mtu == 0) 15601a75e3b2SAlexander V. Chernikov rt->rt_mtu = mtu; 1561*7f948f12SAlexander V. Chernikov if (rt->rt_mtu != mtu) { 1562*7f948f12SAlexander V. Chernikov /* Check if we really need to update */ 1563*7f948f12SAlexander V. Chernikov ifmtu.ifp = rt->rt_ifp; 1564*7f948f12SAlexander V. Chernikov ifmtu.mtu = mtu; 1565*7f948f12SAlexander V. Chernikov if_updatemtu_cb(rt->rt_nodes, &ifmtu); 1566*7f948f12SAlexander V. Chernikov } 15671a75e3b2SAlexander V. Chernikov } 15680fb9298dSAlexander V. Chernikov 1569c77462ddSAlexander V. Chernikov if (ret_nrt) { 1570c77462ddSAlexander V. Chernikov *ret_nrt = rt; 1571c77462ddSAlexander V. Chernikov RT_ADDREF(rt); 1572c77462ddSAlexander V. Chernikov } 1573c77462ddSAlexander V. Chernikov bad: 1574c77462ddSAlexander V. Chernikov RT_UNLOCK(rt); 1575c77462ddSAlexander V. Chernikov if (free_ifa != 0) 1576c77462ddSAlexander V. Chernikov ifa_free(info->rti_ifa); 1577c77462ddSAlexander V. Chernikov return (error); 1578c77462ddSAlexander V. Chernikov } 1579c77462ddSAlexander V. Chernikov 15800fb9298dSAlexander V. Chernikov static void 15810fb9298dSAlexander V. Chernikov rt_setmetrics(const struct rt_addrinfo *info, struct rtentry *rt) 15820fb9298dSAlexander V. Chernikov { 15830fb9298dSAlexander V. Chernikov 1584*7f948f12SAlexander V. Chernikov if (info->rti_mflags & RTV_MTU) { 1585*7f948f12SAlexander V. Chernikov if (info->rti_rmx->rmx_mtu != 0) { 1586*7f948f12SAlexander V. Chernikov 1587*7f948f12SAlexander V. Chernikov /* 1588*7f948f12SAlexander V. Chernikov * MTU was explicitly provided by user. 1589*7f948f12SAlexander V. Chernikov * Keep it. 1590*7f948f12SAlexander V. Chernikov */ 1591*7f948f12SAlexander V. Chernikov rt->rt_flags |= RTF_FIXEDMTU; 1592*7f948f12SAlexander V. Chernikov } else { 1593*7f948f12SAlexander V. Chernikov 1594*7f948f12SAlexander V. Chernikov /* 1595*7f948f12SAlexander V. Chernikov * User explicitly sets MTU to 0. 1596*7f948f12SAlexander V. Chernikov * Assume rollback to default. 1597*7f948f12SAlexander V. Chernikov */ 1598*7f948f12SAlexander V. Chernikov rt->rt_flags &= ~RTF_FIXEDMTU; 1599*7f948f12SAlexander V. Chernikov } 16000fb9298dSAlexander V. Chernikov rt->rt_mtu = info->rti_rmx->rmx_mtu; 1601*7f948f12SAlexander V. Chernikov } 16020fb9298dSAlexander V. Chernikov if (info->rti_mflags & RTV_WEIGHT) 16030fb9298dSAlexander V. Chernikov rt->rt_weight = info->rti_rmx->rmx_weight; 16040fb9298dSAlexander V. Chernikov /* Kernel -> userland timebase conversion. */ 16050fb9298dSAlexander V. Chernikov if (info->rti_mflags & RTV_EXPIRE) 16060fb9298dSAlexander V. Chernikov rt->rt_expire = info->rti_rmx->rmx_expire ? 16070fb9298dSAlexander V. Chernikov info->rti_rmx->rmx_expire - time_second + time_uptime : 0; 16080fb9298dSAlexander V. Chernikov } 16090fb9298dSAlexander V. Chernikov 1610df8bae1dSRodney W. Grimes int 1611d1dd20beSSam Leffler rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate) 1612df8bae1dSRodney W. Grimes { 1613d1dd20beSSam Leffler /* XXX dst may be overwritten, can we move this to below */ 16146e6b3f7cSQing Li int dlen = SA_SIZE(dst), glen = SA_SIZE(gate); 16156e6b3f7cSQing Li #ifdef INVARIANTS 1616c2c2a7c1SBjoern A. Zeeb struct radix_node_head *rnh; 1617c2c2a7c1SBjoern A. Zeeb 1618c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(rt->rt_fibnum, dst->sa_family); 16196e6b3f7cSQing Li #endif 1620d1dd20beSSam Leffler 1621d1dd20beSSam Leffler RT_LOCK_ASSERT(rt); 16223120b9d4SKip Macy RADIX_NODE_HEAD_LOCK_ASSERT(rnh); 1623df8bae1dSRodney W. Grimes 16241db1fffaSBill Fenner /* 162585911824SLuigi Rizzo * Prepare to store the gateway in rt->rt_gateway. 162685911824SLuigi Rizzo * Both dst and gateway are stored one after the other in the same 162785911824SLuigi Rizzo * malloc'd chunk. If we have room, we can reuse the old buffer, 162885911824SLuigi Rizzo * rt_gateway already points to the right place. 162985911824SLuigi Rizzo * Otherwise, malloc a new block and update the 'dst' address. 1630499676dfSJulian Elischer */ 163185911824SLuigi Rizzo if (rt->rt_gateway == NULL || glen > SA_SIZE(rt->rt_gateway)) { 163285911824SLuigi Rizzo caddr_t new; 163385911824SLuigi Rizzo 1634df8bae1dSRodney W. Grimes R_Malloc(new, caddr_t, dlen + glen); 163585911824SLuigi Rizzo if (new == NULL) 16361db1fffaSBill Fenner return ENOBUFS; 1637499676dfSJulian Elischer /* 163885911824SLuigi Rizzo * XXX note, we copy from *dst and not *rt_key(rt) because 163985911824SLuigi Rizzo * rt_setgate() can be called to initialize a newly 164085911824SLuigi Rizzo * allocated route entry, in which case rt_key(rt) == NULL 164185911824SLuigi Rizzo * (and also rt->rt_gateway == NULL). 164285911824SLuigi Rizzo * Free()/free() handle a NULL argument just fine. 1643499676dfSJulian Elischer */ 16441838a647SLuigi Rizzo bcopy(dst, new, dlen); 164585911824SLuigi Rizzo Free(rt_key(rt)); /* free old block, if any */ 1646445e045bSAlexander Kabaev rt_key(rt) = (struct sockaddr *)new; 164785911824SLuigi Rizzo rt->rt_gateway = (struct sockaddr *)(new + dlen); 1648df8bae1dSRodney W. Grimes } 1649499676dfSJulian Elischer 1650499676dfSJulian Elischer /* 165185911824SLuigi Rizzo * Copy the new gateway value into the memory chunk. 165285911824SLuigi Rizzo */ 165385911824SLuigi Rizzo bcopy(gate, rt->rt_gateway, glen); 165485911824SLuigi Rizzo 16556e6b3f7cSQing Li return (0); 1656df8bae1dSRodney W. Grimes } 1657df8bae1dSRodney W. Grimes 1658c7ab6602SQing Li void 1659d1dd20beSSam Leffler rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) 1660df8bae1dSRodney W. Grimes { 1661f59c6cb0SAlexander V. Chernikov u_char *cp1 = (u_char *)src; 1662f59c6cb0SAlexander V. Chernikov u_char *cp2 = (u_char *)dst; 1663f59c6cb0SAlexander V. Chernikov u_char *cp3 = (u_char *)netmask; 1664df8bae1dSRodney W. Grimes u_char *cplim = cp2 + *cp3; 1665df8bae1dSRodney W. Grimes u_char *cplim2 = cp2 + *cp1; 1666df8bae1dSRodney W. Grimes 1667df8bae1dSRodney W. Grimes *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ 1668df8bae1dSRodney W. Grimes cp3 += 2; 1669df8bae1dSRodney W. Grimes if (cplim > cplim2) 1670df8bae1dSRodney W. Grimes cplim = cplim2; 1671df8bae1dSRodney W. Grimes while (cp2 < cplim) 1672df8bae1dSRodney W. Grimes *cp2++ = *cp1++ & *cp3++; 1673df8bae1dSRodney W. Grimes if (cp2 < cplim2) 1674df8bae1dSRodney W. Grimes bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); 1675df8bae1dSRodney W. Grimes } 1676df8bae1dSRodney W. Grimes 1677df8bae1dSRodney W. Grimes /* 1678df8bae1dSRodney W. Grimes * Set up a routing table entry, normally 1679df8bae1dSRodney W. Grimes * for an interface. 1680df8bae1dSRodney W. Grimes */ 16818b07e49aSJulian Elischer #define _SOCKADDR_TMPSIZE 128 /* Not too big.. kernel stack size is limited */ 16828b07e49aSJulian Elischer static inline int 16838b07e49aSJulian Elischer rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) 1684df8bae1dSRodney W. Grimes { 16855aca0b30SLuigi Rizzo struct sockaddr *dst; 16868071913dSRuslan Ermilov struct sockaddr *netmask; 168785911824SLuigi Rizzo struct rtentry *rt = NULL; 16888071913dSRuslan Ermilov struct rt_addrinfo info; 1689e440aed9SQing Li int error = 0; 16908b07e49aSJulian Elischer int startfib, endfib; 16918b07e49aSJulian Elischer char tempbuf[_SOCKADDR_TMPSIZE]; 16928b07e49aSJulian Elischer int didwork = 0; 16938b07e49aSJulian Elischer int a_failure = 0; 16946e6b3f7cSQing Li static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; 16953034f43fSAlexander V. Chernikov struct radix_node_head *rnh; 1696df8bae1dSRodney W. Grimes 16978071913dSRuslan Ermilov if (flags & RTF_HOST) { 16988071913dSRuslan Ermilov dst = ifa->ifa_dstaddr; 16998071913dSRuslan Ermilov netmask = NULL; 17008071913dSRuslan Ermilov } else { 17018071913dSRuslan Ermilov dst = ifa->ifa_addr; 17028071913dSRuslan Ermilov netmask = ifa->ifa_netmask; 17038071913dSRuslan Ermilov } 1704b3dd0771SBjoern A. Zeeb if (dst->sa_len == 0) 1705b3dd0771SBjoern A. Zeeb return(EINVAL); 1706b680a383SBjoern A. Zeeb switch (dst->sa_family) { 1707b680a383SBjoern A. Zeeb case AF_INET6: 1708b680a383SBjoern A. Zeeb case AF_INET: 1709b680a383SBjoern A. Zeeb /* We support multiple FIBs. */ 1710b680a383SBjoern A. Zeeb break; 1711b680a383SBjoern A. Zeeb default: 1712b680a383SBjoern A. Zeeb fibnum = RT_DEFAULT_FIB; 1713b680a383SBjoern A. Zeeb break; 1714b680a383SBjoern A. Zeeb } 17157d9b6df1SAlexander V. Chernikov if (fibnum == RT_ALL_FIBS) { 1716ee0bd4b9SHiroki Sato if (V_rt_add_addr_allfibs == 0 && cmd == (int)RTM_ADD) 17170489b891SAlan Somers startfib = endfib = ifa->ifa_ifp->if_fib; 1718ee0bd4b9SHiroki Sato else { 17198b07e49aSJulian Elischer startfib = 0; 17208b07e49aSJulian Elischer endfib = rt_numfibs - 1; 172166e8505fSJulian Elischer } 17228b07e49aSJulian Elischer } else { 17238b07e49aSJulian Elischer KASSERT((fibnum < rt_numfibs), ("rtinit1: bad fibnum")); 17248b07e49aSJulian Elischer startfib = fibnum; 17258b07e49aSJulian Elischer endfib = fibnum; 17268b07e49aSJulian Elischer } 1727ac4a76ebSBjoern A. Zeeb 1728b0a76b88SJulian Elischer /* 17298b07e49aSJulian Elischer * If it's a delete, check that if it exists, 17308b07e49aSJulian Elischer * it's on the correct interface or we might scrub 17318b07e49aSJulian Elischer * a route to another ifa which would 1732b0a76b88SJulian Elischer * be confusing at best and possibly worse. 1733b0a76b88SJulian Elischer */ 1734df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE) { 1735b0a76b88SJulian Elischer /* 1736b0a76b88SJulian Elischer * It's a delete, so it should already exist.. 1737b0a76b88SJulian Elischer * If it's a net, mask off the host bits 1738b0a76b88SJulian Elischer * (Assuming we have a mask) 17398b07e49aSJulian Elischer * XXX this is kinda inet specific.. 1740b0a76b88SJulian Elischer */ 17418071913dSRuslan Ermilov if (netmask != NULL) { 17428b07e49aSJulian Elischer rt_maskedcopy(dst, (struct sockaddr *)tempbuf, netmask); 17438b07e49aSJulian Elischer dst = (struct sockaddr *)tempbuf; 1744df8bae1dSRodney W. Grimes } 17458b07e49aSJulian Elischer } 17468b07e49aSJulian Elischer /* 17478b07e49aSJulian Elischer * Now go through all the requested tables (fibs) and do the 17488b07e49aSJulian Elischer * requested action. Realistically, this will either be fib 0 17498b07e49aSJulian Elischer * for protocols that don't do multiple tables or all the 1750a8498625SBjoern A. Zeeb * tables for those that do. 17518b07e49aSJulian Elischer */ 17528b07e49aSJulian Elischer for ( fibnum = startfib; fibnum <= endfib; fibnum++) { 17538b07e49aSJulian Elischer if (cmd == RTM_DELETE) { 17548b07e49aSJulian Elischer struct radix_node *rn; 1755b0a76b88SJulian Elischer /* 17568071913dSRuslan Ermilov * Look up an rtentry that is in the routing tree and 17578071913dSRuslan Ermilov * contains the correct info. 1758b0a76b88SJulian Elischer */ 1759c2c2a7c1SBjoern A. Zeeb rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 1760c2c2a7c1SBjoern A. Zeeb if (rnh == NULL) 17618b07e49aSJulian Elischer /* this table doesn't exist but others might */ 17628b07e49aSJulian Elischer continue; 176314126522SAlexander V. Chernikov RADIX_NODE_HEAD_RLOCK(rnh); 1764034c09ffSAlexander V. Chernikov rn = rnh->rnh_lookup(dst, netmask, rnh); 1765e440aed9SQing Li #ifdef RADIX_MPATH 1766e440aed9SQing Li if (rn_mpath_capable(rnh)) { 1767e440aed9SQing Li 1768e440aed9SQing Li if (rn == NULL) 1769e440aed9SQing Li error = ESRCH; 1770e440aed9SQing Li else { 1771e440aed9SQing Li rt = RNTORT(rn); 1772e440aed9SQing Li /* 17738b07e49aSJulian Elischer * for interface route the 17748b07e49aSJulian Elischer * rt->rt_gateway is sockaddr_intf 17758b07e49aSJulian Elischer * for cloning ARP entries, so 17768b07e49aSJulian Elischer * rt_mpath_matchgate must use the 17778b07e49aSJulian Elischer * interface address 1778e440aed9SQing Li */ 17798b07e49aSJulian Elischer rt = rt_mpath_matchgate(rt, 17808b07e49aSJulian Elischer ifa->ifa_addr); 1781034c09ffSAlexander V. Chernikov if (rt == NULL) 1782e440aed9SQing Li error = ESRCH; 1783e440aed9SQing Li } 1784e440aed9SQing Li } 1785e440aed9SQing Li #endif 17868b07e49aSJulian Elischer error = (rn == NULL || 17878071913dSRuslan Ermilov (rn->rn_flags & RNF_ROOT) || 17885a2f4cbdSAlexander V. Chernikov RNTORT(rn)->rt_ifa != ifa); 178914126522SAlexander V. Chernikov RADIX_NODE_HEAD_RUNLOCK(rnh); 1790956b0b65SJeffrey Hsu if (error) { 17918b07e49aSJulian Elischer /* this is only an error if bad on ALL tables */ 17928b07e49aSJulian Elischer continue; 1793df8bae1dSRodney W. Grimes } 1794b0a76b88SJulian Elischer } 1795b0a76b88SJulian Elischer /* 1796b0a76b88SJulian Elischer * Do the actual request 1797b0a76b88SJulian Elischer */ 17988071913dSRuslan Ermilov bzero((caddr_t)&info, sizeof(info)); 17998071913dSRuslan Ermilov info.rti_ifa = ifa; 18003034f43fSAlexander V. Chernikov info.rti_flags = flags | 18013034f43fSAlexander V. Chernikov (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED; 18028071913dSRuslan Ermilov info.rti_info[RTAX_DST] = dst; 18036e6b3f7cSQing Li /* 18046e6b3f7cSQing Li * doing this for compatibility reasons 18056e6b3f7cSQing Li */ 18066e6b3f7cSQing Li if (cmd == RTM_ADD) 18076e6b3f7cSQing Li info.rti_info[RTAX_GATEWAY] = 18086e6b3f7cSQing Li (struct sockaddr *)&null_sdl; 18096e6b3f7cSQing Li else 18108071913dSRuslan Ermilov info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; 18118071913dSRuslan Ermilov info.rti_info[RTAX_NETMASK] = netmask; 18128b07e49aSJulian Elischer error = rtrequest1_fib(cmd, &info, &rt, fibnum); 18133034f43fSAlexander V. Chernikov 18143034f43fSAlexander V. Chernikov if ((error == EEXIST) && (cmd == RTM_ADD)) { 18153034f43fSAlexander V. Chernikov /* 18163034f43fSAlexander V. Chernikov * Interface route addition failed. 18173034f43fSAlexander V. Chernikov * Atomically delete current prefix generating 18183034f43fSAlexander V. Chernikov * RTM_DELETE message, and retry adding 18193034f43fSAlexander V. Chernikov * interface prefix. 18203034f43fSAlexander V. Chernikov */ 18213034f43fSAlexander V. Chernikov rnh = rt_tables_get_rnh(fibnum, dst->sa_family); 18223034f43fSAlexander V. Chernikov RADIX_NODE_HEAD_LOCK(rnh); 18233034f43fSAlexander V. Chernikov 18243034f43fSAlexander V. Chernikov /* Delete old prefix */ 18253034f43fSAlexander V. Chernikov info.rti_ifa = NULL; 18263034f43fSAlexander V. Chernikov info.rti_flags = RTF_RNH_LOCKED; 18273034f43fSAlexander V. Chernikov 1828d54455b0SAlexander V. Chernikov error = rtrequest1_fib(RTM_DELETE, &info, NULL, fibnum); 18293034f43fSAlexander V. Chernikov if (error == 0) { 18303034f43fSAlexander V. Chernikov info.rti_ifa = ifa; 18313034f43fSAlexander V. Chernikov info.rti_flags = flags | RTF_RNH_LOCKED | 18323034f43fSAlexander V. Chernikov (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED; 18333034f43fSAlexander V. Chernikov error = rtrequest1_fib(cmd, &info, &rt, fibnum); 18343034f43fSAlexander V. Chernikov } 18353034f43fSAlexander V. Chernikov 18363034f43fSAlexander V. Chernikov RADIX_NODE_HEAD_UNLOCK(rnh); 18373034f43fSAlexander V. Chernikov } 18383034f43fSAlexander V. Chernikov 18393034f43fSAlexander V. Chernikov 18405aca0b30SLuigi Rizzo if (error == 0 && rt != NULL) { 18418071913dSRuslan Ermilov /* 18426f99b44cSBrian Somers * notify any listening routing agents of the change 18438071913dSRuslan Ermilov */ 1844d1dd20beSSam Leffler RT_LOCK(rt); 1845e440aed9SQing Li #ifdef RADIX_MPATH 1846e440aed9SQing Li /* 1847e440aed9SQing Li * in case address alias finds the first address 18488d74af36SBjoern A. Zeeb * e.g. ifconfig bge0 192.0.2.246/24 18498d74af36SBjoern A. Zeeb * e.g. ifconfig bge0 192.0.2.247/24 18508d74af36SBjoern A. Zeeb * the address set in the route is 192.0.2.246 18518d74af36SBjoern A. Zeeb * so we need to replace it with 192.0.2.247 1852e440aed9SQing Li */ 18538b07e49aSJulian Elischer if (memcmp(rt->rt_ifa->ifa_addr, 18548b07e49aSJulian Elischer ifa->ifa_addr, ifa->ifa_addr->sa_len)) { 18551099f828SRobert Watson ifa_free(rt->rt_ifa); 18561099f828SRobert Watson ifa_ref(ifa); 1857e440aed9SQing Li rt->rt_ifp = ifa->ifa_ifp; 1858e440aed9SQing Li rt->rt_ifa = ifa; 1859e440aed9SQing Li } 1860e440aed9SQing Li #endif 18616e6b3f7cSQing Li /* 18626e6b3f7cSQing Li * doing this for compatibility reasons 18636e6b3f7cSQing Li */ 18646e6b3f7cSQing Li if (cmd == RTM_ADD) { 18656e6b3f7cSQing Li ((struct sockaddr_dl *)rt->rt_gateway)->sdl_type = 18666e6b3f7cSQing Li rt->rt_ifp->if_type; 18676e6b3f7cSQing Li ((struct sockaddr_dl *)rt->rt_gateway)->sdl_index = 18686e6b3f7cSQing Li rt->rt_ifp->if_index; 18696e6b3f7cSQing Li } 18707f279720SMichael Tuexen RT_ADDREF(rt); 18717f279720SMichael Tuexen RT_UNLOCK(rt); 1872528737fdSBjoern A. Zeeb rt_newaddrmsg_fib(cmd, ifa, error, rt, fibnum); 18737f279720SMichael Tuexen RT_LOCK(rt); 18747f279720SMichael Tuexen RT_REMREF(rt); 18758071913dSRuslan Ermilov if (cmd == RTM_DELETE) { 1876b0a76b88SJulian Elischer /* 18778b07e49aSJulian Elischer * If we are deleting, and we found an entry, 18788b07e49aSJulian Elischer * then it's been removed from the tree.. 18798b07e49aSJulian Elischer * now throw it away. 1880b0a76b88SJulian Elischer */ 1881d1dd20beSSam Leffler RTFREE_LOCKED(rt); 1882d1dd20beSSam Leffler } else { 1883d1dd20beSSam Leffler if (cmd == RTM_ADD) { 1884b0a76b88SJulian Elischer /* 18858b07e49aSJulian Elischer * We just wanted to add it.. 18868b07e49aSJulian Elischer * we don't actually need a reference. 1887b0a76b88SJulian Elischer */ 18887138d65cSSam Leffler RT_REMREF(rt); 1889df8bae1dSRodney W. Grimes } 1890d1dd20beSSam Leffler RT_UNLOCK(rt); 1891d1dd20beSSam Leffler } 18928b07e49aSJulian Elischer didwork = 1; 1893df8bae1dSRodney W. Grimes } 18948b07e49aSJulian Elischer if (error) 18958b07e49aSJulian Elischer a_failure = error; 18968b07e49aSJulian Elischer } 18978b07e49aSJulian Elischer if (cmd == RTM_DELETE) { 18988b07e49aSJulian Elischer if (didwork) { 18998b07e49aSJulian Elischer error = 0; 19008b07e49aSJulian Elischer } else { 19018b07e49aSJulian Elischer /* we only give an error if it wasn't in any table */ 19028b07e49aSJulian Elischer error = ((flags & RTF_HOST) ? 19038b07e49aSJulian Elischer EHOSTUNREACH : ENETUNREACH); 19048b07e49aSJulian Elischer } 19058b07e49aSJulian Elischer } else { 19068b07e49aSJulian Elischer if (a_failure) { 19078b07e49aSJulian Elischer /* return an error if any of them failed */ 19088b07e49aSJulian Elischer error = a_failure; 19098b07e49aSJulian Elischer } 19108b07e49aSJulian Elischer } 19113ec66d6cSDavid Greenman return (error); 19123ec66d6cSDavid Greenman } 1913cb64988fSLuoqi Chen 19148b07e49aSJulian Elischer /* 19158b07e49aSJulian Elischer * Set up a routing table entry, normally 19168b07e49aSJulian Elischer * for an interface. 19178b07e49aSJulian Elischer */ 19188b07e49aSJulian Elischer int 19198b07e49aSJulian Elischer rtinit(struct ifaddr *ifa, int cmd, int flags) 19208b07e49aSJulian Elischer { 19218b07e49aSJulian Elischer struct sockaddr *dst; 1922a8498625SBjoern A. Zeeb int fib = RT_DEFAULT_FIB; 19238b07e49aSJulian Elischer 19248b07e49aSJulian Elischer if (flags & RTF_HOST) { 19258b07e49aSJulian Elischer dst = ifa->ifa_dstaddr; 19268b07e49aSJulian Elischer } else { 19278b07e49aSJulian Elischer dst = ifa->ifa_addr; 19288b07e49aSJulian Elischer } 19298b07e49aSJulian Elischer 1930b680a383SBjoern A. Zeeb switch (dst->sa_family) { 1931b680a383SBjoern A. Zeeb case AF_INET6: 1932b680a383SBjoern A. Zeeb case AF_INET: 1933b680a383SBjoern A. Zeeb /* We do support multiple FIBs. */ 19347d9b6df1SAlexander V. Chernikov fib = RT_ALL_FIBS; 1935b680a383SBjoern A. Zeeb break; 1936b680a383SBjoern A. Zeeb } 19378b07e49aSJulian Elischer return (rtinit1(ifa, cmd, flags, fib)); 19388b07e49aSJulian Elischer } 19394cbac30bSAlexander V. Chernikov 19404cbac30bSAlexander V. Chernikov /* 19414cbac30bSAlexander V. Chernikov * Announce interface address arrival/withdraw 19424cbac30bSAlexander V. Chernikov * Returns 0 on success. 19434cbac30bSAlexander V. Chernikov */ 19444cbac30bSAlexander V. Chernikov int 19454cbac30bSAlexander V. Chernikov rt_addrmsg(int cmd, struct ifaddr *ifa, int fibnum) 19464cbac30bSAlexander V. Chernikov { 19474cbac30bSAlexander V. Chernikov 19484cbac30bSAlexander V. Chernikov KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, 1949d375edc9SAlexander V. Chernikov ("unexpected cmd %d", cmd)); 19504cbac30bSAlexander V. Chernikov 1951d375edc9SAlexander V. Chernikov KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), 1952d375edc9SAlexander V. Chernikov ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); 19534cbac30bSAlexander V. Chernikov 19547f946da0SMichael Tuexen #if defined(INET) || defined(INET6) 19557f946da0SMichael Tuexen #ifdef SCTP 19567f946da0SMichael Tuexen /* 19577f946da0SMichael Tuexen * notify the SCTP stack 19587f946da0SMichael Tuexen * this will only get called when an address is added/deleted 19597f946da0SMichael Tuexen * XXX pass the ifaddr struct instead if ifa->ifa_addr... 19607f946da0SMichael Tuexen */ 19617f946da0SMichael Tuexen sctp_addr_change(ifa, cmd); 19627f946da0SMichael Tuexen #endif /* SCTP */ 19637f946da0SMichael Tuexen #endif 19644cbac30bSAlexander V. Chernikov return (rtsock_addrmsg(cmd, ifa, fibnum)); 19654cbac30bSAlexander V. Chernikov } 19664cbac30bSAlexander V. Chernikov 19674cbac30bSAlexander V. Chernikov /* 1968d375edc9SAlexander V. Chernikov * Announce route addition/removal. 19694cbac30bSAlexander V. Chernikov * Users of this function MUST validate input data BEFORE calling. 19704cbac30bSAlexander V. Chernikov * However we have to be able to handle invalid data: 19714cbac30bSAlexander V. Chernikov * if some userland app sends us "invalid" route message (invalid mask, 1972d375edc9SAlexander V. Chernikov * no dst, wrong address families, etc...) we need to pass it back 19734cbac30bSAlexander V. Chernikov * to app (and any other rtsock consumers) with rtm_errno field set to 19744cbac30bSAlexander V. Chernikov * non-zero value. 19754cbac30bSAlexander V. Chernikov * Returns 0 on success. 19764cbac30bSAlexander V. Chernikov */ 19774cbac30bSAlexander V. Chernikov int 19784cbac30bSAlexander V. Chernikov rt_routemsg(int cmd, struct ifnet *ifp, int error, struct rtentry *rt, 19794cbac30bSAlexander V. Chernikov int fibnum) 19804cbac30bSAlexander V. Chernikov { 19814cbac30bSAlexander V. Chernikov 19824cbac30bSAlexander V. Chernikov KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, 1983d375edc9SAlexander V. Chernikov ("unexpected cmd %d", cmd)); 19844cbac30bSAlexander V. Chernikov 1985d375edc9SAlexander V. Chernikov KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), 1986d375edc9SAlexander V. Chernikov ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); 19874cbac30bSAlexander V. Chernikov 19884cbac30bSAlexander V. Chernikov KASSERT(rt_key(rt) != NULL, (":%s: rt_key must be supplied", __func__)); 19894cbac30bSAlexander V. Chernikov 19904cbac30bSAlexander V. Chernikov return (rtsock_routemsg(cmd, ifp, error, rt, fibnum)); 19914cbac30bSAlexander V. Chernikov } 19924cbac30bSAlexander V. Chernikov 19934cbac30bSAlexander V. Chernikov void 19944cbac30bSAlexander V. Chernikov rt_newaddrmsg(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt) 19954cbac30bSAlexander V. Chernikov { 19964cbac30bSAlexander V. Chernikov 19974cbac30bSAlexander V. Chernikov rt_newaddrmsg_fib(cmd, ifa, error, rt, RT_ALL_FIBS); 19984cbac30bSAlexander V. Chernikov } 19994cbac30bSAlexander V. Chernikov 20004cbac30bSAlexander V. Chernikov /* 20014cbac30bSAlexander V. Chernikov * This is called to generate messages from the routing socket 20024cbac30bSAlexander V. Chernikov * indicating a network interface has had addresses associated with it. 20034cbac30bSAlexander V. Chernikov */ 20044cbac30bSAlexander V. Chernikov void 20054cbac30bSAlexander V. Chernikov rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt, 20064cbac30bSAlexander V. Chernikov int fibnum) 20074cbac30bSAlexander V. Chernikov { 20084cbac30bSAlexander V. Chernikov 20094cbac30bSAlexander V. Chernikov KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, 20104cbac30bSAlexander V. Chernikov ("unexpected cmd %u", cmd)); 2011d375edc9SAlexander V. Chernikov KASSERT(fibnum == RT_ALL_FIBS || (fibnum >= 0 && fibnum < rt_numfibs), 2012d375edc9SAlexander V. Chernikov ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); 20134cbac30bSAlexander V. Chernikov 20144cbac30bSAlexander V. Chernikov if (cmd == RTM_ADD) { 20154cbac30bSAlexander V. Chernikov rt_addrmsg(cmd, ifa, fibnum); 20164cbac30bSAlexander V. Chernikov if (rt != NULL) 20174cbac30bSAlexander V. Chernikov rt_routemsg(cmd, ifa->ifa_ifp, error, rt, fibnum); 20184cbac30bSAlexander V. Chernikov } else { 20194cbac30bSAlexander V. Chernikov if (rt != NULL) 20204cbac30bSAlexander V. Chernikov rt_routemsg(cmd, ifa->ifa_ifp, error, rt, fibnum); 20214cbac30bSAlexander V. Chernikov rt_addrmsg(cmd, ifa, fibnum); 20224cbac30bSAlexander V. Chernikov } 20234cbac30bSAlexander V. Chernikov } 20244cbac30bSAlexander V. Chernikov 2025