tables.c revision 27127
1/* 2 * Copyright (c) 1985, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Copyright (c) 1995 John Hay. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * $Id: tables.c,v 1.4 1997/02/22 16:01:03 peter Exp $ 36 */ 37 38#ifndef lint 39static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; 40#endif /* not lint */ 41 42/* 43 * Routing Table Management Daemon 44 */ 45#include "defs.h" 46#include <sys/ioctl.h> 47#include <errno.h> 48#include <stdlib.h> 49#include <unistd.h> 50 51#ifndef DEBUG 52#define DEBUG 0 53#endif 54 55#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));} 56 57int install = !DEBUG; /* if 1 call kernel */ 58int delete = 1; 59 60struct rthash nethash[ROUTEHASHSIZ]; 61struct rthash hosthash[ROUTEHASHSIZ]; 62 63/* 64 * Lookup dst in the tables for an exact match. 65 */ 66struct rt_entry * 67rtlookup(dst) 68 struct sockaddr *dst; 69{ 70 register struct rt_entry *rt; 71 register struct rthash *rh; 72 register u_int hash; 73 struct afhash h; 74 int doinghost = 1; 75 76 if (dst->sa_family >= AF_MAX) 77 return (0); 78 (*afswitch[dst->sa_family].af_hash)(dst, &h); 79 hash = h.afh_hosthash; 80 rh = &hosthash[hash & ROUTEHASHMASK]; 81again: 82 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 83 if (rt->rt_hash != hash) 84 continue; 85 if (equal(&rt->rt_dst, dst)) 86 return (rt); 87 } 88 if (doinghost) { 89 doinghost = 0; 90 hash = h.afh_nethash; 91 rh = &nethash[hash & ROUTEHASHMASK]; 92 goto again; 93 } 94 return (0); 95} 96 97/* 98 * Find a route to dst as the kernel would. 99 */ 100struct rt_entry * 101rtfind(dst) 102 struct sockaddr *dst; 103{ 104 register struct rt_entry *rt; 105 register struct rthash *rh; 106 register u_int hash; 107 struct afhash h; 108 int af = dst->sa_family; 109 int doinghost = 1, (*match)() = 0; 110 111 if (af >= AF_MAX) 112 return (0); 113 (*afswitch[af].af_hash)(dst, &h); 114 hash = h.afh_hosthash; 115 rh = &hosthash[hash & ROUTEHASHMASK]; 116 117again: 118 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 119 if (rt->rt_hash != hash) 120 continue; 121 if (doinghost) { 122 if (equal(&rt->rt_dst, dst)) 123 return (rt); 124 } else { 125 if (rt->rt_dst.sa_family == af && 126 (match != 0) && 127 (*match)(&rt->rt_dst, dst)) 128 return (rt); 129 } 130 } 131 if (doinghost) { 132 doinghost = 0; 133 hash = h.afh_nethash; 134 rh = &nethash[hash & ROUTEHASHMASK]; 135 match = afswitch[af].af_netmatch; 136 goto again; 137 } 138 return (0); 139} 140 141void 142rtadd(dst, gate, metric, ticks, state) 143 struct sockaddr *dst, *gate; 144 short metric, ticks; 145 int state; 146{ 147 struct afhash h; 148 register struct rt_entry *rt; 149 struct rthash *rh; 150 int af = dst->sa_family, flags; 151 u_int hash; 152 153 FIXLEN(dst); 154 FIXLEN(gate); 155 if (af >= AF_MAX) 156 return; 157 (*afswitch[af].af_hash)(dst, &h); 158 flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; 159 if (flags & RTF_HOST) { 160 hash = h.afh_hosthash; 161 rh = &hosthash[hash & ROUTEHASHMASK]; 162 } else { 163 hash = h.afh_nethash; 164 rh = &nethash[hash & ROUTEHASHMASK]; 165 } 166 rt = (struct rt_entry *)malloc(sizeof (*rt)); 167 if (rt == 0) 168 return; 169 rt->rt_hash = hash; 170 rt->rt_dst = *dst; 171 rt->rt_router = *gate; 172 rt->rt_metric = metric; 173 rt->rt_ticks = ticks; 174 rt->rt_timer = 0; 175 rt->rt_flags = RTF_UP | flags; 176 rt->rt_state = state | RTS_CHANGED; 177 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 178 rt->rt_clone = NULL; 179 if (metric) 180 rt->rt_flags |= RTF_GATEWAY; 181 insque(rt, rh); 182 TRACE_ACTION(ADD, rt); 183 /* 184 * If the ioctl fails because the gateway is unreachable 185 * from this host, discard the entry. This should only 186 * occur because of an incorrect entry in /etc/gateways. 187 */ 188 if (install && rtioctl(ADD, &rt->rt_rt) < 0) { 189 if (errno != EEXIST) 190 perror("SIOCADDRT"); 191 if (errno == ENETUNREACH) { 192 TRACE_ACTION(DELETE, rt); 193 remque(rt); 194 free((char *)rt); 195 } 196 } 197} 198 199void 200rtadd_clone(ort, dst, gate, metric, ticks, state) 201 struct rt_entry *ort; 202 struct sockaddr *dst, *gate; 203 short metric, ticks; 204 int state; 205{ 206 struct afhash h; 207 register struct rt_entry *rt; 208 struct rthash *rh; 209 int af = dst->sa_family, flags; 210 u_int hash; 211 212 FIXLEN(dst); 213 FIXLEN(gate); 214 if (af >= AF_MAX) 215 return; 216 (*afswitch[af].af_hash)(dst, &h); 217 flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; 218 if (flags & RTF_HOST) { 219 hash = h.afh_hosthash; 220 rh = &hosthash[hash & ROUTEHASHMASK]; 221 } else { 222 hash = h.afh_nethash; 223 rh = &nethash[hash & ROUTEHASHMASK]; 224 } 225 rt = (struct rt_entry *)malloc(sizeof (*rt)); 226 if (rt == 0) 227 return; 228 rt->rt_hash = hash; 229 rt->rt_dst = *dst; 230 rt->rt_router = *gate; 231 rt->rt_metric = metric; 232 rt->rt_ticks = ticks; 233 rt->rt_timer = 0; 234 rt->rt_flags = RTF_UP | flags; 235 rt->rt_state = state | RTS_CHANGED; 236 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 237 rt->rt_clone = NULL; 238 rt->rt_forw = NULL; 239 rt->rt_back = NULL; 240 if (metric) 241 rt->rt_flags |= RTF_GATEWAY; 242 243 while(ort->rt_clone != NULL) 244 ort = ort->rt_clone; 245 ort->rt_clone = rt; 246 TRACE_ACTION(ADD_CLONE, rt); 247} 248 249void 250rtchange(rt, gate, metric, ticks) 251 struct rt_entry *rt; 252 struct sockaddr *gate; 253 short metric, ticks; 254{ 255 int doioctl = 0, metricchanged = 0; 256 struct rtuentry oldroute; 257 258 FIXLEN(gate); 259 /* 260 * Handling of clones. 261 * When the route changed and it had clones, handle it special. 262 * 1. If the new route is cheaper than the clone(s), free the clones. 263 * 2. If the new route is the same cost, it may be one of the clones, 264 * search for it and free it. 265 * 3. If the new route is more expensive than the clone(s), use the 266 * values of the clone(s). 267 */ 268 if (rt->rt_clone) { 269 if ((ticks < rt->rt_clone->rt_ticks) || 270 ((ticks == rt->rt_clone->rt_ticks) && 271 (metric < rt->rt_clone->rt_metric))) { 272 /* 273 * Free all clones. 274 */ 275 struct rt_entry *trt, *nrt; 276 277 trt = rt->rt_clone; 278 rt->rt_clone = NULL; 279 while(trt) { 280 nrt = trt->rt_clone; 281 free((char *)trt); 282 trt = nrt; 283 } 284 } else if ((ticks == rt->rt_clone->rt_ticks) && 285 (metric == rt->rt_clone->rt_metric)) { 286 struct rt_entry *prt, *trt; 287 288 prt = rt; 289 trt = rt->rt_clone; 290 291 while(trt) { 292 if (equal(&trt->rt_router, gate)) { 293 prt->rt_clone = trt->rt_clone; 294 free(trt); 295 trt = prt->rt_clone; 296 } else { 297 prt = trt; 298 trt = trt->rt_clone; 299 } 300 } 301 } else { 302 /* 303 * Use the values of the first clone. 304 * Delete the corresponding clone. 305 */ 306 struct rt_entry *trt; 307 308 trt = rt->rt_clone; 309 rt->rt_clone = rt->rt_clone->rt_clone; 310 metric = trt->rt_metric; 311 ticks = trt->rt_ticks; 312 *gate = trt->rt_router; 313 free((char *)trt); 314 } 315 } 316 317 if (!equal(&rt->rt_router, gate)) 318 doioctl++; 319 if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks)) 320 metricchanged++; 321 if (doioctl || metricchanged) { 322 TRACE_ACTION(CHANGE FROM, rt); 323 if (doioctl) { 324 oldroute = rt->rt_rt; 325 rt->rt_router = *gate; 326 } 327 rt->rt_metric = metric; 328 rt->rt_ticks = ticks; 329 if ((rt->rt_state & RTS_INTERFACE) && metric) { 330 rt->rt_state &= ~RTS_INTERFACE; 331 if(rt->rt_ifp) 332 syslog(LOG_ERR, 333 "changing route from interface %s (timed out)", 334 rt->rt_ifp->int_name); 335 else 336 syslog(LOG_ERR, 337 "changing route from interface ??? (timed out)"); 338 } 339 if (metric) 340 rt->rt_flags |= RTF_GATEWAY; 341 else 342 rt->rt_flags &= ~RTF_GATEWAY; 343 rt->rt_state |= RTS_CHANGED; 344 TRACE_ACTION(CHANGE TO, rt); 345 } 346 if (doioctl && install) { 347#ifndef RTM_ADD 348 if (rtioctl(ADD, &rt->rt_rt) < 0) 349 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m", 350 xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr), 351 xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr)); 352 if (delete && rtioctl(DELETE, &oldroute) < 0) 353 perror("rtioctl DELETE"); 354#else 355 if (delete == 0) { 356 if (rtioctl(ADD, &rt->rt_rt) >= 0) 357 return; 358 } else { 359 if (rtioctl(CHANGE, &rt->rt_rt) >= 0) 360 return; 361 } 362 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m", 363 ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr), 364 ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr)); 365#endif 366 } 367} 368 369void 370rtdelete(rt) 371 struct rt_entry *rt; 372{ 373 374 struct sockaddr *sa = &(rt->rt_router); 375 FIXLEN(sa); 376 sa = &(rt->rt_dst); 377 FIXLEN(sa); 378 if (rt->rt_clone) { 379 /* 380 * If there is a clone we just do a rt_change to it. 381 */ 382 struct rt_entry *trt = rt->rt_clone; 383 rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks); 384 return; 385 } 386 if (rt->rt_state & RTS_INTERFACE) { 387 if (rt->rt_ifp) 388 syslog(LOG_ERR, 389 "deleting route to interface %s (timed out)", 390 rt->rt_ifp->int_name); 391 else 392 syslog(LOG_ERR, 393 "deleting route to interface ??? (timed out)"); 394 } 395 TRACE_ACTION(DELETE, rt); 396 if (install && rtioctl(DELETE, &rt->rt_rt) < 0) 397 perror("rtioctl DELETE"); 398 remque(rt); 399 free((char *)rt); 400} 401 402void 403rtinit(void) 404{ 405 register struct rthash *rh; 406 407 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 408 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 409 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 410 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 411} 412int seqno; 413 414int 415rtioctl(action, ort) 416 int action; 417 struct rtuentry *ort; 418{ 419#ifndef RTM_ADD 420 if (install == 0) 421 return (errno = 0); 422 423 ort->rtu_rtflags = ort->rtu_flags; 424 425 switch (action) { 426 427 case ADD: 428 return (ioctl(s, SIOCADDRT, (char *)ort)); 429 430 case DELETE: 431 return (ioctl(s, SIOCDELRT, (char *)ort)); 432 433 default: 434 return (-1); 435 } 436#else /* RTM_ADD */ 437 struct { 438 struct rt_msghdr w_rtm; 439 struct sockaddr w_dst; 440 struct sockaddr w_gate; 441 struct sockaddr_ipx w_netmask; 442 } w; 443#define rtm w.w_rtm 444 445 bzero((char *)&w, sizeof(w)); 446 rtm.rtm_msglen = sizeof(w); 447 rtm.rtm_version = RTM_VERSION; 448 rtm.rtm_type = (action == ADD ? RTM_ADD : 449 (action == DELETE ? RTM_DELETE : RTM_CHANGE)); 450 rtm.rtm_flags = ort->rtu_flags; 451 rtm.rtm_seq = ++seqno; 452 rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; 453 bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst)); 454 bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate)); 455 w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX; 456 w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst); 457 if (rtm.rtm_flags & RTF_HOST) { 458 rtm.rtm_msglen -= sizeof(w.w_netmask); 459 } else { 460 rtm.rtm_addrs |= RTA_NETMASK; 461 w.w_netmask = ipx_netmask; 462 rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len; 463 } 464 errno = 0; 465 return write(r, (char *)&w, rtm.rtm_msglen); 466#endif /* RTM_ADD */ 467} 468