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