tables.c revision 189369
1213136Spjd/* 2213136Spjd * Copyright (c) 1985, 1993 3213136Spjd * The Regents of the University of California. All rights reserved. 4213136Spjd * 5213136Spjd * Copyright (c) 1995 John Hay. All rights reserved. 6213136Spjd * 7213136Spjd * Redistribution and use in source and binary forms, with or without 8213136Spjd * modification, are permitted provided that the following conditions 9213136Spjd * are met: 10213136Spjd * 1. Redistributions of source code must retain the above copyright 11213136Spjd * notice, this list of conditions and the following disclaimer. 12213136Spjd * 2. Redistributions in binary form must reproduce the above copyright 13213136Spjd * notice, this list of conditions and the following disclaimer in the 14213136Spjd * documentation and/or other materials provided with the distribution. 15213136Spjd * 3. All advertising materials mentioning features or use of this software 16213136Spjd * must display the following acknowledgement: 17213136Spjd * This product includes software developed by the University of 18213136Spjd * California, Berkeley and its contributors. 19213136Spjd * 4. Neither the name of the University nor the names of its contributors 20213136Spjd * may be used to endorse or promote products derived from this software 21213136Spjd * without specific prior written permission. 22213136Spjd * 23213136Spjd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24213136Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25213136Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26213136Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27213136Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28213136Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29213136Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30213136Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31300460Sngie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32213136Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33213136Spjd * SUCH DAMAGE. 34213136Spjd * 35213136Spjd * $FreeBSD: head/usr.sbin/IPXrouted/tables.c 189369 2009-03-04 18:36:48Z ed $ 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(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(struct sockaddr *dst) 93{ 94 register struct rt_entry *rt; 95 register struct rthash *rh; 96 register u_int hash; 97 struct afhash h; 98 int af = dst->sa_family; 99 int (*match)() = 0; 100 101 if (af >= AF_MAX) 102 return (0); 103 (*afswitch[af].af_hash)(dst, &h); 104 105 hash = h.afh_nethash; 106 rh = &nethash[hash & ROUTEHASHMASK]; 107 match = afswitch[af].af_netmatch; 108 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 109 if (rt->rt_hash != hash) 110 continue; 111 if (rt->rt_dst.sa_family == af && 112 (*match)(&rt->rt_dst, dst)) 113 return (rt); 114 } 115 return (0); 116} 117 118void 119rtadd(struct sockaddr *dst, struct sockaddr *gate, short metric, 120 short ticks, int state) 121{ 122 struct afhash h; 123 register struct rt_entry *rt; 124 struct rthash *rh; 125 int af = dst->sa_family, flags; 126 u_int hash; 127 128 FIXLEN(dst); 129 FIXLEN(gate); 130 if (af >= AF_MAX) 131 return; 132 (*afswitch[af].af_hash)(dst, &h); 133 flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; 134 hash = h.afh_nethash; 135 rh = &nethash[hash & ROUTEHASHMASK]; 136 rt = (struct rt_entry *)malloc(sizeof (*rt)); 137 if (rt == 0) 138 return; 139 rt->rt_hash = hash; 140 rt->rt_dst = *dst; 141 rt->rt_router = *gate; 142 rt->rt_metric = metric; 143 rt->rt_ticks = ticks; 144 rt->rt_timer = 0; 145 rt->rt_flags = RTF_UP | flags; 146 rt->rt_state = state | RTS_CHANGED; 147 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 148 rt->rt_clone = NULL; 149 if (metric) 150 rt->rt_flags |= RTF_GATEWAY; 151 insque(rt, rh); 152 TRACE_ACTION("ADD", rt); 153 /* 154 * If the ioctl fails because the gateway is unreachable 155 * from this host, discard the entry. This should only 156 * occur because of an incorrect entry in /etc/gateways. 157 */ 158 if (install && rtioctl(ADD, &rt->rt_rt) < 0) { 159 if (errno != EEXIST) 160 perror("SIOCADDRT"); 161 if (errno == ENETUNREACH) { 162 TRACE_ACTION("DELETE", rt); 163 remque(rt); 164 free((char *)rt); 165 } 166 } 167} 168 169void 170rtadd_clone(struct rt_entry *ort, struct sockaddr *dst, 171 struct sockaddr *gate, short metric, short ticks, int state) 172{ 173 struct afhash h; 174 register struct rt_entry *rt; 175 struct rthash *rh; 176 int af = dst->sa_family, flags; 177 u_int hash; 178 179 FIXLEN(dst); 180 FIXLEN(gate); 181 if (af >= AF_MAX) 182 return; 183 (*afswitch[af].af_hash)(dst, &h); 184 flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; 185 hash = h.afh_nethash; 186 rh = &nethash[hash & ROUTEHASHMASK]; 187 rt = (struct rt_entry *)malloc(sizeof (*rt)); 188 if (rt == 0) 189 return; 190 rt->rt_hash = hash; 191 rt->rt_dst = *dst; 192 rt->rt_router = *gate; 193 rt->rt_metric = metric; 194 rt->rt_ticks = ticks; 195 rt->rt_timer = 0; 196 rt->rt_flags = RTF_UP | flags; 197 rt->rt_state = state | RTS_CHANGED; 198 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 199 rt->rt_clone = NULL; 200 rt->rt_forw = NULL; 201 rt->rt_back = NULL; 202 if (metric) 203 rt->rt_flags |= RTF_GATEWAY; 204 205 while(ort->rt_clone != NULL) 206 ort = ort->rt_clone; 207 ort->rt_clone = rt; 208 TRACE_ACTION("ADD_CLONE", rt); 209} 210 211void 212rtchange(struct rt_entry *rt, struct sockaddr *gate, short metric, 213 short ticks) 214{ 215 int doioctl = 0, metricchanged = 0; 216 struct rtuentry oldroute; 217 218 FIXLEN(gate); 219 /* 220 * Handling of clones. 221 * When the route changed and it had clones, handle it special. 222 * 1. If the new route is cheaper than the clone(s), free the clones. 223 * 2. If the new route is the same cost, it may be one of the clones, 224 * search for it and free it. 225 * 3. If the new route is more expensive than the clone(s), use the 226 * values of the clone(s). 227 */ 228 if (rt->rt_clone) { 229 if ((ticks < rt->rt_clone->rt_ticks) || 230 ((ticks == rt->rt_clone->rt_ticks) && 231 (metric < rt->rt_clone->rt_metric))) { 232 /* 233 * Free all clones. 234 */ 235 struct rt_entry *trt, *nrt; 236 237 trt = rt->rt_clone; 238 rt->rt_clone = NULL; 239 while(trt) { 240 nrt = trt->rt_clone; 241 free((char *)trt); 242 trt = nrt; 243 } 244 } else if ((ticks == rt->rt_clone->rt_ticks) && 245 (metric == rt->rt_clone->rt_metric)) { 246 struct rt_entry *prt, *trt; 247 248 prt = rt; 249 trt = rt->rt_clone; 250 251 while(trt) { 252 if (equal(&trt->rt_router, gate)) { 253 prt->rt_clone = trt->rt_clone; 254 free(trt); 255 trt = prt->rt_clone; 256 } else { 257 prt = trt; 258 trt = trt->rt_clone; 259 } 260 } 261 } else { 262 /* 263 * Use the values of the first clone. 264 * Delete the corresponding clone. 265 */ 266 struct rt_entry *trt; 267 268 trt = rt->rt_clone; 269 rt->rt_clone = rt->rt_clone->rt_clone; 270 metric = trt->rt_metric; 271 ticks = trt->rt_ticks; 272 *gate = trt->rt_router; 273 free((char *)trt); 274 } 275 } 276 277 if (!equal(&rt->rt_router, gate)) 278 doioctl++; 279 if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks)) 280 metricchanged++; 281 if (doioctl || metricchanged) { 282 TRACE_ACTION("CHANGE FROM", rt); 283 if (doioctl) { 284 oldroute = rt->rt_rt; 285 rt->rt_router = *gate; 286 } 287 rt->rt_metric = metric; 288 rt->rt_ticks = ticks; 289 if ((rt->rt_state & RTS_INTERFACE) && metric) { 290 rt->rt_state &= ~RTS_INTERFACE; 291 if(rt->rt_ifp) 292 syslog(LOG_ERR, 293 "changing route from interface %s (timed out)", 294 rt->rt_ifp->int_name); 295 else 296 syslog(LOG_ERR, 297 "changing route from interface ??? (timed out)"); 298 } 299 if (metric) 300 rt->rt_flags |= RTF_GATEWAY; 301 else 302 rt->rt_flags &= ~RTF_GATEWAY; 303 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 304 rt->rt_state |= RTS_CHANGED; 305 TRACE_ACTION("CHANGE TO", rt); 306 } 307 if (doioctl && install) { 308#ifndef RTM_ADD 309 if (rtioctl(ADD, &rt->rt_rt) < 0) 310 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m", 311 ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr), 312 ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr)); 313 if (delete && rtioctl(DELETE, &oldroute) < 0) 314 perror("rtioctl DELETE"); 315#else 316 if (delete == 0) { 317 if (rtioctl(ADD, &rt->rt_rt) >= 0) 318 return; 319 } else { 320 if (rtioctl(CHANGE, &rt->rt_rt) >= 0) 321 return; 322 } 323 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m", 324 ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr), 325 ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr)); 326#endif 327 } 328} 329 330void 331rtdelete(struct rt_entry *rt) 332{ 333 334 struct sockaddr *sa = &(rt->rt_router); 335 FIXLEN(sa); 336 sa = &(rt->rt_dst); 337 FIXLEN(sa); 338 if (rt->rt_clone) { 339 /* 340 * If there is a clone we just do a rt_change to it. 341 */ 342 struct rt_entry *trt = rt->rt_clone; 343 rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks); 344 return; 345 } 346 if (rt->rt_state & RTS_INTERFACE) { 347 if (rt->rt_ifp) 348 syslog(LOG_ERR, 349 "deleting route to interface %s (timed out)", 350 rt->rt_ifp->int_name); 351 else 352 syslog(LOG_ERR, 353 "deleting route to interface ??? (timed out)"); 354 } 355 TRACE_ACTION("DELETE", rt); 356 if (install && rtioctl(DELETE, &rt->rt_rt) < 0) 357 perror("rtioctl DELETE"); 358 remque(rt); 359 free((char *)rt); 360} 361 362void 363rtinit(void) 364{ 365 register struct rthash *rh; 366 367 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 368 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 369} 370int seqno; 371 372int 373rtioctl(int action, struct rtuentry *ort) 374{ 375#ifndef RTM_ADD 376 if (install == 0) 377 return (errno = 0); 378 379 ort->rtu_rtflags = ort->rtu_flags; 380 381 switch (action) { 382 383 case ADD: 384 return (ioctl(s, SIOCADDRT, (char *)ort)); 385 386 case DELETE: 387 return (ioctl(s, SIOCDELRT, (char *)ort)); 388 389 default: 390 return (-1); 391 } 392#else /* RTM_ADD */ 393 struct { 394 struct rt_msghdr w_rtm; 395 struct sockaddr w_dst; 396 struct sockaddr w_gate; 397 struct sockaddr_ipx w_netmask; 398 } w; 399#define rtm w.w_rtm 400 401 bzero((char *)&w, sizeof(w)); 402 rtm.rtm_msglen = sizeof(w); 403 rtm.rtm_version = RTM_VERSION; 404 rtm.rtm_type = (action == ADD ? RTM_ADD : 405 (action == DELETE ? RTM_DELETE : RTM_CHANGE)); 406 rtm.rtm_flags = ort->rtu_flags; 407 rtm.rtm_seq = ++seqno; 408 rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; 409 bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst)); 410 bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate)); 411 w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX; 412 w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst); 413 if (rtm.rtm_flags & RTF_HOST) { 414 rtm.rtm_msglen -= sizeof(w.w_netmask); 415 } else { 416 rtm.rtm_addrs |= RTA_NETMASK; 417 w.w_netmask = ipx_netmask; 418 rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len; 419 } 420 errno = 0; 421 return write(r, (char *)&w, rtm.rtm_msglen); 422#endif /* RTM_ADD */ 423} 424