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