xref: /trueos/usr.sbin/IPXrouted/tables.c (revision 94d2b7f64912987093f1a98573737a32e4e5d8d1)
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
39 static 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 
58 int	install = !DEBUG;		/* if 1 call kernel */
59 int	delete = 1;
60 
61 struct  rthash nethash[ROUTEHASHSIZ];
62 
63 /*
64  * Lookup dst in the tables for an exact match.
65  */
66 struct rt_entry *
rtlookup(struct sockaddr * dst)67 rtlookup(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  */
91 struct rt_entry *
rtfind(struct sockaddr * dst)92 rtfind(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
rtadd(struct sockaddr * dst,struct sockaddr * gate,short metric,short ticks,int state)119 rtadd(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 
169 void
rtadd_clone(struct rt_entry * ort,struct sockaddr * dst,struct sockaddr * gate,short metric,short ticks,int state)170 rtadd_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 
209 void
rtchange(struct rt_entry * rt,struct sockaddr * gate,short metric,short ticks)210 rtchange(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 
326 void
rtdelete(struct rt_entry * rt)327 rtdelete(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 
358 void
rtinit(void)359 rtinit(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 }
366 int seqno;
367 
368 int
rtioctl(int action,struct rtuentry * ort)369 rtioctl(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