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$ 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 int af = dst->sa_family, flags; 176 u_int hash; 177 178 FIXLEN(dst); 179 FIXLEN(gate); 180 if (af >= AF_MAX) 181 return; 182 (*afswitch[af].af_hash)(dst, &h); 183 flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0; 184 hash = h.afh_nethash; 185 rt = (struct rt_entry *)malloc(sizeof (*rt)); 186 if (rt == 0) 187 return; 188 rt->rt_hash = hash; 189 rt->rt_dst = *dst; 190 rt->rt_router = *gate; 191 rt->rt_metric = metric; 192 rt->rt_ticks = ticks; 193 rt->rt_timer = 0; 194 rt->rt_flags = RTF_UP | flags; 195 rt->rt_state = state | RTS_CHANGED; 196 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 197 rt->rt_clone = NULL; 198 rt->rt_forw = NULL; 199 rt->rt_back = NULL; 200 if (metric) 201 rt->rt_flags |= RTF_GATEWAY; 202 203 while(ort->rt_clone != NULL) 204 ort = ort->rt_clone; 205 ort->rt_clone = rt; 206 TRACE_ACTION("ADD_CLONE", rt); 207} 208 209void 210rtchange(struct rt_entry *rt, struct sockaddr *gate, short metric, 211 short ticks) 212{ 213 int doioctl = 0, metricchanged = 0; 214 215 FIXLEN(gate); 216 /* 217 * Handling of clones. 218 * When the route changed and it had clones, handle it special. 219 * 1. If the new route is cheaper than the clone(s), free the clones. 220 * 2. If the new route is the same cost, it may be one of the clones, 221 * search for it and free it. 222 * 3. If the new route is more expensive than the clone(s), use the 223 * values of the clone(s). 224 */ 225 if (rt->rt_clone) { 226 if ((ticks < rt->rt_clone->rt_ticks) || 227 ((ticks == rt->rt_clone->rt_ticks) && 228 (metric < rt->rt_clone->rt_metric))) { 229 /* 230 * Free all clones. 231 */ 232 struct rt_entry *trt, *nrt; 233 234 trt = rt->rt_clone; 235 rt->rt_clone = NULL; 236 while(trt) { 237 nrt = trt->rt_clone; 238 free((char *)trt); 239 trt = nrt; 240 } 241 } else if ((ticks == rt->rt_clone->rt_ticks) && 242 (metric == rt->rt_clone->rt_metric)) { 243 struct rt_entry *prt, *trt; 244 245 prt = rt; 246 trt = rt->rt_clone; 247 248 while(trt) { 249 if (equal(&trt->rt_router, gate)) { 250 prt->rt_clone = trt->rt_clone; 251 free(trt); 252 trt = prt->rt_clone; 253 } else { 254 prt = trt; 255 trt = trt->rt_clone; 256 } 257 } 258 } else { 259 /* 260 * Use the values of the first clone. 261 * Delete the corresponding clone. 262 */ 263 struct rt_entry *trt; 264 265 trt = rt->rt_clone; 266 rt->rt_clone = rt->rt_clone->rt_clone; 267 metric = trt->rt_metric; 268 ticks = trt->rt_ticks; 269 *gate = trt->rt_router; 270 free((char *)trt); 271 } 272 } 273 274 if (!equal(&rt->rt_router, gate)) 275 doioctl++; 276 if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks)) 277 metricchanged++; 278 if (doioctl || metricchanged) { 279 TRACE_ACTION("CHANGE FROM", rt); 280 if (doioctl) { 281 rt->rt_router = *gate; 282 } 283 rt->rt_metric = metric; 284 rt->rt_ticks = ticks; 285 if ((rt->rt_state & RTS_INTERFACE) && metric) { 286 rt->rt_state &= ~RTS_INTERFACE; 287 if(rt->rt_ifp) 288 syslog(LOG_ERR, 289 "changing route from interface %s (timed out)", 290 rt->rt_ifp->int_name); 291 else 292 syslog(LOG_ERR, 293 "changing route from interface ??? (timed out)"); 294 } 295 if (metric) 296 rt->rt_flags |= RTF_GATEWAY; 297 else 298 rt->rt_flags &= ~RTF_GATEWAY; 299 rt->rt_ifp = if_ifwithnet(&rt->rt_router); 300 rt->rt_state |= RTS_CHANGED; 301 TRACE_ACTION("CHANGE TO", rt); 302 } 303 if (doioctl && install) { 304#ifndef RTM_ADD 305 if (rtioctl(ADD, &rt->rt_rt) < 0) 306 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m", 307 ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr), 308 ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr)); 309 if (delete && rtioctl(DELETE, &oldroute) < 0) 310 perror("rtioctl DELETE"); 311#else 312 if (delete == 0) { 313 if (rtioctl(ADD, &rt->rt_rt) >= 0) 314 return; 315 } else { 316 if (rtioctl(CHANGE, &rt->rt_rt) >= 0) 317 return; 318 } 319 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m", 320 ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr), 321 ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr)); 322#endif 323 } 324} 325 326void 327rtdelete(struct rt_entry *rt) 328{ 329 330 struct sockaddr *sa = &(rt->rt_router); 331 FIXLEN(sa); 332 sa = &(rt->rt_dst); 333 FIXLEN(sa); 334 if (rt->rt_clone) { 335 /* 336 * If there is a clone we just do a rt_change to it. 337 */ 338 struct rt_entry *trt = rt->rt_clone; 339 rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks); 340 return; 341 } 342 if (rt->rt_state & RTS_INTERFACE) { 343 if (rt->rt_ifp) 344 syslog(LOG_ERR, 345 "deleting route to interface %s (timed out)", 346 rt->rt_ifp->int_name); 347 else 348 syslog(LOG_ERR, 349 "deleting route to interface ??? (timed out)"); 350 } 351 TRACE_ACTION("DELETE", rt); 352 if (install && rtioctl(DELETE, &rt->rt_rt) < 0) 353 perror("rtioctl DELETE"); 354 remque(rt); 355 free((char *)rt); 356} 357 358void 359rtinit(void) 360{ 361 register struct rthash *rh; 362 363 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) 364 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh; 365} 366int seqno; 367 368int 369rtioctl(int action, struct rtuentry *ort) 370{ 371#ifndef RTM_ADD 372 if (install == 0) 373 return (errno = 0); 374 375 ort->rtu_rtflags = ort->rtu_flags; 376 377 switch (action) { 378 379 case ADD: 380 return (ioctl(s, SIOCADDRT, (char *)ort)); 381 382 case DELETE: 383 return (ioctl(s, SIOCDELRT, (char *)ort)); 384 385 default: 386 return (-1); 387 } 388#else /* RTM_ADD */ 389 struct { 390 struct rt_msghdr w_rtm; 391 struct sockaddr w_dst; 392 struct sockaddr w_gate; 393 struct sockaddr_ipx w_netmask; 394 } w; 395#define rtm w.w_rtm 396 397 bzero((char *)&w, sizeof(w)); 398 rtm.rtm_msglen = sizeof(w); 399 rtm.rtm_version = RTM_VERSION; 400 rtm.rtm_type = (action == ADD ? RTM_ADD : 401 (action == DELETE ? RTM_DELETE : RTM_CHANGE)); 402 rtm.rtm_flags = ort->rtu_flags; 403 rtm.rtm_seq = ++seqno; 404 rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; 405 bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst)); 406 bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate)); 407 w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX; 408 w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst); 409 if (rtm.rtm_flags & RTF_HOST) { 410 rtm.rtm_msglen -= sizeof(w.w_netmask); 411 } else { 412 rtm.rtm_addrs |= RTA_NETMASK; 413 w.w_netmask = ipx_netmask; 414 rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len; 415 } 416 errno = 0; 417 return write(r, (char *)&w, rtm.rtm_msglen); 418#endif /* RTM_ADD */ 419} 420