1 /* route.c 6.7 84/09/05 */ 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 (if_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 rtfree(rt); 153 rtinit(dst, gateway, flags); 154 } else { 155 /* 156 * Smash the current notion of the gateway to 157 * this destination. This is probably not right, 158 * as it's conceivable a flurry of redirects could 159 * cause the gateway value to fluctuate wildly during 160 * dynamic routing reconfiguration. 161 */ 162 rt->rt_gateway = *gateway; 163 rtfree(rt); 164 } 165 rtstat.rts_newgateway++; 166 } 167 } 168 169 /* 170 * Routing table ioctl interface. 171 */ 172 rtioctl(cmd, data) 173 int cmd; 174 caddr_t data; 175 { 176 177 if (cmd != SIOCADDRT && cmd != SIOCDELRT) 178 return (EINVAL); 179 if (!suser()) 180 return (u.u_error); 181 return (rtrequest(cmd, (struct rtentry *)data)); 182 } 183 184 /* 185 * Carry out a request to change the routing table. Called by 186 * interfaces at boot time to make their ``local routes'' known, 187 * for ioctl's, and as the result of routing redirects. 188 */ 189 rtrequest(req, entry) 190 int req; 191 register struct rtentry *entry; 192 { 193 register struct mbuf *m, **mprev; 194 register struct rtentry *rt; 195 struct afhash h; 196 int s, error = 0, (*match)(); 197 u_int af; 198 u_long hash; 199 struct ifnet *ifp; 200 201 af = entry->rt_dst.sa_family; 202 if (af >= AF_MAX) 203 return (EAFNOSUPPORT); 204 (*afswitch[af].af_hash)(&entry->rt_dst, &h); 205 if (entry->rt_flags & RTF_HOST) { 206 hash = h.afh_hosthash; 207 mprev = &rthost[RTHASHMOD(hash)]; 208 } else { 209 hash = h.afh_nethash; 210 mprev = &rtnet[RTHASHMOD(hash)]; 211 } 212 match = afswitch[af].af_netmatch; 213 s = splimp(); 214 for (; m = *mprev; mprev = &m->m_next) { 215 rt = mtod(m, struct rtentry *); 216 if (rt->rt_hash != hash) 217 continue; 218 if (entry->rt_flags & RTF_HOST) { 219 #define equal(a1, a2) \ 220 (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) 221 if (!equal(&rt->rt_dst, &entry->rt_dst)) 222 continue; 223 } else { 224 if (rt->rt_dst.sa_family != entry->rt_dst.sa_family || 225 (*match)(&rt->rt_dst, &entry->rt_dst) == 0) 226 continue; 227 } 228 if (equal(&rt->rt_gateway, &entry->rt_gateway)) 229 break; 230 } 231 switch (req) { 232 233 case SIOCDELRT: 234 if (m == 0) { 235 error = ESRCH; 236 goto bad; 237 } 238 *mprev = m->m_next; 239 if (rt->rt_refcnt > 0) { 240 rt->rt_flags &= ~RTF_UP; 241 rttrash++; 242 m->m_next = 0; 243 } else 244 (void) m_free(m); 245 break; 246 247 case SIOCADDRT: 248 if (m) { 249 error = EEXIST; 250 goto bad; 251 } 252 ifp = if_ifwithaddr(&entry->rt_gateway); 253 if (ifp == 0) { 254 ifp = if_ifwithnet(&entry->rt_gateway); 255 if (ifp == 0) { 256 error = ENETUNREACH; 257 goto bad; 258 } 259 } 260 m = m_get(M_DONTWAIT, MT_RTABLE); 261 if (m == 0) { 262 error = ENOBUFS; 263 goto bad; 264 } 265 *mprev = m; 266 m->m_off = MMINOFF; 267 m->m_len = sizeof (struct rtentry); 268 rt = mtod(m, struct rtentry *); 269 rt->rt_hash = hash; 270 rt->rt_dst = entry->rt_dst; 271 rt->rt_gateway = entry->rt_gateway; 272 rt->rt_flags = 273 RTF_UP | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY)); 274 rt->rt_refcnt = 0; 275 rt->rt_use = 0; 276 rt->rt_ifp = ifp; 277 break; 278 } 279 bad: 280 splx(s); 281 return (error); 282 } 283 284 /* 285 * Set up a routing table entry, normally 286 * for an interface. 287 */ 288 rtinit(dst, gateway, flags) 289 struct sockaddr *dst, *gateway; 290 int flags; 291 { 292 struct rtentry route; 293 int cmd; 294 295 if (flags == -1) { 296 cmd = (int)SIOCDELRT; 297 flags = 0; 298 } else { 299 cmd = (int)SIOCADDRT; 300 } 301 bzero((caddr_t)&route, sizeof (route)); 302 route.rt_dst = *dst; 303 route.rt_gateway = *gateway; 304 route.rt_flags = flags; 305 (void) rtrequest(cmd, &route); 306 } 307