1 /* 2 * Copyright (c) 1980, 1986 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 * 17 * @(#)route.c 7.12 (Berkeley) 05/04/89 18 */ 19 #include "machine/reg.h" 20 21 #include "param.h" 22 #include "systm.h" 23 #include "user.h" 24 #include "proc.h" 25 #include "mbuf.h" 26 #include "socket.h" 27 #include "socketvar.h" 28 #include "domain.h" 29 #include "protosw.h" 30 #include "errno.h" 31 #include "ioctl.h" 32 33 #include "if.h" 34 #include "af.h" 35 #include "route.h" 36 #include "raw_cb.h" 37 #include "../netinet/in.h" 38 #include "../netinet/in_var.h" 39 40 #include "../netns/ns.h" 41 #include "machine/mtpr.h" 42 #include "netisr.h" 43 44 #include "rtsock.c" 45 46 int rttrash; /* routes not in table but not freed */ 47 struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ 48 int rthashsize = RTHASHSIZ; /* for netstat, etc. */ 49 50 static int rtinits_done = 0; 51 struct radix_node_head *ns_rnhead, *in_rnhead; 52 struct radix_node *rn_match(), *rn_delete(), *rn_addroute(); 53 rtinitheads() 54 { 55 if (rtinits_done == 0 && 56 rn_inithead(&ns_rnhead, 16, AF_NS) && 57 rn_inithead(&in_rnhead, 32, AF_INET)) 58 rtinits_done = 1; 59 } 60 61 /* 62 * Packet routing routines. 63 */ 64 rtalloc(ro) 65 register struct route *ro; 66 { 67 if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 68 return; /* XXX */ 69 ro->ro_rt = rtalloc1(&ro->ro_dst, 1); 70 } 71 72 struct rtentry * 73 rtalloc1(dst, report) 74 struct sockaddr *dst; 75 int report; 76 { 77 register struct radix_node_head *rnh; 78 register struct radix_node *rn; 79 register struct rtentry *rt = 0; 80 u_char af = dst->sa_family; 81 int s = splnet(); 82 83 for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) 84 rnh = rnh->rnh_next; 85 if (rnh && rnh->rnh_treetop && 86 (rn = rn_match((caddr_t)dst, rnh->rnh_treetop)) && 87 ((rn->rn_flags & RNF_ROOT) == 0)) { 88 rt = (struct rtentry *)rn; 89 rt->rt_refcnt++; 90 } else { 91 rtstat.rts_unreach++; 92 if (report) 93 rt_missmsg(RTM_MISS, dst, (struct sockaddr *)0, 94 (struct sockaddr *)0, (struct sockaddr *)0, 0, 0); 95 } 96 splx(s); 97 return (rt); 98 } 99 100 rtfree(rt) 101 register struct rtentry *rt; 102 { 103 if (rt == 0) 104 panic("rtfree"); 105 rt->rt_refcnt--; 106 if (rt->rt_refcnt <= 0 && (rt->rt_flags&RTF_UP) == 0) { 107 rttrash--; 108 if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 109 panic ("rtfree 2"); 110 free((caddr_t)rt, M_RTABLE); 111 } 112 } 113 114 /* 115 * Force a routing table entry to the specified 116 * destination to go through the given gateway. 117 * Normally called as a result of a routing redirect 118 * message from the network layer. 119 * 120 * N.B.: must be called at splnet 121 * 122 */ 123 rtredirect(dst, gateway, netmask, flags, src, rtp) 124 struct sockaddr *dst, *gateway, *netmask, *src; 125 int flags; 126 struct rtentry **rtp; 127 { 128 register struct rtentry *rt; 129 int error = 0; 130 short *stat = 0; 131 132 /* verify the gateway is directly reachable */ 133 if (ifa_ifwithnet(gateway) == 0) { 134 error = ENETUNREACH; 135 goto done; 136 } 137 rt = rtalloc1(dst, 0); 138 /* 139 * If the redirect isn't from our current router for this dst, 140 * it's either old or wrong. If it redirects us to ourselves, 141 * we have a routing loop, perhaps as a result of an interface 142 * going down recently. 143 */ 144 #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) 145 if (!(flags & RTF_DONE) && rt && !equal(src, rt->rt_gateway)) 146 error = EINVAL; 147 else if (ifa_ifwithaddr(gateway)) 148 error = EHOSTUNREACH; 149 if (error) 150 goto done; 151 /* 152 * Create a new entry if we just got back a wildcard entry 153 * or the the lookup failed. This is necessary for hosts 154 * which use routing redirects generated by smart gateways 155 * to dynamically build the routing tables. 156 */ 157 if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) 158 goto create; 159 /* 160 * Don't listen to the redirect if it's 161 * for a route to an interface. 162 */ 163 if (rt->rt_flags & RTF_GATEWAY) { 164 if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 165 /* 166 * Changing from route to net => route to host. 167 * Create new route, rather than smashing route to net. 168 */ 169 create: 170 flags |= RTF_GATEWAY | RTF_DYNAMIC; 171 error = rtrequest((int)RTM_ADD, dst, gateway, 172 (struct sockaddr *)0, flags, 173 (struct rtentry **)0); 174 stat = &rtstat.rts_dynamic; 175 } else { 176 /* 177 * Smash the current notion of the gateway to 178 * this destination. Should check about netmask!!! 179 */ 180 if (gateway->sa_len <= rt->rt_gateway->sa_len) { 181 Bcopy(gateway, rt->rt_gateway, gateway->sa_len); 182 rt->rt_flags |= RTF_MODIFIED; 183 flags |= RTF_MODIFIED; 184 stat = &rtstat.rts_newgateway; 185 } else 186 error = ENOSPC; 187 } 188 } else 189 error = EHOSTUNREACH; 190 done: 191 if (rt) { 192 if (rtp && !error) 193 *rtp = rt; 194 else 195 rtfree(rt); 196 } 197 if (error) 198 rtstat.rts_badredirect++; 199 else 200 (stat && (*stat)++); 201 rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src, flags, error); 202 } 203 204 /* 205 * Routing table ioctl interface. 206 */ 207 rtioctl(req, data) 208 int req; 209 caddr_t data; 210 { 211 #ifndef COMPAT_43 212 return (EOPNOTSUPP); 213 #else 214 register struct ortentry *entry = (struct ortentry *)data; 215 int error; 216 struct sockaddr *netmask = 0; 217 218 if (req == SIOCADDRT) 219 req = RTM_ADD; 220 else if (req == SIOCDELRT) 221 req = RTM_DELETE; 222 else 223 return (EINVAL); 224 225 if (error = suser(u.u_cred, &u.u_acflag)) 226 return (error); 227 #if BYTE_ORDER != BIG_ENDIAN 228 if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) { 229 entry->rt_dst.sa_family = entry->rt_dst.sa_len; 230 entry->rt_dst.sa_len = 16; 231 } 232 if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) { 233 entry->rt_gateway.sa_family = entry->rt_gateway.sa_len; 234 entry->rt_gateway.sa_len = 16; 235 } 236 #else 237 if (entry->rt_dst.sa_len == 0) 238 entry->rt_dst.sa_len = 16; 239 if (entry->rt_gateway.sa_len == 0) 240 entry->rt_gateway.sa_len = 16; 241 #endif 242 if ((entry->rt_flags & RTF_HOST) == 0) 243 switch (entry->rt_dst.sa_family) { 244 #ifdef INET 245 case AF_INET: 246 { 247 extern struct sockaddr_in icmpmask; 248 struct sockaddr_in *dst_in = 249 (struct sockaddr_in *)&entry->rt_dst; 250 251 in_sockmaskof(dst_in->sin_addr, &icmpmask); 252 netmask = (struct sockaddr *)&icmpmask; 253 } 254 break; 255 #endif 256 #ifdef NS 257 case AF_NS: 258 { 259 extern struct sockaddr_ns ns_netmask; 260 netmask = (struct sockaddr *)&ns_netmask; 261 } 262 #endif 263 } 264 error = rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask, 265 entry->rt_flags, (struct rtentry **)0); 266 rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL), 267 &(entry->rt_dst), &(entry->rt_gateway), 268 netmask, (struct sockaddr *)0, entry->rt_flags, error); 269 return (error); 270 #endif 271 } 272 273 rtrequest(req, dst, gateway, netmask, flags, ret_nrt) 274 int req, flags; 275 struct sockaddr *dst, *gateway, *netmask; 276 struct rtentry **ret_nrt; 277 { 278 int s = splnet(), len, error = 0; 279 register struct rtentry *rt; 280 register struct radix_node *rn; 281 register struct radix_node_head *rnh; 282 struct ifaddr *ifa, *ifa_ifwithdstaddr(); 283 u_char af = dst->sa_family; 284 285 if (rtinits_done == 0) 286 rtinitheads(); 287 for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) 288 rnh = rnh->rnh_next; 289 if (rnh == 0) { 290 error = ESRCH; 291 goto bad; 292 } 293 switch (req) { 294 case RTM_DELETE: 295 if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask, 296 rnh->rnh_treetop)) == 0) { 297 error = ESRCH; 298 goto bad; 299 } 300 if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) 301 panic ("rtrequest delete"); 302 rt = (struct rtentry *)rn; 303 rt->rt_flags &= ~RTF_UP; 304 if (rt->rt_refcnt > 0) 305 rttrash++; 306 else 307 free((caddr_t)rt, M_RTABLE); 308 break; 309 310 case RTM_ADD: 311 if ((flags & RTF_GATEWAY) == 0) { 312 /* 313 * If we are adding a route to an interface, 314 * and the interface is a pt to pt link 315 * we should search for the destination 316 * as our clue to the interface. Otherwise 317 * we can use the local address. 318 */ 319 ifa = 0; 320 if (flags & RTF_HOST) 321 ifa = ifa_ifwithdstaddr(dst); 322 if (ifa == 0) 323 ifa = ifa_ifwithaddr(gateway); 324 } else { 325 /* 326 * If we are adding a route to a remote net 327 * or host, the gateway may still be on the 328 * other end of a pt to pt link. 329 */ 330 ifa = ifa_ifwithdstaddr(gateway); 331 } 332 if (ifa == 0) { 333 ifa = ifa_ifwithnet(gateway); 334 if (ifa == 0 && req == RTM_ADD) { 335 error = ENETUNREACH; 336 goto bad; 337 } 338 } 339 len = sizeof (*rt) + ROUNDUP(gateway->sa_len) 340 + ROUNDUP(dst->sa_len); 341 R_Malloc(rt, struct rtentry *, len); 342 if (rt == 0) { 343 error = ENOBUFS; 344 goto bad; 345 } 346 Bzero(rt, len); 347 rn = rn_addroute((caddr_t)dst, (caddr_t)netmask, 348 rnh->rnh_treetop, rt->rt_nodes); 349 if (rn == 0) { 350 free((caddr_t)rt, M_RTABLE); 351 error = EEXIST; 352 goto bad; 353 } 354 if (ret_nrt) 355 *ret_nrt = rt; /* == (struct rtentry *)rn */ 356 rt->rt_ifp = ifa->ifa_ifp; 357 rt->rt_ifa = ifa; 358 rt->rt_use = 0; 359 rt->rt_refcnt = 0; 360 rt->rt_flags = RTF_UP | 361 (flags & (RTF_HOST|RTF_GATEWAY|RTF_DYNAMIC)); 362 rn->rn_key = (caddr_t) (rt + 1); /* == rt_dst */ 363 Bcopy(dst, rn->rn_key, dst->sa_len); 364 rt->rt_gateway = (struct sockaddr *) 365 (rn->rn_key + ROUNDUP(dst->sa_len)); 366 Bcopy(gateway, rt->rt_gateway, gateway->sa_len); 367 break; 368 } 369 bad: 370 splx(s); 371 return (error); 372 } 373 /* 374 * Set up a routing table entry, normally 375 * for an interface. 376 */ 377 rtinit(ifa, cmd, flags) 378 register struct ifaddr *ifa; 379 int cmd, flags; 380 { 381 struct sockaddr net, *netp = &net; 382 register caddr_t cp, cp2, cp3; 383 caddr_t cplim, freeit = 0; 384 int len; 385 386 if (flags & RTF_HOST || ifa->ifa_netmask == 0) { 387 (void) rtrequest(cmd, ifa->ifa_dstaddr, ifa->ifa_addr, 388 (struct sockaddr *)0, flags, (struct rtentry **)0); 389 } else { 390 if ((len = ifa->ifa_addr->sa_len) >= sizeof (*netp)) { 391 R_Malloc(freeit, caddr_t, len); 392 if (freeit == 0) 393 return; 394 netp = (struct sockaddr *)freeit; 395 } 396 bzero((caddr_t)netp, len); 397 cp = (caddr_t) netp->sa_data; 398 cp3 = (caddr_t) ifa->ifa_netmask->sa_data; 399 cplim = ifa->ifa_netmask->sa_len + 400 (caddr_t) ifa->ifa_netmask; 401 cp2 = 1 + (caddr_t)ifa->ifa_addr; 402 netp->sa_family = *cp2++; 403 netp->sa_len = len; 404 while (cp3 < cplim) 405 *cp++ = *cp2++ & *cp3++; 406 (void) rtrequest(cmd, netp, ifa->ifa_addr, ifa->ifa_netmask, 407 flags, (struct rtentry **)0); 408 if (freeit) 409 free((caddr_t)freeit, M_RTABLE); 410 } 411 } 412 #include "radix.c" 413