tables.c revision 231817
190792Sgshapiro/* 2261363Sgshapiro * Copyright (c) 1985, 1993 390792Sgshapiro * The Regents of the University of California. All rights reserved. 490792Sgshapiro * 590792Sgshapiro * Copyright (c) 1995 John Hay. All rights reserved. 690792Sgshapiro * 790792Sgshapiro * Redistribution and use in source and binary forms, with or without 890792Sgshapiro * modification, are permitted provided that the following conditions 990792Sgshapiro * are met: 1090792Sgshapiro * 1. Redistributions of source code must retain the above copyright 1190792Sgshapiro * notice, this list of conditions and the following disclaimer. 1290792Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 1390792Sgshapiro * notice, this list of conditions and the following disclaimer in the 1490792Sgshapiro * documentation and/or other materials provided with the distribution. 1590792Sgshapiro * 3. All advertising materials mentioning features or use of this software 1690792Sgshapiro * must display the following acknowledgement: 1790792Sgshapiro * This product includes software developed by the University of 1890792Sgshapiro * California, Berkeley and its contributors. 1990792Sgshapiro * 4. Neither the name of the University nor the names of its contributors 2090792Sgshapiro * may be used to endorse or promote products derived from this software 2190792Sgshapiro * without specific prior written permission. 2290792Sgshapiro * 2390792Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2490792Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25266692Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2690792Sgshapiro * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2790792Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2890792Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2990792Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3090792Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3190792Sgshapiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3290792Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3390792Sgshapiro * SUCH DAMAGE. 3490792Sgshapiro * 3590792Sgshapiro * $FreeBSD: head/usr.sbin/IPXrouted/tables.c 231817 2012-02-16 05:17:06Z eadler $ 3690792Sgshapiro */ 3790792Sgshapiro 3890792Sgshapiro#ifndef lint 3990792Sgshapirostatic const char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; 4090792Sgshapiro#endif /* not lint */ 4190792Sgshapiro 4290792Sgshapiro/* 4390792Sgshapiro * Routing Table Management Daemon 4490792Sgshapiro */ 4590792Sgshapiro#include "defs.h" 4690792Sgshapiro#include <sys/ioctl.h> 4790792Sgshapiro#include <errno.h> 4890792Sgshapiro#include <search.h> 4990792Sgshapiro#include <stdlib.h> 5090792Sgshapiro#include <unistd.h> 5190792Sgshapiro 5290792Sgshapiro#ifndef DEBUG 5390792Sgshapiro#define DEBUG 0 5490792Sgshapiro#endif 5590792Sgshapiro 5690792Sgshapiro#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));} 5790792Sgshapiro 5890792Sgshapiroint install = !DEBUG; /* if 1 call kernel */ 5990792Sgshapiroint delete = 1; 6090792Sgshapiro 6190792Sgshapirostruct rthash nethash[ROUTEHASHSIZ]; 6290792Sgshapiro 6390792Sgshapiro/* 6490792Sgshapiro * Lookup dst in the tables for an exact match. 6590792Sgshapiro */ 6690792Sgshapirostruct rt_entry * 6790792Sgshapirortlookup(struct sockaddr *dst) 6890792Sgshapiro{ 6990792Sgshapiro register struct rt_entry *rt; 7090792Sgshapiro register struct rthash *rh; 7190792Sgshapiro register u_int hash; 7290792Sgshapiro struct afhash h; 7390792Sgshapiro 7490792Sgshapiro if (dst->sa_family >= AF_MAX) 7590792Sgshapiro return (0); 7690792Sgshapiro (*afswitch[dst->sa_family].af_hash)(dst, &h); 7790792Sgshapiro hash = h.afh_nethash; 7890792Sgshapiro rh = &nethash[hash & ROUTEHASHMASK]; 7990792Sgshapiro for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 8090792Sgshapiro if (rt->rt_hash != hash) 8190792Sgshapiro continue; 8290792Sgshapiro if (equal(&rt->rt_dst, dst)) 8390792Sgshapiro return (rt); 8490792Sgshapiro } 8590792Sgshapiro return (0); 8690792Sgshapiro} 8790792Sgshapiro 8890792Sgshapiro/* 8990792Sgshapiro * Find a route to dst as the kernel would. 9090792Sgshapiro */ 9190792Sgshapirostruct rt_entry * 9290792Sgshapirortfind(struct sockaddr *dst) 9390792Sgshapiro{ 9490792Sgshapiro register struct rt_entry *rt; 9590792Sgshapiro register struct rthash *rh; 9690792Sgshapiro register u_int hash; 9790792Sgshapiro struct afhash h; 9890792Sgshapiro int af = dst->sa_family; 9990792Sgshapiro int (*match)() = 0; 10090792Sgshapiro 10190792Sgshapiro if (af >= AF_MAX) 10290792Sgshapiro return (0); 10390792Sgshapiro (*afswitch[af].af_hash)(dst, &h); 10490792Sgshapiro 10590792Sgshapiro hash = h.afh_nethash; 10690792Sgshapiro rh = &nethash[hash & ROUTEHASHMASK]; 10790792Sgshapiro match = afswitch[af].af_netmatch; 10890792Sgshapiro for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) { 10990792Sgshapiro if (rt->rt_hash != hash) 11090792Sgshapiro continue; 11190792Sgshapiro if (rt->rt_dst.sa_family == af && 11290792Sgshapiro (*match)(&rt->rt_dst, dst)) 11390792Sgshapiro return (rt); 11490792Sgshapiro } 11590792Sgshapiro return (0); 11690792Sgshapiro} 11790792Sgshapiro 11890792Sgshapirovoid 11990792Sgshapirortadd(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