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