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