1 /* 2 * Copyright (c) 1980, 1986, 1991 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * from: @(#)route.c 7.22 (Berkeley) 6/27/91 34 * $Id: route.c,v 1.4 1993/05/22 11:42:19 cgd Exp $ 35 */ 36 37 #include "param.h" 38 #include "systm.h" 39 #include "proc.h" 40 #include "mbuf.h" 41 #include "socket.h" 42 #include "socketvar.h" 43 #include "domain.h" 44 #include "protosw.h" 45 #include "ioctl.h" 46 47 #include "if.h" 48 #include "af.h" 49 #include "route.h" 50 #include "raw_cb.h" 51 52 #include "../netinet/in.h" 53 #include "../netinet/in_var.h" 54 55 #ifdef NS 56 #include "../netns/ns.h" 57 #endif 58 #include "machine/mtpr.h" 59 #include "netisr.h" 60 61 #define SA(p) ((struct sockaddr *)(p)) 62 63 int rttrash; /* routes not in table but not freed */ 64 struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ 65 int rthashsize = RTHASHSIZ; /* for netstat, etc. */ 66 67 static int rtinits_done = 0; 68 struct radix_node_head *ns_rnhead, *in_rnhead; 69 struct radix_node *rn_match(), *rn_delete(), *rn_addroute(); 70 71 rtinitheads() 72 { 73 if (rtinits_done == 0 && 74 #ifdef NS 75 rn_inithead(&ns_rnhead, 16, AF_NS) && 76 #endif 77 rn_inithead(&in_rnhead, 32, AF_INET)) 78 rtinits_done = 1; 79 } 80 81 /* 82 * Packet routing routines. 83 */ 84 rtalloc(ro) 85 register struct route *ro; 86 { 87 if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 88 return; /* XXX */ 89 ro->ro_rt = rtalloc1(&ro->ro_dst, 1); 90 } 91 92 struct rtentry * 93 rtalloc1(dst, report) 94 register struct sockaddr *dst; 95 int report; 96 { 97 register struct radix_node_head *rnh; 98 register struct rtentry *rt; 99 register struct radix_node *rn; 100 struct rtentry *newrt = 0; 101 int s = splnet(), err = 0, msgtype = RTM_MISS; 102 103 for (rnh = radix_node_head; rnh && (dst->sa_family != rnh->rnh_af); ) 104 rnh = rnh->rnh_next; 105 if (rnh && rnh->rnh_treetop && 106 (rn = rn_match((caddr_t)dst, rnh->rnh_treetop)) && 107 ((rn->rn_flags & RNF_ROOT) == 0)) { 108 newrt = rt = (struct rtentry *)rn; 109 if (report && (rt->rt_flags & RTF_CLONING)) { 110 if ((err = rtrequest(RTM_RESOLVE, dst, SA(0), 111 SA(0), 0, &newrt)) || 112 ((rt->rt_flags & RTF_XRESOLVE) 113 && (msgtype = RTM_RESOLVE))) /* intended! */ 114 goto miss; 115 } else 116 rt->rt_refcnt++; 117 } else { 118 rtstat.rts_unreach++; 119 miss: if (report) 120 rt_missmsg(msgtype, dst, SA(0), SA(0), SA(0), 0, err); 121 } 122 splx(s); 123 return (newrt); 124 } 125 126 rtfree(rt) 127 register struct rtentry *rt; 128 { 129 register struct ifaddr *ifa; 130 if (rt == 0) 131 panic("rtfree"); 132 rt->rt_refcnt--; 133 if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { 134 rttrash--; 135 if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 136 panic ("rtfree 2"); 137 free((caddr_t)rt, M_RTABLE); 138 } 139 } 140 141 /* 142 * Force a routing table entry to the specified 143 * destination to go through the given gateway. 144 * Normally called as a result of a routing redirect 145 * message from the network layer. 146 * 147 * N.B.: must be called at splnet 148 * 149 */ 150 rtredirect(dst, gateway, netmask, flags, src, rtp) 151 struct sockaddr *dst, *gateway, *netmask, *src; 152 int flags; 153 struct rtentry **rtp; 154 { 155 register struct rtentry *rt = 0; 156 int error = 0; 157 short *stat = 0; 158 159 /* verify the gateway is directly reachable */ 160 if (ifa_ifwithnet(gateway) == 0) { 161 error = ENETUNREACH; 162 goto done; 163 } 164 rt = rtalloc1(dst, 0); 165 /* 166 * If the redirect isn't from our current router for this dst, 167 * it's either old or wrong. If it redirects us to ourselves, 168 * we have a routing loop, perhaps as a result of an interface 169 * going down recently. 170 */ 171 #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) 172 if (!(flags & RTF_DONE) && rt && !equal(src, rt->rt_gateway)) 173 error = EINVAL; 174 else if (ifa_ifwithaddr(gateway)) 175 error = EHOSTUNREACH; 176 if (error) 177 goto done; 178 /* 179 * Create a new entry if we just got back a wildcard entry 180 * or the the lookup failed. This is necessary for hosts 181 * which use routing redirects generated by smart gateways 182 * to dynamically build the routing tables. 183 */ 184 if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) 185 goto create; 186 /* 187 * Don't listen to the redirect if it's 188 * for a route to an interface. 189 */ 190 if (rt->rt_flags & RTF_GATEWAY) { 191 if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 192 /* 193 * Changing from route to net => route to host. 194 * Create new route, rather than smashing route to net. 195 */ 196 create: 197 flags |= RTF_GATEWAY | RTF_DYNAMIC; 198 error = rtrequest((int)RTM_ADD, dst, gateway, 199 SA(0), flags, 200 (struct rtentry **)0); 201 stat = &rtstat.rts_dynamic; 202 } else { 203 /* 204 * Smash the current notion of the gateway to 205 * this destination. Should check about netmask!!! 206 */ 207 if (gateway->sa_len <= rt->rt_gateway->sa_len) { 208 Bcopy(gateway, rt->rt_gateway, gateway->sa_len); 209 rt->rt_flags |= RTF_MODIFIED; 210 flags |= RTF_MODIFIED; 211 stat = &rtstat.rts_newgateway; 212 } else 213 error = ENOSPC; 214 } 215 } else 216 error = EHOSTUNREACH; 217 done: 218 if (rt) { 219 if (rtp && !error) 220 *rtp = rt; 221 else 222 rtfree(rt); 223 } 224 if (error) 225 rtstat.rts_badredirect++; 226 else 227 (stat && (*stat)++); 228 rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src, flags, error); 229 } 230 231 /* 232 * Routing table ioctl interface. 233 */ 234 rtioctl(req, data, p) 235 int req; 236 caddr_t data; 237 struct proc *p; 238 { 239 #ifndef COMPAT_43 240 return (EOPNOTSUPP); 241 #else 242 register struct ortentry *entry = (struct ortentry *)data; 243 int error; 244 struct sockaddr *netmask = 0; 245 246 if (req == SIOCADDRT) 247 req = RTM_ADD; 248 else if (req == SIOCDELRT) 249 req = RTM_DELETE; 250 else 251 return (EINVAL); 252 253 if (error = suser(p->p_ucred, &p->p_acflag)) 254 return (error); 255 #if BYTE_ORDER != BIG_ENDIAN 256 if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) { 257 entry->rt_dst.sa_family = entry->rt_dst.sa_len; 258 entry->rt_dst.sa_len = 16; 259 } 260 if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) { 261 entry->rt_gateway.sa_family = entry->rt_gateway.sa_len; 262 entry->rt_gateway.sa_len = 16; 263 } 264 #else 265 if (entry->rt_dst.sa_len == 0) 266 entry->rt_dst.sa_len = 16; 267 if (entry->rt_gateway.sa_len == 0) 268 entry->rt_gateway.sa_len = 16; 269 #endif 270 if ((entry->rt_flags & RTF_HOST) == 0) 271 switch (entry->rt_dst.sa_family) { 272 #ifdef INET 273 case AF_INET: 274 { 275 extern struct sockaddr_in icmpmask; 276 struct sockaddr_in *dst_in = 277 (struct sockaddr_in *)&entry->rt_dst; 278 279 in_sockmaskof(dst_in->sin_addr, &icmpmask); 280 netmask = (struct sockaddr *)&icmpmask; 281 } 282 break; 283 #endif 284 #ifdef NS 285 case AF_NS: 286 { 287 extern struct sockaddr_ns ns_netmask; 288 netmask = (struct sockaddr *)&ns_netmask; 289 } 290 #endif 291 } 292 error = rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask, 293 entry->rt_flags, (struct rtentry **)0); 294 rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL), 295 &(entry->rt_dst), &(entry->rt_gateway), 296 netmask, SA(0), entry->rt_flags, error); 297 return (error); 298 #endif 299 } 300 301 struct ifaddr * 302 ifa_ifwithroute(flags, dst, gateway) 303 int flags; 304 struct sockaddr *dst, *gateway; 305 { 306 register struct ifaddr *ifa; 307 if ((flags & RTF_GATEWAY) == 0) { 308 /* 309 * If we are adding a route to an interface, 310 * and the interface is a pt to pt link 311 * we should search for the destination 312 * as our clue to the interface. Otherwise 313 * we can use the local address. 314 */ 315 ifa = 0; 316 if (flags & RTF_HOST) 317 ifa = ifa_ifwithdstaddr(dst); 318 if (ifa == 0) 319 ifa = ifa_ifwithaddr(gateway); 320 } else { 321 /* 322 * If we are adding a route to a remote net 323 * or host, the gateway may still be on the 324 * other end of a pt to pt link. 325 */ 326 ifa = ifa_ifwithdstaddr(gateway); 327 } 328 if (ifa == 0) 329 ifa = ifa_ifwithnet(gateway); 330 if (ifa == 0) { 331 struct rtentry *rt = rtalloc1(dst, 0); 332 if (rt == 0) 333 return (0); 334 rt->rt_refcnt--; 335 if ((ifa = rt->rt_ifa) == 0) 336 return (0); 337 } 338 if (ifa->ifa_addr->sa_family != dst->sa_family) { 339 struct ifaddr *oifa = ifa, *ifaof_ifpforaddr(); 340 ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); 341 if (ifa == 0) 342 ifa = oifa; 343 } 344 return (ifa); 345 } 346 347 #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 348 349 rtrequest(req, dst, gateway, netmask, flags, ret_nrt) 350 int req, flags; 351 struct sockaddr *dst, *gateway, *netmask; 352 struct rtentry **ret_nrt; 353 { 354 int s = splnet(), len, error = 0; 355 register struct rtentry *rt; 356 register struct radix_node *rn; 357 register struct radix_node_head *rnh; 358 struct ifaddr *ifa, *ifa_ifwithdstaddr(); 359 struct sockaddr *ndst; 360 u_char af = dst->sa_family; 361 #define senderr(x) { error = x ; goto bad; } 362 363 if (rtinits_done == 0) 364 rtinitheads(); 365 for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) 366 rnh = rnh->rnh_next; 367 if (rnh == 0) 368 senderr(ESRCH); 369 if (flags & RTF_HOST) 370 netmask = 0; 371 switch (req) { 372 case RTM_DELETE: 373 if (ret_nrt && (rt = *ret_nrt)) { 374 RTFREE(rt); 375 *ret_nrt = 0; 376 } 377 if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask, 378 rnh->rnh_treetop)) == 0) 379 senderr(ESRCH); 380 if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 381 panic ("rtrequest delete"); 382 rt = (struct rtentry *)rn; 383 rt->rt_flags &= ~RTF_UP; 384 if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) 385 ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); 386 rttrash++; 387 if (rt->rt_refcnt <= 0) 388 rtfree(rt); 389 break; 390 391 case RTM_RESOLVE: 392 if (ret_nrt == 0 || (rt = *ret_nrt) == 0) 393 senderr(EINVAL); 394 ifa = rt->rt_ifa; 395 flags = rt->rt_flags & ~RTF_CLONING; 396 gateway = rt->rt_gateway; 397 if ((netmask = rt->rt_genmask) == 0) 398 flags |= RTF_HOST; 399 goto makeroute; 400 401 case RTM_ADD: 402 if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) 403 senderr(ENETUNREACH); 404 makeroute: 405 len = sizeof (*rt) + ROUNDUP(gateway->sa_len) 406 + ROUNDUP(dst->sa_len); 407 R_Malloc(rt, struct rtentry *, len); 408 if (rt == 0) 409 senderr(ENOBUFS); 410 Bzero(rt, len); 411 ndst = (struct sockaddr *)(rt + 1); 412 if (netmask) { 413 rt_maskedcopy(dst, ndst, netmask); 414 } else 415 Bcopy(dst, ndst, dst->sa_len); 416 rn = rn_addroute((caddr_t)ndst, (caddr_t)netmask, 417 rnh->rnh_treetop, rt->rt_nodes); 418 if (rn == 0) { 419 free((caddr_t)rt, M_RTABLE); 420 senderr(EEXIST); 421 } 422 rt->rt_ifa = ifa; 423 rt->rt_ifp = ifa->ifa_ifp; 424 rt->rt_flags = RTF_UP | flags; 425 rt->rt_gateway = (struct sockaddr *) 426 (rn->rn_key + ROUNDUP(dst->sa_len)); 427 Bcopy(gateway, rt->rt_gateway, gateway->sa_len); 428 if (req == RTM_RESOLVE) 429 rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ 430 if (ifa->ifa_rtrequest) 431 ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); 432 if (ret_nrt) { 433 *ret_nrt = rt; 434 rt->rt_refcnt++; 435 } 436 break; 437 } 438 bad: 439 splx(s); 440 return (error); 441 } 442 443 rt_maskedcopy(src, dst, netmask) 444 struct sockaddr *src, *dst, *netmask; 445 { 446 register u_char *cp1 = (u_char *)src; 447 register u_char *cp2 = (u_char *)dst; 448 register u_char *cp3 = (u_char *)netmask; 449 u_char *cplim = cp2 + *cp3; 450 u_char *cplim2 = cp2 + *cp1; 451 452 *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ 453 cp3 += 2; 454 if (cplim > cplim2) 455 cplim = cplim2; 456 while (cp2 < cplim) 457 *cp2++ = *cp1++ & *cp3++; 458 if (cp2 < cplim2) 459 bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); 460 } 461 /* 462 * Set up a routing table entry, normally 463 * for an interface. 464 */ 465 rtinit(ifa, cmd, flags) 466 register struct ifaddr *ifa; 467 int cmd, flags; 468 { 469 register struct rtentry *rt; 470 register struct sockaddr *dst; 471 register struct sockaddr *deldst; 472 struct mbuf *m = 0; 473 int error; 474 475 dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; 476 if (ifa->ifa_flags & IFA_ROUTE) { 477 if ((rt = ifa->ifa_rt) && (rt->rt_flags & RTF_UP) == 0) { 478 RTFREE(rt); 479 ifa->ifa_rt = 0; 480 } 481 } 482 if (cmd == RTM_DELETE) { 483 if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { 484 m = m_get(M_WAIT, MT_SONAME); 485 deldst = mtod(m, struct sockaddr *); 486 rt_maskedcopy(dst, deldst, ifa->ifa_netmask); 487 dst = deldst; 488 } 489 if (rt = rtalloc1(dst, 0)) { 490 rt->rt_refcnt--; 491 if (rt->rt_ifa != ifa) { 492 if (m) 493 (void) m_free(m); 494 return (flags & RTF_HOST ? EHOSTUNREACH 495 : ENETUNREACH); 496 } 497 } 498 } 499 error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, 500 flags | ifa->ifa_flags, &ifa->ifa_rt); 501 if (m) 502 (void) m_free(m); 503 if (cmd == RTM_ADD && error == 0 && (rt = ifa->ifa_rt) 504 && rt->rt_ifa != ifa) { 505 rt->rt_ifa = ifa; 506 rt->rt_ifp = ifa->ifa_ifp; 507 } 508 return (error); 509 } 510