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