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