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