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