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 344d1d4912SBruce Evans * $Id: route.c,v 1.42 1997/03/24 11:24:47 bde Exp $ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 374bd49128SPeter Wemm #include "opt_mrouting.h" 384bd49128SPeter Wemm 39df8bae1dSRodney W. Grimes #include <sys/param.h> 402ee45d7dSDavid Greenman #include <sys/queue.h> 41df8bae1dSRodney W. Grimes #include <sys/systm.h> 425df72964SGarrett Wollman #include <sys/kernel.h> 43df8bae1dSRodney W. Grimes #include <sys/proc.h> 444d1d4912SBruce Evans #include <sys/malloc.h> 45df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 46df8bae1dSRodney W. Grimes #include <sys/socket.h> 47df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 48df8bae1dSRodney W. Grimes #include <sys/domain.h> 49df8bae1dSRodney W. Grimes #include <sys/protosw.h> 50df8bae1dSRodney W. Grimes 51df8bae1dSRodney W. Grimes #include <net/if.h> 52df8bae1dSRodney W. Grimes #include <net/route.h> 53df8bae1dSRodney W. Grimes #include <net/raw_cb.h> 54df8bae1dSRodney W. Grimes 55df8bae1dSRodney W. Grimes #include <netinet/in.h> 56df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 57b5e8ce9fSBruce Evans #include <netinet/ip_mroute.h> 58df8bae1dSRodney W. Grimes 59df8bae1dSRodney W. Grimes #define SA(p) ((struct sockaddr *)(p)) 60df8bae1dSRodney W. Grimes 6128f8db14SBruce Evans struct route_cb route_cb; 62f708ef1bSPoul-Henning Kamp static struct rtstat rtstat; 6328f8db14SBruce Evans struct radix_node_head *rt_tables[AF_MAX+1]; 6428f8db14SBruce Evans 65f708ef1bSPoul-Henning Kamp static int rttrash; /* routes not in table but not freed */ 66df8bae1dSRodney W. Grimes 67f708ef1bSPoul-Henning Kamp static void rt_maskedcopy __P((struct sockaddr *, 68f708ef1bSPoul-Henning Kamp struct sockaddr *, struct sockaddr *)); 69f708ef1bSPoul-Henning Kamp static void rtable_init __P((void **)); 70f708ef1bSPoul-Henning Kamp 71f708ef1bSPoul-Henning Kamp static void 72df8bae1dSRodney W. Grimes rtable_init(table) 73df8bae1dSRodney W. Grimes void **table; 74df8bae1dSRodney W. Grimes { 75df8bae1dSRodney W. Grimes struct domain *dom; 76df8bae1dSRodney W. Grimes for (dom = domains; dom; dom = dom->dom_next) 77df8bae1dSRodney W. Grimes if (dom->dom_rtattach) 78df8bae1dSRodney W. Grimes dom->dom_rtattach(&table[dom->dom_family], 79df8bae1dSRodney W. Grimes dom->dom_rtoffset); 80df8bae1dSRodney W. Grimes } 81df8bae1dSRodney W. Grimes 82df8bae1dSRodney W. Grimes void 83df8bae1dSRodney W. Grimes route_init() 84df8bae1dSRodney W. Grimes { 85df8bae1dSRodney W. Grimes rn_init(); /* initialize all zeroes, all ones, mask table */ 86df8bae1dSRodney W. Grimes rtable_init((void **)rt_tables); 87df8bae1dSRodney W. Grimes } 88df8bae1dSRodney W. Grimes 89df8bae1dSRodney W. Grimes /* 90df8bae1dSRodney W. Grimes * Packet routing routines. 91df8bae1dSRodney W. Grimes */ 92df8bae1dSRodney W. Grimes void 93df8bae1dSRodney W. Grimes rtalloc(ro) 94df8bae1dSRodney W. Grimes register struct route *ro; 95df8bae1dSRodney W. Grimes { 96df8bae1dSRodney W. Grimes if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 97df8bae1dSRodney W. Grimes return; /* XXX */ 98995add1aSGarrett Wollman ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL); 99df8bae1dSRodney W. Grimes } 100df8bae1dSRodney W. Grimes 101652082e6SGarrett Wollman void 102652082e6SGarrett Wollman rtalloc_ign(ro, ignore) 103652082e6SGarrett Wollman register struct route *ro; 104652082e6SGarrett Wollman u_long ignore; 105652082e6SGarrett Wollman { 106652082e6SGarrett Wollman if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 107652082e6SGarrett Wollman return; /* XXX */ 108652082e6SGarrett Wollman ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore); 109652082e6SGarrett Wollman } 110652082e6SGarrett Wollman 111b0a76b88SJulian Elischer /* 112b0a76b88SJulian Elischer * Look up the route that matches the address given 113b0a76b88SJulian Elischer * Or, at least try.. Create a cloned route if needed. 114b0a76b88SJulian Elischer */ 115df8bae1dSRodney W. Grimes struct rtentry * 116995add1aSGarrett Wollman rtalloc1(dst, report, ignflags) 117df8bae1dSRodney W. Grimes register struct sockaddr *dst; 118df8bae1dSRodney W. Grimes int report; 119995add1aSGarrett Wollman u_long ignflags; 120df8bae1dSRodney W. Grimes { 121df8bae1dSRodney W. Grimes register struct radix_node_head *rnh = rt_tables[dst->sa_family]; 122df8bae1dSRodney W. Grimes register struct rtentry *rt; 123df8bae1dSRodney W. Grimes register struct radix_node *rn; 124df8bae1dSRodney W. Grimes struct rtentry *newrt = 0; 125df8bae1dSRodney W. Grimes struct rt_addrinfo info; 126995add1aSGarrett Wollman u_long nflags; 127df8bae1dSRodney W. Grimes int s = splnet(), err = 0, msgtype = RTM_MISS; 128df8bae1dSRodney W. Grimes 129b0a76b88SJulian Elischer /* 130b0a76b88SJulian Elischer * Look up the address in the table for that Address Family 131b0a76b88SJulian Elischer */ 132df8bae1dSRodney W. Grimes if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) && 133df8bae1dSRodney W. Grimes ((rn->rn_flags & RNF_ROOT) == 0)) { 134b0a76b88SJulian Elischer /* 135b0a76b88SJulian Elischer * If we find it and it's not the root node, then 136b0a76b88SJulian Elischer * get a refernce on the rtentry associated. 137b0a76b88SJulian Elischer */ 138df8bae1dSRodney W. Grimes newrt = rt = (struct rtentry *)rn; 139995add1aSGarrett Wollman nflags = rt->rt_flags & ~ignflags; 140995add1aSGarrett Wollman if (report && (nflags & (RTF_CLONING | RTF_PRCLONING))) { 141b0a76b88SJulian Elischer /* 142b0a76b88SJulian Elischer * We are apparently adding (report = 0 in delete). 143b0a76b88SJulian Elischer * If it requires that it be cloned, do so. 144b0a76b88SJulian Elischer * (This implies it wasn't a HOST route.) 145b0a76b88SJulian Elischer */ 146df8bae1dSRodney W. Grimes err = rtrequest(RTM_RESOLVE, dst, SA(0), 147df8bae1dSRodney W. Grimes SA(0), 0, &newrt); 148df8bae1dSRodney W. Grimes if (err) { 149b0a76b88SJulian Elischer /* 150b0a76b88SJulian Elischer * If the cloning didn't succeed, maybe 151b0a76b88SJulian Elischer * what we have will do. Return that. 152b0a76b88SJulian Elischer */ 153df8bae1dSRodney W. Grimes newrt = rt; 154df8bae1dSRodney W. Grimes rt->rt_refcnt++; 155df8bae1dSRodney W. Grimes goto miss; 156df8bae1dSRodney W. Grimes } 157df8bae1dSRodney W. Grimes if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { 158b0a76b88SJulian Elischer /* 159b0a76b88SJulian Elischer * If the new route specifies it be 160b0a76b88SJulian Elischer * externally resolved, then go do that. 161b0a76b88SJulian Elischer */ 162df8bae1dSRodney W. Grimes msgtype = RTM_RESOLVE; 163df8bae1dSRodney W. Grimes goto miss; 164df8bae1dSRodney W. Grimes } 165df8bae1dSRodney W. Grimes } else 166df8bae1dSRodney W. Grimes rt->rt_refcnt++; 167df8bae1dSRodney W. Grimes } else { 168b0a76b88SJulian Elischer /* 169b0a76b88SJulian Elischer * Either we hit the root or couldn't find any match, 170b0a76b88SJulian Elischer * Which basically means 171b0a76b88SJulian Elischer * "caint get there frm here" 172b0a76b88SJulian Elischer */ 173df8bae1dSRodney W. Grimes rtstat.rts_unreach++; 174df8bae1dSRodney W. Grimes miss: if (report) { 175b0a76b88SJulian Elischer /* 176b0a76b88SJulian Elischer * If required, report the failure to the supervising 177b0a76b88SJulian Elischer * Authorities. 178b0a76b88SJulian Elischer * For a delete, this is not an error. (report == 0) 179b0a76b88SJulian Elischer */ 180df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 181df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 182df8bae1dSRodney W. Grimes rt_missmsg(msgtype, &info, 0, err); 183df8bae1dSRodney W. Grimes } 184df8bae1dSRodney W. Grimes } 185df8bae1dSRodney W. Grimes splx(s); 186df8bae1dSRodney W. Grimes return (newrt); 187df8bae1dSRodney W. Grimes } 188df8bae1dSRodney W. Grimes 189499676dfSJulian Elischer /* 190499676dfSJulian Elischer * Remove a reference count from an rtentry. 191499676dfSJulian Elischer * If the count gets low enough, take it out of the routing table 192499676dfSJulian Elischer */ 193df8bae1dSRodney W. Grimes void 194df8bae1dSRodney W. Grimes rtfree(rt) 195df8bae1dSRodney W. Grimes register struct rtentry *rt; 196df8bae1dSRodney W. Grimes { 197499676dfSJulian Elischer /* 198499676dfSJulian Elischer * find the tree for that address family 199499676dfSJulian Elischer */ 2005c2dae8eSGarrett Wollman register struct radix_node_head *rnh = 2015c2dae8eSGarrett Wollman rt_tables[rt_key(rt)->sa_family]; 202df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 203df8bae1dSRodney W. Grimes 2043545b048SGarrett Wollman if (rt == 0 || rnh == 0) 205df8bae1dSRodney W. Grimes panic("rtfree"); 206499676dfSJulian Elischer 207499676dfSJulian Elischer /* 208499676dfSJulian Elischer * decrement the reference count by one and if it reaches 0, 209499676dfSJulian Elischer * and there is a close function defined, call the close function 210499676dfSJulian Elischer */ 211df8bae1dSRodney W. Grimes rt->rt_refcnt--; 2125c2dae8eSGarrett Wollman if(rnh->rnh_close && rt->rt_refcnt == 0) { 2135c2dae8eSGarrett Wollman rnh->rnh_close((struct radix_node *)rt, rnh); 2145c2dae8eSGarrett Wollman } 215499676dfSJulian Elischer 216499676dfSJulian Elischer /* 217499676dfSJulian Elischer * If we are no longer "up" (and ref == 0) 218499676dfSJulian Elischer * then we can free the resources associated 219499676dfSJulian Elischer * with the route. 220499676dfSJulian Elischer */ 221df8bae1dSRodney W. Grimes if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { 222df8bae1dSRodney W. Grimes if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 223df8bae1dSRodney W. Grimes panic ("rtfree 2"); 224499676dfSJulian Elischer /* 225499676dfSJulian Elischer * the rtentry must have been removed from the routing table 226499676dfSJulian Elischer * so it is represented in rttrash.. remove that now. 227499676dfSJulian Elischer */ 228df8bae1dSRodney W. Grimes rttrash--; 229499676dfSJulian Elischer 230499676dfSJulian Elischer #ifdef DIAGNOSTIC 231df8bae1dSRodney W. Grimes if (rt->rt_refcnt < 0) { 232623ae52eSPoul-Henning Kamp printf("rtfree: %p not freed (neg refs)\n", rt); 233df8bae1dSRodney W. Grimes return; 234df8bae1dSRodney W. Grimes } 235499676dfSJulian Elischer #endif 236499676dfSJulian Elischer 237499676dfSJulian Elischer /* 238499676dfSJulian Elischer * release references on items we hold them on.. 239499676dfSJulian Elischer * e.g other routes and ifaddrs. 240499676dfSJulian Elischer */ 241499676dfSJulian Elischer if((ifa = rt->rt_ifa)) 242df8bae1dSRodney W. Grimes IFAFREE(ifa); 243771edb14SGarrett Wollman if (rt->rt_parent) { 244771edb14SGarrett Wollman RTFREE(rt->rt_parent); 245771edb14SGarrett Wollman } 246499676dfSJulian Elischer 247499676dfSJulian Elischer /* 248499676dfSJulian Elischer * The key is separatly alloc'd so free it (see rt_setgate()). 249499676dfSJulian Elischer * This also frees the gateway, as they are always malloc'd 250499676dfSJulian Elischer * together. 251499676dfSJulian Elischer */ 252df8bae1dSRodney W. Grimes Free(rt_key(rt)); 253499676dfSJulian Elischer 254499676dfSJulian Elischer /* 255499676dfSJulian Elischer * and the rtentry itself of course 256499676dfSJulian Elischer */ 257df8bae1dSRodney W. Grimes Free(rt); 258df8bae1dSRodney W. Grimes } 259df8bae1dSRodney W. Grimes } 260df8bae1dSRodney W. Grimes 261df8bae1dSRodney W. Grimes void 262df8bae1dSRodney W. Grimes ifafree(ifa) 263df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 264df8bae1dSRodney W. Grimes { 265df8bae1dSRodney W. Grimes if (ifa == NULL) 266df8bae1dSRodney W. Grimes panic("ifafree"); 267df8bae1dSRodney W. Grimes if (ifa->ifa_refcnt == 0) 268df8bae1dSRodney W. Grimes free(ifa, M_IFADDR); 269df8bae1dSRodney W. Grimes else 270df8bae1dSRodney W. Grimes ifa->ifa_refcnt--; 271df8bae1dSRodney W. Grimes } 272df8bae1dSRodney W. Grimes 273df8bae1dSRodney W. Grimes /* 274df8bae1dSRodney W. Grimes * Force a routing table entry to the specified 275df8bae1dSRodney W. Grimes * destination to go through the given gateway. 276df8bae1dSRodney W. Grimes * Normally called as a result of a routing redirect 277df8bae1dSRodney W. Grimes * message from the network layer. 278df8bae1dSRodney W. Grimes * 279df8bae1dSRodney W. Grimes * N.B.: must be called at splnet 280df8bae1dSRodney W. Grimes * 281df8bae1dSRodney W. Grimes */ 28226f9a767SRodney W. Grimes void 283df8bae1dSRodney W. Grimes rtredirect(dst, gateway, netmask, flags, src, rtp) 284df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway, *netmask, *src; 285df8bae1dSRodney W. Grimes int flags; 286df8bae1dSRodney W. Grimes struct rtentry **rtp; 287df8bae1dSRodney W. Grimes { 288df8bae1dSRodney W. Grimes register struct rtentry *rt; 289df8bae1dSRodney W. Grimes int error = 0; 290df8bae1dSRodney W. Grimes short *stat = 0; 291df8bae1dSRodney W. Grimes struct rt_addrinfo info; 292df8bae1dSRodney W. Grimes struct ifaddr *ifa; 293df8bae1dSRodney W. Grimes 294df8bae1dSRodney W. Grimes /* verify the gateway is directly reachable */ 295df8bae1dSRodney W. Grimes if ((ifa = ifa_ifwithnet(gateway)) == 0) { 296df8bae1dSRodney W. Grimes error = ENETUNREACH; 297df8bae1dSRodney W. Grimes goto out; 298df8bae1dSRodney W. Grimes } 299995add1aSGarrett Wollman rt = rtalloc1(dst, 0, 0UL); 300df8bae1dSRodney W. Grimes /* 301df8bae1dSRodney W. Grimes * If the redirect isn't from our current router for this dst, 302df8bae1dSRodney W. Grimes * it's either old or wrong. If it redirects us to ourselves, 303df8bae1dSRodney W. Grimes * we have a routing loop, perhaps as a result of an interface 304df8bae1dSRodney W. Grimes * going down recently. 305df8bae1dSRodney W. Grimes */ 306df8bae1dSRodney W. Grimes #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) 307df8bae1dSRodney W. Grimes if (!(flags & RTF_DONE) && rt && 308df8bae1dSRodney W. Grimes (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa)) 309df8bae1dSRodney W. Grimes error = EINVAL; 310df8bae1dSRodney W. Grimes else if (ifa_ifwithaddr(gateway)) 311df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 312df8bae1dSRodney W. Grimes if (error) 313df8bae1dSRodney W. Grimes goto done; 314df8bae1dSRodney W. Grimes /* 315df8bae1dSRodney W. Grimes * Create a new entry if we just got back a wildcard entry 316df8bae1dSRodney W. Grimes * or the the lookup failed. This is necessary for hosts 317df8bae1dSRodney W. Grimes * which use routing redirects generated by smart gateways 318df8bae1dSRodney W. Grimes * to dynamically build the routing tables. 319df8bae1dSRodney W. Grimes */ 320df8bae1dSRodney W. Grimes if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) 321df8bae1dSRodney W. Grimes goto create; 322df8bae1dSRodney W. Grimes /* 323df8bae1dSRodney W. Grimes * Don't listen to the redirect if it's 324df8bae1dSRodney W. Grimes * for a route to an interface. 325df8bae1dSRodney W. Grimes */ 326df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 327df8bae1dSRodney W. Grimes if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 328df8bae1dSRodney W. Grimes /* 329df8bae1dSRodney W. Grimes * Changing from route to net => route to host. 330df8bae1dSRodney W. Grimes * Create new route, rather than smashing route to net. 331df8bae1dSRodney W. Grimes */ 332df8bae1dSRodney W. Grimes create: 333df8bae1dSRodney W. Grimes flags |= RTF_GATEWAY | RTF_DYNAMIC; 334df8bae1dSRodney W. Grimes error = rtrequest((int)RTM_ADD, dst, gateway, 335df8bae1dSRodney W. Grimes netmask, flags, 336df8bae1dSRodney W. Grimes (struct rtentry **)0); 337df8bae1dSRodney W. Grimes stat = &rtstat.rts_dynamic; 338df8bae1dSRodney W. Grimes } else { 339df8bae1dSRodney W. Grimes /* 340df8bae1dSRodney W. Grimes * Smash the current notion of the gateway to 341df8bae1dSRodney W. Grimes * this destination. Should check about netmask!!! 342df8bae1dSRodney W. Grimes */ 343df8bae1dSRodney W. Grimes rt->rt_flags |= RTF_MODIFIED; 344df8bae1dSRodney W. Grimes flags |= RTF_MODIFIED; 345df8bae1dSRodney W. Grimes stat = &rtstat.rts_newgateway; 346499676dfSJulian Elischer /* 347499676dfSJulian Elischer * add the key and gateway (in one malloc'd chunk). 348499676dfSJulian Elischer */ 349df8bae1dSRodney W. Grimes rt_setgate(rt, rt_key(rt), gateway); 350df8bae1dSRodney W. Grimes } 351df8bae1dSRodney W. Grimes } else 352df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 353df8bae1dSRodney W. Grimes done: 354df8bae1dSRodney W. Grimes if (rt) { 355df8bae1dSRodney W. Grimes if (rtp && !error) 356df8bae1dSRodney W. Grimes *rtp = rt; 357df8bae1dSRodney W. Grimes else 358df8bae1dSRodney W. Grimes rtfree(rt); 359df8bae1dSRodney W. Grimes } 360df8bae1dSRodney W. Grimes out: 361df8bae1dSRodney W. Grimes if (error) 362df8bae1dSRodney W. Grimes rtstat.rts_badredirect++; 363df8bae1dSRodney W. Grimes else if (stat != NULL) 364df8bae1dSRodney W. Grimes (*stat)++; 365df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 366df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 367df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = gateway; 368df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = netmask; 369df8bae1dSRodney W. Grimes info.rti_info[RTAX_AUTHOR] = src; 370df8bae1dSRodney W. Grimes rt_missmsg(RTM_REDIRECT, &info, flags, error); 371df8bae1dSRodney W. Grimes } 372df8bae1dSRodney W. Grimes 373df8bae1dSRodney W. Grimes /* 374df8bae1dSRodney W. Grimes * Routing table ioctl interface. 375df8bae1dSRodney W. Grimes */ 376df8bae1dSRodney W. Grimes int 377df8bae1dSRodney W. Grimes rtioctl(req, data, p) 378df8bae1dSRodney W. Grimes int req; 379df8bae1dSRodney W. Grimes caddr_t data; 380df8bae1dSRodney W. Grimes struct proc *p; 381df8bae1dSRodney W. Grimes { 382623ae52eSPoul-Henning Kamp #ifdef INET 383f0068c4aSGarrett Wollman /* Multicast goop, grrr... */ 384af32e59fSBruce Evans #ifdef MROUTING 385af32e59fSBruce Evans return mrt_ioctl(req, data); 386af32e59fSBruce Evans #else 387e4ca4481SStefan Eßer return mrt_ioctl(req, data, p); 388af32e59fSBruce Evans #endif 389623ae52eSPoul-Henning Kamp #else /* INET */ 390623ae52eSPoul-Henning Kamp return ENXIO; 391623ae52eSPoul-Henning Kamp #endif /* INET */ 392df8bae1dSRodney W. Grimes } 393df8bae1dSRodney W. Grimes 394df8bae1dSRodney W. Grimes struct ifaddr * 395df8bae1dSRodney W. Grimes ifa_ifwithroute(flags, dst, gateway) 396df8bae1dSRodney W. Grimes int flags; 397df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway; 398df8bae1dSRodney W. Grimes { 399df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 400df8bae1dSRodney W. Grimes if ((flags & RTF_GATEWAY) == 0) { 401df8bae1dSRodney W. Grimes /* 402df8bae1dSRodney W. Grimes * If we are adding a route to an interface, 403df8bae1dSRodney W. Grimes * and the interface is a pt to pt link 404df8bae1dSRodney W. Grimes * we should search for the destination 405df8bae1dSRodney W. Grimes * as our clue to the interface. Otherwise 406df8bae1dSRodney W. Grimes * we can use the local address. 407df8bae1dSRodney W. Grimes */ 408df8bae1dSRodney W. Grimes ifa = 0; 4095df72964SGarrett Wollman if (flags & RTF_HOST) { 410df8bae1dSRodney W. Grimes ifa = ifa_ifwithdstaddr(dst); 4115df72964SGarrett Wollman } 412df8bae1dSRodney W. Grimes if (ifa == 0) 413df8bae1dSRodney W. Grimes ifa = ifa_ifwithaddr(gateway); 414df8bae1dSRodney W. Grimes } else { 415df8bae1dSRodney W. Grimes /* 416df8bae1dSRodney W. Grimes * If we are adding a route to a remote net 417df8bae1dSRodney W. Grimes * or host, the gateway may still be on the 418df8bae1dSRodney W. Grimes * other end of a pt to pt link. 419df8bae1dSRodney W. Grimes */ 420df8bae1dSRodney W. Grimes ifa = ifa_ifwithdstaddr(gateway); 421df8bae1dSRodney W. Grimes } 422df8bae1dSRodney W. Grimes if (ifa == 0) 423df8bae1dSRodney W. Grimes ifa = ifa_ifwithnet(gateway); 424df8bae1dSRodney W. Grimes if (ifa == 0) { 425995add1aSGarrett Wollman struct rtentry *rt = rtalloc1(dst, 0, 0UL); 426df8bae1dSRodney W. Grimes if (rt == 0) 427df8bae1dSRodney W. Grimes return (0); 428df8bae1dSRodney W. Grimes rt->rt_refcnt--; 429df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) == 0) 430df8bae1dSRodney W. Grimes return (0); 431df8bae1dSRodney W. Grimes } 432df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family != dst->sa_family) { 433df8bae1dSRodney W. Grimes struct ifaddr *oifa = ifa; 434df8bae1dSRodney W. Grimes ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); 435df8bae1dSRodney W. Grimes if (ifa == 0) 436df8bae1dSRodney W. Grimes ifa = oifa; 437df8bae1dSRodney W. Grimes } 438df8bae1dSRodney W. Grimes return (ifa); 439df8bae1dSRodney W. Grimes } 440df8bae1dSRodney W. Grimes 441df8bae1dSRodney W. Grimes #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 442df8bae1dSRodney W. Grimes 443c2bed6a3SGarrett Wollman static int rt_fixdelete(struct radix_node *, void *); 444cd02a0b7SGarrett Wollman static int rt_fixchange(struct radix_node *, void *); 445cd02a0b7SGarrett Wollman 446cd02a0b7SGarrett Wollman struct rtfc_arg { 447cd02a0b7SGarrett Wollman struct rtentry *rt0; 448cd02a0b7SGarrett Wollman struct radix_node_head *rnh; 449cd02a0b7SGarrett Wollman }; 45018e1f1f1SGarrett Wollman 451b0a76b88SJulian Elischer /* 452b0a76b88SJulian Elischer * Do appropriate manipulations of a routing tree given 453b0a76b88SJulian Elischer * all the bits of info needed 454b0a76b88SJulian Elischer */ 455df8bae1dSRodney W. Grimes int 456df8bae1dSRodney W. Grimes rtrequest(req, dst, gateway, netmask, flags, ret_nrt) 457df8bae1dSRodney W. Grimes int req, flags; 458df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway, *netmask; 459df8bae1dSRodney W. Grimes struct rtentry **ret_nrt; 460df8bae1dSRodney W. Grimes { 461df8bae1dSRodney W. Grimes int s = splnet(); int error = 0; 462df8bae1dSRodney W. Grimes register struct rtentry *rt; 463df8bae1dSRodney W. Grimes register struct radix_node *rn; 464df8bae1dSRodney W. Grimes register struct radix_node_head *rnh; 465df8bae1dSRodney W. Grimes struct ifaddr *ifa; 466df8bae1dSRodney W. Grimes struct sockaddr *ndst; 467df8bae1dSRodney W. Grimes #define senderr(x) { error = x ; goto bad; } 468df8bae1dSRodney W. Grimes 469b0a76b88SJulian Elischer /* 470b0a76b88SJulian Elischer * Find the correct routing tree to use for this Address Family 471b0a76b88SJulian Elischer */ 472df8bae1dSRodney W. Grimes if ((rnh = rt_tables[dst->sa_family]) == 0) 473df8bae1dSRodney W. Grimes senderr(ESRCH); 474b0a76b88SJulian Elischer /* 475b0a76b88SJulian Elischer * If we are adding a host route then we don't want to put 476b0a76b88SJulian Elischer * a netmask in the tree 477b0a76b88SJulian Elischer */ 478df8bae1dSRodney W. Grimes if (flags & RTF_HOST) 479df8bae1dSRodney W. Grimes netmask = 0; 480df8bae1dSRodney W. Grimes switch (req) { 481df8bae1dSRodney W. Grimes case RTM_DELETE: 482b0a76b88SJulian Elischer /* 483b0a76b88SJulian Elischer * Remove the item from the tree and return it. 484b0a76b88SJulian Elischer * Complain if it is not there and do no more processing. 485b0a76b88SJulian Elischer */ 486df8bae1dSRodney W. Grimes if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == 0) 487df8bae1dSRodney W. Grimes senderr(ESRCH); 488df8bae1dSRodney W. Grimes if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 489df8bae1dSRodney W. Grimes panic ("rtrequest delete"); 490df8bae1dSRodney W. Grimes rt = (struct rtentry *)rn; 491c2bed6a3SGarrett Wollman 492c2bed6a3SGarrett Wollman /* 493c2bed6a3SGarrett Wollman * Now search what's left of the subtree for any cloned 494c2bed6a3SGarrett Wollman * routes which might have been formed from this node. 495c2bed6a3SGarrett Wollman */ 4963545b048SGarrett Wollman if ((rt->rt_flags & RTF_PRCLONING) && netmask) { 497c2bed6a3SGarrett Wollman rnh->rnh_walktree_from(rnh, dst, netmask, 498c2bed6a3SGarrett Wollman rt_fixdelete, rt); 499c2bed6a3SGarrett Wollman } 5003545b048SGarrett Wollman 501b0a76b88SJulian Elischer /* 502b0a76b88SJulian Elischer * Remove any external references we may have. 503b0a76b88SJulian Elischer * This might result in another rtentry being freed if 504b0a76b88SJulian Elischer * we held it's last reference. 505b0a76b88SJulian Elischer */ 5066ac3b69dSBill Fenner if (rt->rt_gwroute) { 507b0a76b88SJulian Elischer rt = rt->rt_gwroute; 508b0a76b88SJulian Elischer RTFREE(rt); 5096ac3b69dSBill Fenner (rt = (struct rtentry *)rn)->rt_gwroute = 0; 5106ac3b69dSBill Fenner } 5116ac3b69dSBill Fenner 5123545b048SGarrett Wollman /* 5133545b048SGarrett Wollman * NB: RTF_UP must be set during the search above, 5143545b048SGarrett Wollman * because we might delete the last ref, causing 5153545b048SGarrett Wollman * rt to get freed prematurely. 516499676dfSJulian Elischer * eh? then why not just add a reference? 517499676dfSJulian Elischer * I'm not sure how RTF_UP helps matters. (JRE) 5183545b048SGarrett Wollman */ 5193545b048SGarrett Wollman rt->rt_flags &= ~RTF_UP; 5203545b048SGarrett Wollman 521b0a76b88SJulian Elischer /* 522499676dfSJulian Elischer * give the protocol a chance to keep things in sync. 523b0a76b88SJulian Elischer */ 524df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) 525df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 526499676dfSJulian Elischer 527b0a76b88SJulian Elischer /* 528499676dfSJulian Elischer * one more rtentry floating around that is not 529499676dfSJulian Elischer * linked to the routing table. 530499676dfSJulian Elischer */ 531499676dfSJulian Elischer rttrash++; 532499676dfSJulian Elischer 533499676dfSJulian Elischer /* 534499676dfSJulian Elischer * If the caller wants it, then it can have it, 535499676dfSJulian Elischer * but it's up to it to free the rtentry as we won't be 536499676dfSJulian Elischer * doing it. 537b0a76b88SJulian Elischer */ 538df8bae1dSRodney W. Grimes if (ret_nrt) 539df8bae1dSRodney W. Grimes *ret_nrt = rt; 540df8bae1dSRodney W. Grimes else if (rt->rt_refcnt <= 0) { 541b0a76b88SJulian Elischer rt->rt_refcnt++; /* make a 1->0 transition */ 542df8bae1dSRodney W. Grimes rtfree(rt); 543df8bae1dSRodney W. Grimes } 544df8bae1dSRodney W. Grimes break; 545df8bae1dSRodney W. Grimes 546df8bae1dSRodney W. Grimes case RTM_RESOLVE: 547df8bae1dSRodney W. Grimes if (ret_nrt == 0 || (rt = *ret_nrt) == 0) 548df8bae1dSRodney W. Grimes senderr(EINVAL); 549df8bae1dSRodney W. Grimes ifa = rt->rt_ifa; 5503682d2baSDavid Greenman flags = rt->rt_flags & 5513682d2baSDavid Greenman ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC); 552995add1aSGarrett Wollman flags |= RTF_WASCLONED; 553df8bae1dSRodney W. Grimes gateway = rt->rt_gateway; 554df8bae1dSRodney W. Grimes if ((netmask = rt->rt_genmask) == 0) 555df8bae1dSRodney W. Grimes flags |= RTF_HOST; 556df8bae1dSRodney W. Grimes goto makeroute; 557df8bae1dSRodney W. Grimes 558df8bae1dSRodney W. Grimes case RTM_ADD: 5595df72964SGarrett Wollman if ((flags & RTF_GATEWAY) && !gateway) 5605df72964SGarrett Wollman panic("rtrequest: GATEWAY but no gateway"); 5615df72964SGarrett Wollman 562df8bae1dSRodney W. Grimes if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) 563df8bae1dSRodney W. Grimes senderr(ENETUNREACH); 5645df72964SGarrett Wollman 565df8bae1dSRodney W. Grimes makeroute: 566df8bae1dSRodney W. Grimes R_Malloc(rt, struct rtentry *, sizeof(*rt)); 567df8bae1dSRodney W. Grimes if (rt == 0) 568df8bae1dSRodney W. Grimes senderr(ENOBUFS); 569df8bae1dSRodney W. Grimes Bzero(rt, sizeof(*rt)); 570df8bae1dSRodney W. Grimes rt->rt_flags = RTF_UP | flags; 571499676dfSJulian Elischer /* 572499676dfSJulian Elischer * Add the gateway. Possibly re-malloc-ing the storage for it 573499676dfSJulian Elischer * also add the rt_gwroute if possible. 574499676dfSJulian Elischer */ 575704b0666SBill Fenner if (error = rt_setgate(rt, dst, gateway)) { 576df8bae1dSRodney W. Grimes Free(rt); 577704b0666SBill Fenner senderr(error); 578df8bae1dSRodney W. Grimes } 579499676dfSJulian Elischer 580499676dfSJulian Elischer /* 581499676dfSJulian Elischer * point to the (possibly newly malloc'd) dest address. 582499676dfSJulian Elischer */ 583df8bae1dSRodney W. Grimes ndst = rt_key(rt); 584499676dfSJulian Elischer 585499676dfSJulian Elischer /* 586499676dfSJulian Elischer * make sure it contains the value we want (masked if needed). 587499676dfSJulian Elischer */ 588df8bae1dSRodney W. Grimes if (netmask) { 589df8bae1dSRodney W. Grimes rt_maskedcopy(dst, ndst, netmask); 590df8bae1dSRodney W. Grimes } else 591df8bae1dSRodney W. Grimes Bcopy(dst, ndst, dst->sa_len); 5928e718bb4SGarrett Wollman 5938e718bb4SGarrett Wollman /* 594499676dfSJulian Elischer * Note that we now have a reference to the ifa. 5958e718bb4SGarrett Wollman * This moved from below so that rnh->rnh_addaddr() can 596499676dfSJulian Elischer * examine the ifa and ifa->ifa_ifp if it so desires. 5978e718bb4SGarrett Wollman */ 5988e718bb4SGarrett Wollman ifa->ifa_refcnt++; 5998e718bb4SGarrett Wollman rt->rt_ifa = ifa; 6008e718bb4SGarrett Wollman rt->rt_ifp = ifa->ifa_ifp; 6018e718bb4SGarrett Wollman 602df8bae1dSRodney W. Grimes rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask, 603df8bae1dSRodney W. Grimes rnh, rt->rt_nodes); 604df8bae1dSRodney W. Grimes if (rn == 0) { 605aca1a47cSGarrett Wollman struct rtentry *rt2; 606aca1a47cSGarrett Wollman /* 607aca1a47cSGarrett Wollman * Uh-oh, we already have one of these in the tree. 608aca1a47cSGarrett Wollman * We do a special hack: if the route that's already 609aca1a47cSGarrett Wollman * there was generated by the protocol-cloning 610aca1a47cSGarrett Wollman * mechanism, then we just blow it away and retry 611aca1a47cSGarrett Wollman * the insertion of the new one. 612aca1a47cSGarrett Wollman */ 613aca1a47cSGarrett Wollman rt2 = rtalloc1(dst, 0, RTF_PRCLONING); 614aca1a47cSGarrett Wollman if (rt2 && rt2->rt_parent) { 615aca1a47cSGarrett Wollman rtrequest(RTM_DELETE, 616aca1a47cSGarrett Wollman (struct sockaddr *)rt_key(rt2), 617aca1a47cSGarrett Wollman rt2->rt_gateway, 618aca1a47cSGarrett Wollman rt_mask(rt2), rt2->rt_flags, 0); 619aca1a47cSGarrett Wollman RTFREE(rt2); 620aca1a47cSGarrett Wollman rn = rnh->rnh_addaddr((caddr_t)ndst, 621aca1a47cSGarrett Wollman (caddr_t)netmask, 622aca1a47cSGarrett Wollman rnh, rt->rt_nodes); 623fde327d6SGarrett Wollman } else if (rt2) { 624499676dfSJulian Elischer /* undo the extra ref we got */ 625fde327d6SGarrett Wollman RTFREE(rt2); 626aca1a47cSGarrett Wollman } 627aca1a47cSGarrett Wollman } 628aca1a47cSGarrett Wollman 629499676dfSJulian Elischer /* 630499676dfSJulian Elischer * If it still failed to go into the tree, 631499676dfSJulian Elischer * then un-make it (this should be a function) 632499676dfSJulian Elischer */ 633aca1a47cSGarrett Wollman if (rn == 0) { 634df8bae1dSRodney W. Grimes if (rt->rt_gwroute) 635df8bae1dSRodney W. Grimes rtfree(rt->rt_gwroute); 6368e718bb4SGarrett Wollman if (rt->rt_ifa) { 6378e718bb4SGarrett Wollman IFAFREE(rt->rt_ifa); 6388e718bb4SGarrett Wollman } 639df8bae1dSRodney W. Grimes Free(rt_key(rt)); 640df8bae1dSRodney W. Grimes Free(rt); 641df8bae1dSRodney W. Grimes senderr(EEXIST); 642df8bae1dSRodney W. Grimes } 643499676dfSJulian Elischer 644771edb14SGarrett Wollman rt->rt_parent = 0; 645771edb14SGarrett Wollman 646499676dfSJulian Elischer /* 647499676dfSJulian Elischer * If we got here from RESOLVE, then we are cloning 648499676dfSJulian Elischer * so clone the rest, and note that we 649499676dfSJulian Elischer * are a clone (and increment the parent's references) 650499676dfSJulian Elischer */ 651c2bed6a3SGarrett Wollman if (req == RTM_RESOLVE) { 652df8bae1dSRodney W. Grimes rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ 653771edb14SGarrett Wollman if ((*ret_nrt)->rt_flags & RTF_PRCLONING) { 65418e1f1f1SGarrett Wollman rt->rt_parent = (*ret_nrt); 655771edb14SGarrett Wollman (*ret_nrt)->rt_refcnt++; 656771edb14SGarrett Wollman } 65718e1f1f1SGarrett Wollman } 658499676dfSJulian Elischer 659499676dfSJulian Elischer /* 660499676dfSJulian Elischer * if this protocol has something to add to this then 661499676dfSJulian Elischer * allow it to do that as well. 662499676dfSJulian Elischer */ 663df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 664df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); 665499676dfSJulian Elischer 666cd02a0b7SGarrett Wollman /* 667cd02a0b7SGarrett Wollman * We repeat the same procedure from rt_setgate() here because 668cd02a0b7SGarrett Wollman * it doesn't fire when we call it there because the node 669cd02a0b7SGarrett Wollman * hasn't been added to the tree yet. 670cd02a0b7SGarrett Wollman */ 6713271a3a4SPeter Wemm if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { 672cd02a0b7SGarrett Wollman struct rtfc_arg arg; 673cd02a0b7SGarrett Wollman arg.rnh = rnh; 674cd02a0b7SGarrett Wollman arg.rt0 = rt; 675cd02a0b7SGarrett Wollman rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), 676cd02a0b7SGarrett Wollman rt_fixchange, &arg); 677cd02a0b7SGarrett Wollman } 678cd02a0b7SGarrett Wollman 679499676dfSJulian Elischer /* 680499676dfSJulian Elischer * actually return a resultant rtentry and 681499676dfSJulian Elischer * give the caller a single reference. 682499676dfSJulian Elischer */ 683df8bae1dSRodney W. Grimes if (ret_nrt) { 684df8bae1dSRodney W. Grimes *ret_nrt = rt; 685df8bae1dSRodney W. Grimes rt->rt_refcnt++; 686df8bae1dSRodney W. Grimes } 687df8bae1dSRodney W. Grimes break; 688df8bae1dSRodney W. Grimes } 689df8bae1dSRodney W. Grimes bad: 690df8bae1dSRodney W. Grimes splx(s); 691df8bae1dSRodney W. Grimes return (error); 692df8bae1dSRodney W. Grimes } 693df8bae1dSRodney W. Grimes 69418e1f1f1SGarrett Wollman /* 69518e1f1f1SGarrett Wollman * Called from rtrequest(RTM_DELETE, ...) to fix up the route's ``family'' 69618e1f1f1SGarrett Wollman * (i.e., the routes related to it by the operation of cloning). This 697c2bed6a3SGarrett Wollman * routine is iterated over all potential former-child-routes by way of 698c2bed6a3SGarrett Wollman * rnh->rnh_walktree_from() above, and those that actually are children of 699c2bed6a3SGarrett Wollman * the late parent (passed in as VP here) are themselves deleted. 70018e1f1f1SGarrett Wollman */ 701c2bed6a3SGarrett Wollman static int 702c2bed6a3SGarrett Wollman rt_fixdelete(struct radix_node *rn, void *vp) 70318e1f1f1SGarrett Wollman { 704c2bed6a3SGarrett Wollman struct rtentry *rt = (struct rtentry *)rn; 705c2bed6a3SGarrett Wollman struct rtentry *rt0 = vp; 70618e1f1f1SGarrett Wollman 707a29ae2a1SGarrett Wollman if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) { 708c2bed6a3SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), 70918e1f1f1SGarrett Wollman (struct sockaddr *)0, rt_mask(rt), 71018e1f1f1SGarrett Wollman rt->rt_flags, (struct rtentry **)0); 71118e1f1f1SGarrett Wollman } 712c2bed6a3SGarrett Wollman return 0; 71318e1f1f1SGarrett Wollman } 71418e1f1f1SGarrett Wollman 715cd02a0b7SGarrett Wollman /* 716cd02a0b7SGarrett Wollman * This routine is called from rt_setgate() to do the analogous thing for 717cd02a0b7SGarrett Wollman * adds and changes. There is the added complication in this case of a 718cd02a0b7SGarrett Wollman * middle insert; i.e., insertion of a new network route between an older 719cd02a0b7SGarrett Wollman * network route and (cloned) host routes. For this reason, a simple check 720cd02a0b7SGarrett Wollman * of rt->rt_parent is insufficient; each candidate route must be tested 721cd02a0b7SGarrett Wollman * against the (mask, value) of the new route (passed as before in vp) 722cd02a0b7SGarrett Wollman * to see if the new route matches it. Unfortunately, this has the obnoxious 723cd02a0b7SGarrett Wollman * property of also triggering for insertion /above/ a pre-existing network 724cd02a0b7SGarrett Wollman * route and clones. Sigh. This may be fixed some day. 725cd02a0b7SGarrett Wollman * 726cd02a0b7SGarrett Wollman * XXX - it may be possible to do fixdelete() for changes and reserve this 727cd02a0b7SGarrett Wollman * routine just for adds. I'm not sure why I thought it was necessary to do 728cd02a0b7SGarrett Wollman * changes this way. 729cd02a0b7SGarrett Wollman */ 730cd02a0b7SGarrett Wollman #ifdef DEBUG 731cd02a0b7SGarrett Wollman int rtfcdebug = 0; 732cd02a0b7SGarrett Wollman #endif 733cd02a0b7SGarrett Wollman 734cd02a0b7SGarrett Wollman static int 735cd02a0b7SGarrett Wollman rt_fixchange(struct radix_node *rn, void *vp) 736cd02a0b7SGarrett Wollman { 737cd02a0b7SGarrett Wollman struct rtentry *rt = (struct rtentry *)rn; 738cd02a0b7SGarrett Wollman struct rtfc_arg *ap = vp; 739cd02a0b7SGarrett Wollman struct rtentry *rt0 = ap->rt0; 740cd02a0b7SGarrett Wollman struct radix_node_head *rnh = ap->rnh; 741cd02a0b7SGarrett Wollman u_char *xk1, *xm1, *xk2; 742cd02a0b7SGarrett Wollman int i, len; 743cd02a0b7SGarrett Wollman 744cd02a0b7SGarrett Wollman #ifdef DEBUG 745cd02a0b7SGarrett Wollman if (rtfcdebug) 746cd02a0b7SGarrett Wollman printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0); 747cd02a0b7SGarrett Wollman #endif 748cd02a0b7SGarrett Wollman 749cd02a0b7SGarrett Wollman if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) { 750cd02a0b7SGarrett Wollman #ifdef DEBUG 751cd02a0b7SGarrett Wollman if(rtfcdebug) printf("no parent or pinned\n"); 752cd02a0b7SGarrett Wollman #endif 753cd02a0b7SGarrett Wollman return 0; 754cd02a0b7SGarrett Wollman } 755cd02a0b7SGarrett Wollman 756cd02a0b7SGarrett Wollman if (rt->rt_parent == rt0) { 757cd02a0b7SGarrett Wollman #ifdef DEBUG 758cd02a0b7SGarrett Wollman if(rtfcdebug) printf("parent match\n"); 759cd02a0b7SGarrett Wollman #endif 760cd02a0b7SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), 761cd02a0b7SGarrett Wollman (struct sockaddr *)0, rt_mask(rt), 762cd02a0b7SGarrett Wollman rt->rt_flags, (struct rtentry **)0); 763cd02a0b7SGarrett Wollman } 764cd02a0b7SGarrett Wollman 765cd02a0b7SGarrett Wollman /* 766cd02a0b7SGarrett Wollman * There probably is a function somewhere which does this... 767cd02a0b7SGarrett Wollman * if not, there should be. 768cd02a0b7SGarrett Wollman */ 769cd02a0b7SGarrett Wollman len = imin(((struct sockaddr *)rt_key(rt0))->sa_len, 770cd02a0b7SGarrett Wollman ((struct sockaddr *)rt_key(rt))->sa_len); 771cd02a0b7SGarrett Wollman 772cd02a0b7SGarrett Wollman xk1 = (u_char *)rt_key(rt0); 773cd02a0b7SGarrett Wollman xm1 = (u_char *)rt_mask(rt0); 774cd02a0b7SGarrett Wollman xk2 = (u_char *)rt_key(rt); 775cd02a0b7SGarrett Wollman 776cd02a0b7SGarrett Wollman for (i = rnh->rnh_treetop->rn_off; i < len; i++) { 777cd02a0b7SGarrett Wollman if ((xk2[i] & xm1[i]) != xk1[i]) { 778cd02a0b7SGarrett Wollman #ifdef DEBUG 779cd02a0b7SGarrett Wollman if(rtfcdebug) printf("no match\n"); 780cd02a0b7SGarrett Wollman #endif 781cd02a0b7SGarrett Wollman return 0; 782cd02a0b7SGarrett Wollman } 783cd02a0b7SGarrett Wollman } 784cd02a0b7SGarrett Wollman 785cd02a0b7SGarrett Wollman /* 786cd02a0b7SGarrett Wollman * OK, this node is a clone, and matches the node currently being 787cd02a0b7SGarrett Wollman * changed/added under the node's mask. So, get rid of it. 788cd02a0b7SGarrett Wollman */ 789cd02a0b7SGarrett Wollman #ifdef DEBUG 790cd02a0b7SGarrett Wollman if(rtfcdebug) printf("deleting\n"); 791cd02a0b7SGarrett Wollman #endif 792cd02a0b7SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, 793cd02a0b7SGarrett Wollman rt_mask(rt), rt->rt_flags, (struct rtentry **)0); 794cd02a0b7SGarrett Wollman } 795cd02a0b7SGarrett Wollman 796df8bae1dSRodney W. Grimes int 797df8bae1dSRodney W. Grimes rt_setgate(rt0, dst, gate) 798df8bae1dSRodney W. Grimes struct rtentry *rt0; 799df8bae1dSRodney W. Grimes struct sockaddr *dst, *gate; 800df8bae1dSRodney W. Grimes { 801df8bae1dSRodney W. Grimes caddr_t new, old; 802df8bae1dSRodney W. Grimes int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len); 803df8bae1dSRodney W. Grimes register struct rtentry *rt = rt0; 804cd02a0b7SGarrett Wollman struct radix_node_head *rnh = rt_tables[dst->sa_family]; 805df8bae1dSRodney W. Grimes 8061db1fffaSBill Fenner /* 8071db1fffaSBill Fenner * A host route with the destination equal to the gateway 8081db1fffaSBill Fenner * will interfere with keeping LLINFO in the routing 8091db1fffaSBill Fenner * table, so disallow it. 8101db1fffaSBill Fenner */ 8111db1fffaSBill Fenner if (((rt0->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) == 8121db1fffaSBill Fenner (RTF_HOST|RTF_GATEWAY)) && 8131db1fffaSBill Fenner (dst->sa_len == gate->sa_len) && 8141db1fffaSBill Fenner (bcmp(dst, gate, dst->sa_len) == 0)) { 8151db1fffaSBill Fenner /* 8161db1fffaSBill Fenner * The route might already exist if this is an RTM_CHANGE 8171db1fffaSBill Fenner * or a routing redirect, so try to delete it. 8181db1fffaSBill Fenner */ 819704b0666SBill Fenner if (rt_key(rt0)) 8201db1fffaSBill Fenner rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt0), 8211db1fffaSBill Fenner rt0->rt_gateway, rt_mask(rt0), rt0->rt_flags, 0); 8221db1fffaSBill Fenner return EADDRNOTAVAIL; 8231db1fffaSBill Fenner } 8241db1fffaSBill Fenner 825499676dfSJulian Elischer /* 826499676dfSJulian Elischer * Both dst and gateway are stored in the same malloc'd chunk 827499676dfSJulian Elischer * (If I ever get my hands on....) 828499676dfSJulian Elischer * if we need to malloc a new chunk, then keep the old one around 829499676dfSJulian Elischer * till we don't need it any more. 830499676dfSJulian Elischer */ 831df8bae1dSRodney W. Grimes if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) { 832df8bae1dSRodney W. Grimes old = (caddr_t)rt_key(rt); 833df8bae1dSRodney W. Grimes R_Malloc(new, caddr_t, dlen + glen); 834df8bae1dSRodney W. Grimes if (new == 0) 8351db1fffaSBill Fenner return ENOBUFS; 836df8bae1dSRodney W. Grimes rt->rt_nodes->rn_key = new; 837df8bae1dSRodney W. Grimes } else { 838499676dfSJulian Elischer /* 839499676dfSJulian Elischer * otherwise just overwrite the old one 840499676dfSJulian Elischer */ 841df8bae1dSRodney W. Grimes new = rt->rt_nodes->rn_key; 842df8bae1dSRodney W. Grimes old = 0; 843df8bae1dSRodney W. Grimes } 844499676dfSJulian Elischer 845499676dfSJulian Elischer /* 846499676dfSJulian Elischer * copy the new gateway value into the memory chunk 847499676dfSJulian Elischer */ 848df8bae1dSRodney W. Grimes Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen); 849499676dfSJulian Elischer 850499676dfSJulian Elischer /* 851499676dfSJulian Elischer * if we are replacing the chunk (or it's new) we need to 852499676dfSJulian Elischer * replace the dst as well 853499676dfSJulian Elischer */ 854df8bae1dSRodney W. Grimes if (old) { 855df8bae1dSRodney W. Grimes Bcopy(dst, new, dlen); 856df8bae1dSRodney W. Grimes Free(old); 857df8bae1dSRodney W. Grimes } 858499676dfSJulian Elischer 859499676dfSJulian Elischer /* 860499676dfSJulian Elischer * If there is already a gwroute, it's now almost definitly wrong 861499676dfSJulian Elischer * so drop it. 862499676dfSJulian Elischer */ 863df8bae1dSRodney W. Grimes if (rt->rt_gwroute) { 864df8bae1dSRodney W. Grimes rt = rt->rt_gwroute; RTFREE(rt); 865df8bae1dSRodney W. Grimes rt = rt0; rt->rt_gwroute = 0; 866df8bae1dSRodney W. Grimes } 867cd02a0b7SGarrett Wollman /* 868cd02a0b7SGarrett Wollman * Cloning loop avoidance: 869cd02a0b7SGarrett Wollman * In the presence of protocol-cloning and bad configuration, 870cd02a0b7SGarrett Wollman * it is possible to get stuck in bottomless mutual recursion 871cd02a0b7SGarrett Wollman * (rtrequest rt_setgate rtalloc1). We avoid this by not allowing 872cd02a0b7SGarrett Wollman * protocol-cloning to operate for gateways (which is probably the 873cd02a0b7SGarrett Wollman * correct choice anyway), and avoid the resulting reference loops 874cd02a0b7SGarrett Wollman * by disallowing any route to run through itself as a gateway. 875499676dfSJulian Elischer * This is obviously mandatory when we get rt->rt_output(). 876cd02a0b7SGarrett Wollman */ 877df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 878cd02a0b7SGarrett Wollman rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING); 879cd02a0b7SGarrett Wollman if (rt->rt_gwroute == rt) { 880cd02a0b7SGarrett Wollman RTFREE(rt->rt_gwroute); 881cd02a0b7SGarrett Wollman rt->rt_gwroute = 0; 8821db1fffaSBill Fenner return EDQUOT; /* failure */ 883df8bae1dSRodney W. Grimes } 884cd02a0b7SGarrett Wollman } 885cd02a0b7SGarrett Wollman 886cd02a0b7SGarrett Wollman /* 887cd02a0b7SGarrett Wollman * This isn't going to do anything useful for host routes, so 888cd02a0b7SGarrett Wollman * don't bother. Also make sure we have a reasonable mask 889cd02a0b7SGarrett Wollman * (we don't yet have one during adds). 890cd02a0b7SGarrett Wollman */ 891cd02a0b7SGarrett Wollman if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { 892cd02a0b7SGarrett Wollman struct rtfc_arg arg; 893cd02a0b7SGarrett Wollman arg.rnh = rnh; 894cd02a0b7SGarrett Wollman arg.rt0 = rt; 895cd02a0b7SGarrett Wollman rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), 896cd02a0b7SGarrett Wollman rt_fixchange, &arg); 897cd02a0b7SGarrett Wollman } 898cd02a0b7SGarrett Wollman 899df8bae1dSRodney W. Grimes return 0; 900df8bae1dSRodney W. Grimes } 901df8bae1dSRodney W. Grimes 902f708ef1bSPoul-Henning Kamp static void 903df8bae1dSRodney W. Grimes rt_maskedcopy(src, dst, netmask) 904df8bae1dSRodney W. Grimes struct sockaddr *src, *dst, *netmask; 905df8bae1dSRodney W. Grimes { 906df8bae1dSRodney W. Grimes register u_char *cp1 = (u_char *)src; 907df8bae1dSRodney W. Grimes register u_char *cp2 = (u_char *)dst; 908df8bae1dSRodney W. Grimes register u_char *cp3 = (u_char *)netmask; 909df8bae1dSRodney W. Grimes u_char *cplim = cp2 + *cp3; 910df8bae1dSRodney W. Grimes u_char *cplim2 = cp2 + *cp1; 911df8bae1dSRodney W. Grimes 912df8bae1dSRodney W. Grimes *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ 913df8bae1dSRodney W. Grimes cp3 += 2; 914df8bae1dSRodney W. Grimes if (cplim > cplim2) 915df8bae1dSRodney W. Grimes cplim = cplim2; 916df8bae1dSRodney W. Grimes while (cp2 < cplim) 917df8bae1dSRodney W. Grimes *cp2++ = *cp1++ & *cp3++; 918df8bae1dSRodney W. Grimes if (cp2 < cplim2) 919df8bae1dSRodney W. Grimes bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); 920df8bae1dSRodney W. Grimes } 921df8bae1dSRodney W. Grimes 922df8bae1dSRodney W. Grimes /* 923df8bae1dSRodney W. Grimes * Set up a routing table entry, normally 924df8bae1dSRodney W. Grimes * for an interface. 925df8bae1dSRodney W. Grimes */ 926df8bae1dSRodney W. Grimes int 927df8bae1dSRodney W. Grimes rtinit(ifa, cmd, flags) 928df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 929df8bae1dSRodney W. Grimes int cmd, flags; 930df8bae1dSRodney W. Grimes { 931df8bae1dSRodney W. Grimes register struct rtentry *rt; 932df8bae1dSRodney W. Grimes register struct sockaddr *dst; 933df8bae1dSRodney W. Grimes register struct sockaddr *deldst; 934df8bae1dSRodney W. Grimes struct mbuf *m = 0; 935df8bae1dSRodney W. Grimes struct rtentry *nrt = 0; 936df8bae1dSRodney W. Grimes int error; 937df8bae1dSRodney W. Grimes 938df8bae1dSRodney W. Grimes dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; 939b0a76b88SJulian Elischer /* 940b0a76b88SJulian Elischer * If it's a delete, check that if it exists, it's on the correct 941b0a76b88SJulian Elischer * interface or we might scrub a route to another ifa which would 942b0a76b88SJulian Elischer * be confusing at best and possibly worse. 943b0a76b88SJulian Elischer */ 944df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE) { 945b0a76b88SJulian Elischer /* 946b0a76b88SJulian Elischer * It's a delete, so it should already exist.. 947b0a76b88SJulian Elischer * If it's a net, mask off the host bits 948b0a76b88SJulian Elischer * (Assuming we have a mask) 949b0a76b88SJulian Elischer */ 950df8bae1dSRodney W. Grimes if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { 951df8bae1dSRodney W. Grimes m = m_get(M_WAIT, MT_SONAME); 952df8bae1dSRodney W. Grimes deldst = mtod(m, struct sockaddr *); 953df8bae1dSRodney W. Grimes rt_maskedcopy(dst, deldst, ifa->ifa_netmask); 954df8bae1dSRodney W. Grimes dst = deldst; 955df8bae1dSRodney W. Grimes } 956b0a76b88SJulian Elischer /* 957b0a76b88SJulian Elischer * Get an rtentry that is in the routing tree and 958499676dfSJulian Elischer * contains the correct info. (if this fails, can't get there). 959b0a76b88SJulian Elischer * We set "report" to FALSE so that if it doesn't exist, 960b0a76b88SJulian Elischer * it doesn't report an error or clone a route, etc. etc. 961b0a76b88SJulian Elischer */ 962995add1aSGarrett Wollman rt = rtalloc1(dst, 0, 0UL); 963623ae52eSPoul-Henning Kamp if (rt) { 964b0a76b88SJulian Elischer /* 965b0a76b88SJulian Elischer * Ok so we found the rtentry. it has an extra reference 966b0a76b88SJulian Elischer * for us at this stage. we won't need that so 967b0a76b88SJulian Elischer * lop that off now. 968b0a76b88SJulian Elischer */ 969df8bae1dSRodney W. Grimes rt->rt_refcnt--; 970df8bae1dSRodney W. Grimes if (rt->rt_ifa != ifa) { 971b0a76b88SJulian Elischer /* 972b0a76b88SJulian Elischer * If the interface in the rtentry doesn't match 973b0a76b88SJulian Elischer * the interface we are using, then we don't 974b0a76b88SJulian Elischer * want to delete it, so return an error. 975b0a76b88SJulian Elischer * This seems to be the only point of 976b0a76b88SJulian Elischer * this whole RTM_DELETE clause. 977b0a76b88SJulian Elischer */ 978df8bae1dSRodney W. Grimes if (m) 979df8bae1dSRodney W. Grimes (void) m_free(m); 980df8bae1dSRodney W. Grimes return (flags & RTF_HOST ? EHOSTUNREACH 981df8bae1dSRodney W. Grimes : ENETUNREACH); 982df8bae1dSRodney W. Grimes } 983df8bae1dSRodney W. Grimes } 984b0a76b88SJulian Elischer /* XXX */ 985b0a76b88SJulian Elischer #if 0 986b0a76b88SJulian Elischer else { 987b0a76b88SJulian Elischer /* 988b0a76b88SJulian Elischer * One would think that as we are deleting, and we know 989b0a76b88SJulian Elischer * it doesn't exist, we could just return at this point 990b0a76b88SJulian Elischer * with an "ELSE" clause, but apparently not.. 991b0a76b88SJulian Elischer */ 992b0a76b88SJulian Elischer return (flags & RTF_HOST ? EHOSTUNREACH 993b0a76b88SJulian Elischer : ENETUNREACH); 994df8bae1dSRodney W. Grimes } 995b0a76b88SJulian Elischer #endif 996b0a76b88SJulian Elischer } 997b0a76b88SJulian Elischer /* 998b0a76b88SJulian Elischer * Do the actual request 999b0a76b88SJulian Elischer */ 1000df8bae1dSRodney W. Grimes error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, 1001df8bae1dSRodney W. Grimes flags | ifa->ifa_flags, &nrt); 1002df8bae1dSRodney W. Grimes if (m) 1003df8bae1dSRodney W. Grimes (void) m_free(m); 1004b0a76b88SJulian Elischer /* 1005b0a76b88SJulian Elischer * If we are deleting, and we found an entry, then 1006b0a76b88SJulian Elischer * it's been removed from the tree.. now throw it away. 1007b0a76b88SJulian Elischer */ 1008df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) { 1009b0a76b88SJulian Elischer /* 1010b0a76b88SJulian Elischer * notify any listenning routing agents of the change 1011b0a76b88SJulian Elischer */ 1012df8bae1dSRodney W. Grimes rt_newaddrmsg(cmd, ifa, error, nrt); 1013df8bae1dSRodney W. Grimes if (rt->rt_refcnt <= 0) { 1014b0a76b88SJulian Elischer rt->rt_refcnt++; /* need a 1->0 transition to free */ 1015df8bae1dSRodney W. Grimes rtfree(rt); 1016df8bae1dSRodney W. Grimes } 1017df8bae1dSRodney W. Grimes } 1018b0a76b88SJulian Elischer 1019b0a76b88SJulian Elischer /* 1020b0a76b88SJulian Elischer * We are adding, and we have a returned routing entry. 1021b0a76b88SJulian Elischer * We need to sanity check the result. 1022b0a76b88SJulian Elischer */ 1023df8bae1dSRodney W. Grimes if (cmd == RTM_ADD && error == 0 && (rt = nrt)) { 1024b0a76b88SJulian Elischer /* 1025b0a76b88SJulian Elischer * We just wanted to add it.. we don't actually need a reference 1026b0a76b88SJulian Elischer */ 1027df8bae1dSRodney W. Grimes rt->rt_refcnt--; 1028b0a76b88SJulian Elischer /* 1029b0a76b88SJulian Elischer * If it came back with an unexpected interface, then it must 1030b0a76b88SJulian Elischer * have already existed or something. (XXX) 1031b0a76b88SJulian Elischer */ 1032df8bae1dSRodney W. Grimes if (rt->rt_ifa != ifa) { 1033623ae52eSPoul-Henning Kamp printf("rtinit: wrong ifa (%p) was (%p)\n", ifa, 1034df8bae1dSRodney W. Grimes rt->rt_ifa); 1035b0a76b88SJulian Elischer /* 1036499676dfSJulian Elischer * Ask that the protocol in question 1037499676dfSJulian Elischer * remove anything it has associated with 1038499676dfSJulian Elischer * this route and ifaddr. 1039b0a76b88SJulian Elischer */ 1040df8bae1dSRodney W. Grimes if (rt->rt_ifa->ifa_rtrequest) 1041df8bae1dSRodney W. Grimes rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 1042b0a76b88SJulian Elischer /* 1043b0a76b88SJulian Elischer * Remove the referenve to the it's ifaddr. 1044b0a76b88SJulian Elischer */ 1045df8bae1dSRodney W. Grimes IFAFREE(rt->rt_ifa); 1046b0a76b88SJulian Elischer /* 1047b0a76b88SJulian Elischer * And substitute in references to the ifaddr 1048b0a76b88SJulian Elischer * we are adding. 1049b0a76b88SJulian Elischer */ 1050df8bae1dSRodney W. Grimes rt->rt_ifa = ifa; 1051df8bae1dSRodney W. Grimes rt->rt_ifp = ifa->ifa_ifp; 1052df8bae1dSRodney W. Grimes ifa->ifa_refcnt++; 1053b0a76b88SJulian Elischer /* 1054499676dfSJulian Elischer * Now ask the protocol to check if it needs 1055499676dfSJulian Elischer * any special processing in it's new form. 1056b0a76b88SJulian Elischer */ 1057df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 1058df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(RTM_ADD, rt, SA(0)); 1059df8bae1dSRodney W. Grimes } 1060b0a76b88SJulian Elischer /* 1061b0a76b88SJulian Elischer * notify any listenning routing agents of the change 1062b0a76b88SJulian Elischer */ 1063df8bae1dSRodney W. Grimes rt_newaddrmsg(cmd, ifa, error, nrt); 1064df8bae1dSRodney W. Grimes } 10653ec66d6cSDavid Greenman return (error); 10663ec66d6cSDavid Greenman } 1067