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.6 (Berkeley) 10/18/88 18 */ 19 20 #include "param.h" 21 #include "systm.h" 22 #include "mbuf.h" 23 #include "protosw.h" 24 #include "socket.h" 25 #include "dir.h" 26 #include "user.h" 27 #include "ioctl.h" 28 #include "errno.h" 29 30 #include "if.h" 31 #include "af.h" 32 #include "route.h" 33 34 int rttrash; /* routes not in table but not freed */ 35 struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ 36 int rthashsize = RTHASHSIZ; /* for netstat, etc. */ 37 38 /* 39 * Packet routing routines. 40 */ 41 rtalloc(ro) 42 register struct route *ro; 43 { 44 register struct rtentry *rt; 45 register struct mbuf *m; 46 register u_long hash; 47 struct sockaddr *dst = &ro->ro_dst; 48 int (*match)(), doinghost, s; 49 struct afhash h; 50 u_int af = dst->sa_family; 51 struct mbuf **table; 52 53 if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) 54 return; /* XXX */ 55 if (af >= AF_MAX) 56 return; 57 (*afswitch[af].af_hash)(dst, &h); 58 match = afswitch[af].af_netmatch; 59 hash = h.afh_hosthash, table = rthost, doinghost = 1; 60 s = splnet(); 61 again: 62 for (m = table[RTHASHMOD(hash)]; m; m = m->m_next) { 63 rt = mtod(m, struct rtentry *); 64 if (rt->rt_hash != hash) 65 continue; 66 if ((rt->rt_flags & RTF_UP) == 0 || 67 (rt->rt_ifp->if_flags & IFF_UP) == 0) 68 continue; 69 if (doinghost) { 70 if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst, 71 sizeof (*dst))) 72 continue; 73 } else { 74 if (rt->rt_dst.sa_family != af || 75 !(*match)(&rt->rt_dst, dst)) 76 continue; 77 } 78 rt->rt_refcnt++; 79 splx(s); 80 if (dst == &wildcard) 81 rtstat.rts_wildcard++; 82 ro->ro_rt = rt; 83 return; 84 } 85 if (doinghost) { 86 doinghost = 0; 87 hash = h.afh_nethash, table = rtnet; 88 goto again; 89 } 90 /* 91 * Check for wildcard gateway, by convention network 0. 92 */ 93 if (dst != &wildcard) { 94 dst = &wildcard, hash = 0; 95 goto again; 96 } 97 splx(s); 98 rtstat.rts_unreach++; 99 } 100 101 rtfree(rt) 102 register struct rtentry *rt; 103 { 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 (void) m_free(dtom(rt)); 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 or higher 121 * 122 */ 123 rtredirect(dst, gateway, flags, src) 124 struct sockaddr *dst, *gateway, *src; 125 int flags; 126 { 127 struct route ro; 128 register struct rtentry *rt; 129 130 /* verify the gateway is directly reachable */ 131 if (ifa_ifwithnet(gateway) == 0) { 132 rtstat.rts_badredirect++; 133 return; 134 } 135 ro.ro_dst = *dst; 136 ro.ro_rt = 0; 137 rtalloc(&ro); 138 rt = ro.ro_rt; 139 #define equal(a1, a2) \ 140 (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) 141 /* 142 * If the redirect isn't from our current router for this dst, 143 * it's either old or wrong. If it redirects us to ourselves, 144 * we have a routing loop, perhaps as a result of an interface 145 * going down recently. 146 */ 147 if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) { 148 rtstat.rts_badredirect++; 149 if (rt) 150 rtfree(rt); 151 return; 152 } 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 && 160 (*afswitch[dst->sa_family].af_netmatch)(&wildcard, &rt->rt_dst)) { 161 rtfree(rt); 162 rt = 0; 163 } 164 if (rt == 0) { 165 rtinit(dst, gateway, (int)SIOCADDRT, 166 (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC); 167 rtstat.rts_dynamic++; 168 return; 169 } 170 /* 171 * Don't listen to the redirect if it's 172 * for a route to an interface. 173 */ 174 if (rt->rt_flags & RTF_GATEWAY) { 175 if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 176 /* 177 * Changing from route to net => route to host. 178 * Create new route, rather than smashing route to net. 179 */ 180 rtinit(dst, gateway, (int)SIOCADDRT, 181 flags | RTF_DYNAMIC); 182 rtstat.rts_dynamic++; 183 } else { 184 /* 185 * Smash the current notion of the gateway to 186 * this destination. 187 */ 188 rt->rt_gateway = *gateway; 189 rt->rt_flags |= RTF_MODIFIED; 190 rtstat.rts_newgateway++; 191 } 192 } else 193 rtstat.rts_badredirect++; 194 rtfree(rt); 195 } 196 197 /* 198 * Routing table ioctl interface. 199 */ 200 rtioctl(cmd, data) 201 int cmd; 202 caddr_t data; 203 { 204 205 if (cmd != SIOCADDRT && cmd != SIOCDELRT) 206 return (EINVAL); 207 if (!suser()) 208 return (u.u_error); 209 return (rtrequest(cmd, (struct rtentry *)data)); 210 } 211 212 /* 213 * Carry out a request to change the routing table. Called by 214 * interfaces at boot time to make their ``local routes'' known, 215 * for ioctl's, and as the result of routing redirects. 216 */ 217 rtrequest(req, entry) 218 int req; 219 register struct rtentry *entry; 220 { 221 register struct mbuf *m, **mprev; 222 struct mbuf **mfirst; 223 register struct rtentry *rt; 224 struct afhash h; 225 int s, error = 0, (*match)(); 226 u_int af; 227 u_long hash; 228 struct ifaddr *ifa; 229 struct ifaddr *ifa_ifwithdstaddr(); 230 231 #ifdef COMPAT_43 232 #if BYTE_ORDER != BIG_ENDIAN 233 if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) { 234 entry->rt_dst.sa_family = entry->rt_dst.sa_len; 235 entry->rt_dst.sa_len = 16; 236 } 237 if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) { 238 entry->rt_gateway.sa_family = entry->rt_gateway.sa_len; 239 entry->rt_gateway.sa_len = 16; 240 } 241 #else 242 if (entry->rt_dst.sa_len == 0) 243 entry->rt_dst.sa_len = 16; 244 if (entry->rt_gateway.sa_len == 0) 245 entry->rt_gateway.sa_len = 16; 246 #endif 247 #endif 248 af = entry->rt_dst.sa_family; 249 if (af >= AF_MAX) 250 return (EAFNOSUPPORT); 251 (*afswitch[af].af_hash)(&entry->rt_dst, &h); 252 if (entry->rt_flags & RTF_HOST) { 253 hash = h.afh_hosthash; 254 mprev = &rthost[RTHASHMOD(hash)]; 255 } else { 256 hash = h.afh_nethash; 257 mprev = &rtnet[RTHASHMOD(hash)]; 258 } 259 match = afswitch[af].af_netmatch; 260 s = splimp(); 261 for (mfirst = mprev; m = *mprev; mprev = &m->m_next) { 262 rt = mtod(m, struct rtentry *); 263 if (rt->rt_hash != hash) 264 continue; 265 if (entry->rt_flags & RTF_HOST) { 266 if (!equal(&rt->rt_dst, &entry->rt_dst)) 267 continue; 268 } else { 269 if (rt->rt_dst.sa_family != entry->rt_dst.sa_family || 270 (*match)(&rt->rt_dst, &entry->rt_dst) == 0) 271 continue; 272 } 273 if (equal(&rt->rt_gateway, &entry->rt_gateway)) 274 break; 275 } 276 switch (req) { 277 278 case SIOCDELRT: 279 if (m == 0) { 280 error = ESRCH; 281 goto bad; 282 } 283 *mprev = m->m_next; 284 if (rt->rt_refcnt > 0) { 285 rt->rt_flags &= ~RTF_UP; 286 rttrash++; 287 m->m_next = 0; 288 } else 289 (void) m_free(m); 290 break; 291 292 case SIOCADDRT: 293 if (m) { 294 error = EEXIST; 295 goto bad; 296 } 297 if ((entry->rt_flags & RTF_GATEWAY) == 0) { 298 /* 299 * If we are adding a route to an interface, 300 * and the interface is a pt to pt link 301 * we should search for the destination 302 * as our clue to the interface. Otherwise 303 * we can use the local address. 304 */ 305 ifa = 0; 306 if (entry->rt_flags & RTF_HOST) 307 ifa = ifa_ifwithdstaddr(&entry->rt_dst); 308 if (ifa == 0) 309 ifa = ifa_ifwithaddr(&entry->rt_gateway); 310 } else { 311 /* 312 * If we are adding a route to a remote net 313 * or host, the gateway may still be on the 314 * other end of a pt to pt link. 315 */ 316 ifa = ifa_ifwithdstaddr(&entry->rt_gateway); 317 } 318 if (ifa == 0) { 319 ifa = ifa_ifwithnet(&entry->rt_gateway); 320 if (ifa == 0) { 321 error = ENETUNREACH; 322 goto bad; 323 } 324 } 325 m = m_get(M_DONTWAIT, MT_RTABLE); 326 if (m == 0) { 327 error = ENOBUFS; 328 goto bad; 329 } 330 m->m_next = *mfirst; 331 *mfirst = m; 332 m->m_len = sizeof (struct rtentry); 333 rt = mtod(m, struct rtentry *); 334 rt->rt_hash = hash; 335 rt->rt_dst = entry->rt_dst; 336 rt->rt_gateway = entry->rt_gateway; 337 rt->rt_flags = RTF_UP | 338 (entry->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_DYNAMIC)); 339 rt->rt_refcnt = 0; 340 rt->rt_use = 0; 341 rt->rt_ifp = ifa->ifa_ifp; 342 break; 343 } 344 bad: 345 splx(s); 346 return (error); 347 } 348 349 /* 350 * Set up a routing table entry, normally 351 * for an interface. 352 */ 353 rtinit(dst, gateway, cmd, flags) 354 struct sockaddr *dst, *gateway; 355 int cmd, flags; 356 { 357 struct rtentry route; 358 359 bzero((caddr_t)&route, sizeof (route)); 360 route.rt_dst = *dst; 361 route.rt_gateway = *gateway; 362 route.rt_flags = flags; 363 (void) rtrequest(cmd, &route); 364 } 365