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 341d5e9e22SEivind Eklund * $Id: route.c,v 1.45 1997/10/28 15:58:35 bde 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> 46df8bae1dSRodney W. Grimes 47df8bae1dSRodney W. Grimes #include <net/if.h> 48df8bae1dSRodney W. Grimes #include <net/route.h> 49df8bae1dSRodney W. Grimes 50df8bae1dSRodney W. Grimes #include <netinet/in.h> 51b5e8ce9fSBruce Evans #include <netinet/ip_mroute.h> 52df8bae1dSRodney W. Grimes 53df8bae1dSRodney W. Grimes #define SA(p) ((struct sockaddr *)(p)) 54df8bae1dSRodney W. Grimes 5528f8db14SBruce Evans struct route_cb route_cb; 56f708ef1bSPoul-Henning Kamp static struct rtstat rtstat; 5728f8db14SBruce Evans struct radix_node_head *rt_tables[AF_MAX+1]; 5828f8db14SBruce Evans 59f708ef1bSPoul-Henning Kamp static int rttrash; /* routes not in table but not freed */ 60df8bae1dSRodney W. Grimes 61f708ef1bSPoul-Henning Kamp static void rt_maskedcopy __P((struct sockaddr *, 62f708ef1bSPoul-Henning Kamp struct sockaddr *, struct sockaddr *)); 63f708ef1bSPoul-Henning Kamp static void rtable_init __P((void **)); 64f708ef1bSPoul-Henning Kamp 65f708ef1bSPoul-Henning Kamp static void 66df8bae1dSRodney W. Grimes rtable_init(table) 67df8bae1dSRodney W. Grimes void **table; 68df8bae1dSRodney W. Grimes { 69df8bae1dSRodney W. Grimes struct domain *dom; 70df8bae1dSRodney W. Grimes for (dom = domains; dom; dom = dom->dom_next) 71df8bae1dSRodney W. Grimes if (dom->dom_rtattach) 72df8bae1dSRodney W. Grimes dom->dom_rtattach(&table[dom->dom_family], 73df8bae1dSRodney W. Grimes dom->dom_rtoffset); 74df8bae1dSRodney W. Grimes } 75df8bae1dSRodney W. Grimes 76df8bae1dSRodney W. Grimes void 77df8bae1dSRodney W. Grimes route_init() 78df8bae1dSRodney W. Grimes { 79df8bae1dSRodney W. Grimes rn_init(); /* initialize all zeroes, all ones, mask table */ 80df8bae1dSRodney W. Grimes rtable_init((void **)rt_tables); 81df8bae1dSRodney W. Grimes } 82df8bae1dSRodney W. Grimes 83df8bae1dSRodney W. Grimes /* 84df8bae1dSRodney W. Grimes * Packet routing routines. 85df8bae1dSRodney W. Grimes */ 86df8bae1dSRodney W. Grimes void 87df8bae1dSRodney W. Grimes rtalloc(ro) 88df8bae1dSRodney W. Grimes register struct route *ro; 89df8bae1dSRodney W. Grimes { 90df8bae1dSRodney W. Grimes if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 91df8bae1dSRodney W. Grimes return; /* XXX */ 92995add1aSGarrett Wollman ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL); 93df8bae1dSRodney W. Grimes } 94df8bae1dSRodney W. Grimes 95652082e6SGarrett Wollman void 96652082e6SGarrett Wollman rtalloc_ign(ro, ignore) 97652082e6SGarrett Wollman register struct route *ro; 98652082e6SGarrett Wollman u_long ignore; 99652082e6SGarrett Wollman { 100652082e6SGarrett Wollman if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 101652082e6SGarrett Wollman return; /* XXX */ 102652082e6SGarrett Wollman ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore); 103652082e6SGarrett Wollman } 104652082e6SGarrett Wollman 105b0a76b88SJulian Elischer /* 106b0a76b88SJulian Elischer * Look up the route that matches the address given 107b0a76b88SJulian Elischer * Or, at least try.. Create a cloned route if needed. 108b0a76b88SJulian Elischer */ 109df8bae1dSRodney W. Grimes struct rtentry * 110995add1aSGarrett Wollman rtalloc1(dst, report, ignflags) 111df8bae1dSRodney W. Grimes register struct sockaddr *dst; 112df8bae1dSRodney W. Grimes int report; 113995add1aSGarrett Wollman u_long ignflags; 114df8bae1dSRodney W. Grimes { 115df8bae1dSRodney W. Grimes register struct radix_node_head *rnh = rt_tables[dst->sa_family]; 116df8bae1dSRodney W. Grimes register struct rtentry *rt; 117df8bae1dSRodney W. Grimes register struct radix_node *rn; 118df8bae1dSRodney W. Grimes struct rtentry *newrt = 0; 119df8bae1dSRodney W. Grimes struct rt_addrinfo info; 120995add1aSGarrett Wollman u_long nflags; 121df8bae1dSRodney W. Grimes int s = splnet(), err = 0, msgtype = RTM_MISS; 122df8bae1dSRodney W. Grimes 123b0a76b88SJulian Elischer /* 124b0a76b88SJulian Elischer * Look up the address in the table for that Address Family 125b0a76b88SJulian Elischer */ 126df8bae1dSRodney W. Grimes if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) && 127df8bae1dSRodney W. Grimes ((rn->rn_flags & RNF_ROOT) == 0)) { 128b0a76b88SJulian Elischer /* 129b0a76b88SJulian Elischer * If we find it and it's not the root node, then 130b0a76b88SJulian Elischer * get a refernce on the rtentry associated. 131b0a76b88SJulian Elischer */ 132df8bae1dSRodney W. Grimes newrt = rt = (struct rtentry *)rn; 133995add1aSGarrett Wollman nflags = rt->rt_flags & ~ignflags; 134995add1aSGarrett Wollman if (report && (nflags & (RTF_CLONING | RTF_PRCLONING))) { 135b0a76b88SJulian Elischer /* 136b0a76b88SJulian Elischer * We are apparently adding (report = 0 in delete). 137b0a76b88SJulian Elischer * If it requires that it be cloned, do so. 138b0a76b88SJulian Elischer * (This implies it wasn't a HOST route.) 139b0a76b88SJulian Elischer */ 140df8bae1dSRodney W. Grimes err = rtrequest(RTM_RESOLVE, dst, SA(0), 141df8bae1dSRodney W. Grimes SA(0), 0, &newrt); 142df8bae1dSRodney W. Grimes if (err) { 143b0a76b88SJulian Elischer /* 144b0a76b88SJulian Elischer * If the cloning didn't succeed, maybe 145b0a76b88SJulian Elischer * what we have will do. Return that. 146b0a76b88SJulian Elischer */ 147df8bae1dSRodney W. Grimes newrt = rt; 148df8bae1dSRodney W. Grimes rt->rt_refcnt++; 149df8bae1dSRodney W. Grimes goto miss; 150df8bae1dSRodney W. Grimes } 151df8bae1dSRodney W. Grimes if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { 152b0a76b88SJulian Elischer /* 153b0a76b88SJulian Elischer * If the new route specifies it be 154b0a76b88SJulian Elischer * externally resolved, then go do that. 155b0a76b88SJulian Elischer */ 156df8bae1dSRodney W. Grimes msgtype = RTM_RESOLVE; 157df8bae1dSRodney W. Grimes goto miss; 158df8bae1dSRodney W. Grimes } 159df8bae1dSRodney W. Grimes } else 160df8bae1dSRodney W. Grimes rt->rt_refcnt++; 161df8bae1dSRodney W. Grimes } else { 162b0a76b88SJulian Elischer /* 163b0a76b88SJulian Elischer * Either we hit the root or couldn't find any match, 164b0a76b88SJulian Elischer * Which basically means 165b0a76b88SJulian Elischer * "caint get there frm here" 166b0a76b88SJulian Elischer */ 167df8bae1dSRodney W. Grimes rtstat.rts_unreach++; 168df8bae1dSRodney W. Grimes miss: if (report) { 169b0a76b88SJulian Elischer /* 170b0a76b88SJulian Elischer * If required, report the failure to the supervising 171b0a76b88SJulian Elischer * Authorities. 172b0a76b88SJulian Elischer * For a delete, this is not an error. (report == 0) 173b0a76b88SJulian Elischer */ 174df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 175df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 176df8bae1dSRodney W. Grimes rt_missmsg(msgtype, &info, 0, err); 177df8bae1dSRodney W. Grimes } 178df8bae1dSRodney W. Grimes } 179df8bae1dSRodney W. Grimes splx(s); 180df8bae1dSRodney W. Grimes return (newrt); 181df8bae1dSRodney W. Grimes } 182df8bae1dSRodney W. Grimes 183499676dfSJulian Elischer /* 184499676dfSJulian Elischer * Remove a reference count from an rtentry. 185499676dfSJulian Elischer * If the count gets low enough, take it out of the routing table 186499676dfSJulian Elischer */ 187df8bae1dSRodney W. Grimes void 188df8bae1dSRodney W. Grimes rtfree(rt) 189df8bae1dSRodney W. Grimes register struct rtentry *rt; 190df8bae1dSRodney W. Grimes { 191499676dfSJulian Elischer /* 192499676dfSJulian Elischer * find the tree for that address family 193499676dfSJulian Elischer */ 1945c2dae8eSGarrett Wollman register struct radix_node_head *rnh = 1955c2dae8eSGarrett Wollman rt_tables[rt_key(rt)->sa_family]; 196df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 197df8bae1dSRodney W. Grimes 1983545b048SGarrett Wollman if (rt == 0 || rnh == 0) 199df8bae1dSRodney W. Grimes panic("rtfree"); 200499676dfSJulian Elischer 201499676dfSJulian Elischer /* 202499676dfSJulian Elischer * decrement the reference count by one and if it reaches 0, 203499676dfSJulian Elischer * and there is a close function defined, call the close function 204499676dfSJulian Elischer */ 205df8bae1dSRodney W. Grimes rt->rt_refcnt--; 2065c2dae8eSGarrett Wollman if(rnh->rnh_close && rt->rt_refcnt == 0) { 2075c2dae8eSGarrett Wollman rnh->rnh_close((struct radix_node *)rt, rnh); 2085c2dae8eSGarrett Wollman } 209499676dfSJulian Elischer 210499676dfSJulian Elischer /* 211499676dfSJulian Elischer * If we are no longer "up" (and ref == 0) 212499676dfSJulian Elischer * then we can free the resources associated 213499676dfSJulian Elischer * with the route. 214499676dfSJulian Elischer */ 215df8bae1dSRodney W. Grimes if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { 216df8bae1dSRodney W. Grimes if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 217df8bae1dSRodney W. Grimes panic ("rtfree 2"); 218499676dfSJulian Elischer /* 219499676dfSJulian Elischer * the rtentry must have been removed from the routing table 220499676dfSJulian Elischer * so it is represented in rttrash.. remove that now. 221499676dfSJulian Elischer */ 222df8bae1dSRodney W. Grimes rttrash--; 223499676dfSJulian Elischer 224499676dfSJulian Elischer #ifdef DIAGNOSTIC 225df8bae1dSRodney W. Grimes if (rt->rt_refcnt < 0) { 226623ae52eSPoul-Henning Kamp printf("rtfree: %p not freed (neg refs)\n", rt); 227df8bae1dSRodney W. Grimes return; 228df8bae1dSRodney W. Grimes } 229499676dfSJulian Elischer #endif 230499676dfSJulian Elischer 231499676dfSJulian Elischer /* 232499676dfSJulian Elischer * release references on items we hold them on.. 233499676dfSJulian Elischer * e.g other routes and ifaddrs. 234499676dfSJulian Elischer */ 235499676dfSJulian Elischer if((ifa = rt->rt_ifa)) 236df8bae1dSRodney W. Grimes IFAFREE(ifa); 237771edb14SGarrett Wollman if (rt->rt_parent) { 238771edb14SGarrett Wollman RTFREE(rt->rt_parent); 239771edb14SGarrett Wollman } 240499676dfSJulian Elischer 241499676dfSJulian Elischer /* 242499676dfSJulian Elischer * The key is separatly alloc'd so free it (see rt_setgate()). 243499676dfSJulian Elischer * This also frees the gateway, as they are always malloc'd 244499676dfSJulian Elischer * together. 245499676dfSJulian Elischer */ 246df8bae1dSRodney W. Grimes Free(rt_key(rt)); 247499676dfSJulian Elischer 248499676dfSJulian Elischer /* 249499676dfSJulian Elischer * and the rtentry itself of course 250499676dfSJulian Elischer */ 251df8bae1dSRodney W. Grimes Free(rt); 252df8bae1dSRodney W. Grimes } 253df8bae1dSRodney W. Grimes } 254df8bae1dSRodney W. Grimes 255df8bae1dSRodney W. Grimes void 256df8bae1dSRodney W. Grimes ifafree(ifa) 257df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 258df8bae1dSRodney W. Grimes { 259df8bae1dSRodney W. Grimes if (ifa == NULL) 260df8bae1dSRodney W. Grimes panic("ifafree"); 261df8bae1dSRodney W. Grimes if (ifa->ifa_refcnt == 0) 262df8bae1dSRodney W. Grimes free(ifa, M_IFADDR); 263df8bae1dSRodney W. Grimes else 264df8bae1dSRodney W. Grimes ifa->ifa_refcnt--; 265df8bae1dSRodney W. Grimes } 266df8bae1dSRodney W. Grimes 267df8bae1dSRodney W. Grimes /* 268df8bae1dSRodney W. Grimes * Force a routing table entry to the specified 269df8bae1dSRodney W. Grimes * destination to go through the given gateway. 270df8bae1dSRodney W. Grimes * Normally called as a result of a routing redirect 271df8bae1dSRodney W. Grimes * message from the network layer. 272df8bae1dSRodney W. Grimes * 273df8bae1dSRodney W. Grimes * N.B.: must be called at splnet 274df8bae1dSRodney W. Grimes * 275df8bae1dSRodney W. Grimes */ 27626f9a767SRodney W. Grimes void 277df8bae1dSRodney W. Grimes rtredirect(dst, gateway, netmask, flags, src, rtp) 278df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway, *netmask, *src; 279df8bae1dSRodney W. Grimes int flags; 280df8bae1dSRodney W. Grimes struct rtentry **rtp; 281df8bae1dSRodney W. Grimes { 282df8bae1dSRodney W. Grimes register struct rtentry *rt; 283df8bae1dSRodney W. Grimes int error = 0; 284df8bae1dSRodney W. Grimes short *stat = 0; 285df8bae1dSRodney W. Grimes struct rt_addrinfo info; 286df8bae1dSRodney W. Grimes struct ifaddr *ifa; 287df8bae1dSRodney W. Grimes 288df8bae1dSRodney W. Grimes /* verify the gateway is directly reachable */ 289df8bae1dSRodney W. Grimes if ((ifa = ifa_ifwithnet(gateway)) == 0) { 290df8bae1dSRodney W. Grimes error = ENETUNREACH; 291df8bae1dSRodney W. Grimes goto out; 292df8bae1dSRodney W. Grimes } 293995add1aSGarrett Wollman rt = rtalloc1(dst, 0, 0UL); 294df8bae1dSRodney W. Grimes /* 295df8bae1dSRodney W. Grimes * If the redirect isn't from our current router for this dst, 296df8bae1dSRodney W. Grimes * it's either old or wrong. If it redirects us to ourselves, 297df8bae1dSRodney W. Grimes * we have a routing loop, perhaps as a result of an interface 298df8bae1dSRodney W. Grimes * going down recently. 299df8bae1dSRodney W. Grimes */ 300df8bae1dSRodney W. Grimes #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) 301df8bae1dSRodney W. Grimes if (!(flags & RTF_DONE) && rt && 302df8bae1dSRodney W. Grimes (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa)) 303df8bae1dSRodney W. Grimes error = EINVAL; 304df8bae1dSRodney W. Grimes else if (ifa_ifwithaddr(gateway)) 305df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 306df8bae1dSRodney W. Grimes if (error) 307df8bae1dSRodney W. Grimes goto done; 308df8bae1dSRodney W. Grimes /* 309df8bae1dSRodney W. Grimes * Create a new entry if we just got back a wildcard entry 310df8bae1dSRodney W. Grimes * or the the lookup failed. This is necessary for hosts 311df8bae1dSRodney W. Grimes * which use routing redirects generated by smart gateways 312df8bae1dSRodney W. Grimes * to dynamically build the routing tables. 313df8bae1dSRodney W. Grimes */ 314df8bae1dSRodney W. Grimes if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) 315df8bae1dSRodney W. Grimes goto create; 316df8bae1dSRodney W. Grimes /* 317df8bae1dSRodney W. Grimes * Don't listen to the redirect if it's 318df8bae1dSRodney W. Grimes * for a route to an interface. 319df8bae1dSRodney W. Grimes */ 320df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 321df8bae1dSRodney W. Grimes if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 322df8bae1dSRodney W. Grimes /* 323df8bae1dSRodney W. Grimes * Changing from route to net => route to host. 324df8bae1dSRodney W. Grimes * Create new route, rather than smashing route to net. 325df8bae1dSRodney W. Grimes */ 326df8bae1dSRodney W. Grimes create: 327df8bae1dSRodney W. Grimes flags |= RTF_GATEWAY | RTF_DYNAMIC; 328df8bae1dSRodney W. Grimes error = rtrequest((int)RTM_ADD, dst, gateway, 329df8bae1dSRodney W. Grimes netmask, flags, 330df8bae1dSRodney W. Grimes (struct rtentry **)0); 331df8bae1dSRodney W. Grimes stat = &rtstat.rts_dynamic; 332df8bae1dSRodney W. Grimes } else { 333df8bae1dSRodney W. Grimes /* 334df8bae1dSRodney W. Grimes * Smash the current notion of the gateway to 335df8bae1dSRodney W. Grimes * this destination. Should check about netmask!!! 336df8bae1dSRodney W. Grimes */ 337df8bae1dSRodney W. Grimes rt->rt_flags |= RTF_MODIFIED; 338df8bae1dSRodney W. Grimes flags |= RTF_MODIFIED; 339df8bae1dSRodney W. Grimes stat = &rtstat.rts_newgateway; 340499676dfSJulian Elischer /* 341499676dfSJulian Elischer * add the key and gateway (in one malloc'd chunk). 342499676dfSJulian Elischer */ 343df8bae1dSRodney W. Grimes rt_setgate(rt, rt_key(rt), gateway); 344df8bae1dSRodney W. Grimes } 345df8bae1dSRodney W. Grimes } else 346df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 347df8bae1dSRodney W. Grimes done: 348df8bae1dSRodney W. Grimes if (rt) { 349df8bae1dSRodney W. Grimes if (rtp && !error) 350df8bae1dSRodney W. Grimes *rtp = rt; 351df8bae1dSRodney W. Grimes else 352df8bae1dSRodney W. Grimes rtfree(rt); 353df8bae1dSRodney W. Grimes } 354df8bae1dSRodney W. Grimes out: 355df8bae1dSRodney W. Grimes if (error) 356df8bae1dSRodney W. Grimes rtstat.rts_badredirect++; 357df8bae1dSRodney W. Grimes else if (stat != NULL) 358df8bae1dSRodney W. Grimes (*stat)++; 359df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 360df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 361df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = gateway; 362df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = netmask; 363df8bae1dSRodney W. Grimes info.rti_info[RTAX_AUTHOR] = src; 364df8bae1dSRodney W. Grimes rt_missmsg(RTM_REDIRECT, &info, flags, error); 365df8bae1dSRodney W. Grimes } 366df8bae1dSRodney W. Grimes 367df8bae1dSRodney W. Grimes /* 368df8bae1dSRodney W. Grimes * Routing table ioctl interface. 369df8bae1dSRodney W. Grimes */ 370df8bae1dSRodney W. Grimes int 371df8bae1dSRodney W. Grimes rtioctl(req, data, p) 372df8bae1dSRodney W. Grimes int req; 373df8bae1dSRodney W. Grimes caddr_t data; 374df8bae1dSRodney W. Grimes struct proc *p; 375df8bae1dSRodney W. Grimes { 376623ae52eSPoul-Henning Kamp #ifdef INET 377f0068c4aSGarrett Wollman /* Multicast goop, grrr... */ 378af32e59fSBruce Evans #ifdef MROUTING 379af32e59fSBruce Evans return mrt_ioctl(req, data); 380af32e59fSBruce Evans #else 381e4ca4481SStefan Eßer return mrt_ioctl(req, data, p); 382af32e59fSBruce Evans #endif 383623ae52eSPoul-Henning Kamp #else /* INET */ 384623ae52eSPoul-Henning Kamp return ENXIO; 385623ae52eSPoul-Henning Kamp #endif /* INET */ 386df8bae1dSRodney W. Grimes } 387df8bae1dSRodney W. Grimes 388df8bae1dSRodney W. Grimes struct ifaddr * 389df8bae1dSRodney W. Grimes ifa_ifwithroute(flags, dst, gateway) 390df8bae1dSRodney W. Grimes int flags; 391df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway; 392df8bae1dSRodney W. Grimes { 393df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 394df8bae1dSRodney W. Grimes if ((flags & RTF_GATEWAY) == 0) { 395df8bae1dSRodney W. Grimes /* 396df8bae1dSRodney W. Grimes * If we are adding a route to an interface, 397df8bae1dSRodney W. Grimes * and the interface is a pt to pt link 398df8bae1dSRodney W. Grimes * we should search for the destination 399df8bae1dSRodney W. Grimes * as our clue to the interface. Otherwise 400df8bae1dSRodney W. Grimes * we can use the local address. 401df8bae1dSRodney W. Grimes */ 402df8bae1dSRodney W. Grimes ifa = 0; 4035df72964SGarrett Wollman if (flags & RTF_HOST) { 404df8bae1dSRodney W. Grimes ifa = ifa_ifwithdstaddr(dst); 4055df72964SGarrett Wollman } 406df8bae1dSRodney W. Grimes if (ifa == 0) 407df8bae1dSRodney W. Grimes ifa = ifa_ifwithaddr(gateway); 408df8bae1dSRodney W. Grimes } else { 409df8bae1dSRodney W. Grimes /* 410df8bae1dSRodney W. Grimes * If we are adding a route to a remote net 411df8bae1dSRodney W. Grimes * or host, the gateway may still be on the 412df8bae1dSRodney W. Grimes * other end of a pt to pt link. 413df8bae1dSRodney W. Grimes */ 414df8bae1dSRodney W. Grimes ifa = ifa_ifwithdstaddr(gateway); 415df8bae1dSRodney W. Grimes } 416df8bae1dSRodney W. Grimes if (ifa == 0) 417df8bae1dSRodney W. Grimes ifa = ifa_ifwithnet(gateway); 418df8bae1dSRodney W. Grimes if (ifa == 0) { 419995add1aSGarrett Wollman struct rtentry *rt = rtalloc1(dst, 0, 0UL); 420df8bae1dSRodney W. Grimes if (rt == 0) 421df8bae1dSRodney W. Grimes return (0); 422df8bae1dSRodney W. Grimes rt->rt_refcnt--; 423df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) == 0) 424df8bae1dSRodney W. Grimes return (0); 425df8bae1dSRodney W. Grimes } 426df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family != dst->sa_family) { 427df8bae1dSRodney W. Grimes struct ifaddr *oifa = ifa; 428df8bae1dSRodney W. Grimes ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); 429df8bae1dSRodney W. Grimes if (ifa == 0) 430df8bae1dSRodney W. Grimes ifa = oifa; 431df8bae1dSRodney W. Grimes } 432df8bae1dSRodney W. Grimes return (ifa); 433df8bae1dSRodney W. Grimes } 434df8bae1dSRodney W. Grimes 435df8bae1dSRodney W. Grimes #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 436df8bae1dSRodney W. Grimes 437514ede09SBruce Evans static int rt_fixdelete __P((struct radix_node *, void *)); 438514ede09SBruce Evans static int rt_fixchange __P((struct radix_node *, void *)); 439cd02a0b7SGarrett Wollman 440cd02a0b7SGarrett Wollman struct rtfc_arg { 441cd02a0b7SGarrett Wollman struct rtentry *rt0; 442cd02a0b7SGarrett Wollman struct radix_node_head *rnh; 443cd02a0b7SGarrett Wollman }; 44418e1f1f1SGarrett Wollman 445b0a76b88SJulian Elischer /* 446b0a76b88SJulian Elischer * Do appropriate manipulations of a routing tree given 447b0a76b88SJulian Elischer * all the bits of info needed 448b0a76b88SJulian Elischer */ 449df8bae1dSRodney W. Grimes int 450df8bae1dSRodney W. Grimes rtrequest(req, dst, gateway, netmask, flags, ret_nrt) 451df8bae1dSRodney W. Grimes int req, flags; 452df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway, *netmask; 453df8bae1dSRodney W. Grimes struct rtentry **ret_nrt; 454df8bae1dSRodney W. Grimes { 455df8bae1dSRodney W. Grimes int s = splnet(); int error = 0; 456df8bae1dSRodney W. Grimes register struct rtentry *rt; 457df8bae1dSRodney W. Grimes register struct radix_node *rn; 458df8bae1dSRodney W. Grimes register struct radix_node_head *rnh; 459df8bae1dSRodney W. Grimes struct ifaddr *ifa; 460df8bae1dSRodney W. Grimes struct sockaddr *ndst; 461df8bae1dSRodney W. Grimes #define senderr(x) { error = x ; goto bad; } 462df8bae1dSRodney W. Grimes 463b0a76b88SJulian Elischer /* 464b0a76b88SJulian Elischer * Find the correct routing tree to use for this Address Family 465b0a76b88SJulian Elischer */ 466df8bae1dSRodney W. Grimes if ((rnh = rt_tables[dst->sa_family]) == 0) 467df8bae1dSRodney W. Grimes senderr(ESRCH); 468b0a76b88SJulian Elischer /* 469b0a76b88SJulian Elischer * If we are adding a host route then we don't want to put 470b0a76b88SJulian Elischer * a netmask in the tree 471b0a76b88SJulian Elischer */ 472df8bae1dSRodney W. Grimes if (flags & RTF_HOST) 473df8bae1dSRodney W. Grimes netmask = 0; 474df8bae1dSRodney W. Grimes switch (req) { 475df8bae1dSRodney W. Grimes case RTM_DELETE: 476b0a76b88SJulian Elischer /* 477b0a76b88SJulian Elischer * Remove the item from the tree and return it. 478b0a76b88SJulian Elischer * Complain if it is not there and do no more processing. 479b0a76b88SJulian Elischer */ 480df8bae1dSRodney W. Grimes if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == 0) 481df8bae1dSRodney W. Grimes senderr(ESRCH); 482df8bae1dSRodney W. Grimes if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 483df8bae1dSRodney W. Grimes panic ("rtrequest delete"); 484df8bae1dSRodney W. Grimes rt = (struct rtentry *)rn; 485c2bed6a3SGarrett Wollman 486c2bed6a3SGarrett Wollman /* 487c2bed6a3SGarrett Wollman * Now search what's left of the subtree for any cloned 488c2bed6a3SGarrett Wollman * routes which might have been formed from this node. 489c2bed6a3SGarrett Wollman */ 4903545b048SGarrett Wollman if ((rt->rt_flags & RTF_PRCLONING) && netmask) { 491c2bed6a3SGarrett Wollman rnh->rnh_walktree_from(rnh, dst, netmask, 492c2bed6a3SGarrett Wollman rt_fixdelete, rt); 493c2bed6a3SGarrett Wollman } 4943545b048SGarrett Wollman 495b0a76b88SJulian Elischer /* 496b0a76b88SJulian Elischer * Remove any external references we may have. 497b0a76b88SJulian Elischer * This might result in another rtentry being freed if 498b0a76b88SJulian Elischer * we held it's last reference. 499b0a76b88SJulian Elischer */ 5006ac3b69dSBill Fenner if (rt->rt_gwroute) { 501b0a76b88SJulian Elischer rt = rt->rt_gwroute; 502b0a76b88SJulian Elischer RTFREE(rt); 5036ac3b69dSBill Fenner (rt = (struct rtentry *)rn)->rt_gwroute = 0; 5046ac3b69dSBill Fenner } 5056ac3b69dSBill Fenner 5063545b048SGarrett Wollman /* 5073545b048SGarrett Wollman * NB: RTF_UP must be set during the search above, 5083545b048SGarrett Wollman * because we might delete the last ref, causing 5093545b048SGarrett Wollman * rt to get freed prematurely. 510499676dfSJulian Elischer * eh? then why not just add a reference? 511499676dfSJulian Elischer * I'm not sure how RTF_UP helps matters. (JRE) 5123545b048SGarrett Wollman */ 5133545b048SGarrett Wollman rt->rt_flags &= ~RTF_UP; 5143545b048SGarrett Wollman 515b0a76b88SJulian Elischer /* 516499676dfSJulian Elischer * give the protocol a chance to keep things in sync. 517b0a76b88SJulian Elischer */ 518df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) 519df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 520499676dfSJulian Elischer 521b0a76b88SJulian Elischer /* 522499676dfSJulian Elischer * one more rtentry floating around that is not 523499676dfSJulian Elischer * linked to the routing table. 524499676dfSJulian Elischer */ 525499676dfSJulian Elischer rttrash++; 526499676dfSJulian Elischer 527499676dfSJulian Elischer /* 528499676dfSJulian Elischer * If the caller wants it, then it can have it, 529499676dfSJulian Elischer * but it's up to it to free the rtentry as we won't be 530499676dfSJulian Elischer * doing it. 531b0a76b88SJulian Elischer */ 532df8bae1dSRodney W. Grimes if (ret_nrt) 533df8bae1dSRodney W. Grimes *ret_nrt = rt; 534df8bae1dSRodney W. Grimes else if (rt->rt_refcnt <= 0) { 535b0a76b88SJulian Elischer rt->rt_refcnt++; /* make a 1->0 transition */ 536df8bae1dSRodney W. Grimes rtfree(rt); 537df8bae1dSRodney W. Grimes } 538df8bae1dSRodney W. Grimes break; 539df8bae1dSRodney W. Grimes 540df8bae1dSRodney W. Grimes case RTM_RESOLVE: 541df8bae1dSRodney W. Grimes if (ret_nrt == 0 || (rt = *ret_nrt) == 0) 542df8bae1dSRodney W. Grimes senderr(EINVAL); 543df8bae1dSRodney W. Grimes ifa = rt->rt_ifa; 5443682d2baSDavid Greenman flags = rt->rt_flags & 5453682d2baSDavid Greenman ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC); 546995add1aSGarrett Wollman flags |= RTF_WASCLONED; 547df8bae1dSRodney W. Grimes gateway = rt->rt_gateway; 548df8bae1dSRodney W. Grimes if ((netmask = rt->rt_genmask) == 0) 549df8bae1dSRodney W. Grimes flags |= RTF_HOST; 550df8bae1dSRodney W. Grimes goto makeroute; 551df8bae1dSRodney W. Grimes 552df8bae1dSRodney W. Grimes case RTM_ADD: 5535df72964SGarrett Wollman if ((flags & RTF_GATEWAY) && !gateway) 5545df72964SGarrett Wollman panic("rtrequest: GATEWAY but no gateway"); 5555df72964SGarrett Wollman 556df8bae1dSRodney W. Grimes if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) 557df8bae1dSRodney W. Grimes senderr(ENETUNREACH); 5585df72964SGarrett Wollman 559df8bae1dSRodney W. Grimes makeroute: 560df8bae1dSRodney W. Grimes R_Malloc(rt, struct rtentry *, sizeof(*rt)); 561df8bae1dSRodney W. Grimes if (rt == 0) 562df8bae1dSRodney W. Grimes senderr(ENOBUFS); 563df8bae1dSRodney W. Grimes Bzero(rt, sizeof(*rt)); 564df8bae1dSRodney W. Grimes rt->rt_flags = RTF_UP | flags; 565499676dfSJulian Elischer /* 566499676dfSJulian Elischer * Add the gateway. Possibly re-malloc-ing the storage for it 567499676dfSJulian Elischer * also add the rt_gwroute if possible. 568499676dfSJulian Elischer */ 569704b0666SBill Fenner if (error = rt_setgate(rt, dst, gateway)) { 570df8bae1dSRodney W. Grimes Free(rt); 571704b0666SBill Fenner senderr(error); 572df8bae1dSRodney W. Grimes } 573499676dfSJulian Elischer 574499676dfSJulian Elischer /* 575499676dfSJulian Elischer * point to the (possibly newly malloc'd) dest address. 576499676dfSJulian Elischer */ 577df8bae1dSRodney W. Grimes ndst = rt_key(rt); 578499676dfSJulian Elischer 579499676dfSJulian Elischer /* 580499676dfSJulian Elischer * make sure it contains the value we want (masked if needed). 581499676dfSJulian Elischer */ 582df8bae1dSRodney W. Grimes if (netmask) { 583df8bae1dSRodney W. Grimes rt_maskedcopy(dst, ndst, netmask); 584df8bae1dSRodney W. Grimes } else 585df8bae1dSRodney W. Grimes Bcopy(dst, ndst, dst->sa_len); 5868e718bb4SGarrett Wollman 5878e718bb4SGarrett Wollman /* 588499676dfSJulian Elischer * Note that we now have a reference to the ifa. 5898e718bb4SGarrett Wollman * This moved from below so that rnh->rnh_addaddr() can 590499676dfSJulian Elischer * examine the ifa and ifa->ifa_ifp if it so desires. 5918e718bb4SGarrett Wollman */ 5928e718bb4SGarrett Wollman ifa->ifa_refcnt++; 5938e718bb4SGarrett Wollman rt->rt_ifa = ifa; 5948e718bb4SGarrett Wollman rt->rt_ifp = ifa->ifa_ifp; 5958e718bb4SGarrett Wollman 596df8bae1dSRodney W. Grimes rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask, 597df8bae1dSRodney W. Grimes rnh, rt->rt_nodes); 598df8bae1dSRodney W. Grimes if (rn == 0) { 599aca1a47cSGarrett Wollman struct rtentry *rt2; 600aca1a47cSGarrett Wollman /* 601aca1a47cSGarrett Wollman * Uh-oh, we already have one of these in the tree. 602aca1a47cSGarrett Wollman * We do a special hack: if the route that's already 603aca1a47cSGarrett Wollman * there was generated by the protocol-cloning 604aca1a47cSGarrett Wollman * mechanism, then we just blow it away and retry 605aca1a47cSGarrett Wollman * the insertion of the new one. 606aca1a47cSGarrett Wollman */ 607aca1a47cSGarrett Wollman rt2 = rtalloc1(dst, 0, RTF_PRCLONING); 608aca1a47cSGarrett Wollman if (rt2 && rt2->rt_parent) { 609aca1a47cSGarrett Wollman rtrequest(RTM_DELETE, 610aca1a47cSGarrett Wollman (struct sockaddr *)rt_key(rt2), 611aca1a47cSGarrett Wollman rt2->rt_gateway, 612aca1a47cSGarrett Wollman rt_mask(rt2), rt2->rt_flags, 0); 613aca1a47cSGarrett Wollman RTFREE(rt2); 614aca1a47cSGarrett Wollman rn = rnh->rnh_addaddr((caddr_t)ndst, 615aca1a47cSGarrett Wollman (caddr_t)netmask, 616aca1a47cSGarrett Wollman rnh, rt->rt_nodes); 617fde327d6SGarrett Wollman } else if (rt2) { 618499676dfSJulian Elischer /* undo the extra ref we got */ 619fde327d6SGarrett Wollman RTFREE(rt2); 620aca1a47cSGarrett Wollman } 621aca1a47cSGarrett Wollman } 622aca1a47cSGarrett Wollman 623499676dfSJulian Elischer /* 624499676dfSJulian Elischer * If it still failed to go into the tree, 625499676dfSJulian Elischer * then un-make it (this should be a function) 626499676dfSJulian Elischer */ 627aca1a47cSGarrett Wollman if (rn == 0) { 628df8bae1dSRodney W. Grimes if (rt->rt_gwroute) 629df8bae1dSRodney W. Grimes rtfree(rt->rt_gwroute); 6308e718bb4SGarrett Wollman if (rt->rt_ifa) { 6318e718bb4SGarrett Wollman IFAFREE(rt->rt_ifa); 6328e718bb4SGarrett Wollman } 633df8bae1dSRodney W. Grimes Free(rt_key(rt)); 634df8bae1dSRodney W. Grimes Free(rt); 635df8bae1dSRodney W. Grimes senderr(EEXIST); 636df8bae1dSRodney W. Grimes } 637499676dfSJulian Elischer 638771edb14SGarrett Wollman rt->rt_parent = 0; 639771edb14SGarrett Wollman 640499676dfSJulian Elischer /* 641499676dfSJulian Elischer * If we got here from RESOLVE, then we are cloning 642499676dfSJulian Elischer * so clone the rest, and note that we 643499676dfSJulian Elischer * are a clone (and increment the parent's references) 644499676dfSJulian Elischer */ 645c2bed6a3SGarrett Wollman if (req == RTM_RESOLVE) { 646df8bae1dSRodney W. Grimes rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ 647771edb14SGarrett Wollman if ((*ret_nrt)->rt_flags & RTF_PRCLONING) { 64818e1f1f1SGarrett Wollman rt->rt_parent = (*ret_nrt); 649771edb14SGarrett Wollman (*ret_nrt)->rt_refcnt++; 650771edb14SGarrett Wollman } 65118e1f1f1SGarrett Wollman } 652499676dfSJulian Elischer 653499676dfSJulian Elischer /* 654499676dfSJulian Elischer * if this protocol has something to add to this then 655499676dfSJulian Elischer * allow it to do that as well. 656499676dfSJulian Elischer */ 657df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 658df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); 659499676dfSJulian Elischer 660cd02a0b7SGarrett Wollman /* 661cd02a0b7SGarrett Wollman * We repeat the same procedure from rt_setgate() here because 662cd02a0b7SGarrett Wollman * it doesn't fire when we call it there because the node 663cd02a0b7SGarrett Wollman * hasn't been added to the tree yet. 664cd02a0b7SGarrett Wollman */ 6653271a3a4SPeter Wemm if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { 666cd02a0b7SGarrett Wollman struct rtfc_arg arg; 667cd02a0b7SGarrett Wollman arg.rnh = rnh; 668cd02a0b7SGarrett Wollman arg.rt0 = rt; 669cd02a0b7SGarrett Wollman rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), 670cd02a0b7SGarrett Wollman rt_fixchange, &arg); 671cd02a0b7SGarrett Wollman } 672cd02a0b7SGarrett Wollman 673499676dfSJulian Elischer /* 674499676dfSJulian Elischer * actually return a resultant rtentry and 675499676dfSJulian Elischer * give the caller a single reference. 676499676dfSJulian Elischer */ 677df8bae1dSRodney W. Grimes if (ret_nrt) { 678df8bae1dSRodney W. Grimes *ret_nrt = rt; 679df8bae1dSRodney W. Grimes rt->rt_refcnt++; 680df8bae1dSRodney W. Grimes } 681df8bae1dSRodney W. Grimes break; 682df8bae1dSRodney W. Grimes } 683df8bae1dSRodney W. Grimes bad: 684df8bae1dSRodney W. Grimes splx(s); 685df8bae1dSRodney W. Grimes return (error); 686df8bae1dSRodney W. Grimes } 687df8bae1dSRodney W. Grimes 68818e1f1f1SGarrett Wollman /* 68918e1f1f1SGarrett Wollman * Called from rtrequest(RTM_DELETE, ...) to fix up the route's ``family'' 69018e1f1f1SGarrett Wollman * (i.e., the routes related to it by the operation of cloning). This 691c2bed6a3SGarrett Wollman * routine is iterated over all potential former-child-routes by way of 692c2bed6a3SGarrett Wollman * rnh->rnh_walktree_from() above, and those that actually are children of 693c2bed6a3SGarrett Wollman * the late parent (passed in as VP here) are themselves deleted. 69418e1f1f1SGarrett Wollman */ 695c2bed6a3SGarrett Wollman static int 696514ede09SBruce Evans rt_fixdelete(rn, vp) 697514ede09SBruce Evans struct radix_node *rn; 698514ede09SBruce Evans void *vp; 69918e1f1f1SGarrett Wollman { 700c2bed6a3SGarrett Wollman struct rtentry *rt = (struct rtentry *)rn; 701c2bed6a3SGarrett Wollman struct rtentry *rt0 = vp; 70218e1f1f1SGarrett Wollman 703a29ae2a1SGarrett Wollman if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) { 704c2bed6a3SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), 70518e1f1f1SGarrett Wollman (struct sockaddr *)0, rt_mask(rt), 70618e1f1f1SGarrett Wollman rt->rt_flags, (struct rtentry **)0); 70718e1f1f1SGarrett Wollman } 708c2bed6a3SGarrett Wollman return 0; 70918e1f1f1SGarrett Wollman } 71018e1f1f1SGarrett Wollman 711cd02a0b7SGarrett Wollman /* 712cd02a0b7SGarrett Wollman * This routine is called from rt_setgate() to do the analogous thing for 713cd02a0b7SGarrett Wollman * adds and changes. There is the added complication in this case of a 714cd02a0b7SGarrett Wollman * middle insert; i.e., insertion of a new network route between an older 715cd02a0b7SGarrett Wollman * network route and (cloned) host routes. For this reason, a simple check 716cd02a0b7SGarrett Wollman * of rt->rt_parent is insufficient; each candidate route must be tested 717cd02a0b7SGarrett Wollman * against the (mask, value) of the new route (passed as before in vp) 718cd02a0b7SGarrett Wollman * to see if the new route matches it. Unfortunately, this has the obnoxious 719cd02a0b7SGarrett Wollman * property of also triggering for insertion /above/ a pre-existing network 720cd02a0b7SGarrett Wollman * route and clones. Sigh. This may be fixed some day. 721cd02a0b7SGarrett Wollman * 722cd02a0b7SGarrett Wollman * XXX - it may be possible to do fixdelete() for changes and reserve this 723cd02a0b7SGarrett Wollman * routine just for adds. I'm not sure why I thought it was necessary to do 724cd02a0b7SGarrett Wollman * changes this way. 725cd02a0b7SGarrett Wollman */ 726cd02a0b7SGarrett Wollman #ifdef DEBUG 727cd02a0b7SGarrett Wollman int rtfcdebug = 0; 728cd02a0b7SGarrett Wollman #endif 729cd02a0b7SGarrett Wollman 730cd02a0b7SGarrett Wollman static int 731514ede09SBruce Evans rt_fixchange(rn, vp) 732514ede09SBruce Evans struct radix_node *rn; 733514ede09SBruce Evans void *vp; 734cd02a0b7SGarrett Wollman { 735cd02a0b7SGarrett Wollman struct rtentry *rt = (struct rtentry *)rn; 736cd02a0b7SGarrett Wollman struct rtfc_arg *ap = vp; 737cd02a0b7SGarrett Wollman struct rtentry *rt0 = ap->rt0; 738cd02a0b7SGarrett Wollman struct radix_node_head *rnh = ap->rnh; 739cd02a0b7SGarrett Wollman u_char *xk1, *xm1, *xk2; 740cd02a0b7SGarrett Wollman int i, len; 741cd02a0b7SGarrett Wollman 742cd02a0b7SGarrett Wollman #ifdef DEBUG 743cd02a0b7SGarrett Wollman if (rtfcdebug) 744cd02a0b7SGarrett Wollman printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0); 745cd02a0b7SGarrett Wollman #endif 746cd02a0b7SGarrett Wollman 747cd02a0b7SGarrett Wollman if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) { 748cd02a0b7SGarrett Wollman #ifdef DEBUG 749cd02a0b7SGarrett Wollman if(rtfcdebug) printf("no parent or pinned\n"); 750cd02a0b7SGarrett Wollman #endif 751cd02a0b7SGarrett Wollman return 0; 752cd02a0b7SGarrett Wollman } 753cd02a0b7SGarrett Wollman 754cd02a0b7SGarrett Wollman if (rt->rt_parent == rt0) { 755cd02a0b7SGarrett Wollman #ifdef DEBUG 756cd02a0b7SGarrett Wollman if(rtfcdebug) printf("parent match\n"); 757cd02a0b7SGarrett Wollman #endif 758cd02a0b7SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), 759cd02a0b7SGarrett Wollman (struct sockaddr *)0, rt_mask(rt), 760cd02a0b7SGarrett Wollman rt->rt_flags, (struct rtentry **)0); 761cd02a0b7SGarrett Wollman } 762cd02a0b7SGarrett Wollman 763cd02a0b7SGarrett Wollman /* 764cd02a0b7SGarrett Wollman * There probably is a function somewhere which does this... 765cd02a0b7SGarrett Wollman * if not, there should be. 766cd02a0b7SGarrett Wollman */ 767cd02a0b7SGarrett Wollman len = imin(((struct sockaddr *)rt_key(rt0))->sa_len, 768cd02a0b7SGarrett Wollman ((struct sockaddr *)rt_key(rt))->sa_len); 769cd02a0b7SGarrett Wollman 770cd02a0b7SGarrett Wollman xk1 = (u_char *)rt_key(rt0); 771cd02a0b7SGarrett Wollman xm1 = (u_char *)rt_mask(rt0); 772cd02a0b7SGarrett Wollman xk2 = (u_char *)rt_key(rt); 773cd02a0b7SGarrett Wollman 774cd02a0b7SGarrett Wollman for (i = rnh->rnh_treetop->rn_off; i < len; i++) { 775cd02a0b7SGarrett Wollman if ((xk2[i] & xm1[i]) != xk1[i]) { 776cd02a0b7SGarrett Wollman #ifdef DEBUG 777cd02a0b7SGarrett Wollman if(rtfcdebug) printf("no match\n"); 778cd02a0b7SGarrett Wollman #endif 779cd02a0b7SGarrett Wollman return 0; 780cd02a0b7SGarrett Wollman } 781cd02a0b7SGarrett Wollman } 782cd02a0b7SGarrett Wollman 783cd02a0b7SGarrett Wollman /* 784cd02a0b7SGarrett Wollman * OK, this node is a clone, and matches the node currently being 785cd02a0b7SGarrett Wollman * changed/added under the node's mask. So, get rid of it. 786cd02a0b7SGarrett Wollman */ 787cd02a0b7SGarrett Wollman #ifdef DEBUG 788cd02a0b7SGarrett Wollman if(rtfcdebug) printf("deleting\n"); 789cd02a0b7SGarrett Wollman #endif 790cd02a0b7SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, 791cd02a0b7SGarrett Wollman rt_mask(rt), rt->rt_flags, (struct rtentry **)0); 792cd02a0b7SGarrett Wollman } 793cd02a0b7SGarrett Wollman 794df8bae1dSRodney W. Grimes int 795df8bae1dSRodney W. Grimes rt_setgate(rt0, dst, gate) 796df8bae1dSRodney W. Grimes struct rtentry *rt0; 797df8bae1dSRodney W. Grimes struct sockaddr *dst, *gate; 798df8bae1dSRodney W. Grimes { 799df8bae1dSRodney W. Grimes caddr_t new, old; 800df8bae1dSRodney W. Grimes int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len); 801df8bae1dSRodney W. Grimes register struct rtentry *rt = rt0; 802cd02a0b7SGarrett Wollman struct radix_node_head *rnh = rt_tables[dst->sa_family]; 803df8bae1dSRodney W. Grimes 8041db1fffaSBill Fenner /* 8051db1fffaSBill Fenner * A host route with the destination equal to the gateway 8061db1fffaSBill Fenner * will interfere with keeping LLINFO in the routing 8071db1fffaSBill Fenner * table, so disallow it. 8081db1fffaSBill Fenner */ 8091db1fffaSBill Fenner if (((rt0->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) == 8101db1fffaSBill Fenner (RTF_HOST|RTF_GATEWAY)) && 8111db1fffaSBill Fenner (dst->sa_len == gate->sa_len) && 8121db1fffaSBill Fenner (bcmp(dst, gate, dst->sa_len) == 0)) { 8131db1fffaSBill Fenner /* 8141db1fffaSBill Fenner * The route might already exist if this is an RTM_CHANGE 8151db1fffaSBill Fenner * or a routing redirect, so try to delete it. 8161db1fffaSBill Fenner */ 817704b0666SBill Fenner if (rt_key(rt0)) 8181db1fffaSBill Fenner rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt0), 8191db1fffaSBill Fenner rt0->rt_gateway, rt_mask(rt0), rt0->rt_flags, 0); 8201db1fffaSBill Fenner return EADDRNOTAVAIL; 8211db1fffaSBill Fenner } 8221db1fffaSBill Fenner 823499676dfSJulian Elischer /* 824499676dfSJulian Elischer * Both dst and gateway are stored in the same malloc'd chunk 825499676dfSJulian Elischer * (If I ever get my hands on....) 826499676dfSJulian Elischer * if we need to malloc a new chunk, then keep the old one around 827499676dfSJulian Elischer * till we don't need it any more. 828499676dfSJulian Elischer */ 829df8bae1dSRodney W. Grimes if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) { 830df8bae1dSRodney W. Grimes old = (caddr_t)rt_key(rt); 831df8bae1dSRodney W. Grimes R_Malloc(new, caddr_t, dlen + glen); 832df8bae1dSRodney W. Grimes if (new == 0) 8331db1fffaSBill Fenner return ENOBUFS; 834df8bae1dSRodney W. Grimes rt->rt_nodes->rn_key = new; 835df8bae1dSRodney W. Grimes } else { 836499676dfSJulian Elischer /* 837499676dfSJulian Elischer * otherwise just overwrite the old one 838499676dfSJulian Elischer */ 839df8bae1dSRodney W. Grimes new = rt->rt_nodes->rn_key; 840df8bae1dSRodney W. Grimes old = 0; 841df8bae1dSRodney W. Grimes } 842499676dfSJulian Elischer 843499676dfSJulian Elischer /* 844499676dfSJulian Elischer * copy the new gateway value into the memory chunk 845499676dfSJulian Elischer */ 846df8bae1dSRodney W. Grimes Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen); 847499676dfSJulian Elischer 848499676dfSJulian Elischer /* 849499676dfSJulian Elischer * if we are replacing the chunk (or it's new) we need to 850499676dfSJulian Elischer * replace the dst as well 851499676dfSJulian Elischer */ 852df8bae1dSRodney W. Grimes if (old) { 853df8bae1dSRodney W. Grimes Bcopy(dst, new, dlen); 854df8bae1dSRodney W. Grimes Free(old); 855df8bae1dSRodney W. Grimes } 856499676dfSJulian Elischer 857499676dfSJulian Elischer /* 858499676dfSJulian Elischer * If there is already a gwroute, it's now almost definitly wrong 859499676dfSJulian Elischer * so drop it. 860499676dfSJulian Elischer */ 861df8bae1dSRodney W. Grimes if (rt->rt_gwroute) { 862df8bae1dSRodney W. Grimes rt = rt->rt_gwroute; RTFREE(rt); 863df8bae1dSRodney W. Grimes rt = rt0; rt->rt_gwroute = 0; 864df8bae1dSRodney W. Grimes } 865cd02a0b7SGarrett Wollman /* 866cd02a0b7SGarrett Wollman * Cloning loop avoidance: 867cd02a0b7SGarrett Wollman * In the presence of protocol-cloning and bad configuration, 868cd02a0b7SGarrett Wollman * it is possible to get stuck in bottomless mutual recursion 869cd02a0b7SGarrett Wollman * (rtrequest rt_setgate rtalloc1). We avoid this by not allowing 870cd02a0b7SGarrett Wollman * protocol-cloning to operate for gateways (which is probably the 871cd02a0b7SGarrett Wollman * correct choice anyway), and avoid the resulting reference loops 872cd02a0b7SGarrett Wollman * by disallowing any route to run through itself as a gateway. 873499676dfSJulian Elischer * This is obviously mandatory when we get rt->rt_output(). 874cd02a0b7SGarrett Wollman */ 875df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 876cd02a0b7SGarrett Wollman rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING); 877cd02a0b7SGarrett Wollman if (rt->rt_gwroute == rt) { 878cd02a0b7SGarrett Wollman RTFREE(rt->rt_gwroute); 879cd02a0b7SGarrett Wollman rt->rt_gwroute = 0; 8801db1fffaSBill Fenner return EDQUOT; /* failure */ 881df8bae1dSRodney W. Grimes } 882cd02a0b7SGarrett Wollman } 883cd02a0b7SGarrett Wollman 884cd02a0b7SGarrett Wollman /* 885cd02a0b7SGarrett Wollman * This isn't going to do anything useful for host routes, so 886cd02a0b7SGarrett Wollman * don't bother. Also make sure we have a reasonable mask 887cd02a0b7SGarrett Wollman * (we don't yet have one during adds). 888cd02a0b7SGarrett Wollman */ 889cd02a0b7SGarrett Wollman if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { 890cd02a0b7SGarrett Wollman struct rtfc_arg arg; 891cd02a0b7SGarrett Wollman arg.rnh = rnh; 892cd02a0b7SGarrett Wollman arg.rt0 = rt; 893cd02a0b7SGarrett Wollman rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), 894cd02a0b7SGarrett Wollman rt_fixchange, &arg); 895cd02a0b7SGarrett Wollman } 896cd02a0b7SGarrett Wollman 897df8bae1dSRodney W. Grimes return 0; 898df8bae1dSRodney W. Grimes } 899df8bae1dSRodney W. Grimes 900f708ef1bSPoul-Henning Kamp static void 901df8bae1dSRodney W. Grimes rt_maskedcopy(src, dst, netmask) 902df8bae1dSRodney W. Grimes struct sockaddr *src, *dst, *netmask; 903df8bae1dSRodney W. Grimes { 904df8bae1dSRodney W. Grimes register u_char *cp1 = (u_char *)src; 905df8bae1dSRodney W. Grimes register u_char *cp2 = (u_char *)dst; 906df8bae1dSRodney W. Grimes register u_char *cp3 = (u_char *)netmask; 907df8bae1dSRodney W. Grimes u_char *cplim = cp2 + *cp3; 908df8bae1dSRodney W. Grimes u_char *cplim2 = cp2 + *cp1; 909df8bae1dSRodney W. Grimes 910df8bae1dSRodney W. Grimes *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ 911df8bae1dSRodney W. Grimes cp3 += 2; 912df8bae1dSRodney W. Grimes if (cplim > cplim2) 913df8bae1dSRodney W. Grimes cplim = cplim2; 914df8bae1dSRodney W. Grimes while (cp2 < cplim) 915df8bae1dSRodney W. Grimes *cp2++ = *cp1++ & *cp3++; 916df8bae1dSRodney W. Grimes if (cp2 < cplim2) 917df8bae1dSRodney W. Grimes bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); 918df8bae1dSRodney W. Grimes } 919df8bae1dSRodney W. Grimes 920df8bae1dSRodney W. Grimes /* 921df8bae1dSRodney W. Grimes * Set up a routing table entry, normally 922df8bae1dSRodney W. Grimes * for an interface. 923df8bae1dSRodney W. Grimes */ 924df8bae1dSRodney W. Grimes int 925df8bae1dSRodney W. Grimes rtinit(ifa, cmd, flags) 926df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 927df8bae1dSRodney W. Grimes int cmd, flags; 928df8bae1dSRodney W. Grimes { 929df8bae1dSRodney W. Grimes register struct rtentry *rt; 930df8bae1dSRodney W. Grimes register struct sockaddr *dst; 931df8bae1dSRodney W. Grimes register struct sockaddr *deldst; 932df8bae1dSRodney W. Grimes struct mbuf *m = 0; 933df8bae1dSRodney W. Grimes struct rtentry *nrt = 0; 934df8bae1dSRodney W. Grimes int error; 935df8bae1dSRodney W. Grimes 936df8bae1dSRodney W. Grimes dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; 937b0a76b88SJulian Elischer /* 938b0a76b88SJulian Elischer * If it's a delete, check that if it exists, it's on the correct 939b0a76b88SJulian Elischer * interface or we might scrub a route to another ifa which would 940b0a76b88SJulian Elischer * be confusing at best and possibly worse. 941b0a76b88SJulian Elischer */ 942df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE) { 943b0a76b88SJulian Elischer /* 944b0a76b88SJulian Elischer * It's a delete, so it should already exist.. 945b0a76b88SJulian Elischer * If it's a net, mask off the host bits 946b0a76b88SJulian Elischer * (Assuming we have a mask) 947b0a76b88SJulian Elischer */ 948df8bae1dSRodney W. Grimes if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { 949df8bae1dSRodney W. Grimes m = m_get(M_WAIT, MT_SONAME); 950df8bae1dSRodney W. Grimes deldst = mtod(m, struct sockaddr *); 951df8bae1dSRodney W. Grimes rt_maskedcopy(dst, deldst, ifa->ifa_netmask); 952df8bae1dSRodney W. Grimes dst = deldst; 953df8bae1dSRodney W. Grimes } 954b0a76b88SJulian Elischer /* 955b0a76b88SJulian Elischer * Get an rtentry that is in the routing tree and 956499676dfSJulian Elischer * contains the correct info. (if this fails, can't get there). 957b0a76b88SJulian Elischer * We set "report" to FALSE so that if it doesn't exist, 958b0a76b88SJulian Elischer * it doesn't report an error or clone a route, etc. etc. 959b0a76b88SJulian Elischer */ 960995add1aSGarrett Wollman rt = rtalloc1(dst, 0, 0UL); 961623ae52eSPoul-Henning Kamp if (rt) { 962b0a76b88SJulian Elischer /* 963b0a76b88SJulian Elischer * Ok so we found the rtentry. it has an extra reference 964b0a76b88SJulian Elischer * for us at this stage. we won't need that so 965b0a76b88SJulian Elischer * lop that off now. 966b0a76b88SJulian Elischer */ 967df8bae1dSRodney W. Grimes rt->rt_refcnt--; 968df8bae1dSRodney W. Grimes if (rt->rt_ifa != ifa) { 969b0a76b88SJulian Elischer /* 970b0a76b88SJulian Elischer * If the interface in the rtentry doesn't match 971b0a76b88SJulian Elischer * the interface we are using, then we don't 972b0a76b88SJulian Elischer * want to delete it, so return an error. 973b0a76b88SJulian Elischer * This seems to be the only point of 974b0a76b88SJulian Elischer * this whole RTM_DELETE clause. 975b0a76b88SJulian Elischer */ 976df8bae1dSRodney W. Grimes if (m) 977df8bae1dSRodney W. Grimes (void) m_free(m); 978df8bae1dSRodney W. Grimes return (flags & RTF_HOST ? EHOSTUNREACH 979df8bae1dSRodney W. Grimes : ENETUNREACH); 980df8bae1dSRodney W. Grimes } 981df8bae1dSRodney W. Grimes } 982b0a76b88SJulian Elischer /* XXX */ 983b0a76b88SJulian Elischer #if 0 984b0a76b88SJulian Elischer else { 985b0a76b88SJulian Elischer /* 986b0a76b88SJulian Elischer * One would think that as we are deleting, and we know 987b0a76b88SJulian Elischer * it doesn't exist, we could just return at this point 988b0a76b88SJulian Elischer * with an "ELSE" clause, but apparently not.. 989b0a76b88SJulian Elischer */ 990b0a76b88SJulian Elischer return (flags & RTF_HOST ? EHOSTUNREACH 991b0a76b88SJulian Elischer : ENETUNREACH); 992df8bae1dSRodney W. Grimes } 993b0a76b88SJulian Elischer #endif 994b0a76b88SJulian Elischer } 995b0a76b88SJulian Elischer /* 996b0a76b88SJulian Elischer * Do the actual request 997b0a76b88SJulian Elischer */ 998df8bae1dSRodney W. Grimes error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, 999df8bae1dSRodney W. Grimes flags | ifa->ifa_flags, &nrt); 1000df8bae1dSRodney W. Grimes if (m) 1001df8bae1dSRodney W. Grimes (void) m_free(m); 1002b0a76b88SJulian Elischer /* 1003b0a76b88SJulian Elischer * If we are deleting, and we found an entry, then 1004b0a76b88SJulian Elischer * it's been removed from the tree.. now throw it away. 1005b0a76b88SJulian Elischer */ 1006df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) { 1007b0a76b88SJulian Elischer /* 1008b0a76b88SJulian Elischer * notify any listenning routing agents of the change 1009b0a76b88SJulian Elischer */ 1010df8bae1dSRodney W. Grimes rt_newaddrmsg(cmd, ifa, error, nrt); 1011df8bae1dSRodney W. Grimes if (rt->rt_refcnt <= 0) { 1012b0a76b88SJulian Elischer rt->rt_refcnt++; /* need a 1->0 transition to free */ 1013df8bae1dSRodney W. Grimes rtfree(rt); 1014df8bae1dSRodney W. Grimes } 1015df8bae1dSRodney W. Grimes } 1016b0a76b88SJulian Elischer 1017b0a76b88SJulian Elischer /* 1018b0a76b88SJulian Elischer * We are adding, and we have a returned routing entry. 1019b0a76b88SJulian Elischer * We need to sanity check the result. 1020b0a76b88SJulian Elischer */ 1021df8bae1dSRodney W. Grimes if (cmd == RTM_ADD && error == 0 && (rt = nrt)) { 1022b0a76b88SJulian Elischer /* 1023b0a76b88SJulian Elischer * We just wanted to add it.. we don't actually need a reference 1024b0a76b88SJulian Elischer */ 1025df8bae1dSRodney W. Grimes rt->rt_refcnt--; 1026b0a76b88SJulian Elischer /* 1027b0a76b88SJulian Elischer * If it came back with an unexpected interface, then it must 1028b0a76b88SJulian Elischer * have already existed or something. (XXX) 1029b0a76b88SJulian Elischer */ 1030df8bae1dSRodney W. Grimes if (rt->rt_ifa != ifa) { 1031623ae52eSPoul-Henning Kamp printf("rtinit: wrong ifa (%p) was (%p)\n", ifa, 1032df8bae1dSRodney W. Grimes rt->rt_ifa); 1033b0a76b88SJulian Elischer /* 1034499676dfSJulian Elischer * Ask that the protocol in question 1035499676dfSJulian Elischer * remove anything it has associated with 1036499676dfSJulian Elischer * this route and ifaddr. 1037b0a76b88SJulian Elischer */ 1038df8bae1dSRodney W. Grimes if (rt->rt_ifa->ifa_rtrequest) 1039df8bae1dSRodney W. Grimes rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 1040b0a76b88SJulian Elischer /* 1041b0a76b88SJulian Elischer * Remove the referenve to the it's ifaddr. 1042b0a76b88SJulian Elischer */ 1043df8bae1dSRodney W. Grimes IFAFREE(rt->rt_ifa); 1044b0a76b88SJulian Elischer /* 1045b0a76b88SJulian Elischer * And substitute in references to the ifaddr 1046b0a76b88SJulian Elischer * we are adding. 1047b0a76b88SJulian Elischer */ 1048df8bae1dSRodney W. Grimes rt->rt_ifa = ifa; 1049df8bae1dSRodney W. Grimes rt->rt_ifp = ifa->ifa_ifp; 1050df8bae1dSRodney W. Grimes ifa->ifa_refcnt++; 1051b0a76b88SJulian Elischer /* 1052499676dfSJulian Elischer * Now ask the protocol to check if it needs 1053499676dfSJulian Elischer * any special processing in it's new form. 1054b0a76b88SJulian Elischer */ 1055df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 1056df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(RTM_ADD, rt, SA(0)); 1057df8bae1dSRodney W. Grimes } 1058b0a76b88SJulian Elischer /* 1059b0a76b88SJulian Elischer * notify any listenning routing agents of the change 1060b0a76b88SJulian Elischer */ 1061df8bae1dSRodney W. Grimes rt_newaddrmsg(cmd, ifa, error, nrt); 1062df8bae1dSRodney W. Grimes } 10633ec66d6cSDavid Greenman return (error); 10643ec66d6cSDavid Greenman } 1065