1 /* 2 * Copyright (c) 1983, 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)tables.c 8.2 (Berkeley) 04/28/95"; 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 ((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 rtuentry 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 ? 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 #ifndef RTM_ADD 242 if (add && rtioctl(ADD, &rt->rt_rt) < 0) 243 perror("ADD ROUTE"); 244 if (delete && rtioctl(DELETE, &oldroute) < 0) 245 perror("DELETE ROUTE"); 246 #else 247 if (delete && !add) { 248 if (rtioctl(DELETE, &oldroute) < 0) 249 perror("DELETE ROUTE"); 250 } else if (!delete && add) { 251 if (rtioctl(ADD, &rt->rt_rt) < 0) 252 perror("ADD ROUTE"); 253 } else if (delete && add) { 254 if (rtioctl(CHANGE, &rt->rt_rt) < 0) 255 perror("CHANGE ROUTE"); 256 } 257 #endif 258 } 259 260 rtdelete(rt) 261 struct rt_entry *rt; 262 { 263 264 TRACE_ACTION("DELETE", rt); 265 FIXLEN(&(rt->rt_router)); 266 FIXLEN(&(rt->rt_dst)); 267 if (rt->rt_metric < HOPCNT_INFINITY) { 268 if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE) 269 syslog(LOG_ERR, 270 "deleting route to interface %s? (timed out?)", 271 rt->rt_ifp->int_name); 272 if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 && 273 rtioctl(DELETE, &rt->rt_rt) < 0) 274 perror("rtdelete"); 275 } 276 remque(rt); 277 free((char *)rt); 278 } 279 280 rtdeleteall(sig) 281 int sig; 282 { 283 register struct rthash *rh; 284 register struct rt_entry *rt; 285 struct rthash *base = hosthash; 286 int doinghost = 1; 287 288 again: 289 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) { 290 rt = rh->rt_forw; 291 for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 292 if (rt->rt_state & RTS_INTERFACE || 293 rt->rt_metric >= HOPCNT_INFINITY) 294 continue; 295 TRACE_ACTION("DELETE", rt); 296 if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 && 297 rtioctl(DELETE, &rt->rt_rt) < 0) 298 perror("rtdeleteall"); 299 } 300 } 301 if (doinghost) { 302 doinghost = 0; 303 base = nethash; 304 goto again; 305 } 306 exit(sig); 307 } 308 309 /* 310 * If we have an interface to the wide, wide world, 311 * add an entry for an Internet default route (wildcard) to the internal 312 * tables and advertise it. This route is not added to the kernel routes, 313 * but this entry prevents us from listening to other people's defaults 314 * and installing them in the kernel here. 315 */ 316 rtdefault() 317 { 318 extern struct sockaddr inet_default; 319 320 rtadd(&inet_default, &inet_default, 1, 321 RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL); 322 } 323 324 rtinit() 325 { 326 register struct rthash *rh; 327 328 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 329 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 330 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 331 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 332 } 333 334 rtioctl(action, ort) 335 int action; 336 struct rtuentry *ort; 337 { 338 #ifndef RTM_ADD 339 if (install == 0) 340 return (errno = 0); 341 ort->rtu_rtflags = ort->rtu_flags; 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 memset(&w, 0, 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_dst 368 rtm.rtm_flags = ort->rtu_flags; 369 rtm.rtm_seq = ++seqno; 370 rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; 371 memmove(&w.w_dst, &ort->rtu_dst, sizeof(w.w_dst)); 372 memmove(&w.w_gate, &ort->rtu_router, sizeof(w.w_gate)); 373 w.w_dst.sin_family = AF_INET; 374 w.w_dst.sin_len = sizeof(w.w_dst); 375 w.w_gate.sa_family = AF_INET; 376 w.w_gate.sa_len = sizeof(w.w_gate); 377 if (rtm.rtm_flags & RTF_HOST) { 378 rtm.rtm_msglen -= sizeof(w.w_netmask); 379 } else { 380 register char *cp; 381 int len; 382 383 rtm.rtm_addrs |= RTA_NETMASK; 384 w.w_netmask.sin_addr.s_addr = 385 inet_maskof(w.w_dst.sin_addr.s_addr); 386 for (cp = (char *)(1 + &w.w_netmask.sin_addr); 387 --cp > (char *) &w.w_netmask; ) 388 if (*cp) 389 break; 390 len = cp - (char *)&w.w_netmask; 391 if (len) { 392 len++; 393 w.w_netmask.sin_len = len; 394 len = 1 + ((len - 1) | (sizeof(long) - 1)); 395 } else 396 len = sizeof(long); 397 rtm.rtm_msglen -= (sizeof(w.w_netmask) - len); 398 } 399 errno = 0; 400 return (install ? write(r, (char *)&w, rtm.rtm_msglen) : (errno = 0)); 401 #endif /* RTM_ADD */ 402 } 403