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