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