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