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