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 344bd49128SPeter Wemm * $Id: route.c,v 1.30 1996/01/24 20:27:19 wollman Exp $ 35df8bae1dSRodney W. Grimes */ 36df8bae1dSRodney W. Grimes 374bd49128SPeter Wemm #include "opt_mrouting.h" 384bd49128SPeter Wemm 39df8bae1dSRodney W. Grimes #include <sys/param.h> 40df8bae1dSRodney W. Grimes #include <sys/systm.h> 415df72964SGarrett Wollman #include <sys/kernel.h> 42df8bae1dSRodney W. Grimes #include <sys/proc.h> 43df8bae1dSRodney W. Grimes #include <sys/mbuf.h> 44df8bae1dSRodney W. Grimes #include <sys/socket.h> 45df8bae1dSRodney W. Grimes #include <sys/socketvar.h> 46df8bae1dSRodney W. Grimes #include <sys/domain.h> 47df8bae1dSRodney W. Grimes #include <sys/protosw.h> 48df8bae1dSRodney W. Grimes #include <sys/ioctl.h> 49df8bae1dSRodney W. Grimes 50df8bae1dSRodney W. Grimes #include <net/if.h> 51df8bae1dSRodney W. Grimes #include <net/route.h> 52df8bae1dSRodney W. Grimes #include <net/raw_cb.h> 53df8bae1dSRodney W. Grimes 54df8bae1dSRodney W. Grimes #include <netinet/in.h> 55df8bae1dSRodney W. Grimes #include <netinet/in_var.h> 56b5e8ce9fSBruce Evans #include <netinet/ip_mroute.h> 57df8bae1dSRodney W. Grimes 58df8bae1dSRodney W. Grimes #define SA(p) ((struct sockaddr *)(p)) 59df8bae1dSRodney W. Grimes 6028f8db14SBruce Evans struct route_cb route_cb; 61f708ef1bSPoul-Henning Kamp static struct rtstat rtstat; 6228f8db14SBruce Evans struct radix_node_head *rt_tables[AF_MAX+1]; 6328f8db14SBruce Evans 64f708ef1bSPoul-Henning Kamp static int rttrash; /* routes not in table but not freed */ 65df8bae1dSRodney W. Grimes 66f708ef1bSPoul-Henning Kamp static void rt_maskedcopy __P((struct sockaddr *, 67f708ef1bSPoul-Henning Kamp struct sockaddr *, struct sockaddr *)); 68f708ef1bSPoul-Henning Kamp static void rtable_init __P((void **)); 69f708ef1bSPoul-Henning Kamp 70f708ef1bSPoul-Henning Kamp static void 71df8bae1dSRodney W. Grimes rtable_init(table) 72df8bae1dSRodney W. Grimes void **table; 73df8bae1dSRodney W. Grimes { 74df8bae1dSRodney W. Grimes struct domain *dom; 75df8bae1dSRodney W. Grimes for (dom = domains; dom; dom = dom->dom_next) 76df8bae1dSRodney W. Grimes if (dom->dom_rtattach) 77df8bae1dSRodney W. Grimes dom->dom_rtattach(&table[dom->dom_family], 78df8bae1dSRodney W. Grimes dom->dom_rtoffset); 79df8bae1dSRodney W. Grimes } 80df8bae1dSRodney W. Grimes 81df8bae1dSRodney W. Grimes void 82df8bae1dSRodney W. Grimes route_init() 83df8bae1dSRodney W. Grimes { 84df8bae1dSRodney W. Grimes rn_init(); /* initialize all zeroes, all ones, mask table */ 85df8bae1dSRodney W. Grimes rtable_init((void **)rt_tables); 86df8bae1dSRodney W. Grimes } 87df8bae1dSRodney W. Grimes 88df8bae1dSRodney W. Grimes /* 89df8bae1dSRodney W. Grimes * Packet routing routines. 90df8bae1dSRodney W. Grimes */ 91df8bae1dSRodney W. Grimes void 92df8bae1dSRodney W. Grimes rtalloc(ro) 93df8bae1dSRodney W. Grimes register struct route *ro; 94df8bae1dSRodney W. Grimes { 95df8bae1dSRodney W. Grimes if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 96df8bae1dSRodney W. Grimes return; /* XXX */ 97995add1aSGarrett Wollman ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL); 98df8bae1dSRodney W. Grimes } 99df8bae1dSRodney W. Grimes 100652082e6SGarrett Wollman void 101652082e6SGarrett Wollman rtalloc_ign(ro, ignore) 102652082e6SGarrett Wollman register struct route *ro; 103652082e6SGarrett Wollman u_long ignore; 104652082e6SGarrett Wollman { 105652082e6SGarrett Wollman if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 106652082e6SGarrett Wollman return; /* XXX */ 107652082e6SGarrett Wollman ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore); 108652082e6SGarrett Wollman } 109652082e6SGarrett Wollman 110df8bae1dSRodney W. Grimes struct rtentry * 111995add1aSGarrett Wollman rtalloc1(dst, report, ignflags) 112df8bae1dSRodney W. Grimes register struct sockaddr *dst; 113df8bae1dSRodney W. Grimes int report; 114995add1aSGarrett Wollman u_long ignflags; 115df8bae1dSRodney W. Grimes { 116df8bae1dSRodney W. Grimes register struct radix_node_head *rnh = rt_tables[dst->sa_family]; 117df8bae1dSRodney W. Grimes register struct rtentry *rt; 118df8bae1dSRodney W. Grimes register struct radix_node *rn; 119df8bae1dSRodney W. Grimes struct rtentry *newrt = 0; 120df8bae1dSRodney W. Grimes struct rt_addrinfo info; 121995add1aSGarrett Wollman u_long nflags; 122df8bae1dSRodney W. Grimes int s = splnet(), err = 0, msgtype = RTM_MISS; 123df8bae1dSRodney W. Grimes 124df8bae1dSRodney W. Grimes if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) && 125df8bae1dSRodney W. Grimes ((rn->rn_flags & RNF_ROOT) == 0)) { 126df8bae1dSRodney W. Grimes newrt = rt = (struct rtentry *)rn; 127995add1aSGarrett Wollman nflags = rt->rt_flags & ~ignflags; 128995add1aSGarrett Wollman if (report && (nflags & (RTF_CLONING | RTF_PRCLONING))) { 129df8bae1dSRodney W. Grimes err = rtrequest(RTM_RESOLVE, dst, SA(0), 130df8bae1dSRodney W. Grimes SA(0), 0, &newrt); 131df8bae1dSRodney W. Grimes if (err) { 132df8bae1dSRodney W. Grimes newrt = rt; 133df8bae1dSRodney W. Grimes rt->rt_refcnt++; 134df8bae1dSRodney W. Grimes goto miss; 135df8bae1dSRodney W. Grimes } 136df8bae1dSRodney W. Grimes if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { 137df8bae1dSRodney W. Grimes msgtype = RTM_RESOLVE; 138df8bae1dSRodney W. Grimes goto miss; 139df8bae1dSRodney W. Grimes } 140df8bae1dSRodney W. Grimes } else 141df8bae1dSRodney W. Grimes rt->rt_refcnt++; 142df8bae1dSRodney W. Grimes } else { 143df8bae1dSRodney W. Grimes rtstat.rts_unreach++; 144df8bae1dSRodney W. Grimes miss: if (report) { 145df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 146df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 147df8bae1dSRodney W. Grimes rt_missmsg(msgtype, &info, 0, err); 148df8bae1dSRodney W. Grimes } 149df8bae1dSRodney W. Grimes } 150df8bae1dSRodney W. Grimes splx(s); 151df8bae1dSRodney W. Grimes return (newrt); 152df8bae1dSRodney W. Grimes } 153df8bae1dSRodney W. Grimes 154df8bae1dSRodney W. Grimes void 155df8bae1dSRodney W. Grimes rtfree(rt) 156df8bae1dSRodney W. Grimes register struct rtentry *rt; 157df8bae1dSRodney W. Grimes { 1585c2dae8eSGarrett Wollman register struct radix_node_head *rnh = 1595c2dae8eSGarrett Wollman rt_tables[rt_key(rt)->sa_family]; 160df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 161df8bae1dSRodney W. Grimes 1623545b048SGarrett Wollman if (rt == 0 || rnh == 0) 163df8bae1dSRodney W. Grimes panic("rtfree"); 164df8bae1dSRodney W. Grimes rt->rt_refcnt--; 1655c2dae8eSGarrett Wollman if(rnh->rnh_close && rt->rt_refcnt == 0) { 1665c2dae8eSGarrett Wollman rnh->rnh_close((struct radix_node *)rt, rnh); 1675c2dae8eSGarrett Wollman } 168df8bae1dSRodney W. Grimes if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { 169df8bae1dSRodney W. Grimes if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 170df8bae1dSRodney W. Grimes panic ("rtfree 2"); 171df8bae1dSRodney W. Grimes rttrash--; 172df8bae1dSRodney W. Grimes if (rt->rt_refcnt < 0) { 173623ae52eSPoul-Henning Kamp printf("rtfree: %p not freed (neg refs)\n", rt); 174df8bae1dSRodney W. Grimes return; 175df8bae1dSRodney W. Grimes } 176df8bae1dSRodney W. Grimes ifa = rt->rt_ifa; 177df8bae1dSRodney W. Grimes IFAFREE(ifa); 178771edb14SGarrett Wollman if (rt->rt_parent) { 179771edb14SGarrett Wollman RTFREE(rt->rt_parent); 180771edb14SGarrett Wollman } 181df8bae1dSRodney W. Grimes Free(rt_key(rt)); 182df8bae1dSRodney W. Grimes Free(rt); 183df8bae1dSRodney W. Grimes } 184df8bae1dSRodney W. Grimes } 185df8bae1dSRodney W. Grimes 186df8bae1dSRodney W. Grimes void 187df8bae1dSRodney W. Grimes ifafree(ifa) 188df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 189df8bae1dSRodney W. Grimes { 190df8bae1dSRodney W. Grimes if (ifa == NULL) 191df8bae1dSRodney W. Grimes panic("ifafree"); 192df8bae1dSRodney W. Grimes if (ifa->ifa_refcnt == 0) 193df8bae1dSRodney W. Grimes free(ifa, M_IFADDR); 194df8bae1dSRodney W. Grimes else 195df8bae1dSRodney W. Grimes ifa->ifa_refcnt--; 196df8bae1dSRodney W. Grimes } 197df8bae1dSRodney W. Grimes 198df8bae1dSRodney W. Grimes /* 199df8bae1dSRodney W. Grimes * Force a routing table entry to the specified 200df8bae1dSRodney W. Grimes * destination to go through the given gateway. 201df8bae1dSRodney W. Grimes * Normally called as a result of a routing redirect 202df8bae1dSRodney W. Grimes * message from the network layer. 203df8bae1dSRodney W. Grimes * 204df8bae1dSRodney W. Grimes * N.B.: must be called at splnet 205df8bae1dSRodney W. Grimes * 206df8bae1dSRodney W. Grimes */ 20726f9a767SRodney W. Grimes void 208df8bae1dSRodney W. Grimes rtredirect(dst, gateway, netmask, flags, src, rtp) 209df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway, *netmask, *src; 210df8bae1dSRodney W. Grimes int flags; 211df8bae1dSRodney W. Grimes struct rtentry **rtp; 212df8bae1dSRodney W. Grimes { 213df8bae1dSRodney W. Grimes register struct rtentry *rt; 214df8bae1dSRodney W. Grimes int error = 0; 215df8bae1dSRodney W. Grimes short *stat = 0; 216df8bae1dSRodney W. Grimes struct rt_addrinfo info; 217df8bae1dSRodney W. Grimes struct ifaddr *ifa; 218df8bae1dSRodney W. Grimes 219df8bae1dSRodney W. Grimes /* verify the gateway is directly reachable */ 220df8bae1dSRodney W. Grimes if ((ifa = ifa_ifwithnet(gateway)) == 0) { 221df8bae1dSRodney W. Grimes error = ENETUNREACH; 222df8bae1dSRodney W. Grimes goto out; 223df8bae1dSRodney W. Grimes } 224995add1aSGarrett Wollman rt = rtalloc1(dst, 0, 0UL); 225df8bae1dSRodney W. Grimes /* 226df8bae1dSRodney W. Grimes * If the redirect isn't from our current router for this dst, 227df8bae1dSRodney W. Grimes * it's either old or wrong. If it redirects us to ourselves, 228df8bae1dSRodney W. Grimes * we have a routing loop, perhaps as a result of an interface 229df8bae1dSRodney W. Grimes * going down recently. 230df8bae1dSRodney W. Grimes */ 231df8bae1dSRodney W. Grimes #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) 232df8bae1dSRodney W. Grimes if (!(flags & RTF_DONE) && rt && 233df8bae1dSRodney W. Grimes (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa)) 234df8bae1dSRodney W. Grimes error = EINVAL; 235df8bae1dSRodney W. Grimes else if (ifa_ifwithaddr(gateway)) 236df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 237df8bae1dSRodney W. Grimes if (error) 238df8bae1dSRodney W. Grimes goto done; 239df8bae1dSRodney W. Grimes /* 240df8bae1dSRodney W. Grimes * Create a new entry if we just got back a wildcard entry 241df8bae1dSRodney W. Grimes * or the the lookup failed. This is necessary for hosts 242df8bae1dSRodney W. Grimes * which use routing redirects generated by smart gateways 243df8bae1dSRodney W. Grimes * to dynamically build the routing tables. 244df8bae1dSRodney W. Grimes */ 245df8bae1dSRodney W. Grimes if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) 246df8bae1dSRodney W. Grimes goto create; 247df8bae1dSRodney W. Grimes /* 248df8bae1dSRodney W. Grimes * Don't listen to the redirect if it's 249df8bae1dSRodney W. Grimes * for a route to an interface. 250df8bae1dSRodney W. Grimes */ 251df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 252df8bae1dSRodney W. Grimes if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 253df8bae1dSRodney W. Grimes /* 254df8bae1dSRodney W. Grimes * Changing from route to net => route to host. 255df8bae1dSRodney W. Grimes * Create new route, rather than smashing route to net. 256df8bae1dSRodney W. Grimes */ 257df8bae1dSRodney W. Grimes create: 258df8bae1dSRodney W. Grimes flags |= RTF_GATEWAY | RTF_DYNAMIC; 259df8bae1dSRodney W. Grimes error = rtrequest((int)RTM_ADD, dst, gateway, 260df8bae1dSRodney W. Grimes netmask, flags, 261df8bae1dSRodney W. Grimes (struct rtentry **)0); 262df8bae1dSRodney W. Grimes stat = &rtstat.rts_dynamic; 263df8bae1dSRodney W. Grimes } else { 264df8bae1dSRodney W. Grimes /* 265df8bae1dSRodney W. Grimes * Smash the current notion of the gateway to 266df8bae1dSRodney W. Grimes * this destination. Should check about netmask!!! 267df8bae1dSRodney W. Grimes */ 268df8bae1dSRodney W. Grimes rt->rt_flags |= RTF_MODIFIED; 269df8bae1dSRodney W. Grimes flags |= RTF_MODIFIED; 270df8bae1dSRodney W. Grimes stat = &rtstat.rts_newgateway; 271df8bae1dSRodney W. Grimes rt_setgate(rt, rt_key(rt), gateway); 272df8bae1dSRodney W. Grimes } 273df8bae1dSRodney W. Grimes } else 274df8bae1dSRodney W. Grimes error = EHOSTUNREACH; 275df8bae1dSRodney W. Grimes done: 276df8bae1dSRodney W. Grimes if (rt) { 277df8bae1dSRodney W. Grimes if (rtp && !error) 278df8bae1dSRodney W. Grimes *rtp = rt; 279df8bae1dSRodney W. Grimes else 280df8bae1dSRodney W. Grimes rtfree(rt); 281df8bae1dSRodney W. Grimes } 282df8bae1dSRodney W. Grimes out: 283df8bae1dSRodney W. Grimes if (error) 284df8bae1dSRodney W. Grimes rtstat.rts_badredirect++; 285df8bae1dSRodney W. Grimes else if (stat != NULL) 286df8bae1dSRodney W. Grimes (*stat)++; 287df8bae1dSRodney W. Grimes bzero((caddr_t)&info, sizeof(info)); 288df8bae1dSRodney W. Grimes info.rti_info[RTAX_DST] = dst; 289df8bae1dSRodney W. Grimes info.rti_info[RTAX_GATEWAY] = gateway; 290df8bae1dSRodney W. Grimes info.rti_info[RTAX_NETMASK] = netmask; 291df8bae1dSRodney W. Grimes info.rti_info[RTAX_AUTHOR] = src; 292df8bae1dSRodney W. Grimes rt_missmsg(RTM_REDIRECT, &info, flags, error); 293df8bae1dSRodney W. Grimes } 294df8bae1dSRodney W. Grimes 295df8bae1dSRodney W. Grimes /* 296df8bae1dSRodney W. Grimes * Routing table ioctl interface. 297df8bae1dSRodney W. Grimes */ 298df8bae1dSRodney W. Grimes int 299df8bae1dSRodney W. Grimes rtioctl(req, data, p) 300df8bae1dSRodney W. Grimes int req; 301df8bae1dSRodney W. Grimes caddr_t data; 302df8bae1dSRodney W. Grimes struct proc *p; 303df8bae1dSRodney W. Grimes { 304623ae52eSPoul-Henning Kamp #ifdef INET 305f0068c4aSGarrett Wollman /* Multicast goop, grrr... */ 306af32e59fSBruce Evans #ifdef MROUTING 307af32e59fSBruce Evans return mrt_ioctl(req, data); 308af32e59fSBruce Evans #else 309e4ca4481SStefan Eßer return mrt_ioctl(req, data, p); 310af32e59fSBruce Evans #endif 311623ae52eSPoul-Henning Kamp #else /* INET */ 312623ae52eSPoul-Henning Kamp return ENXIO; 313623ae52eSPoul-Henning Kamp #endif /* INET */ 314df8bae1dSRodney W. Grimes } 315df8bae1dSRodney W. Grimes 316df8bae1dSRodney W. Grimes struct ifaddr * 317df8bae1dSRodney W. Grimes ifa_ifwithroute(flags, dst, gateway) 318df8bae1dSRodney W. Grimes int flags; 319df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway; 320df8bae1dSRodney W. Grimes { 321df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 322df8bae1dSRodney W. Grimes if ((flags & RTF_GATEWAY) == 0) { 323df8bae1dSRodney W. Grimes /* 324df8bae1dSRodney W. Grimes * If we are adding a route to an interface, 325df8bae1dSRodney W. Grimes * and the interface is a pt to pt link 326df8bae1dSRodney W. Grimes * we should search for the destination 327df8bae1dSRodney W. Grimes * as our clue to the interface. Otherwise 328df8bae1dSRodney W. Grimes * we can use the local address. 329df8bae1dSRodney W. Grimes */ 330df8bae1dSRodney W. Grimes ifa = 0; 3315df72964SGarrett Wollman if (flags & RTF_HOST) { 332df8bae1dSRodney W. Grimes ifa = ifa_ifwithdstaddr(dst); 3335df72964SGarrett Wollman } 334df8bae1dSRodney W. Grimes if (ifa == 0) 335df8bae1dSRodney W. Grimes ifa = ifa_ifwithaddr(gateway); 336df8bae1dSRodney W. Grimes } else { 337df8bae1dSRodney W. Grimes /* 338df8bae1dSRodney W. Grimes * If we are adding a route to a remote net 339df8bae1dSRodney W. Grimes * or host, the gateway may still be on the 340df8bae1dSRodney W. Grimes * other end of a pt to pt link. 341df8bae1dSRodney W. Grimes */ 342df8bae1dSRodney W. Grimes ifa = ifa_ifwithdstaddr(gateway); 343df8bae1dSRodney W. Grimes } 344df8bae1dSRodney W. Grimes if (ifa == 0) 345df8bae1dSRodney W. Grimes ifa = ifa_ifwithnet(gateway); 346df8bae1dSRodney W. Grimes if (ifa == 0) { 347995add1aSGarrett Wollman struct rtentry *rt = rtalloc1(dst, 0, 0UL); 348df8bae1dSRodney W. Grimes if (rt == 0) 349df8bae1dSRodney W. Grimes return (0); 350df8bae1dSRodney W. Grimes rt->rt_refcnt--; 351df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) == 0) 352df8bae1dSRodney W. Grimes return (0); 353df8bae1dSRodney W. Grimes } 354df8bae1dSRodney W. Grimes if (ifa->ifa_addr->sa_family != dst->sa_family) { 355df8bae1dSRodney W. Grimes struct ifaddr *oifa = ifa; 356df8bae1dSRodney W. Grimes ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); 357df8bae1dSRodney W. Grimes if (ifa == 0) 358df8bae1dSRodney W. Grimes ifa = oifa; 359df8bae1dSRodney W. Grimes } 360df8bae1dSRodney W. Grimes return (ifa); 361df8bae1dSRodney W. Grimes } 362df8bae1dSRodney W. Grimes 363df8bae1dSRodney W. Grimes #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 364df8bae1dSRodney W. Grimes 365c2bed6a3SGarrett Wollman static int rt_fixdelete(struct radix_node *, void *); 366cd02a0b7SGarrett Wollman static int rt_fixchange(struct radix_node *, void *); 367cd02a0b7SGarrett Wollman 368cd02a0b7SGarrett Wollman struct rtfc_arg { 369cd02a0b7SGarrett Wollman struct rtentry *rt0; 370cd02a0b7SGarrett Wollman struct radix_node_head *rnh; 371cd02a0b7SGarrett Wollman }; 37218e1f1f1SGarrett Wollman 373df8bae1dSRodney W. Grimes int 374df8bae1dSRodney W. Grimes rtrequest(req, dst, gateway, netmask, flags, ret_nrt) 375df8bae1dSRodney W. Grimes int req, flags; 376df8bae1dSRodney W. Grimes struct sockaddr *dst, *gateway, *netmask; 377df8bae1dSRodney W. Grimes struct rtentry **ret_nrt; 378df8bae1dSRodney W. Grimes { 379df8bae1dSRodney W. Grimes int s = splnet(); int error = 0; 380df8bae1dSRodney W. Grimes register struct rtentry *rt; 381df8bae1dSRodney W. Grimes register struct radix_node *rn; 382df8bae1dSRodney W. Grimes register struct radix_node_head *rnh; 383df8bae1dSRodney W. Grimes struct ifaddr *ifa; 384df8bae1dSRodney W. Grimes struct sockaddr *ndst; 385df8bae1dSRodney W. Grimes #define senderr(x) { error = x ; goto bad; } 386df8bae1dSRodney W. Grimes 387df8bae1dSRodney W. Grimes if ((rnh = rt_tables[dst->sa_family]) == 0) 388df8bae1dSRodney W. Grimes senderr(ESRCH); 389df8bae1dSRodney W. Grimes if (flags & RTF_HOST) 390df8bae1dSRodney W. Grimes netmask = 0; 391df8bae1dSRodney W. Grimes switch (req) { 392df8bae1dSRodney W. Grimes case RTM_DELETE: 393df8bae1dSRodney W. Grimes if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == 0) 394df8bae1dSRodney W. Grimes senderr(ESRCH); 395df8bae1dSRodney W. Grimes if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 396df8bae1dSRodney W. Grimes panic ("rtrequest delete"); 397df8bae1dSRodney W. Grimes rt = (struct rtentry *)rn; 398c2bed6a3SGarrett Wollman 399c2bed6a3SGarrett Wollman /* 400c2bed6a3SGarrett Wollman * Now search what's left of the subtree for any cloned 401c2bed6a3SGarrett Wollman * routes which might have been formed from this node. 402c2bed6a3SGarrett Wollman */ 4033545b048SGarrett Wollman if ((rt->rt_flags & RTF_PRCLONING) && netmask) { 404c2bed6a3SGarrett Wollman rnh->rnh_walktree_from(rnh, dst, netmask, 405c2bed6a3SGarrett Wollman rt_fixdelete, rt); 406c2bed6a3SGarrett Wollman } 4073545b048SGarrett Wollman 4083545b048SGarrett Wollman /* 4093545b048SGarrett Wollman * NB: RTF_UP must be set during the search above, 4103545b048SGarrett Wollman * because we might delete the last ref, causing 4113545b048SGarrett Wollman * rt to get freed prematurely. 4123545b048SGarrett Wollman */ 4133545b048SGarrett Wollman rt->rt_flags &= ~RTF_UP; 4143545b048SGarrett Wollman 415df8bae1dSRodney W. Grimes if (rt->rt_gwroute) { 416df8bae1dSRodney W. Grimes rt = rt->rt_gwroute; RTFREE(rt); 417df8bae1dSRodney W. Grimes (rt = (struct rtentry *)rn)->rt_gwroute = 0; 418df8bae1dSRodney W. Grimes } 419df8bae1dSRodney W. Grimes if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) 420df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 421df8bae1dSRodney W. Grimes rttrash++; 422df8bae1dSRodney W. Grimes if (ret_nrt) 423df8bae1dSRodney W. Grimes *ret_nrt = rt; 424df8bae1dSRodney W. Grimes else if (rt->rt_refcnt <= 0) { 425df8bae1dSRodney W. Grimes rt->rt_refcnt++; 426df8bae1dSRodney W. Grimes rtfree(rt); 427df8bae1dSRodney W. Grimes } 428df8bae1dSRodney W. Grimes break; 429df8bae1dSRodney W. Grimes 430df8bae1dSRodney W. Grimes case RTM_RESOLVE: 431df8bae1dSRodney W. Grimes if (ret_nrt == 0 || (rt = *ret_nrt) == 0) 432df8bae1dSRodney W. Grimes senderr(EINVAL); 433df8bae1dSRodney W. Grimes ifa = rt->rt_ifa; 4343682d2baSDavid Greenman flags = rt->rt_flags & 4353682d2baSDavid Greenman ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC); 436995add1aSGarrett Wollman flags |= RTF_WASCLONED; 437df8bae1dSRodney W. Grimes gateway = rt->rt_gateway; 438df8bae1dSRodney W. Grimes if ((netmask = rt->rt_genmask) == 0) 439df8bae1dSRodney W. Grimes flags |= RTF_HOST; 440df8bae1dSRodney W. Grimes goto makeroute; 441df8bae1dSRodney W. Grimes 442df8bae1dSRodney W. Grimes case RTM_ADD: 4435df72964SGarrett Wollman if ((flags & RTF_GATEWAY) && !gateway) 4445df72964SGarrett Wollman panic("rtrequest: GATEWAY but no gateway"); 4455df72964SGarrett Wollman 446df8bae1dSRodney W. Grimes if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) 447df8bae1dSRodney W. Grimes senderr(ENETUNREACH); 4485df72964SGarrett Wollman 449df8bae1dSRodney W. Grimes makeroute: 450df8bae1dSRodney W. Grimes R_Malloc(rt, struct rtentry *, sizeof(*rt)); 451df8bae1dSRodney W. Grimes if (rt == 0) 452df8bae1dSRodney W. Grimes senderr(ENOBUFS); 453df8bae1dSRodney W. Grimes Bzero(rt, sizeof(*rt)); 454df8bae1dSRodney W. Grimes rt->rt_flags = RTF_UP | flags; 455df8bae1dSRodney W. Grimes if (rt_setgate(rt, dst, gateway)) { 456df8bae1dSRodney W. Grimes Free(rt); 457df8bae1dSRodney W. Grimes senderr(ENOBUFS); 458df8bae1dSRodney W. Grimes } 459df8bae1dSRodney W. Grimes ndst = rt_key(rt); 460df8bae1dSRodney W. Grimes if (netmask) { 461df8bae1dSRodney W. Grimes rt_maskedcopy(dst, ndst, netmask); 462df8bae1dSRodney W. Grimes } else 463df8bae1dSRodney W. Grimes Bcopy(dst, ndst, dst->sa_len); 4648e718bb4SGarrett Wollman 4658e718bb4SGarrett Wollman /* 4668e718bb4SGarrett Wollman * This moved from below so that rnh->rnh_addaddr() can 4678e718bb4SGarrett Wollman * examine the ifa and ifp if it so desires. 4688e718bb4SGarrett Wollman */ 4698e718bb4SGarrett Wollman ifa->ifa_refcnt++; 4708e718bb4SGarrett Wollman rt->rt_ifa = ifa; 4718e718bb4SGarrett Wollman rt->rt_ifp = ifa->ifa_ifp; 4728e718bb4SGarrett Wollman 473df8bae1dSRodney W. Grimes rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask, 474df8bae1dSRodney W. Grimes rnh, rt->rt_nodes); 475df8bae1dSRodney W. Grimes if (rn == 0) { 476aca1a47cSGarrett Wollman struct rtentry *rt2; 477aca1a47cSGarrett Wollman /* 478aca1a47cSGarrett Wollman * Uh-oh, we already have one of these in the tree. 479aca1a47cSGarrett Wollman * We do a special hack: if the route that's already 480aca1a47cSGarrett Wollman * there was generated by the protocol-cloning 481aca1a47cSGarrett Wollman * mechanism, then we just blow it away and retry 482aca1a47cSGarrett Wollman * the insertion of the new one. 483aca1a47cSGarrett Wollman */ 484aca1a47cSGarrett Wollman rt2 = rtalloc1(dst, 0, RTF_PRCLONING); 485aca1a47cSGarrett Wollman if (rt2 && rt2->rt_parent) { 486aca1a47cSGarrett Wollman rtrequest(RTM_DELETE, 487aca1a47cSGarrett Wollman (struct sockaddr *)rt_key(rt2), 488aca1a47cSGarrett Wollman rt2->rt_gateway, 489aca1a47cSGarrett Wollman rt_mask(rt2), rt2->rt_flags, 0); 490aca1a47cSGarrett Wollman RTFREE(rt2); 491aca1a47cSGarrett Wollman rn = rnh->rnh_addaddr((caddr_t)ndst, 492aca1a47cSGarrett Wollman (caddr_t)netmask, 493aca1a47cSGarrett Wollman rnh, rt->rt_nodes); 494fde327d6SGarrett Wollman } else if (rt2) { 495fde327d6SGarrett Wollman RTFREE(rt2); 496aca1a47cSGarrett Wollman } 497aca1a47cSGarrett Wollman } 498aca1a47cSGarrett Wollman 499aca1a47cSGarrett Wollman if (rn == 0) { 500df8bae1dSRodney W. Grimes if (rt->rt_gwroute) 501df8bae1dSRodney W. Grimes rtfree(rt->rt_gwroute); 5028e718bb4SGarrett Wollman if (rt->rt_ifa) { 5038e718bb4SGarrett Wollman IFAFREE(rt->rt_ifa); 5048e718bb4SGarrett Wollman } 505df8bae1dSRodney W. Grimes Free(rt_key(rt)); 506df8bae1dSRodney W. Grimes Free(rt); 507df8bae1dSRodney W. Grimes senderr(EEXIST); 508df8bae1dSRodney W. Grimes } 509771edb14SGarrett Wollman rt->rt_parent = 0; 510771edb14SGarrett Wollman 511c2bed6a3SGarrett Wollman if (req == RTM_RESOLVE) { 512df8bae1dSRodney W. Grimes rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ 513771edb14SGarrett Wollman if ((*ret_nrt)->rt_flags & RTF_PRCLONING) { 51418e1f1f1SGarrett Wollman rt->rt_parent = (*ret_nrt); 515771edb14SGarrett Wollman (*ret_nrt)->rt_refcnt++; 516771edb14SGarrett Wollman } 51718e1f1f1SGarrett Wollman } 518df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 519df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); 520cd02a0b7SGarrett Wollman /* 521cd02a0b7SGarrett Wollman * We repeat the same procedure from rt_setgate() here because 522cd02a0b7SGarrett Wollman * it doesn't fire when we call it there because the node 523cd02a0b7SGarrett Wollman * hasn't been added to the tree yet. 524cd02a0b7SGarrett Wollman */ 525cd02a0b7SGarrett Wollman if (!(rt->rt_flags & RTF_HOST)) { 526cd02a0b7SGarrett Wollman struct rtfc_arg arg; 527cd02a0b7SGarrett Wollman arg.rnh = rnh; 528cd02a0b7SGarrett Wollman arg.rt0 = rt; 529cd02a0b7SGarrett Wollman rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), 530cd02a0b7SGarrett Wollman rt_fixchange, &arg); 531cd02a0b7SGarrett Wollman } 532cd02a0b7SGarrett Wollman 533df8bae1dSRodney W. Grimes if (ret_nrt) { 534df8bae1dSRodney W. Grimes *ret_nrt = rt; 535df8bae1dSRodney W. Grimes rt->rt_refcnt++; 536df8bae1dSRodney W. Grimes } 537df8bae1dSRodney W. Grimes break; 538df8bae1dSRodney W. Grimes } 539df8bae1dSRodney W. Grimes bad: 540df8bae1dSRodney W. Grimes splx(s); 541df8bae1dSRodney W. Grimes return (error); 542df8bae1dSRodney W. Grimes } 543df8bae1dSRodney W. Grimes 54418e1f1f1SGarrett Wollman /* 54518e1f1f1SGarrett Wollman * Called from rtrequest(RTM_DELETE, ...) to fix up the route's ``family'' 54618e1f1f1SGarrett Wollman * (i.e., the routes related to it by the operation of cloning). This 547c2bed6a3SGarrett Wollman * routine is iterated over all potential former-child-routes by way of 548c2bed6a3SGarrett Wollman * rnh->rnh_walktree_from() above, and those that actually are children of 549c2bed6a3SGarrett Wollman * the late parent (passed in as VP here) are themselves deleted. 55018e1f1f1SGarrett Wollman */ 551c2bed6a3SGarrett Wollman static int 552c2bed6a3SGarrett Wollman rt_fixdelete(struct radix_node *rn, void *vp) 55318e1f1f1SGarrett Wollman { 554c2bed6a3SGarrett Wollman struct rtentry *rt = (struct rtentry *)rn; 555c2bed6a3SGarrett Wollman struct rtentry *rt0 = vp; 55618e1f1f1SGarrett Wollman 557a29ae2a1SGarrett Wollman if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) { 558c2bed6a3SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), 55918e1f1f1SGarrett Wollman (struct sockaddr *)0, rt_mask(rt), 56018e1f1f1SGarrett Wollman rt->rt_flags, (struct rtentry **)0); 56118e1f1f1SGarrett Wollman } 562c2bed6a3SGarrett Wollman return 0; 56318e1f1f1SGarrett Wollman } 56418e1f1f1SGarrett Wollman 565cd02a0b7SGarrett Wollman /* 566cd02a0b7SGarrett Wollman * This routine is called from rt_setgate() to do the analogous thing for 567cd02a0b7SGarrett Wollman * adds and changes. There is the added complication in this case of a 568cd02a0b7SGarrett Wollman * middle insert; i.e., insertion of a new network route between an older 569cd02a0b7SGarrett Wollman * network route and (cloned) host routes. For this reason, a simple check 570cd02a0b7SGarrett Wollman * of rt->rt_parent is insufficient; each candidate route must be tested 571cd02a0b7SGarrett Wollman * against the (mask, value) of the new route (passed as before in vp) 572cd02a0b7SGarrett Wollman * to see if the new route matches it. Unfortunately, this has the obnoxious 573cd02a0b7SGarrett Wollman * property of also triggering for insertion /above/ a pre-existing network 574cd02a0b7SGarrett Wollman * route and clones. Sigh. This may be fixed some day. 575cd02a0b7SGarrett Wollman * 576cd02a0b7SGarrett Wollman * XXX - it may be possible to do fixdelete() for changes and reserve this 577cd02a0b7SGarrett Wollman * routine just for adds. I'm not sure why I thought it was necessary to do 578cd02a0b7SGarrett Wollman * changes this way. 579cd02a0b7SGarrett Wollman */ 580cd02a0b7SGarrett Wollman #ifdef DEBUG 581cd02a0b7SGarrett Wollman int rtfcdebug = 0; 582cd02a0b7SGarrett Wollman #endif 583cd02a0b7SGarrett Wollman 584cd02a0b7SGarrett Wollman static int 585cd02a0b7SGarrett Wollman rt_fixchange(struct radix_node *rn, void *vp) 586cd02a0b7SGarrett Wollman { 587cd02a0b7SGarrett Wollman struct rtentry *rt = (struct rtentry *)rn; 588cd02a0b7SGarrett Wollman struct rtfc_arg *ap = vp; 589cd02a0b7SGarrett Wollman struct rtentry *rt0 = ap->rt0; 590cd02a0b7SGarrett Wollman struct radix_node_head *rnh = ap->rnh; 591cd02a0b7SGarrett Wollman u_char *xk1, *xm1, *xk2; 592cd02a0b7SGarrett Wollman int i, len; 593cd02a0b7SGarrett Wollman 594cd02a0b7SGarrett Wollman #ifdef DEBUG 595cd02a0b7SGarrett Wollman if (rtfcdebug) 596cd02a0b7SGarrett Wollman printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0); 597cd02a0b7SGarrett Wollman #endif 598cd02a0b7SGarrett Wollman 599cd02a0b7SGarrett Wollman if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) { 600cd02a0b7SGarrett Wollman #ifdef DEBUG 601cd02a0b7SGarrett Wollman if(rtfcdebug) printf("no parent or pinned\n"); 602cd02a0b7SGarrett Wollman #endif 603cd02a0b7SGarrett Wollman return 0; 604cd02a0b7SGarrett Wollman } 605cd02a0b7SGarrett Wollman 606cd02a0b7SGarrett Wollman if (rt->rt_parent == rt0) { 607cd02a0b7SGarrett Wollman #ifdef DEBUG 608cd02a0b7SGarrett Wollman if(rtfcdebug) printf("parent match\n"); 609cd02a0b7SGarrett Wollman #endif 610cd02a0b7SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), 611cd02a0b7SGarrett Wollman (struct sockaddr *)0, rt_mask(rt), 612cd02a0b7SGarrett Wollman rt->rt_flags, (struct rtentry **)0); 613cd02a0b7SGarrett Wollman } 614cd02a0b7SGarrett Wollman 615cd02a0b7SGarrett Wollman /* 616cd02a0b7SGarrett Wollman * There probably is a function somewhere which does this... 617cd02a0b7SGarrett Wollman * if not, there should be. 618cd02a0b7SGarrett Wollman */ 619cd02a0b7SGarrett Wollman len = imin(((struct sockaddr *)rt_key(rt0))->sa_len, 620cd02a0b7SGarrett Wollman ((struct sockaddr *)rt_key(rt))->sa_len); 621cd02a0b7SGarrett Wollman 622cd02a0b7SGarrett Wollman xk1 = (u_char *)rt_key(rt0); 623cd02a0b7SGarrett Wollman xm1 = (u_char *)rt_mask(rt0); 624cd02a0b7SGarrett Wollman xk2 = (u_char *)rt_key(rt); 625cd02a0b7SGarrett Wollman 626cd02a0b7SGarrett Wollman for (i = rnh->rnh_treetop->rn_off; i < len; i++) { 627cd02a0b7SGarrett Wollman if ((xk2[i] & xm1[i]) != xk1[i]) { 628cd02a0b7SGarrett Wollman #ifdef DEBUG 629cd02a0b7SGarrett Wollman if(rtfcdebug) printf("no match\n"); 630cd02a0b7SGarrett Wollman #endif 631cd02a0b7SGarrett Wollman return 0; 632cd02a0b7SGarrett Wollman } 633cd02a0b7SGarrett Wollman } 634cd02a0b7SGarrett Wollman 635cd02a0b7SGarrett Wollman /* 636cd02a0b7SGarrett Wollman * OK, this node is a clone, and matches the node currently being 637cd02a0b7SGarrett Wollman * changed/added under the node's mask. So, get rid of it. 638cd02a0b7SGarrett Wollman */ 639cd02a0b7SGarrett Wollman #ifdef DEBUG 640cd02a0b7SGarrett Wollman if(rtfcdebug) printf("deleting\n"); 641cd02a0b7SGarrett Wollman #endif 642cd02a0b7SGarrett Wollman return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, 643cd02a0b7SGarrett Wollman rt_mask(rt), rt->rt_flags, (struct rtentry **)0); 644cd02a0b7SGarrett Wollman } 645cd02a0b7SGarrett Wollman 646df8bae1dSRodney W. Grimes int 647df8bae1dSRodney W. Grimes rt_setgate(rt0, dst, gate) 648df8bae1dSRodney W. Grimes struct rtentry *rt0; 649df8bae1dSRodney W. Grimes struct sockaddr *dst, *gate; 650df8bae1dSRodney W. Grimes { 651df8bae1dSRodney W. Grimes caddr_t new, old; 652df8bae1dSRodney W. Grimes int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len); 653df8bae1dSRodney W. Grimes register struct rtentry *rt = rt0; 654cd02a0b7SGarrett Wollman struct radix_node_head *rnh = rt_tables[dst->sa_family]; 655df8bae1dSRodney W. Grimes 656df8bae1dSRodney W. Grimes if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) { 657df8bae1dSRodney W. Grimes old = (caddr_t)rt_key(rt); 658df8bae1dSRodney W. Grimes R_Malloc(new, caddr_t, dlen + glen); 659df8bae1dSRodney W. Grimes if (new == 0) 660df8bae1dSRodney W. Grimes return 1; 661df8bae1dSRodney W. Grimes rt->rt_nodes->rn_key = new; 662df8bae1dSRodney W. Grimes } else { 663df8bae1dSRodney W. Grimes new = rt->rt_nodes->rn_key; 664df8bae1dSRodney W. Grimes old = 0; 665df8bae1dSRodney W. Grimes } 666df8bae1dSRodney W. Grimes Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen); 667df8bae1dSRodney W. Grimes if (old) { 668df8bae1dSRodney W. Grimes Bcopy(dst, new, dlen); 669df8bae1dSRodney W. Grimes Free(old); 670df8bae1dSRodney W. Grimes } 671df8bae1dSRodney W. Grimes if (rt->rt_gwroute) { 672df8bae1dSRodney W. Grimes rt = rt->rt_gwroute; RTFREE(rt); 673df8bae1dSRodney W. Grimes rt = rt0; rt->rt_gwroute = 0; 674df8bae1dSRodney W. Grimes } 675cd02a0b7SGarrett Wollman /* 676cd02a0b7SGarrett Wollman * Cloning loop avoidance: 677cd02a0b7SGarrett Wollman * In the presence of protocol-cloning and bad configuration, 678cd02a0b7SGarrett Wollman * it is possible to get stuck in bottomless mutual recursion 679cd02a0b7SGarrett Wollman * (rtrequest rt_setgate rtalloc1). We avoid this by not allowing 680cd02a0b7SGarrett Wollman * protocol-cloning to operate for gateways (which is probably the 681cd02a0b7SGarrett Wollman * correct choice anyway), and avoid the resulting reference loops 682cd02a0b7SGarrett Wollman * by disallowing any route to run through itself as a gateway. 683cd02a0b7SGarrett Wollman * This is obviuosly mandatory when we get rt->rt_output(). 684cd02a0b7SGarrett Wollman */ 685df8bae1dSRodney W. Grimes if (rt->rt_flags & RTF_GATEWAY) { 686cd02a0b7SGarrett Wollman rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING); 687cd02a0b7SGarrett Wollman if (rt->rt_gwroute == rt) { 688cd02a0b7SGarrett Wollman RTFREE(rt->rt_gwroute); 689cd02a0b7SGarrett Wollman rt->rt_gwroute = 0; 690cd02a0b7SGarrett Wollman return 1; /* failure */ 691df8bae1dSRodney W. Grimes } 692cd02a0b7SGarrett Wollman } 693cd02a0b7SGarrett Wollman 694cd02a0b7SGarrett Wollman /* 695cd02a0b7SGarrett Wollman * This isn't going to do anything useful for host routes, so 696cd02a0b7SGarrett Wollman * don't bother. Also make sure we have a reasonable mask 697cd02a0b7SGarrett Wollman * (we don't yet have one during adds). 698cd02a0b7SGarrett Wollman */ 699cd02a0b7SGarrett Wollman if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { 700cd02a0b7SGarrett Wollman struct rtfc_arg arg; 701cd02a0b7SGarrett Wollman arg.rnh = rnh; 702cd02a0b7SGarrett Wollman arg.rt0 = rt; 703cd02a0b7SGarrett Wollman rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), 704cd02a0b7SGarrett Wollman rt_fixchange, &arg); 705cd02a0b7SGarrett Wollman } 706cd02a0b7SGarrett Wollman 707df8bae1dSRodney W. Grimes return 0; 708df8bae1dSRodney W. Grimes } 709df8bae1dSRodney W. Grimes 710f708ef1bSPoul-Henning Kamp static void 711df8bae1dSRodney W. Grimes rt_maskedcopy(src, dst, netmask) 712df8bae1dSRodney W. Grimes struct sockaddr *src, *dst, *netmask; 713df8bae1dSRodney W. Grimes { 714df8bae1dSRodney W. Grimes register u_char *cp1 = (u_char *)src; 715df8bae1dSRodney W. Grimes register u_char *cp2 = (u_char *)dst; 716df8bae1dSRodney W. Grimes register u_char *cp3 = (u_char *)netmask; 717df8bae1dSRodney W. Grimes u_char *cplim = cp2 + *cp3; 718df8bae1dSRodney W. Grimes u_char *cplim2 = cp2 + *cp1; 719df8bae1dSRodney W. Grimes 720df8bae1dSRodney W. Grimes *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ 721df8bae1dSRodney W. Grimes cp3 += 2; 722df8bae1dSRodney W. Grimes if (cplim > cplim2) 723df8bae1dSRodney W. Grimes cplim = cplim2; 724df8bae1dSRodney W. Grimes while (cp2 < cplim) 725df8bae1dSRodney W. Grimes *cp2++ = *cp1++ & *cp3++; 726df8bae1dSRodney W. Grimes if (cp2 < cplim2) 727df8bae1dSRodney W. Grimes bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); 728df8bae1dSRodney W. Grimes } 729df8bae1dSRodney W. Grimes 730df8bae1dSRodney W. Grimes /* 731df8bae1dSRodney W. Grimes * Set up a routing table entry, normally 732df8bae1dSRodney W. Grimes * for an interface. 733df8bae1dSRodney W. Grimes */ 734df8bae1dSRodney W. Grimes int 735df8bae1dSRodney W. Grimes rtinit(ifa, cmd, flags) 736df8bae1dSRodney W. Grimes register struct ifaddr *ifa; 737df8bae1dSRodney W. Grimes int cmd, flags; 738df8bae1dSRodney W. Grimes { 739df8bae1dSRodney W. Grimes register struct rtentry *rt; 740df8bae1dSRodney W. Grimes register struct sockaddr *dst; 741df8bae1dSRodney W. Grimes register struct sockaddr *deldst; 742df8bae1dSRodney W. Grimes struct mbuf *m = 0; 743df8bae1dSRodney W. Grimes struct rtentry *nrt = 0; 744df8bae1dSRodney W. Grimes int error; 745df8bae1dSRodney W. Grimes 746df8bae1dSRodney W. Grimes dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; 747df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE) { 748df8bae1dSRodney W. Grimes if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { 749df8bae1dSRodney W. Grimes m = m_get(M_WAIT, MT_SONAME); 750df8bae1dSRodney W. Grimes deldst = mtod(m, struct sockaddr *); 751df8bae1dSRodney W. Grimes rt_maskedcopy(dst, deldst, ifa->ifa_netmask); 752df8bae1dSRodney W. Grimes dst = deldst; 753df8bae1dSRodney W. Grimes } 754995add1aSGarrett Wollman rt = rtalloc1(dst, 0, 0UL); 755623ae52eSPoul-Henning Kamp if (rt) { 756df8bae1dSRodney W. Grimes rt->rt_refcnt--; 757df8bae1dSRodney W. Grimes if (rt->rt_ifa != ifa) { 758df8bae1dSRodney W. Grimes if (m) 759df8bae1dSRodney W. Grimes (void) m_free(m); 760df8bae1dSRodney W. Grimes return (flags & RTF_HOST ? EHOSTUNREACH 761df8bae1dSRodney W. Grimes : ENETUNREACH); 762df8bae1dSRodney W. Grimes } 763df8bae1dSRodney W. Grimes } 764df8bae1dSRodney W. Grimes } 765df8bae1dSRodney W. Grimes error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, 766df8bae1dSRodney W. Grimes flags | ifa->ifa_flags, &nrt); 767df8bae1dSRodney W. Grimes if (m) 768df8bae1dSRodney W. Grimes (void) m_free(m); 769df8bae1dSRodney W. Grimes if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) { 770df8bae1dSRodney W. Grimes rt_newaddrmsg(cmd, ifa, error, nrt); 771df8bae1dSRodney W. Grimes if (rt->rt_refcnt <= 0) { 772df8bae1dSRodney W. Grimes rt->rt_refcnt++; 773df8bae1dSRodney W. Grimes rtfree(rt); 774df8bae1dSRodney W. Grimes } 775df8bae1dSRodney W. Grimes } 776df8bae1dSRodney W. Grimes if (cmd == RTM_ADD && error == 0 && (rt = nrt)) { 777df8bae1dSRodney W. Grimes rt->rt_refcnt--; 778df8bae1dSRodney W. Grimes if (rt->rt_ifa != ifa) { 779623ae52eSPoul-Henning Kamp printf("rtinit: wrong ifa (%p) was (%p)\n", ifa, 780df8bae1dSRodney W. Grimes rt->rt_ifa); 781df8bae1dSRodney W. Grimes if (rt->rt_ifa->ifa_rtrequest) 782df8bae1dSRodney W. Grimes rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 783df8bae1dSRodney W. Grimes IFAFREE(rt->rt_ifa); 784df8bae1dSRodney W. Grimes rt->rt_ifa = ifa; 785df8bae1dSRodney W. Grimes rt->rt_ifp = ifa->ifa_ifp; 786df8bae1dSRodney W. Grimes ifa->ifa_refcnt++; 787df8bae1dSRodney W. Grimes if (ifa->ifa_rtrequest) 788df8bae1dSRodney W. Grimes ifa->ifa_rtrequest(RTM_ADD, rt, SA(0)); 789df8bae1dSRodney W. Grimes } 790df8bae1dSRodney W. Grimes rt_newaddrmsg(cmd, ifa, error, nrt); 791df8bae1dSRodney W. Grimes } 7923ec66d6cSDavid Greenman return (error); 7933ec66d6cSDavid Greenman } 794