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