1df8bae1dSRodney W. Grimes /* 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 * 3. All advertising materials mentioning features or use of this software 14df8bae1dSRodney W. Grimes * must display the following acknowledgement: 15df8bae1dSRodney W. Grimes * This product includes software developed by the University of 16df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 17df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 33df8bae1dSRodney W. Grimes * @(#)route.c 8.2 (Berkeley) 11/15/93 34cb64988fSLuoqi Chen * $Id: route.c,v 1.51 1999/01/27 22:42:14 dillon Exp $ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 371d5e9e22SEivind Eklund #include "opt_inet.h" 384bd49128SPeter Wemm #include "opt_mrouting.h" 394bd49128SPeter Wemm 40df8bae1dSRodney W. Grimes #include <sys/param.h> 41df8bae1dSRodney W. Grimes #include <sys/systm.h> 424d1d4912SBruce Evans #include <sys/malloc.h> 43df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 44df8bae1dSRodney W. Grimes #include <sys/socket.h> 45df8bae1dSRodney W. Grimes #include <sys/domain.h> 46cb64988fSLuoqi Chen #include <sys/kernel.h> 47df8bae1dSRodney W. Grimes 48df8bae1dSRodney W. Grimes #include <net/if.h> 49df8bae1dSRodney W. Grimes #include <net/route.h> 50df8bae1dSRodney W. Grimes 51df8bae1dSRodney W. Grimes #include <netinet/in.h> 52b5e8ce9fSBruce Evans #include <netinet/ip_mroute.h> 53df8bae1dSRodney W. Grimes 54df8bae1dSRodney W. Grimes #define SA(p) ((struct sockaddr *)(p)) 55df8bae1dSRodney W. Grimes 5628f8db14SBruce Evans struct route_cb route_cb; 57f708ef1bSPoul-Henning Kamp static struct rtstat rtstat; 5828f8db14SBruce Evans struct radix_node_head *rt_tables[AF_MAX+1]; 5928f8db14SBruce Evans 60f708ef1bSPoul-Henning Kamp static int rttrash; /* routes not in table but not freed */ 61df8bae1dSRodney W. Grimes 62f708ef1bSPoul-Henning Kamp static void rt_maskedcopy __P((struct sockaddr *, 63f708ef1bSPoul-Henning Kamp struct sockaddr *, struct sockaddr *)); 64f708ef1bSPoul-Henning Kamp static void rtable_init __P((void **)); 65f708ef1bSPoul-Henning Kamp 66f708ef1bSPoul-Henning Kamp static void 67df8bae1dSRodney W. Grimes rtable_init(table) 68df8bae1dSRodney W. Grimes void **table; 69df8bae1dSRodney W. Grimes { 70df8bae1dSRodney W. Grimes struct domain *dom; 71df8bae1dSRodney W. Grimes for (dom = domains; dom; dom = dom->dom_next) 72df8bae1dSRodney W. Grimes if (dom->dom_rtattach) 73df8bae1dSRodney W. Grimes dom->dom_rtattach(&table[dom->dom_family], 74df8bae1dSRodney W. Grimes dom->dom_rtoffset); 75df8bae1dSRodney W. Grimes } 76df8bae1dSRodney W. Grimes 77df8bae1dSRodney W. Grimes void 78df8bae1dSRodney W. Grimes route_init() 79df8bae1dSRodney W. Grimes { 80df8bae1dSRodney W. Grimes rn_init(); /* initialize all zeroes, all ones, mask table */ 81df8bae1dSRodney W. Grimes rtable_init((void **)rt_tables); 82df8bae1dSRodney W. Grimes } 83df8bae1dSRodney W. Grimes 84df8bae1dSRodney W. Grimes /* 85df8bae1dSRodney W. Grimes * Packet routing routines. 86df8bae1dSRodney W. Grimes */ 87df8bae1dSRodney W. Grimes void 88df8bae1dSRodney W. Grimes rtalloc(ro) 89df8bae1dSRodney W. Grimes register struct route *ro; 90df8bae1dSRodney W. Grimes { 91df8bae1dSRodney W. Grimes if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 92df8bae1dSRodney W. Grimes return; /* XXX */ 93995add1aSGarrett Wollman ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL); 94df8bae1dSRodney W. Grimes } 95df8bae1dSRodney W. Grimes 96652082e6SGarrett Wollman void 97652082e6SGarrett Wollman rtalloc_ign(ro, ignore) 98652082e6SGarrett Wollman register struct route *ro; 99652082e6SGarrett Wollman u_long ignore; 100652082e6SGarrett Wollman { 101652082e6SGarrett Wollman if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 102652082e6SGarrett Wollman return; /* XXX */ 103652082e6SGarrett Wollman ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore); 104652082e6SGarrett Wollman } 105652082e6SGarrett Wollman 106b0a76b88SJulian Elischer /* 107b0a76b88SJulian Elischer * Look up the route that matches the address given 108b0a76b88SJulian Elischer * Or, at least try.. Create a cloned route if needed. 109b0a76b88SJulian Elischer */ 110df8bae1dSRodney W. Grimes struct rtentry * 111995add1aSGarrett Wollman rtalloc1(dst, report, ignflags) 112df8bae1dSRodney W. Grimes register struct sockaddr *dst; 113df8bae1dSRodney W. Grimes int report; 114995add1aSGarrett Wollman u_long ignflags; 115df8bae1dSRodney W. Grimes { 116df8bae1dSRodney W. Grimes register struct radix_node_head *rnh = rt_tables[dst->sa_family]; 117df8bae1dSRodney W. Grimes register struct rtentry *rt; 118df8bae1dSRodney W. Grimes register struct radix_node *rn; 119df8bae1dSRodney W. Grimes struct rtentry *newrt = 0; 120df8bae1dSRodney W. Grimes struct rt_addrinfo info; 121995add1aSGarrett Wollman u_long nflags; 122df8bae1dSRodney W. Grimes int s = splnet(), err = 0, msgtype = RTM_MISS; 123df8bae1dSRodney W. Grimes 124b0a76b88SJulian Elischer /* 125b0a76b88SJulian Elischer * Look up the address in the table for that Address Family 126b0a76b88SJulian Elischer */ 127df8bae1dSRodney W. Grimes if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) && 128df8bae1dSRodney W. Grimes ((rn->rn_flags & RNF_ROOT) == 0)) { 129b0a76b88SJulian Elischer /* 130b0a76b88SJulian Elischer * If we find it and it's not the root node, then 131b0a76b88SJulian Elischer * get a refernce on the rtentry associated. 132b0a76b88SJulian Elischer */ 133df8bae1dSRodney W. Grimes newrt = rt = (struct rtentry *)rn; 134995add1aSGarrett Wollman nflags = rt->rt_flags & ~ignflags; 135995add1aSGarrett Wollman if (report && (nflags & (RTF_CLONING | RTF_PRCLONING))) { 136b0a76b88SJulian Elischer /* 137b0a76b88SJulian Elischer * We are apparently adding (report = 0 in delete). 138b0a76b88SJulian Elischer * If it requires that it be cloned, do so. 139b0a76b88SJulian Elischer * (This implies it wasn't a HOST route.) 140b0a76b88SJulian Elischer */ 141df8bae1dSRodney W. Grimes err = rtrequest(RTM_RESOLVE, dst, SA(0), 142df8bae1dSRodney W. Grimes SA(0), 0, &newrt); 143df8bae1dSRodney W. Grimes if (err) { 144b0a76b88SJulian Elischer /* 145b0a76b88SJulian Elischer * If the cloning didn't succeed, maybe 146b0a76b88SJulian Elischer * what we have will do. Return that. 147b0a76b88SJulian Elischer */ 148df8bae1dSRodney W. Grimes newrt = rt; 149df8bae1dSRodney W. Grimes rt->rt_refcnt++; 150df8bae1dSRodney W. Grimes goto miss; 151df8bae1dSRodney W. Grimes } 152df8bae1dSRodney W. Grimes if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { 153b0a76b88SJulian Elischer /* 154b0a76b88SJulian Elischer * If the new route specifies it be 155b0a76b88SJulian Elischer * externally resolved, then go do that. 156b0a76b88SJulian Elischer */ 157df8bae1dSRodney W. Grimes msgtype = RTM_RESOLVE; 158df8bae1dSRodney W. Grimes goto miss; 159df8bae1dSRodney W. Grimes } 160df8bae1dSRodney W. Grimes } else 161df8bae1dSRodney W. Grimes rt->rt_refcnt++; 162df8bae1dSRodney W. Grimes } else { 163b0a76b88SJulian Elischer /* 164b0a76b88SJulian Elischer * Either we hit the root or couldn't find any match, 165b0a76b88SJulian Elischer * Which basically means 166b0a76b88SJulian Elischer * "caint get there frm here" 167b0a76b88SJulian Elischer */ 168df8bae1dSRodney W. Grimes rtstat.rts_unreach++; 169df8bae1dSRodney W. Grimes miss: if (report) { 170b0a76b88SJulian Elischer /* 171b0a76b88SJulian Elischer * If required, report the failure to the supervising 172b0a76b88SJulian Elischer * Authorities. 173b0a76b88SJulian Elischer * For a delete, this is not an error. (report == 0) 174b0a76b88SJulian Elischer */ 175df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 176df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 177df8bae1dSRodney W. Grimes rt_missmsg(msgtype, &info, 0, err); 178df8bae1dSRodney W. Grimes } 179df8bae1dSRodney W. Grimes } 180df8bae1dSRodney W. Grimes splx(s); 181df8bae1dSRodney W. Grimes return (newrt); 182df8bae1dSRodney W. Grimes } 183df8bae1dSRodney W. Grimes 184499676dfSJulian Elischer /* 185499676dfSJulian Elischer * Remove a reference count from an rtentry. 186499676dfSJulian Elischer * If the count gets low enough, take it out of the routing table 187499676dfSJulian Elischer */ 188df8bae1dSRodney W. Grimes void 189df8bae1dSRodney W. Grimes rtfree(rt) 190df8bae1dSRodney W. Grimes register struct rtentry *rt; 191df8bae1dSRodney W. Grimes { 192499676dfSJulian Elischer /* 193499676dfSJulian Elischer * find the tree for that address family 194499676dfSJulian Elischer */ 1955c2dae8eSGarrett Wollman register struct radix_node_head *rnh = 1965c2dae8eSGarrett Wollman rt_tables[rt_key(rt)->sa_family]; 197df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 198df8bae1dSRodney W. Grimes 1993545b048SGarrett Wollman if (rt == 0 || rnh == 0) 200df8bae1dSRodney W. Grimes panic("rtfree"); 201499676dfSJulian Elischer 202499676dfSJulian Elischer /* 203499676dfSJulian Elischer * decrement the reference count by one and if it reaches 0, 204499676dfSJulian Elischer * and there is a close function defined, call the close function 205499676dfSJulian Elischer */ 206df8bae1dSRodney W. Grimes rt->rt_refcnt--; 2075c2dae8eSGarrett Wollman if(rnh->rnh_close && rt->rt_refcnt == 0) { 2085c2dae8eSGarrett Wollman rnh->rnh_close((struct radix_node *)rt, rnh); 2095c2dae8eSGarrett Wollman } 210499676dfSJulian Elischer 211499676dfSJulian Elischer /* 212499676dfSJulian Elischer * If we are no longer "up" (and ref == 0) 213499676dfSJulian Elischer * then we can free the resources associated 214499676dfSJulian Elischer * with the route. 215499676dfSJulian Elischer */ 216df8bae1dSRodney W. Grimes if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { 217df8bae1dSRodney W. Grimes if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 218df8bae1dSRodney W. Grimes panic ("rtfree 2"); 219499676dfSJulian Elischer /* 220499676dfSJulian Elischer * the rtentry must have been removed from the routing table 221499676dfSJulian Elischer * so it is represented in rttrash.. remove that now. 222499676dfSJulian Elischer */ 223df8bae1dSRodney W. Grimes rttrash--; 224499676dfSJulian Elischer 225499676dfSJulian Elischer #ifdef DIAGNOSTIC 226df8bae1dSRodney W. Grimes if (rt->rt_refcnt < 0) { 227623ae52eSPoul-Henning Kamp printf("rtfree: %p not freed (neg refs)\n", rt); 228df8bae1dSRodney W. Grimes return; 229df8bae1dSRodney W. Grimes } 230499676dfSJulian Elischer #endif 231499676dfSJulian Elischer 232499676dfSJulian Elischer /* 233499676dfSJulian Elischer * release references on items we hold them on.. 234499676dfSJulian Elischer * e.g other routes and ifaddrs. 235499676dfSJulian Elischer */ 236499676dfSJulian Elischer if((ifa = rt->rt_ifa)) 237df8bae1dSRodney W. Grimes IFAFREE(ifa); 238771edb14SGarrett Wollman if (rt->rt_parent) { 239771edb14SGarrett Wollman RTFREE(rt->rt_parent); 240771edb14SGarrett Wollman } 241499676dfSJulian Elischer 242499676dfSJulian Elischer /* 243499676dfSJulian Elischer * The key is separatly alloc'd so free it (see rt_setgate()). 244499676dfSJulian Elischer * This also frees the gateway, as they are always malloc'd 245499676dfSJulian Elischer * together. 246499676dfSJulian Elischer */ 247df8bae1dSRodney W. Grimes Free(rt_key(rt)); 248499676dfSJulian Elischer 249499676dfSJulian Elischer /* 250499676dfSJulian Elischer * and the rtentry itself of course 251499676dfSJulian Elischer */ 252df8bae1dSRodney W. Grimes Free(rt); 253df8bae1dSRodney W. Grimes } 254df8bae1dSRodney W. Grimes } 255df8bae1dSRodney W. Grimes 256df8bae1dSRodney W. Grimes void 257df8bae1dSRodney W. Grimes ifafree(ifa) 258df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 259df8bae1dSRodney W. Grimes { 260df8bae1dSRodney W. Grimes if (ifa == NULL) 261df8bae1dSRodney W. Grimes panic("ifafree"); 262df8bae1dSRodney W. Grimes if (ifa->ifa_refcnt == 0) 263df8bae1dSRodney W. Grimes free(ifa, M_IFADDR); 264df8bae1dSRodney W. Grimes else 265df8bae1dSRodney W. Grimes ifa->ifa_refcnt--; 266df8bae1dSRodney W. Grimes } 267df8bae1dSRodney W. Grimes 268df8bae1dSRodney W. Grimes /* 269df8bae1dSRodney W. Grimes * Force a routing table entry to the specified 270df8bae1dSRodney W. Grimes * destination to go through the given gateway. 271df8bae1dSRodney W. Grimes * Normally called as a result of a routing redirect 272df8bae1dSRodney W. Grimes * message from the network layer. 273df8bae1dSRodney W. Grimes * 274df8bae1dSRodney W. Grimes * N.B.: must be called at splnet 275df8bae1dSRodney W. Grimes * 276df8bae1dSRodney W. Grimes */ 27726f9a767SRodney W. Grimes void 278df8bae1dSRodney W. Grimes rtredirect(dst, gateway, netmask, flags, src, rtp) 279df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway, *netmask, *src; 280df8bae1dSRodney W. Grimes int flags; 281df8bae1dSRodney W. Grimes struct rtentry **rtp; 282df8bae1dSRodney W. Grimes { 283df8bae1dSRodney W. Grimes register struct rtentry *rt; 284df8bae1dSRodney W. Grimes int error = 0; 285df8bae1dSRodney W. Grimes short *stat = 0; 286df8bae1dSRodney W. Grimes struct rt_addrinfo info; 287df8bae1dSRodney W. Grimes struct ifaddr *ifa; 288df8bae1dSRodney W. Grimes 289df8bae1dSRodney W. Grimes /* verify the gateway is directly reachable */ 290df8bae1dSRodney W. Grimes if ((ifa = ifa_ifwithnet(gateway)) == 0) { 291df8bae1dSRodney W. Grimes error = ENETUNREACH; 292df8bae1dSRodney W. Grimes goto out; 293df8bae1dSRodney W. Grimes } 294995add1aSGarrett Wollman rt = rtalloc1(dst, 0, 0UL); 295df8bae1dSRodney W. Grimes /* 296df8bae1dSRodney W. Grimes * If the redirect isn't from our current router for this dst, 297df8bae1dSRodney W. Grimes * it's either old or wrong. If it redirects us to ourselves, 298df8bae1dSRodney W. Grimes * we have a routing loop, perhaps as a result of an interface 299df8bae1dSRodney W. Grimes * going down recently. 300df8bae1dSRodney W. Grimes */ 301df8bae1dSRodney W. Grimes #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) 302df8bae1dSRodney W. Grimes if (!(flags & RTF_DONE) && rt && 303df8bae1dSRodney W. Grimes (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa)) 304df8bae1dSRodney W. Grimes error = EINVAL; 305df8bae1dSRodney W. Grimes else if (ifa_ifwithaddr(gateway)) 306df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 307df8bae1dSRodney W. Grimes if (error) 308df8bae1dSRodney W. Grimes goto done; 309df8bae1dSRodney W. Grimes /* 310df8bae1dSRodney W. Grimes * Create a new entry if we just got back a wildcard entry 311df8bae1dSRodney W. Grimes * or the the lookup failed. This is necessary for hosts 312df8bae1dSRodney W. Grimes * which use routing redirects generated by smart gateways 313df8bae1dSRodney W. Grimes * to dynamically build the routing tables. 314df8bae1dSRodney W. Grimes */ 315df8bae1dSRodney W. Grimes if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) 316df8bae1dSRodney W. Grimes goto create; 317df8bae1dSRodney W. Grimes /* 318df8bae1dSRodney W. Grimes * Don't listen to the redirect if it's 319df8bae1dSRodney W. Grimes * for a route to an interface. 320df8bae1dSRodney W. Grimes */ 321df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 322df8bae1dSRodney W. Grimes if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 323df8bae1dSRodney W. Grimes /* 324df8bae1dSRodney W. Grimes * Changing from route to net => route to host. 325df8bae1dSRodney W. Grimes * Create new route, rather than smashing route to net. 326df8bae1dSRodney W. Grimes */ 327df8bae1dSRodney W. Grimes create: 328df8bae1dSRodney W. Grimes flags |= RTF_GATEWAY | RTF_DYNAMIC; 329df8bae1dSRodney W. Grimes error = rtrequest((int)RTM_ADD, dst, gateway, 330df8bae1dSRodney W. Grimes netmask, flags, 331df8bae1dSRodney W. Grimes (struct rtentry **)0); 332df8bae1dSRodney W. Grimes stat = &rtstat.rts_dynamic; 333df8bae1dSRodney W. Grimes } else { 334df8bae1dSRodney W. Grimes /* 335df8bae1dSRodney W. Grimes * Smash the current notion of the gateway to 336df8bae1dSRodney W. Grimes * this destination. Should check about netmask!!! 337df8bae1dSRodney W. Grimes */ 338df8bae1dSRodney W. Grimes rt->rt_flags |= RTF_MODIFIED; 339df8bae1dSRodney W. Grimes flags |= RTF_MODIFIED; 340df8bae1dSRodney W. Grimes stat = &rtstat.rts_newgateway; 341499676dfSJulian Elischer /* 342499676dfSJulian Elischer * add the key and gateway (in one malloc'd chunk). 343499676dfSJulian Elischer */ 344df8bae1dSRodney W. Grimes rt_setgate(rt, rt_key(rt), gateway); 345df8bae1dSRodney W. Grimes } 346df8bae1dSRodney W. Grimes } else 347df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 348df8bae1dSRodney W. Grimes done: 349df8bae1dSRodney W. Grimes if (rt) { 350df8bae1dSRodney W. Grimes if (rtp && !error) 351df8bae1dSRodney W. Grimes *rtp = rt; 352df8bae1dSRodney W. Grimes else 353df8bae1dSRodney W. Grimes rtfree(rt); 354df8bae1dSRodney W. Grimes } 355df8bae1dSRodney W. Grimes out: 356df8bae1dSRodney W. Grimes if (error) 357df8bae1dSRodney W. Grimes rtstat.rts_badredirect++; 358df8bae1dSRodney W. Grimes else if (stat != NULL) 359df8bae1dSRodney W. Grimes (*stat)++; 360df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 361df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 362df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = gateway; 363df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = netmask; 364df8bae1dSRodney W. Grimes info.rti_info[RTAX_AUTHOR] = src; 365df8bae1dSRodney W. Grimes rt_missmsg(RTM_REDIRECT, &info, flags, error); 366df8bae1dSRodney W. Grimes } 367df8bae1dSRodney W. Grimes 368df8bae1dSRodney W. Grimes /* 369df8bae1dSRodney W. Grimes * Routing table ioctl interface. 370df8bae1dSRodney W. Grimes */ 371df8bae1dSRodney W. Grimes int 372df8bae1dSRodney W. Grimes rtioctl(req, data, p) 373df8bae1dSRodney W. Grimes int req; 374df8bae1dSRodney W. Grimes caddr_t data; 375df8bae1dSRodney W. Grimes struct proc *p; 376df8bae1dSRodney W. Grimes { 377623ae52eSPoul-Henning Kamp #ifdef INET 378f0068c4aSGarrett Wollman /* Multicast goop, grrr... */ 379af32e59fSBruce Evans #ifdef MROUTING 380af32e59fSBruce Evans return mrt_ioctl(req, data); 381af32e59fSBruce Evans #else 382e4ca4481SStefan Eßer return mrt_ioctl(req, data, p); 383af32e59fSBruce Evans #endif 384623ae52eSPoul-Henning Kamp #else /* INET */ 385623ae52eSPoul-Henning Kamp return ENXIO; 386623ae52eSPoul-Henning Kamp #endif /* INET */ 387df8bae1dSRodney W. Grimes } 388df8bae1dSRodney W. Grimes 389df8bae1dSRodney W. Grimes struct ifaddr * 390df8bae1dSRodney W. Grimes ifa_ifwithroute(flags, dst, gateway) 391df8bae1dSRodney W. Grimes int flags; 392df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway; 393df8bae1dSRodney W. Grimes { 394df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 395df8bae1dSRodney W. Grimes if ((flags & RTF_GATEWAY) == 0) { 396df8bae1dSRodney W. Grimes /* 397df8bae1dSRodney W. Grimes * If we are adding a route to an interface, 398df8bae1dSRodney W. Grimes * and the interface is a pt to pt link 399df8bae1dSRodney W. Grimes * we should search for the destination 400df8bae1dSRodney W. Grimes * as our clue to the interface. Otherwise 401df8bae1dSRodney W. Grimes * we can use the local address. 402df8bae1dSRodney W. Grimes */ 403df8bae1dSRodney W. Grimes ifa = 0; 4045df72964SGarrett Wollman if (flags & RTF_HOST) { 405df8bae1dSRodney W. Grimes ifa = ifa_ifwithdstaddr(dst); 4065df72964SGarrett Wollman } 407df8bae1dSRodney W. Grimes if (ifa == 0) 408df8bae1dSRodney W. Grimes ifa = ifa_ifwithaddr(gateway); 409df8bae1dSRodney W. Grimes } else { 410df8bae1dSRodney W. Grimes /* 411df8bae1dSRodney W. Grimes * If we are adding a route to a remote net 412df8bae1dSRodney W. Grimes * or host, the gateway may still be on the 413df8bae1dSRodney W. Grimes * other end of a pt to pt link. 414df8bae1dSRodney W. Grimes */ 415df8bae1dSRodney W. Grimes ifa = ifa_ifwithdstaddr(gateway); 416df8bae1dSRodney W. Grimes } 417df8bae1dSRodney W. Grimes if (ifa == 0) 418df8bae1dSRodney W. Grimes ifa = ifa_ifwithnet(gateway); 419df8bae1dSRodney W. Grimes if (ifa == 0) { 420995add1aSGarrett Wollman struct rtentry *rt = rtalloc1(dst, 0, 0UL); 421df8bae1dSRodney W. Grimes if (rt == 0) 422df8bae1dSRodney W. Grimes return (0); 423df8bae1dSRodney W. Grimes rt->rt_refcnt--; 424df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) == 0) 425df8bae1dSRodney W. Grimes return (0); 426df8bae1dSRodney W. Grimes } 427df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family != dst->sa_family) { 428df8bae1dSRodney W. Grimes struct ifaddr *oifa = ifa; 429df8bae1dSRodney W. Grimes ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); 430df8bae1dSRodney W. Grimes if (ifa == 0) 431df8bae1dSRodney W. Grimes ifa = oifa; 432df8bae1dSRodney W. Grimes } 433df8bae1dSRodney W. Grimes return (ifa); 434df8bae1dSRodney W. Grimes } 435df8bae1dSRodney W. Grimes 436df8bae1dSRodney W. Grimes #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 437df8bae1dSRodney W. Grimes 438514ede09SBruce Evans static int rt_fixdelete __P((struct radix_node *, void *)); 439514ede09SBruce Evans static int rt_fixchange __P((struct radix_node *, void *)); 440cd02a0b7SGarrett Wollman 441cd02a0b7SGarrett Wollman struct rtfc_arg { 442cd02a0b7SGarrett Wollman struct rtentry *rt0; 443cd02a0b7SGarrett Wollman struct radix_node_head *rnh; 444cd02a0b7SGarrett Wollman }; 44518e1f1f1SGarrett Wollman 446b0a76b88SJulian Elischer /* 447b0a76b88SJulian Elischer * Do appropriate manipulations of a routing tree given 448b0a76b88SJulian Elischer * all the bits of info needed 449b0a76b88SJulian Elischer */ 450df8bae1dSRodney W. Grimes int 451df8bae1dSRodney W. Grimes rtrequest(req, dst, gateway, netmask, flags, ret_nrt) 452df8bae1dSRodney W. Grimes int req, flags; 453df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway, *netmask; 454df8bae1dSRodney W. Grimes struct rtentry **ret_nrt; 455df8bae1dSRodney W. Grimes { 456df8bae1dSRodney W. Grimes int s = splnet(); int error = 0; 457df8bae1dSRodney W. Grimes register struct rtentry *rt; 458df8bae1dSRodney W. Grimes register struct radix_node *rn; 459df8bae1dSRodney W. Grimes register struct radix_node_head *rnh; 460df8bae1dSRodney W. Grimes struct ifaddr *ifa; 461df8bae1dSRodney W. Grimes struct sockaddr *ndst; 462df8bae1dSRodney W. Grimes #define senderr(x) { error = x ; goto bad; } 463df8bae1dSRodney W. Grimes 464b0a76b88SJulian Elischer /* 465b0a76b88SJulian Elischer * Find the correct routing tree to use for this Address Family 466b0a76b88SJulian Elischer */ 467df8bae1dSRodney W. Grimes if ((rnh = rt_tables[dst->sa_family]) == 0) 468df8bae1dSRodney W. Grimes senderr(ESRCH); 469b0a76b88SJulian Elischer /* 470b0a76b88SJulian Elischer * If we are adding a host route then we don't want to put 471b0a76b88SJulian Elischer * a netmask in the tree 472b0a76b88SJulian Elischer */ 473df8bae1dSRodney W. Grimes if (flags & RTF_HOST) 474df8bae1dSRodney W. Grimes netmask = 0; 475df8bae1dSRodney W. Grimes switch (req) { 476df8bae1dSRodney W. Grimes case RTM_DELETE: 477b0a76b88SJulian Elischer /* 478b0a76b88SJulian Elischer * Remove the item from the tree and return it. 479b0a76b88SJulian Elischer * Complain if it is not there and do no more processing. 480b0a76b88SJulian Elischer */ 481df8bae1dSRodney W. Grimes if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == 0) 482df8bae1dSRodney W. Grimes senderr(ESRCH); 483df8bae1dSRodney W. Grimes if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 484df8bae1dSRodney W. Grimes panic ("rtrequest delete"); 485df8bae1dSRodney W. Grimes rt = (struct rtentry *)rn; 486c2bed6a3SGarrett Wollman 487c2bed6a3SGarrett Wollman /* 488c2bed6a3SGarrett Wollman * Now search what's left of the subtree for any cloned 489c2bed6a3SGarrett Wollman * routes which might have been formed from this node. 490c2bed6a3SGarrett Wollman */ 4913545b048SGarrett Wollman if ((rt->rt_flags & RTF_PRCLONING) && netmask) { 492c2bed6a3SGarrett Wollman rnh->rnh_walktree_from(rnh, dst, netmask, 493c2bed6a3SGarrett Wollman rt_fixdelete, rt); 494c2bed6a3SGarrett Wollman } 4953545b048SGarrett Wollman 496b0a76b88SJulian Elischer /* 497b0a76b88SJulian Elischer * Remove any external references we may have. 498b0a76b88SJulian Elischer * This might result in another rtentry being freed if 499dc733423SDag-Erling Smørgrav * we held its last reference. 500b0a76b88SJulian Elischer */ 5016ac3b69dSBill Fenner if (rt->rt_gwroute) { 502b0a76b88SJulian Elischer rt = rt->rt_gwroute; 503b0a76b88SJulian Elischer RTFREE(rt); 5046ac3b69dSBill Fenner (rt = (struct rtentry *)rn)->rt_gwroute = 0; 5056ac3b69dSBill Fenner } 5066ac3b69dSBill Fenner 5073545b048SGarrett Wollman /* 5083545b048SGarrett Wollman * NB: RTF_UP must be set during the search above, 5093545b048SGarrett Wollman * because we might delete the last ref, causing 5103545b048SGarrett Wollman * rt to get freed prematurely. 511499676dfSJulian Elischer * eh? then why not just add a reference? 512499676dfSJulian Elischer * I'm not sure how RTF_UP helps matters. (JRE) 5133545b048SGarrett Wollman */ 5143545b048SGarrett Wollman rt->rt_flags &= ~RTF_UP; 5153545b048SGarrett Wollman 516b0a76b88SJulian Elischer /* 517499676dfSJulian Elischer * give the protocol a chance to keep things in sync. 518b0a76b88SJulian Elischer */ 519df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) 520df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 521499676dfSJulian Elischer 522b0a76b88SJulian Elischer /* 523499676dfSJulian Elischer * one more rtentry floating around that is not 524499676dfSJulian Elischer * linked to the routing table. 525499676dfSJulian Elischer */ 526499676dfSJulian Elischer rttrash++; 527499676dfSJulian Elischer 528499676dfSJulian Elischer /* 529499676dfSJulian Elischer * If the caller wants it, then it can have it, 530499676dfSJulian Elischer * but it's up to it to free the rtentry as we won't be 531499676dfSJulian Elischer * doing it. 532b0a76b88SJulian Elischer */ 533df8bae1dSRodney W. Grimes if (ret_nrt) 534df8bae1dSRodney W. Grimes *ret_nrt = rt; 535df8bae1dSRodney W. Grimes else if (rt->rt_refcnt <= 0) { 536b0a76b88SJulian Elischer rt->rt_refcnt++; /* make a 1->0 transition */ 537df8bae1dSRodney W. Grimes rtfree(rt); 538df8bae1dSRodney W. Grimes } 539df8bae1dSRodney W. Grimes break; 540df8bae1dSRodney W. Grimes 541df8bae1dSRodney W. Grimes case RTM_RESOLVE: 542df8bae1dSRodney W. Grimes if (ret_nrt == 0 || (rt = *ret_nrt) == 0) 543df8bae1dSRodney W. Grimes senderr(EINVAL); 544df8bae1dSRodney W. Grimes ifa = rt->rt_ifa; 5453682d2baSDavid Greenman flags = rt->rt_flags & 5463682d2baSDavid Greenman ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC); 547995add1aSGarrett Wollman flags |= RTF_WASCLONED; 548df8bae1dSRodney W. Grimes gateway = rt->rt_gateway; 549df8bae1dSRodney W. Grimes if ((netmask = rt->rt_genmask) == 0) 550df8bae1dSRodney W. Grimes flags |= RTF_HOST; 551df8bae1dSRodney W. Grimes goto makeroute; 552df8bae1dSRodney W. Grimes 553df8bae1dSRodney W. Grimes case RTM_ADD: 5545df72964SGarrett Wollman if ((flags & RTF_GATEWAY) && !gateway) 5555df72964SGarrett Wollman panic("rtrequest: GATEWAY but no gateway"); 5565df72964SGarrett Wollman 557df8bae1dSRodney W. Grimes if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) 558df8bae1dSRodney W. Grimes senderr(ENETUNREACH); 5595df72964SGarrett Wollman 560df8bae1dSRodney W. Grimes makeroute: 561df8bae1dSRodney W. Grimes R_Malloc(rt, struct rtentry *, sizeof(*rt)); 562df8bae1dSRodney W. Grimes if (rt == 0) 563df8bae1dSRodney W. Grimes senderr(ENOBUFS); 564df8bae1dSRodney W. Grimes Bzero(rt, sizeof(*rt)); 565df8bae1dSRodney W. Grimes rt->rt_flags = RTF_UP | flags; 566499676dfSJulian Elischer /* 567499676dfSJulian Elischer * Add the gateway. Possibly re-malloc-ing the storage for it 568499676dfSJulian Elischer * also add the rt_gwroute if possible. 569499676dfSJulian Elischer */ 570831a80b0SMatthew Dillon if ((error = rt_setgate(rt, dst, gateway)) != 0) { 571df8bae1dSRodney W. Grimes Free(rt); 572704b0666SBill Fenner senderr(error); 573df8bae1dSRodney W. Grimes } 574499676dfSJulian Elischer 575499676dfSJulian Elischer /* 576499676dfSJulian Elischer * point to the (possibly newly malloc'd) dest address. 577499676dfSJulian Elischer */ 578df8bae1dSRodney W. Grimes ndst = rt_key(rt); 579499676dfSJulian Elischer 580499676dfSJulian Elischer /* 581499676dfSJulian Elischer * make sure it contains the value we want (masked if needed). 582499676dfSJulian Elischer */ 583df8bae1dSRodney W. Grimes if (netmask) { 584df8bae1dSRodney W. Grimes rt_maskedcopy(dst, ndst, netmask); 585df8bae1dSRodney W. Grimes } else 586df8bae1dSRodney W. Grimes Bcopy(dst, ndst, dst->sa_len); 5878e718bb4SGarrett Wollman 5888e718bb4SGarrett Wollman /* 589499676dfSJulian Elischer * Note that we now have a reference to the ifa. 5908e718bb4SGarrett Wollman * This moved from below so that rnh->rnh_addaddr() can 591499676dfSJulian Elischer * examine the ifa and ifa->ifa_ifp if it so desires. 5928e718bb4SGarrett Wollman */ 5938e718bb4SGarrett Wollman ifa->ifa_refcnt++; 5948e718bb4SGarrett Wollman rt->rt_ifa = ifa; 5958e718bb4SGarrett Wollman rt->rt_ifp = ifa->ifa_ifp; 5968e718bb4SGarrett Wollman 597df8bae1dSRodney W. Grimes rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask, 598df8bae1dSRodney W. Grimes rnh, rt->rt_nodes); 599df8bae1dSRodney W. Grimes if (rn == 0) { 600aca1a47cSGarrett Wollman struct rtentry *rt2; 601aca1a47cSGarrett Wollman /* 602aca1a47cSGarrett Wollman * Uh-oh, we already have one of these in the tree. 603aca1a47cSGarrett Wollman * We do a special hack: if the route that's already 604aca1a47cSGarrett Wollman * there was generated by the protocol-cloning 605aca1a47cSGarrett Wollman * mechanism, then we just blow it away and retry 606aca1a47cSGarrett Wollman * the insertion of the new one. 607aca1a47cSGarrett Wollman */ 608aca1a47cSGarrett Wollman rt2 = rtalloc1(dst, 0, RTF_PRCLONING); 609aca1a47cSGarrett Wollman if (rt2 && rt2->rt_parent) { 610aca1a47cSGarrett Wollman rtrequest(RTM_DELETE, 611aca1a47cSGarrett Wollman (struct sockaddr *)rt_key(rt2), 612aca1a47cSGarrett Wollman rt2->rt_gateway, 613aca1a47cSGarrett Wollman rt_mask(rt2), rt2->rt_flags, 0); 614aca1a47cSGarrett Wollman RTFREE(rt2); 615aca1a47cSGarrett Wollman rn = rnh->rnh_addaddr((caddr_t)ndst, 616aca1a47cSGarrett Wollman (caddr_t)netmask, 617aca1a47cSGarrett Wollman rnh, rt->rt_nodes); 618fde327d6SGarrett Wollman } else if (rt2) { 619499676dfSJulian Elischer /* undo the extra ref we got */ 620fde327d6SGarrett Wollman RTFREE(rt2); 621aca1a47cSGarrett Wollman } 622aca1a47cSGarrett Wollman } 623aca1a47cSGarrett Wollman 624499676dfSJulian Elischer /* 625499676dfSJulian Elischer * If it still failed to go into the tree, 626499676dfSJulian Elischer * then un-make it (this should be a function) 627499676dfSJulian Elischer */ 628aca1a47cSGarrett Wollman if (rn == 0) { 629df8bae1dSRodney W. Grimes if (rt->rt_gwroute) 630df8bae1dSRodney W. Grimes rtfree(rt->rt_gwroute); 6318e718bb4SGarrett Wollman if (rt->rt_ifa) { 6328e718bb4SGarrett Wollman IFAFREE(rt->rt_ifa); 6338e718bb4SGarrett Wollman } 634df8bae1dSRodney W. Grimes Free(rt_key(rt)); 635df8bae1dSRodney W. Grimes Free(rt); 636df8bae1dSRodney W. Grimes senderr(EEXIST); 637df8bae1dSRodney W. Grimes } 638499676dfSJulian Elischer 639771edb14SGarrett Wollman rt->rt_parent = 0; 640771edb14SGarrett Wollman 641499676dfSJulian Elischer /* 642499676dfSJulian Elischer * If we got here from RESOLVE, then we are cloning 643499676dfSJulian Elischer * so clone the rest, and note that we 644499676dfSJulian Elischer * are a clone (and increment the parent's references) 645499676dfSJulian Elischer */ 646c2bed6a3SGarrett Wollman if (req == RTM_RESOLVE) { 647df8bae1dSRodney W. Grimes rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ 648771edb14SGarrett Wollman if ((*ret_nrt)->rt_flags & RTF_PRCLONING) { 64918e1f1f1SGarrett Wollman rt->rt_parent = (*ret_nrt); 650771edb14SGarrett Wollman (*ret_nrt)->rt_refcnt++; 651771edb14SGarrett Wollman } 65218e1f1f1SGarrett Wollman } 653499676dfSJulian Elischer 654499676dfSJulian Elischer /* 655499676dfSJulian Elischer * if this protocol has something to add to this then 656499676dfSJulian Elischer * allow it to do that as well. 657499676dfSJulian Elischer */ 658df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 659df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); 660499676dfSJulian Elischer 661cd02a0b7SGarrett Wollman /* 662cd02a0b7SGarrett Wollman * We repeat the same procedure from rt_setgate() here because 663cd02a0b7SGarrett Wollman * it doesn't fire when we call it there because the node 664cd02a0b7SGarrett Wollman * hasn't been added to the tree yet. 665cd02a0b7SGarrett Wollman */ 6663271a3a4SPeter Wemm if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { 667cd02a0b7SGarrett Wollman struct rtfc_arg arg; 668cd02a0b7SGarrett Wollman arg.rnh = rnh; 669cd02a0b7SGarrett Wollman arg.rt0 = rt; 670cd02a0b7SGarrett Wollman rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), 671cd02a0b7SGarrett Wollman rt_fixchange, &arg); 672cd02a0b7SGarrett Wollman } 673cd02a0b7SGarrett Wollman 674499676dfSJulian Elischer /* 675499676dfSJulian Elischer * actually return a resultant rtentry and 676499676dfSJulian Elischer * give the caller a single reference. 677499676dfSJulian Elischer */ 678df8bae1dSRodney W. Grimes if (ret_nrt) { 679df8bae1dSRodney W. Grimes *ret_nrt = rt; 680df8bae1dSRodney W. Grimes rt->rt_refcnt++; 681df8bae1dSRodney W. Grimes } 682df8bae1dSRodney W. Grimes break; 683df8bae1dSRodney W. Grimes } 684df8bae1dSRodney W. Grimes bad: 685df8bae1dSRodney W. Grimes splx(s); 686df8bae1dSRodney W. Grimes return (error); 687df8bae1dSRodney W. Grimes } 688df8bae1dSRodney W. Grimes 68918e1f1f1SGarrett Wollman /* 69018e1f1f1SGarrett Wollman * Called from rtrequest(RTM_DELETE, ...) to fix up the route's ``family'' 69118e1f1f1SGarrett Wollman * (i.e., the routes related to it by the operation of cloning). This 692c2bed6a3SGarrett Wollman * routine is iterated over all potential former-child-routes by way of 693c2bed6a3SGarrett Wollman * rnh->rnh_walktree_from() above, and those that actually are children of 694c2bed6a3SGarrett Wollman * the late parent (passed in as VP here) are themselves deleted. 69518e1f1f1SGarrett Wollman */ 696c2bed6a3SGarrett Wollman static int 697514ede09SBruce Evans rt_fixdelete(rn, vp) 698514ede09SBruce Evans struct radix_node *rn; 699514ede09SBruce Evans void *vp; 70018e1f1f1SGarrett Wollman { 701c2bed6a3SGarrett Wollman struct rtentry *rt = (struct rtentry *)rn; 702c2bed6a3SGarrett Wollman struct rtentry *rt0 = vp; 70318e1f1f1SGarrett Wollman 704a29ae2a1SGarrett Wollman if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) { 705c2bed6a3SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), 70618e1f1f1SGarrett Wollman (struct sockaddr *)0, rt_mask(rt), 70718e1f1f1SGarrett Wollman rt->rt_flags, (struct rtentry **)0); 70818e1f1f1SGarrett Wollman } 709c2bed6a3SGarrett Wollman return 0; 71018e1f1f1SGarrett Wollman } 71118e1f1f1SGarrett Wollman 712cd02a0b7SGarrett Wollman /* 713cd02a0b7SGarrett Wollman * This routine is called from rt_setgate() to do the analogous thing for 714cd02a0b7SGarrett Wollman * adds and changes. There is the added complication in this case of a 715cd02a0b7SGarrett Wollman * middle insert; i.e., insertion of a new network route between an older 716cd02a0b7SGarrett Wollman * network route and (cloned) host routes. For this reason, a simple check 717cd02a0b7SGarrett Wollman * of rt->rt_parent is insufficient; each candidate route must be tested 718cd02a0b7SGarrett Wollman * against the (mask, value) of the new route (passed as before in vp) 719cd02a0b7SGarrett Wollman * to see if the new route matches it. Unfortunately, this has the obnoxious 720cd02a0b7SGarrett Wollman * property of also triggering for insertion /above/ a pre-existing network 721cd02a0b7SGarrett Wollman * route and clones. Sigh. This may be fixed some day. 722cd02a0b7SGarrett Wollman * 723cd02a0b7SGarrett Wollman * XXX - it may be possible to do fixdelete() for changes and reserve this 724cd02a0b7SGarrett Wollman * routine just for adds. I'm not sure why I thought it was necessary to do 725cd02a0b7SGarrett Wollman * changes this way. 726cd02a0b7SGarrett Wollman */ 727cd02a0b7SGarrett Wollman #ifdef DEBUG 728303b270bSEivind Eklund static int rtfcdebug = 0; 729cd02a0b7SGarrett Wollman #endif 730cd02a0b7SGarrett Wollman 731cd02a0b7SGarrett Wollman static int 732514ede09SBruce Evans rt_fixchange(rn, vp) 733514ede09SBruce Evans struct radix_node *rn; 734514ede09SBruce Evans void *vp; 735cd02a0b7SGarrett Wollman { 736cd02a0b7SGarrett Wollman struct rtentry *rt = (struct rtentry *)rn; 737cd02a0b7SGarrett Wollman struct rtfc_arg *ap = vp; 738cd02a0b7SGarrett Wollman struct rtentry *rt0 = ap->rt0; 739cd02a0b7SGarrett Wollman struct radix_node_head *rnh = ap->rnh; 740cd02a0b7SGarrett Wollman u_char *xk1, *xm1, *xk2; 741cd02a0b7SGarrett Wollman int i, len; 742cd02a0b7SGarrett Wollman 743cd02a0b7SGarrett Wollman #ifdef DEBUG 744cd02a0b7SGarrett Wollman if (rtfcdebug) 745cd02a0b7SGarrett Wollman printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0); 746cd02a0b7SGarrett Wollman #endif 747cd02a0b7SGarrett Wollman 748cd02a0b7SGarrett Wollman if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) { 749cd02a0b7SGarrett Wollman #ifdef DEBUG 750cd02a0b7SGarrett Wollman if(rtfcdebug) printf("no parent or pinned\n"); 751cd02a0b7SGarrett Wollman #endif 752cd02a0b7SGarrett Wollman return 0; 753cd02a0b7SGarrett Wollman } 754cd02a0b7SGarrett Wollman 755cd02a0b7SGarrett Wollman if (rt->rt_parent == rt0) { 756cd02a0b7SGarrett Wollman #ifdef DEBUG 757cd02a0b7SGarrett Wollman if(rtfcdebug) printf("parent match\n"); 758cd02a0b7SGarrett Wollman #endif 759cd02a0b7SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), 760cd02a0b7SGarrett Wollman (struct sockaddr *)0, rt_mask(rt), 761cd02a0b7SGarrett Wollman rt->rt_flags, (struct rtentry **)0); 762cd02a0b7SGarrett Wollman } 763cd02a0b7SGarrett Wollman 764cd02a0b7SGarrett Wollman /* 765cd02a0b7SGarrett Wollman * There probably is a function somewhere which does this... 766cd02a0b7SGarrett Wollman * if not, there should be. 767cd02a0b7SGarrett Wollman */ 768cd02a0b7SGarrett Wollman len = imin(((struct sockaddr *)rt_key(rt0))->sa_len, 769cd02a0b7SGarrett Wollman ((struct sockaddr *)rt_key(rt))->sa_len); 770cd02a0b7SGarrett Wollman 771cd02a0b7SGarrett Wollman xk1 = (u_char *)rt_key(rt0); 772cd02a0b7SGarrett Wollman xm1 = (u_char *)rt_mask(rt0); 773cd02a0b7SGarrett Wollman xk2 = (u_char *)rt_key(rt); 774cd02a0b7SGarrett Wollman 775cd02a0b7SGarrett Wollman for (i = rnh->rnh_treetop->rn_off; i < len; i++) { 776cd02a0b7SGarrett Wollman if ((xk2[i] & xm1[i]) != xk1[i]) { 777cd02a0b7SGarrett Wollman #ifdef DEBUG 778cd02a0b7SGarrett Wollman if(rtfcdebug) printf("no match\n"); 779cd02a0b7SGarrett Wollman #endif 780cd02a0b7SGarrett Wollman return 0; 781cd02a0b7SGarrett Wollman } 782cd02a0b7SGarrett Wollman } 783cd02a0b7SGarrett Wollman 784cd02a0b7SGarrett Wollman /* 785cd02a0b7SGarrett Wollman * OK, this node is a clone, and matches the node currently being 786cd02a0b7SGarrett Wollman * changed/added under the node's mask. So, get rid of it. 787cd02a0b7SGarrett Wollman */ 788cd02a0b7SGarrett Wollman #ifdef DEBUG 789cd02a0b7SGarrett Wollman if(rtfcdebug) printf("deleting\n"); 790cd02a0b7SGarrett Wollman #endif 791cd02a0b7SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, 792cd02a0b7SGarrett Wollman rt_mask(rt), rt->rt_flags, (struct rtentry **)0); 793cd02a0b7SGarrett Wollman } 794cd02a0b7SGarrett Wollman 795df8bae1dSRodney W. Grimes int 796df8bae1dSRodney W. Grimes rt_setgate(rt0, dst, gate) 797df8bae1dSRodney W. Grimes struct rtentry *rt0; 798df8bae1dSRodney W. Grimes struct sockaddr *dst, *gate; 799df8bae1dSRodney W. Grimes { 800df8bae1dSRodney W. Grimes caddr_t new, old; 801df8bae1dSRodney W. Grimes int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len); 802df8bae1dSRodney W. Grimes register struct rtentry *rt = rt0; 803cd02a0b7SGarrett Wollman struct radix_node_head *rnh = rt_tables[dst->sa_family]; 804df8bae1dSRodney W. Grimes 8051db1fffaSBill Fenner /* 8061db1fffaSBill Fenner * A host route with the destination equal to the gateway 8071db1fffaSBill Fenner * will interfere with keeping LLINFO in the routing 8081db1fffaSBill Fenner * table, so disallow it. 8091db1fffaSBill Fenner */ 8101db1fffaSBill Fenner if (((rt0->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) == 8111db1fffaSBill Fenner (RTF_HOST|RTF_GATEWAY)) && 8121db1fffaSBill Fenner (dst->sa_len == gate->sa_len) && 8131db1fffaSBill Fenner (bcmp(dst, gate, dst->sa_len) == 0)) { 8141db1fffaSBill Fenner /* 8151db1fffaSBill Fenner * The route might already exist if this is an RTM_CHANGE 8161db1fffaSBill Fenner * or a routing redirect, so try to delete it. 8171db1fffaSBill Fenner */ 818704b0666SBill Fenner if (rt_key(rt0)) 8191db1fffaSBill Fenner rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt0), 8201db1fffaSBill Fenner rt0->rt_gateway, rt_mask(rt0), rt0->rt_flags, 0); 8211db1fffaSBill Fenner return EADDRNOTAVAIL; 8221db1fffaSBill Fenner } 8231db1fffaSBill Fenner 824499676dfSJulian Elischer /* 825499676dfSJulian Elischer * Both dst and gateway are stored in the same malloc'd chunk 826499676dfSJulian Elischer * (If I ever get my hands on....) 827499676dfSJulian Elischer * if we need to malloc a new chunk, then keep the old one around 828499676dfSJulian Elischer * till we don't need it any more. 829499676dfSJulian Elischer */ 830df8bae1dSRodney W. Grimes if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) { 831df8bae1dSRodney W. Grimes old = (caddr_t)rt_key(rt); 832df8bae1dSRodney W. Grimes R_Malloc(new, caddr_t, dlen + glen); 833df8bae1dSRodney W. Grimes if (new == 0) 8341db1fffaSBill Fenner return ENOBUFS; 835df8bae1dSRodney W. Grimes rt->rt_nodes->rn_key = new; 836df8bae1dSRodney W. Grimes } else { 837499676dfSJulian Elischer /* 838499676dfSJulian Elischer * otherwise just overwrite the old one 839499676dfSJulian Elischer */ 840df8bae1dSRodney W. Grimes new = rt->rt_nodes->rn_key; 841df8bae1dSRodney W. Grimes old = 0; 842df8bae1dSRodney W. Grimes } 843499676dfSJulian Elischer 844499676dfSJulian Elischer /* 845499676dfSJulian Elischer * copy the new gateway value into the memory chunk 846499676dfSJulian Elischer */ 847df8bae1dSRodney W. Grimes Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen); 848499676dfSJulian Elischer 849499676dfSJulian Elischer /* 850499676dfSJulian Elischer * if we are replacing the chunk (or it's new) we need to 851499676dfSJulian Elischer * replace the dst as well 852499676dfSJulian Elischer */ 853df8bae1dSRodney W. Grimes if (old) { 854df8bae1dSRodney W. Grimes Bcopy(dst, new, dlen); 855df8bae1dSRodney W. Grimes Free(old); 856df8bae1dSRodney W. Grimes } 857499676dfSJulian Elischer 858499676dfSJulian Elischer /* 859499676dfSJulian Elischer * If there is already a gwroute, it's now almost definitly wrong 860499676dfSJulian Elischer * so drop it. 861499676dfSJulian Elischer */ 862df8bae1dSRodney W. Grimes if (rt->rt_gwroute) { 863df8bae1dSRodney W. Grimes rt = rt->rt_gwroute; RTFREE(rt); 864df8bae1dSRodney W. Grimes rt = rt0; rt->rt_gwroute = 0; 865df8bae1dSRodney W. Grimes } 866cd02a0b7SGarrett Wollman /* 867cd02a0b7SGarrett Wollman * Cloning loop avoidance: 868cd02a0b7SGarrett Wollman * In the presence of protocol-cloning and bad configuration, 869cd02a0b7SGarrett Wollman * it is possible to get stuck in bottomless mutual recursion 870cd02a0b7SGarrett Wollman * (rtrequest rt_setgate rtalloc1). We avoid this by not allowing 871cd02a0b7SGarrett Wollman * protocol-cloning to operate for gateways (which is probably the 872cd02a0b7SGarrett Wollman * correct choice anyway), and avoid the resulting reference loops 873cd02a0b7SGarrett Wollman * by disallowing any route to run through itself as a gateway. 874499676dfSJulian Elischer * This is obviously mandatory when we get rt->rt_output(). 875cd02a0b7SGarrett Wollman */ 876df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 877cd02a0b7SGarrett Wollman rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING); 878cd02a0b7SGarrett Wollman if (rt->rt_gwroute == rt) { 879cd02a0b7SGarrett Wollman RTFREE(rt->rt_gwroute); 880cd02a0b7SGarrett Wollman rt->rt_gwroute = 0; 8811db1fffaSBill Fenner return EDQUOT; /* failure */ 882df8bae1dSRodney W. Grimes } 883cd02a0b7SGarrett Wollman } 884cd02a0b7SGarrett Wollman 885cd02a0b7SGarrett Wollman /* 886cd02a0b7SGarrett Wollman * This isn't going to do anything useful for host routes, so 887cd02a0b7SGarrett Wollman * don't bother. Also make sure we have a reasonable mask 888cd02a0b7SGarrett Wollman * (we don't yet have one during adds). 889cd02a0b7SGarrett Wollman */ 890cd02a0b7SGarrett Wollman if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { 891cd02a0b7SGarrett Wollman struct rtfc_arg arg; 892cd02a0b7SGarrett Wollman arg.rnh = rnh; 893cd02a0b7SGarrett Wollman arg.rt0 = rt; 894cd02a0b7SGarrett Wollman rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), 895cd02a0b7SGarrett Wollman rt_fixchange, &arg); 896cd02a0b7SGarrett Wollman } 897cd02a0b7SGarrett Wollman 898df8bae1dSRodney W. Grimes return 0; 899df8bae1dSRodney W. Grimes } 900df8bae1dSRodney W. Grimes 901f708ef1bSPoul-Henning Kamp static void 902df8bae1dSRodney W. Grimes rt_maskedcopy(src, dst, netmask) 903df8bae1dSRodney W. Grimes struct sockaddr *src, *dst, *netmask; 904df8bae1dSRodney W. Grimes { 905df8bae1dSRodney W. Grimes register u_char *cp1 = (u_char *)src; 906df8bae1dSRodney W. Grimes register u_char *cp2 = (u_char *)dst; 907df8bae1dSRodney W. Grimes register u_char *cp3 = (u_char *)netmask; 908df8bae1dSRodney W. Grimes u_char *cplim = cp2 + *cp3; 909df8bae1dSRodney W. Grimes u_char *cplim2 = cp2 + *cp1; 910df8bae1dSRodney W. Grimes 911df8bae1dSRodney W. Grimes *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ 912df8bae1dSRodney W. Grimes cp3 += 2; 913df8bae1dSRodney W. Grimes if (cplim > cplim2) 914df8bae1dSRodney W. Grimes cplim = cplim2; 915df8bae1dSRodney W. Grimes while (cp2 < cplim) 916df8bae1dSRodney W. Grimes *cp2++ = *cp1++ & *cp3++; 917df8bae1dSRodney W. Grimes if (cp2 < cplim2) 918df8bae1dSRodney W. Grimes bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); 919df8bae1dSRodney W. Grimes } 920df8bae1dSRodney W. Grimes 921df8bae1dSRodney W. Grimes /* 922df8bae1dSRodney W. Grimes * Set up a routing table entry, normally 923df8bae1dSRodney W. Grimes * for an interface. 924df8bae1dSRodney W. Grimes */ 925df8bae1dSRodney W. Grimes int 926df8bae1dSRodney W. Grimes rtinit(ifa, cmd, flags) 927df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 928df8bae1dSRodney W. Grimes int cmd, flags; 929df8bae1dSRodney W. Grimes { 930df8bae1dSRodney W. Grimes register struct rtentry *rt; 931df8bae1dSRodney W. Grimes register struct sockaddr *dst; 932df8bae1dSRodney W. Grimes register struct sockaddr *deldst; 933df8bae1dSRodney W. Grimes struct mbuf *m = 0; 934df8bae1dSRodney W. Grimes struct rtentry *nrt = 0; 935df8bae1dSRodney W. Grimes int error; 936df8bae1dSRodney W. Grimes 937df8bae1dSRodney W. Grimes dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; 938b0a76b88SJulian Elischer /* 939b0a76b88SJulian Elischer * If it's a delete, check that if it exists, it's on the correct 940b0a76b88SJulian Elischer * interface or we might scrub a route to another ifa which would 941b0a76b88SJulian Elischer * be confusing at best and possibly worse. 942b0a76b88SJulian Elischer */ 943df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE) { 944b0a76b88SJulian Elischer /* 945b0a76b88SJulian Elischer * It's a delete, so it should already exist.. 946b0a76b88SJulian Elischer * If it's a net, mask off the host bits 947b0a76b88SJulian Elischer * (Assuming we have a mask) 948b0a76b88SJulian Elischer */ 949df8bae1dSRodney W. Grimes if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { 950df8bae1dSRodney W. Grimes m = m_get(M_WAIT, MT_SONAME); 951df8bae1dSRodney W. Grimes deldst = mtod(m, struct sockaddr *); 952df8bae1dSRodney W. Grimes rt_maskedcopy(dst, deldst, ifa->ifa_netmask); 953df8bae1dSRodney W. Grimes dst = deldst; 954df8bae1dSRodney W. Grimes } 955b0a76b88SJulian Elischer /* 956b0a76b88SJulian Elischer * Get an rtentry that is in the routing tree and 957499676dfSJulian Elischer * contains the correct info. (if this fails, can't get there). 958b0a76b88SJulian Elischer * We set "report" to FALSE so that if it doesn't exist, 959b0a76b88SJulian Elischer * it doesn't report an error or clone a route, etc. etc. 960b0a76b88SJulian Elischer */ 961995add1aSGarrett Wollman rt = rtalloc1(dst, 0, 0UL); 962623ae52eSPoul-Henning Kamp if (rt) { 963b0a76b88SJulian Elischer /* 964b0a76b88SJulian Elischer * Ok so we found the rtentry. it has an extra reference 965b0a76b88SJulian Elischer * for us at this stage. we won't need that so 966b0a76b88SJulian Elischer * lop that off now. 967b0a76b88SJulian Elischer */ 968df8bae1dSRodney W. Grimes rt->rt_refcnt--; 969df8bae1dSRodney W. Grimes if (rt->rt_ifa != ifa) { 970b0a76b88SJulian Elischer /* 971b0a76b88SJulian Elischer * If the interface in the rtentry doesn't match 972b0a76b88SJulian Elischer * the interface we are using, then we don't 973b0a76b88SJulian Elischer * want to delete it, so return an error. 974b0a76b88SJulian Elischer * This seems to be the only point of 975b0a76b88SJulian Elischer * this whole RTM_DELETE clause. 976b0a76b88SJulian Elischer */ 977df8bae1dSRodney W. Grimes if (m) 978df8bae1dSRodney W. Grimes (void) m_free(m); 979df8bae1dSRodney W. Grimes return (flags & RTF_HOST ? EHOSTUNREACH 980df8bae1dSRodney W. Grimes : ENETUNREACH); 981df8bae1dSRodney W. Grimes } 982df8bae1dSRodney W. Grimes } 983b0a76b88SJulian Elischer /* XXX */ 984b0a76b88SJulian Elischer #if 0 985b0a76b88SJulian Elischer else { 986b0a76b88SJulian Elischer /* 987b0a76b88SJulian Elischer * One would think that as we are deleting, and we know 988b0a76b88SJulian Elischer * it doesn't exist, we could just return at this point 989b0a76b88SJulian Elischer * with an "ELSE" clause, but apparently not.. 990b0a76b88SJulian Elischer */ 991b0a76b88SJulian Elischer return (flags & RTF_HOST ? EHOSTUNREACH 992b0a76b88SJulian Elischer : ENETUNREACH); 993df8bae1dSRodney W. Grimes } 994b0a76b88SJulian Elischer #endif 995b0a76b88SJulian Elischer } 996b0a76b88SJulian Elischer /* 997b0a76b88SJulian Elischer * Do the actual request 998b0a76b88SJulian Elischer */ 999df8bae1dSRodney W. Grimes error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, 1000df8bae1dSRodney W. Grimes flags | ifa->ifa_flags, &nrt); 1001df8bae1dSRodney W. Grimes if (m) 1002df8bae1dSRodney W. Grimes (void) m_free(m); 1003b0a76b88SJulian Elischer /* 1004b0a76b88SJulian Elischer * If we are deleting, and we found an entry, then 1005b0a76b88SJulian Elischer * it's been removed from the tree.. now throw it away. 1006b0a76b88SJulian Elischer */ 1007df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) { 1008b0a76b88SJulian Elischer /* 1009b0a76b88SJulian Elischer * notify any listenning routing agents of the change 1010b0a76b88SJulian Elischer */ 1011df8bae1dSRodney W. Grimes rt_newaddrmsg(cmd, ifa, error, nrt); 1012df8bae1dSRodney W. Grimes if (rt->rt_refcnt <= 0) { 1013b0a76b88SJulian Elischer rt->rt_refcnt++; /* need a 1->0 transition to free */ 1014df8bae1dSRodney W. Grimes rtfree(rt); 1015df8bae1dSRodney W. Grimes } 1016df8bae1dSRodney W. Grimes } 1017b0a76b88SJulian Elischer 1018b0a76b88SJulian Elischer /* 1019b0a76b88SJulian Elischer * We are adding, and we have a returned routing entry. 1020b0a76b88SJulian Elischer * We need to sanity check the result. 1021b0a76b88SJulian Elischer */ 1022df8bae1dSRodney W. Grimes if (cmd == RTM_ADD && error == 0 && (rt = nrt)) { 1023b0a76b88SJulian Elischer /* 1024b0a76b88SJulian Elischer * We just wanted to add it.. we don't actually need a reference 1025b0a76b88SJulian Elischer */ 1026df8bae1dSRodney W. Grimes rt->rt_refcnt--; 1027b0a76b88SJulian Elischer /* 1028b0a76b88SJulian Elischer * If it came back with an unexpected interface, then it must 1029b0a76b88SJulian Elischer * have already existed or something. (XXX) 1030b0a76b88SJulian Elischer */ 1031df8bae1dSRodney W. Grimes if (rt->rt_ifa != ifa) { 1032623ae52eSPoul-Henning Kamp printf("rtinit: wrong ifa (%p) was (%p)\n", ifa, 1033df8bae1dSRodney W. Grimes rt->rt_ifa); 1034b0a76b88SJulian Elischer /* 1035499676dfSJulian Elischer * Ask that the protocol in question 1036499676dfSJulian Elischer * remove anything it has associated with 1037499676dfSJulian Elischer * this route and ifaddr. 1038b0a76b88SJulian Elischer */ 1039df8bae1dSRodney W. Grimes if (rt->rt_ifa->ifa_rtrequest) 1040df8bae1dSRodney W. Grimes rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 1041b0a76b88SJulian Elischer /* 1042b0a76b88SJulian Elischer * Remove the referenve to the it's ifaddr. 1043b0a76b88SJulian Elischer */ 1044df8bae1dSRodney W. Grimes IFAFREE(rt->rt_ifa); 1045b0a76b88SJulian Elischer /* 1046b0a76b88SJulian Elischer * And substitute in references to the ifaddr 1047b0a76b88SJulian Elischer * we are adding. 1048b0a76b88SJulian Elischer */ 1049df8bae1dSRodney W. Grimes rt->rt_ifa = ifa; 1050df8bae1dSRodney W. Grimes rt->rt_ifp = ifa->ifa_ifp; 1051df8bae1dSRodney W. Grimes ifa->ifa_refcnt++; 1052b0a76b88SJulian Elischer /* 1053499676dfSJulian Elischer * Now ask the protocol to check if it needs 1054dc733423SDag-Erling Smørgrav * any special processing in its new form. 1055b0a76b88SJulian Elischer */ 1056df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 1057df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(RTM_ADD, rt, SA(0)); 1058df8bae1dSRodney W. Grimes } 1059b0a76b88SJulian Elischer /* 1060b0a76b88SJulian Elischer * notify any listenning routing agents of the change 1061b0a76b88SJulian Elischer */ 1062df8bae1dSRodney W. Grimes rt_newaddrmsg(cmd, ifa, error, nrt); 1063df8bae1dSRodney W. Grimes } 10643ec66d6cSDavid Greenman return (error); 10653ec66d6cSDavid Greenman } 1066cb64988fSLuoqi Chen 1067cb64988fSLuoqi Chen SYSINIT(route, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, route_init, 0); 1068