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