xref: /original-bsd/sbin/XNSrouted/tables.c (revision 21b9697b)
1 /*
2  * Copyright (c) 1985 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)tables.c	5.8 (Berkeley) 08/21/89";
20 #endif /* not lint */
21 
22 /*
23  * Routing Table Management Daemon
24  */
25 #include "defs.h"
26 #include <sys/ioctl.h>
27 #include <errno.h>
28 
29 #ifndef DEBUG
30 #define	DEBUG	0
31 #endif
32 
33 extern	char *xns_ntoa();
34 #define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
35 
36 int	install = !DEBUG;		/* if 1 call kernel */
37 int	delete = 1;
38 /*
39  * Lookup dst in the tables for an exact match.
40  */
41 struct rt_entry *
42 rtlookup(dst)
43 	struct sockaddr *dst;
44 {
45 	register struct rt_entry *rt;
46 	register struct rthash *rh;
47 	register u_int hash;
48 	struct afhash h;
49 	int doinghost = 1;
50 
51 	if (dst->sa_family >= AF_MAX)
52 		return (0);
53 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
54 	hash = h.afh_hosthash;
55 	rh = &hosthash[hash & ROUTEHASHMASK];
56 again:
57 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
58 		if (rt->rt_hash != hash)
59 			continue;
60 		if (equal(&rt->rt_dst, dst))
61 			return (rt);
62 	}
63 	if (doinghost) {
64 		doinghost = 0;
65 		hash = h.afh_nethash;
66 		rh = &nethash[hash & ROUTEHASHMASK];
67 		goto again;
68 	}
69 	return (0);
70 }
71 
72 /*
73  * Find a route to dst as the kernel would.
74  */
75 struct rt_entry *
76 rtfind(dst)
77 	struct sockaddr *dst;
78 {
79 	register struct rt_entry *rt;
80 	register struct rthash *rh;
81 	register u_int hash;
82 	struct afhash h;
83 	int af = dst->sa_family;
84 	int doinghost = 1, (*match)();
85 
86 	if (af >= AF_MAX)
87 		return (0);
88 	(*afswitch[af].af_hash)(dst, &h);
89 	hash = h.afh_hosthash;
90 	rh = &hosthash[hash & ROUTEHASHMASK];
91 
92 again:
93 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
94 		if (rt->rt_hash != hash)
95 			continue;
96 		if (doinghost) {
97 			if (equal(&rt->rt_dst, dst))
98 				return (rt);
99 		} else {
100 			if (rt->rt_dst.sa_family == af &&
101 			    (*match)(&rt->rt_dst, dst))
102 				return (rt);
103 		}
104 	}
105 	if (doinghost) {
106 		doinghost = 0;
107 		hash = h.afh_nethash;
108 		rh = &nethash[hash & ROUTEHASHMASK];
109 		match = afswitch[af].af_netmatch;
110 		goto again;
111 	}
112 	return (0);
113 }
114 
115 rtadd(dst, gate, metric, state)
116 	struct sockaddr *dst, *gate;
117 	int metric, state;
118 {
119 	struct afhash h;
120 	register struct rt_entry *rt;
121 	struct rthash *rh;
122 	int af = dst->sa_family, flags;
123 	u_int hash;
124 
125 	FIXLEN(dst);
126 	FIXLEN(gate);
127 	if (af >= AF_MAX)
128 		return;
129 	(*afswitch[af].af_hash)(dst, &h);
130 	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
131 	if (flags & RTF_HOST) {
132 		hash = h.afh_hosthash;
133 		rh = &hosthash[hash & ROUTEHASHMASK];
134 	} else {
135 		hash = h.afh_nethash;
136 		rh = &nethash[hash & ROUTEHASHMASK];
137 	}
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_timer = 0;
146 	rt->rt_flags = RTF_UP | flags;
147 	rt->rt_state = state | RTS_CHANGED;
148 	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
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 && ioctl(s, SIOCADDRT, (char *)&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 rtchange(rt, gate, metric)
170 	struct rt_entry *rt;
171 	struct sockaddr *gate;
172 	short metric;
173 {
174 	int doioctl = 0, metricchanged = 0;
175 	struct rtentry oldroute;
176 
177 	FIXLEN(gate);
178 	if (!equal(&rt->rt_router, gate))
179 		doioctl++;
180 	if (metric != rt->rt_metric)
181 		metricchanged++;
182 	if (doioctl || metricchanged) {
183 		TRACE_ACTION(CHANGE FROM, rt);
184 		if (doioctl) {
185 			oldroute = rt->rt_rt;
186 			rt->rt_router = *gate;
187 		}
188 		rt->rt_metric = metric;
189 		if ((rt->rt_state & RTS_INTERFACE) && metric) {
190 			rt->rt_state &= ~RTS_INTERFACE;
191 			syslog(LOG_ERR,
192 				"changing route from interface %s (timed out)",
193 				rt->rt_ifp->int_name);
194 		}
195 		if (metric)
196 			rt->rt_flags |= RTF_GATEWAY;
197 		else
198 			rt->rt_flags &= ~RTF_GATEWAY;
199 		rt->rt_state |= RTS_CHANGED;
200 		TRACE_ACTION(CHANGE TO, rt);
201 	}
202 	if (doioctl && install) {
203 #ifndef RTM_ADD
204 		if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
205 		  syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m",
206 		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
207 		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
208 		if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
209 			perror("SIOCDELRT");
210 #else
211 		if (delete && ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
212 			perror("SIOCDELRT");
213 		if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
214 		  syslog(LOG_ERR, "SIOCADDRT dst %s, gw %s: %m",
215 		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
216 		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
217 #endif
218 	}
219 }
220 
221 rtdelete(rt)
222 	struct rt_entry *rt;
223 {
224 
225 	struct sockaddr *sa = &(rt->rt_rt.rt_gateway);
226 	FIXLEN(sa);
227 #undef rt_dst
228 	sa = &(rt->rt_rt.rt_dst);
229 	FIXLEN(sa);
230 	if (rt->rt_state & RTS_INTERFACE) {
231 		syslog(LOG_ERR, "deleting route to interface %s (timed out)",
232 			rt->rt_ifp->int_name);
233 	}
234 	TRACE_ACTION(DELETE, rt);
235 	if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
236 		perror("SIOCDELRT");
237 	remque(rt);
238 	free((char *)rt);
239 }
240 
241 rtinit()
242 {
243 	register struct rthash *rh;
244 
245 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
246 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
247 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
248 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
249 }
250