1 /* 2 * Copyright (c) 1980, 1986 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 7.2 (Berkeley) 01/15/87 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 */ 112 rtredirect(dst, gateway, flags, src) 113 struct sockaddr *dst, *gateway, *src; 114 int flags; 115 { 116 struct route ro; 117 register struct rtentry *rt; 118 119 /* verify the gateway is directly reachable */ 120 if (ifa_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 #define equal(a1, a2) \ 129 (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof(struct sockaddr)) == 0) 130 /* 131 * If the redirect isn't from our current router for this dst, 132 * it's either old or wrong. If it redirects us to ourselves, 133 * we have a routing loop, perhaps as a result of an interface 134 * going down recently. 135 */ 136 if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) { 137 rtstat.rts_badredirect++; 138 if (rt) 139 rtfree(rt); 140 return; 141 } 142 /* 143 * Create a new entry if we just got back a wildcard entry 144 * or the the lookup failed. This is necessary for hosts 145 * which use routing redirects generated by smart gateways 146 * to dynamically build the routing tables. 147 */ 148 if (rt && 149 (*afswitch[dst->sa_family].af_netmatch)(&wildcard, &rt->rt_dst)) { 150 rtfree(rt); 151 rt = 0; 152 } 153 if (rt == 0) { 154 rtinit(dst, gateway, (int)SIOCADDRT, 155 (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC); 156 rtstat.rts_dynamic++; 157 return; 158 } 159 /* 160 * Don't listen to the redirect if it's 161 * for a route to an interface. 162 */ 163 if (rt->rt_flags & RTF_GATEWAY) { 164 if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { 165 /* 166 * Changing from route to net => route to host. 167 * Create new route, rather than smashing route to net. 168 */ 169 rtinit(dst, gateway, (int)SIOCADDRT, 170 flags | RTF_DYNAMIC); 171 rtstat.rts_dynamic++; 172 } else { 173 /* 174 * Smash the current notion of the gateway to 175 * this destination. 176 */ 177 rt->rt_gateway = *gateway; 178 rt->rt_flags |= RTF_MODIFIED; 179 rtstat.rts_newgateway++; 180 } 181 } else 182 rtstat.rts_badredirect++; 183 rtfree(rt); 184 } 185 186 /* 187 * Routing table ioctl interface. 188 */ 189 rtioctl(cmd, data) 190 int cmd; 191 caddr_t data; 192 { 193 194 if (cmd != SIOCADDRT && cmd != SIOCDELRT) 195 return (EINVAL); 196 if (!suser()) 197 return (u.u_error); 198 return (rtrequest(cmd, (struct rtentry *)data)); 199 } 200 201 /* 202 * Carry out a request to change the routing table. Called by 203 * interfaces at boot time to make their ``local routes'' known, 204 * for ioctl's, and as the result of routing redirects. 205 */ 206 rtrequest(req, entry) 207 int req; 208 register struct rtentry *entry; 209 { 210 register struct mbuf *m, **mprev; 211 struct mbuf **mfirst; 212 register struct rtentry *rt; 213 struct afhash h; 214 int s, error = 0, (*match)(); 215 u_int af; 216 u_long hash; 217 struct ifaddr *ifa; 218 struct ifaddr *ifa_ifwithdstaddr(); 219 220 af = entry->rt_dst.sa_family; 221 if (af >= AF_MAX) 222 return (EAFNOSUPPORT); 223 (*afswitch[af].af_hash)(&entry->rt_dst, &h); 224 if (entry->rt_flags & RTF_HOST) { 225 hash = h.afh_hosthash; 226 mprev = &rthost[RTHASHMOD(hash)]; 227 } else { 228 hash = h.afh_nethash; 229 mprev = &rtnet[RTHASHMOD(hash)]; 230 } 231 match = afswitch[af].af_netmatch; 232 s = splimp(); 233 for (mfirst = mprev; m = *mprev; mprev = &m->m_next) { 234 rt = mtod(m, struct rtentry *); 235 if (rt->rt_hash != hash) 236 continue; 237 if (entry->rt_flags & RTF_HOST) { 238 if (!equal(&rt->rt_dst, &entry->rt_dst)) 239 continue; 240 } else { 241 if (rt->rt_dst.sa_family != entry->rt_dst.sa_family || 242 (*match)(&rt->rt_dst, &entry->rt_dst) == 0) 243 continue; 244 } 245 if (equal(&rt->rt_gateway, &entry->rt_gateway)) 246 break; 247 } 248 switch (req) { 249 250 case SIOCDELRT: 251 if (m == 0) { 252 error = ESRCH; 253 goto bad; 254 } 255 *mprev = m->m_next; 256 if (rt->rt_refcnt > 0) { 257 rt->rt_flags &= ~RTF_UP; 258 rttrash++; 259 m->m_next = 0; 260 } else 261 (void) m_free(m); 262 break; 263 264 case SIOCADDRT: 265 if (m) { 266 error = EEXIST; 267 goto bad; 268 } 269 if ((entry->rt_flags & RTF_GATEWAY) == 0) { 270 /* 271 * If we are adding a route to an interface, 272 * and the interface is a pt to pt link 273 * we should search for the destination 274 * as our clue to the interface. Otherwise 275 * we can use the local address. 276 */ 277 ifa = 0; 278 if (entry->rt_flags & RTF_HOST) 279 ifa = ifa_ifwithdstaddr(&entry->rt_dst); 280 if (ifa == 0) 281 ifa = ifa_ifwithaddr(&entry->rt_gateway); 282 } else { 283 /* 284 * If we are adding a route to a remote net 285 * or host, the gateway may still be on the 286 * other end of a pt to pt link. 287 */ 288 ifa = ifa_ifwithdstaddr(&entry->rt_gateway); 289 } 290 if (ifa == 0) { 291 ifa = ifa_ifwithnet(&entry->rt_gateway); 292 if (ifa == 0) { 293 error = ENETUNREACH; 294 goto bad; 295 } 296 } 297 m = m_get(M_DONTWAIT, MT_RTABLE); 298 if (m == 0) { 299 error = ENOBUFS; 300 goto bad; 301 } 302 m->m_next = *mfirst; 303 *mfirst = m; 304 m->m_off = MMINOFF; 305 m->m_len = sizeof (struct rtentry); 306 rt = mtod(m, struct rtentry *); 307 rt->rt_hash = hash; 308 rt->rt_dst = entry->rt_dst; 309 rt->rt_gateway = entry->rt_gateway; 310 rt->rt_flags = RTF_UP | 311 (entry->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_DYNAMIC)); 312 rt->rt_refcnt = 0; 313 rt->rt_use = 0; 314 rt->rt_ifp = ifa->ifa_ifp; 315 break; 316 } 317 bad: 318 splx(s); 319 return (error); 320 } 321 322 /* 323 * Set up a routing table entry, normally 324 * for an interface. 325 */ 326 rtinit(dst, gateway, cmd, flags) 327 struct sockaddr *dst, *gateway; 328 int cmd, flags; 329 { 330 struct rtentry route; 331 332 bzero((caddr_t)&route, sizeof (route)); 333 route.rt_dst = *dst; 334 route.rt_gateway = *gateway; 335 route.rt_flags = flags; 336 (void) rtrequest(cmd, &route); 337 } 338