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