tables.c revision 21673
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 * $FreeBSD: head/usr.sbin/IPXrouted/tables.c 21673 1997-01-14 07:20:47Z jkh $ 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/* XXX I thought that this should work! #include <sys/systm.h> */ 51#include <machine/cpufunc.h> 52 53#ifndef DEBUG 54#define DEBUG 0 55#endif 56 57#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));} 58 59int install = !DEBUG; /* if 1 call kernel */ 60int delete = 1; 61 62struct rthash nethash[ROUTEHASHSIZ]; 63struct rthash hosthash[ROUTEHASHSIZ]; 64 65/* 66 * Lookup dst in the tables for an exact match. 67 */ 68struct rt_entry * 69rtlookup(dst) 70 struct sockaddr *dst; 71{ 72 register struct rt_entry *rt; 73 register struct rthash *rh; 74 register u_int hash; 75 struct afhash h; 76 int doinghost = 1; 77 78 if (dst->sa_family >= AF_MAX) 79 return (0); 80 (*afswitch[dst->sa_family].af_hash)(dst, &h); 81 hash = h.afh_hosthash; 82 rh = &hosthash[hash & ROUTEHASHMASK]; 83again: 84 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 85 if (rt->rt_hash != hash) 86 continue; 87 if (equal(&rt->rt_dst, dst)) 88 return (rt); 89 } 90 if (doinghost) { 91 doinghost = 0; 92 hash = h.afh_nethash; 93 rh = &nethash[hash & ROUTEHASHMASK]; 94 goto again; 95 } 96 return (0); 97} 98 99/* 100 * Find a route to dst as the kernel would. 101 */ 102struct rt_entry * 103rtfind(dst) 104 struct sockaddr *dst; 105{ 106 register struct rt_entry *rt; 107 register struct rthash *rh; 108 register u_int hash; 109 struct afhash h; 110 int af = dst->sa_family; 111 int doinghost = 1, (*match)() = 0; 112 113 if (af >= AF_MAX) 114 return (0); 115 (*afswitch[af].af_hash)(dst, &h); 116 hash = h.afh_hosthash; 117 rh = &hosthash[hash & ROUTEHASHMASK]; 118 119again: 120 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 121 if (rt->rt_hash != hash) 122 continue; 123 if (doinghost) { 124 if (equal(&rt->rt_dst, dst)) 125 return (rt); 126 } else { 127 if (rt->rt_dst.sa_family == af && 128 (match != 0) && 129 (*match)(&rt->rt_dst, dst)) 130 return (rt); 131 } 132 } 133 if (doinghost) { 134 doinghost = 0; 135 hash = h.afh_nethash; 136 rh = &nethash[hash & ROUTEHASHMASK]; 137 match = afswitch[af].af_netmatch; 138 goto again; 139 } 140 return (0); 141} 142 143void 144rtadd(dst, gate, metric, ticks, state) 145 struct sockaddr *dst, *gate; 146 short metric, ticks; 147 int state; 148{ 149 struct afhash h; 150 register struct rt_entry *rt; 151 struct rthash *rh; 152 int af = dst->sa_family, flags; 153 u_int hash; 154 155 FIXLEN(dst); 156 FIXLEN(gate); 157 if (af >= AF_MAX) 158 return; 159 (*afswitch[af].af_hash)(dst, &h); 160 flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; 161 if (flags & RTF_HOST) { 162 hash = h.afh_hosthash; 163 rh = &hosthash[hash & ROUTEHASHMASK]; 164 } else { 165 hash = h.afh_nethash; 166 rh = &nethash[hash & ROUTEHASHMASK]; 167 } 168 rt = (struct rt_entry *)malloc(sizeof (*rt)); 169 if (rt == 0) 170 return; 171 rt->rt_hash = hash; 172 rt->rt_dst = *dst; 173 rt->rt_router = *gate; 174 rt->rt_metric = metric; 175 rt->rt_ticks = ticks; 176 rt->rt_timer = 0; 177 rt->rt_flags = RTF_UP | flags; 178 rt->rt_state = state | RTS_CHANGED; 179 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 180 rt->rt_clone = NULL; 181 if (metric) 182 rt->rt_flags |= RTF_GATEWAY; 183 insque(rt, rh); 184 TRACE_ACTION(ADD, rt); 185 /* 186 * If the ioctl fails because the gateway is unreachable 187 * from this host, discard the entry. This should only 188 * occur because of an incorrect entry in /etc/gateways. 189 */ 190 if (install && rtioctl(ADD, &rt->rt_rt) < 0) { 191 if (errno != EEXIST) 192 perror("SIOCADDRT"); 193 if (errno == ENETUNREACH) { 194 TRACE_ACTION(DELETE, rt); 195 remque(rt); 196 free((char *)rt); 197 } 198 } 199} 200 201void 202rtadd_clone(ort, dst, gate, metric, ticks, state) 203 struct rt_entry *ort; 204 struct sockaddr *dst, *gate; 205 short metric, ticks; 206 int state; 207{ 208 struct afhash h; 209 register struct rt_entry *rt; 210 struct rthash *rh; 211 int af = dst->sa_family, flags; 212 u_int hash; 213 214 FIXLEN(dst); 215 FIXLEN(gate); 216 if (af >= AF_MAX) 217 return; 218 (*afswitch[af].af_hash)(dst, &h); 219 flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; 220 if (flags & RTF_HOST) { 221 hash = h.afh_hosthash; 222 rh = &hosthash[hash & ROUTEHASHMASK]; 223 } else { 224 hash = h.afh_nethash; 225 rh = &nethash[hash & ROUTEHASHMASK]; 226 } 227 rt = (struct rt_entry *)malloc(sizeof (*rt)); 228 if (rt == 0) 229 return; 230 rt->rt_hash = hash; 231 rt->rt_dst = *dst; 232 rt->rt_router = *gate; 233 rt->rt_metric = metric; 234 rt->rt_ticks = ticks; 235 rt->rt_timer = 0; 236 rt->rt_flags = RTF_UP | flags; 237 rt->rt_state = state | RTS_CHANGED; 238 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 239 rt->rt_clone = NULL; 240 rt->rt_forw = NULL; 241 rt->rt_back = NULL; 242 if (metric) 243 rt->rt_flags |= RTF_GATEWAY; 244 245 while(ort->rt_clone != NULL) 246 ort = ort->rt_clone; 247 ort->rt_clone = rt; 248 TRACE_ACTION(ADD_CLONE, rt); 249} 250 251void 252rtchange(rt, gate, metric, ticks) 253 struct rt_entry *rt; 254 struct sockaddr *gate; 255 short metric, ticks; 256{ 257 int doioctl = 0, metricchanged = 0; 258 struct rtuentry oldroute; 259 260 FIXLEN(gate); 261 /* 262 * Handling of clones. 263 * When the route changed and it had clones, handle it special. 264 * 1. If the new route is cheaper than the clone(s), free the clones. 265 * 2. If the new route is the same cost, it may be one of the clones, 266 * search for it and free it. 267 * 3. If the new route is more expensive than the clone(s), use the 268 * values of the clone(s). 269 */ 270 if (rt->rt_clone) { 271 if ((ticks < rt->rt_clone->rt_ticks) || 272 ((ticks == rt->rt_clone->rt_ticks) && 273 (metric < rt->rt_clone->rt_metric))) { 274 /* 275 * Free all clones. 276 */ 277 struct rt_entry *trt, *nrt; 278 279 trt = rt->rt_clone; 280 rt->rt_clone = NULL; 281 while(trt) { 282 nrt = trt->rt_clone; 283 free((char *)trt); 284 trt = nrt; 285 } 286 } else if ((ticks == rt->rt_clone->rt_ticks) && 287 (metric == rt->rt_clone->rt_metric)) { 288 struct rt_entry *prt, *trt; 289 290 prt = rt; 291 trt = rt->rt_clone; 292 293 while(trt) { 294 if (equal(&trt->rt_router, gate)) { 295 prt->rt_clone = trt->rt_clone; 296 free(trt); 297 trt = prt->rt_clone; 298 } else { 299 prt = trt; 300 trt = trt->rt_clone; 301 } 302 } 303 } else { 304 /* 305 * Use the values of the first clone. 306 * Delete the corresponding clone. 307 */ 308 struct rt_entry *trt; 309 310 trt = rt->rt_clone; 311 rt->rt_clone = rt->rt_clone->rt_clone; 312 metric = trt->rt_metric; 313 ticks = trt->rt_ticks; 314 *gate = trt->rt_router; 315 free((char *)trt); 316 } 317 } 318 319 if (!equal(&rt->rt_router, gate)) 320 doioctl++; 321 if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks)) 322 metricchanged++; 323 if (doioctl || metricchanged) { 324 TRACE_ACTION(CHANGE FROM, rt); 325 if (doioctl) { 326 oldroute = rt->rt_rt; 327 rt->rt_router = *gate; 328 } 329 rt->rt_metric = metric; 330 rt->rt_ticks = ticks; 331 if ((rt->rt_state & RTS_INTERFACE) && metric) { 332 rt->rt_state &= ~RTS_INTERFACE; 333 if(rt->rt_ifp) 334 syslog(LOG_ERR, 335 "changing route from interface %s (timed out)", 336 rt->rt_ifp->int_name); 337 else 338 syslog(LOG_ERR, 339 "changing route from interface ??? (timed out)"); 340 } 341 if (metric) 342 rt->rt_flags |= RTF_GATEWAY; 343 else 344 rt->rt_flags &= ~RTF_GATEWAY; 345 rt->rt_state |= RTS_CHANGED; 346 TRACE_ACTION(CHANGE TO, rt); 347 } 348 if (doioctl && install) { 349#ifndef RTM_ADD 350 if (rtioctl(ADD, &rt->rt_rt) < 0) 351 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m", 352 xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr), 353 xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr)); 354 if (delete && rtioctl(DELETE, &oldroute) < 0) 355 perror("rtioctl DELETE"); 356#else 357 if (delete == 0) { 358 if (rtioctl(ADD, &rt->rt_rt) >= 0) 359 return; 360 } else { 361 if (rtioctl(CHANGE, &rt->rt_rt) >= 0) 362 return; 363 } 364 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m", 365 ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr), 366 ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr)); 367#endif 368 } 369} 370 371void 372rtdelete(rt) 373 struct rt_entry *rt; 374{ 375 376 struct sockaddr *sa = &(rt->rt_router); 377 FIXLEN(sa); 378 sa = &(rt->rt_dst); 379 FIXLEN(sa); 380 if (rt->rt_clone) { 381 /* 382 * If there is a clone we just do a rt_change to it. 383 */ 384 struct rt_entry *trt = rt->rt_clone; 385 rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks); 386 return; 387 } 388 if (rt->rt_state & RTS_INTERFACE) { 389 if (rt->rt_ifp) 390 syslog(LOG_ERR, 391 "deleting route to interface %s (timed out)", 392 rt->rt_ifp->int_name); 393 else 394 syslog(LOG_ERR, 395 "deleting route to interface ??? (timed out)"); 396 } 397 TRACE_ACTION(DELETE, rt); 398 if (install && rtioctl(DELETE, &rt->rt_rt) < 0) 399 perror("rtioctl DELETE"); 400 remque(rt); 401 free((char *)rt); 402} 403 404void 405rtinit(void) 406{ 407 register struct rthash *rh; 408 409 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 410 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 411 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++) 412 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 413} 414int seqno; 415 416int 417rtioctl(action, ort) 418 int action; 419 struct rtuentry *ort; 420{ 421#ifndef RTM_ADD 422 if (install == 0) 423 return (errno = 0); 424 425 ort->rtu_rtflags = ort->rtu_flags; 426 427 switch (action) { 428 429 case ADD: 430 return (ioctl(s, SIOCADDRT, (char *)ort)); 431 432 case DELETE: 433 return (ioctl(s, SIOCDELRT, (char *)ort)); 434 435 default: 436 return (-1); 437 } 438#else /* RTM_ADD */ 439 struct { 440 struct rt_msghdr w_rtm; 441 struct sockaddr w_dst; 442 struct sockaddr w_gate; 443 struct sockaddr_ipx w_netmask; 444 } w; 445#define rtm w.w_rtm 446 447 bzero((char *)&w, sizeof(w)); 448 rtm.rtm_msglen = sizeof(w); 449 rtm.rtm_version = RTM_VERSION; 450 rtm.rtm_type = (action == ADD ? RTM_ADD : 451 (action == DELETE ? RTM_DELETE : RTM_CHANGE)); 452 rtm.rtm_flags = ort->rtu_flags; 453 rtm.rtm_seq = ++seqno; 454 rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; 455 bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst)); 456 bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate)); 457 w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX; 458 w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst); 459 if (rtm.rtm_flags & RTF_HOST) { 460 rtm.rtm_msglen -= sizeof(w.w_netmask); 461 } else { 462 rtm.rtm_addrs |= RTA_NETMASK; 463 w.w_netmask = ipx_netmask; 464 rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len; 465 } 466 errno = 0; 467 return write(r, (char *)&w, rtm.rtm_msglen); 468#endif /* RTM_ADD */ 469} 470 471