1 /* 2 * Copyright (c) 1983, 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)tables.c 5.19 (Berkeley) 02/20/92"; 10 #endif /* not lint */ 11 12 /* 13 * Routing Table Management Daemon 14 */ 15 #include "defs.h" 16 #include <sys/ioctl.h> 17 #include <errno.h> 18 #include <sys/syslog.h> 19 20 #ifndef DEBUG 21 #define DEBUG 0 22 #endif 23 24 #ifdef RTM_ADD 25 #define FIXLEN(s) {if ((s)->sa_len == 0) (s)->sa_len = sizeof *(s);} 26 #else 27 #define FIXLEN(s) { } 28 #endif 29 30 int install = !DEBUG; /* if 1 call kernel */ 31 32 /* 33 * Lookup dst in the tables for an exact match. 34 */ 35 struct rt_entry * 36 rtlookup(dst) 37 struct sockaddr *dst; 38 { 39 register struct rt_entry *rt; 40 register struct rthash *rh; 41 register u_int hash; 42 struct afhash h; 43 int doinghost = 1; 44 45 if (dst->sa_family >= af_max) 46 return (0); 47 (*afswitch[dst->sa_family].af_hash)(dst, &h); 48 hash = h.afh_hosthash; 49 rh = &hosthash[hash & ROUTEHASHMASK]; 50 again: 51 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 52 if (rt->rt_hash != hash) 53 continue; 54 if (equal(&rt->rt_dst, dst)) 55 return (rt); 56 } 57 if (doinghost) { 58 doinghost = 0; 59 hash = h.afh_nethash; 60 rh = &nethash[hash & ROUTEHASHMASK]; 61 goto again; 62 } 63 return (0); 64 } 65 66 struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ 67 68 /* 69 * Find a route to dst as the kernel would. 70 */ 71 struct rt_entry * 72 rtfind(dst) 73 struct sockaddr *dst; 74 { 75 register struct rt_entry *rt; 76 register struct rthash *rh; 77 register u_int hash; 78 struct afhash h; 79 int af = dst->sa_family; 80 int doinghost = 1, (*match)(); 81 82 if (af >= af_max) 83 return (0); 84 (*afswitch[af].af_hash)(dst, &h); 85 hash = h.afh_hosthash; 86 rh = &hosthash[hash & ROUTEHASHMASK]; 87 88 again: 89 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 90 if (rt->rt_hash != hash) 91 continue; 92 if (doinghost) { 93 if (equal(&rt->rt_dst, dst)) 94 return (rt); 95 } else { 96 if (rt->rt_dst.sa_family == af && 97 (*match)(&rt->rt_dst, dst)) 98 return (rt); 99 } 100 } 101 if (doinghost) { 102 doinghost = 0; 103 hash = h.afh_nethash; 104 rh = &nethash[hash & ROUTEHASHMASK]; 105 match = afswitch[af].af_netmatch; 106 goto again; 107 } 108 #ifdef notyet 109 /* 110 * Check for wildcard gateway, by convention network 0. 111 */ 112 if (dst != &wildcard) { 113 dst = &wildcard, hash = 0; 114 goto again; 115 } 116 #endif 117 return (0); 118 } 119 120 rtadd(dst, gate, metric, state) 121 struct sockaddr *dst, *gate; 122 int metric, state; 123 { 124 struct afhash h; 125 register struct rt_entry *rt; 126 struct rthash *rh; 127 int af = dst->sa_family, flags; 128 u_int hash; 129 130 if (af >= af_max) 131 return; 132 (*afswitch[af].af_hash)(dst, &h); 133 flags = (*afswitch[af].af_rtflags)(dst); 134 /* 135 * Subnet flag isn't visible to kernel, move to state. XXX 136 */ 137 FIXLEN(dst); 138 FIXLEN(gate); 139 if (flags & RTF_SUBNET) { 140 state |= RTS_SUBNET; 141 flags &= ~RTF_SUBNET; 142 } 143 if (flags & RTF_HOST) { 144 hash = h.afh_hosthash; 145 rh = &hosthash[hash & ROUTEHASHMASK]; 146 } else { 147 hash = h.afh_nethash; 148 rh = &nethash[hash & ROUTEHASHMASK]; 149 } 150 rt = (struct rt_entry *)malloc(sizeof (*rt)); 151 if (rt == 0) 152 return; 153 rt->rt_hash = hash; 154 rt->rt_dst = *dst; 155 rt->rt_router = *gate; 156 rt->rt_timer = 0; 157 rt->rt_flags = RTF_UP | flags; 158 rt->rt_state = state | RTS_CHANGED; 159 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst); 160 if (rt->rt_ifp == 0) 161 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 162 if ((state & RTS_INTERFACE) == 0) 163 rt->rt_flags |= RTF_GATEWAY; 164 rt->rt_metric = metric; 165 insque(rt, rh); 166 TRACE_ACTION("ADD", rt); 167 /* 168 * If the ioctl fails because the gateway is unreachable 169 * from this host, discard the entry. This should only 170 * occur because of an incorrect entry in /etc/gateways. 171 */ 172 if (install && (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 && 173 rtioctl(ADD, &rt->rt_rt) < 0) { 174 if (errno != EEXIST && gate->sa_family < af_max) 175 syslog(LOG_ERR, 176 "adding route to net/host %s through gateway %s: %m\n", 177 (*afswitch[dst->sa_family].af_format)(dst), 178 (*afswitch[gate->sa_family].af_format)(gate)); 179 perror("ADD ROUTE"); 180 if (errno == ENETUNREACH) { 181 TRACE_ACTION("DELETE", rt); 182 remque(rt); 183 free((char *)rt); 184 } 185 } 186 } 187 188 rtchange(rt, gate, metric) 189 struct rt_entry *rt; 190 struct sockaddr *gate; 191 short metric; 192 { 193 int add = 0, delete = 0, newgateway = 0; 194 struct rtentry oldroute; 195 196 FIXLEN(gate); 197 FIXLEN(&(rt->rt_router)); 198 FIXLEN(&(rt->rt_dst)); 199 if (!equal(&rt->rt_router, gate)) { 200 newgateway++; 201 TRACE_ACTION("CHANGE FROM ", rt); 202 } else if (metric != rt->rt_metric) 203 TRACE_NEWMETRIC(rt, metric); 204 if ((rt->rt_state & RTS_INTERNAL) == 0) { 205 /* 206 * If changing to different router, we need to add 207 * new route and delete old one if in the kernel. 208 * If the router is the same, we need to delete 209 * the route if has become unreachable, or re-add 210 * it if it had been unreachable. 211 */ 212 if (newgateway) { 213 add++; 214 if (rt->rt_metric != HOPCNT_INFINITY) 215 delete++; 216 } else if (metric == HOPCNT_INFINITY) 217 delete++; 218 else if (rt->rt_metric == HOPCNT_INFINITY) 219 add++; 220 } 221 if (delete) 222 oldroute = rt->rt_rt; 223 if ((rt->rt_state & RTS_INTERFACE) && delete) { 224 rt->rt_state &= ~RTS_INTERFACE; 225 rt->rt_flags |= RTF_GATEWAY; 226 if (metric > rt->rt_metric && delete) 227 syslog(LOG_ERR, "%s route to interface %s (timed out)", 228 add? "changing" : "deleting", 229 rt->rt_ifp->int_name); 230 } 231 if (add) { 232 rt->rt_router = *gate; 233 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router); 234 if (rt->rt_ifp == 0) 235 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 236 } 237 rt->rt_metric = metric; 238 rt->rt_state |= RTS_CHANGED; 239 if (newgateway) 240 TRACE_ACTION("CHANGE TO ", rt); 241 if (install == 0) 242 return; 243 #ifndef RTM_ADD 244 if (add && rtioctl(ADD, &rt->rt_rt) < 0) 245 perror("ADD ROUTE"); 246 if (delete && rtioctl(DELETE, &oldroute) < 0) 247 perror("DELETE ROUTE"); 248 #else 249 if (delete && !add) { 250 if (rtioctl(DELETE, &oldroute) < 0) 251 perror("DELETE ROUTE"); 252 } else if (!delete && add) { 253 if (rtioctl(ADD, &rt->rt_rt) < 0) 254 perror("ADD ROUTE"); 255 } else if (delete && add) { 256 if (rtioctl(CHANGE, &rt->rt_rt) < 0) 257 perror("CHANGE ROUTE"); 258 } 259 #endif 260 } 261 262 rtdelete(rt) 263 struct rt_entry *rt; 264 { 265 266 TRACE_ACTION("DELETE", rt); 267 FIXLEN(&(rt->rt_router)); 268 FIXLEN(&(rt->rt_dst)); 269 if (rt->rt_metric < HOPCNT_INFINITY) { 270 if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE) 271 syslog(LOG_ERR, 272 "deleting route to interface %s? (timed out?)", 273 rt->rt_ifp->int_name); 274 if (install && 275 (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 && 276 rtioctl(DELETE, &rt->rt_rt) < 0) 277 perror("rtdelete"); 278 } 279 remque(rt); 280 free((char *)rt); 281 } 282 283 rtdeleteall(sig) 284 int sig; 285 { 286 register struct rthash *rh; 287 register struct rt_entry *rt; 288 struct rthash *base = hosthash; 289 int doinghost = 1; 290 291 again: 292 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { 293 rt = rh->rt_forw; 294 for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 295 if (rt->rt_state & RTS_INTERFACE || 296 rt->rt_metric >= HOPCNT_INFINITY) 297 continue; 298 TRACE_ACTION("DELETE", rt); 299 if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 && 300 rtioctl(DELETE, &rt->rt_rt) < 0) 301 perror("rtdeleteall"); 302 } 303 } 304 if (doinghost) { 305 doinghost = 0; 306 base = nethash; 307 goto again; 308 } 309 exit(sig); 310 } 311 312 /* 313 * If we have an interface to the wide, wide world, 314 * add an entry for an Internet default route (wildcard) to the internal 315 * tables and advertise it. This route is not added to the kernel routes, 316 * but this entry prevents us from listening to other people's defaults 317 * and installing them in the kernel here. 318 */ 319 rtdefault() 320 { 321 extern struct sockaddr inet_default; 322 323 rtadd(&inet_default, &inet_default, 1, 324 RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL); 325 } 326 327 rtinit() 328 { 329 register struct rthash *rh; 330 331 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 332 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 333 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 334 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 335 } 336 337 rtioctl(action, ort) 338 int action; 339 struct ortentry *ort; 340 { 341 #ifndef RTM_ADD 342 switch (action) { 343 344 case ADD: 345 return (ioctl(s, SIOCADDRT, (char *)ort)); 346 347 case DELETE: 348 return (ioctl(s, SIOCDELRT, (char *)ort)); 349 350 default: 351 return (-1); 352 } 353 #else /* RTM_ADD */ 354 struct { 355 struct rt_msghdr w_rtm; 356 struct sockaddr_in w_dst; 357 struct sockaddr w_gate; 358 struct sockaddr_in w_netmask; 359 } w; 360 #define rtm w.w_rtm 361 362 bzero((char *)&w, sizeof(w)); 363 rtm.rtm_msglen = sizeof(w); 364 rtm.rtm_version = RTM_VERSION; 365 rtm.rtm_type = (action == ADD ? RTM_ADD : 366 (action == DELETE ? RTM_DELETE : RTM_CHANGE)); 367 #undef rt_flags 368 #undef rt_dst 369 rtm.rtm_flags = ort->rt_flags; 370 rtm.rtm_seq = ++seqno; 371 rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; 372 bcopy((char *)&ort->rt_dst, (char *)&w.w_dst, sizeof(w.w_dst)); 373 bcopy((char *)&ort->rt_gateway, (char *)&w.w_gate, sizeof(w.w_gate)); 374 w.w_dst.sin_family = AF_INET; 375 w.w_dst.sin_len = sizeof(w.w_dst); 376 w.w_gate.sa_family = AF_INET; 377 w.w_gate.sa_len = sizeof(w.w_gate); 378 if (rtm.rtm_flags & RTF_HOST) { 379 rtm.rtm_msglen -= sizeof(w.w_netmask); 380 } else { 381 register char *cp; 382 int len; 383 384 rtm.rtm_addrs |= RTA_NETMASK; 385 w.w_netmask.sin_addr.s_addr = 386 inet_maskof(w.w_dst.sin_addr.s_addr); 387 for (cp = (char *)(1 + &w.w_netmask.sin_addr); 388 --cp > (char *) &w.w_netmask; ) 389 if (*cp) 390 break; 391 len = cp - (char *)&w.w_netmask; 392 if (len) { 393 len++; 394 w.w_netmask.sin_len = len; 395 len = 1 + ((len - 1) | (sizeof(long) - 1)); 396 } else 397 len = sizeof(long); 398 rtm.rtm_msglen -= (sizeof(w.w_netmask) - len); 399 } 400 errno = 0; 401 return write(r, (char *)&w, rtm.rtm_msglen); 402 #endif /* RTM_ADD */ 403 } 404