xref: /original-bsd/sbin/XNSrouted/tables.c (revision c3e32dec)
1 /*
2  * Copyright (c) 1985, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)tables.c	8.1 (Berkeley) 06/05/93";
10 #endif /* not lint */
11 
12 /*
13  * Routing Table Management Daemon
14  */
15 #include "defs.h"
16 #include <sys/ioctl.h>
17 #include <errno.h>
18 
19 #ifndef DEBUG
20 #define	DEBUG	0
21 #endif
22 
23 extern	char *xns_ntoa();
24 #define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
25 
26 int	install = !DEBUG;		/* if 1 call kernel */
27 int	delete = 1;
28 /*
29  * Lookup dst in the tables for an exact match.
30  */
31 struct rt_entry *
32 rtlookup(dst)
33 	struct sockaddr *dst;
34 {
35 	register struct rt_entry *rt;
36 	register struct rthash *rh;
37 	register u_int hash;
38 	struct afhash h;
39 	int doinghost = 1;
40 
41 	if (dst->sa_family >= AF_MAX)
42 		return (0);
43 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
44 	hash = h.afh_hosthash;
45 	rh = &hosthash[hash & ROUTEHASHMASK];
46 again:
47 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
48 		if (rt->rt_hash != hash)
49 			continue;
50 		if (equal(&rt->rt_dst, dst))
51 			return (rt);
52 	}
53 	if (doinghost) {
54 		doinghost = 0;
55 		hash = h.afh_nethash;
56 		rh = &nethash[hash & ROUTEHASHMASK];
57 		goto again;
58 	}
59 	return (0);
60 }
61 
62 /*
63  * Find a route to dst as the kernel would.
64  */
65 struct rt_entry *
66 rtfind(dst)
67 	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 	int af = dst->sa_family;
74 	int doinghost = 1, (*match)();
75 
76 	if (af >= AF_MAX)
77 		return (0);
78 	(*afswitch[af].af_hash)(dst, &h);
79 	hash = h.afh_hosthash;
80 	rh = &hosthash[hash & ROUTEHASHMASK];
81 
82 again:
83 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
84 		if (rt->rt_hash != hash)
85 			continue;
86 		if (doinghost) {
87 			if (equal(&rt->rt_dst, dst))
88 				return (rt);
89 		} else {
90 			if (rt->rt_dst.sa_family == af &&
91 			    (*match)(&rt->rt_dst, dst))
92 				return (rt);
93 		}
94 	}
95 	if (doinghost) {
96 		doinghost = 0;
97 		hash = h.afh_nethash;
98 		rh = &nethash[hash & ROUTEHASHMASK];
99 		match = afswitch[af].af_netmatch;
100 		goto again;
101 	}
102 	return (0);
103 }
104 
105 rtadd(dst, gate, metric, state)
106 	struct sockaddr *dst, *gate;
107 	int metric, state;
108 {
109 	struct afhash h;
110 	register struct rt_entry *rt;
111 	struct rthash *rh;
112 	int af = dst->sa_family, flags;
113 	u_int hash;
114 
115 	FIXLEN(dst);
116 	FIXLEN(gate);
117 	if (af >= AF_MAX)
118 		return;
119 	(*afswitch[af].af_hash)(dst, &h);
120 	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
121 	if (flags & RTF_HOST) {
122 		hash = h.afh_hosthash;
123 		rh = &hosthash[hash & ROUTEHASHMASK];
124 	} else {
125 		hash = h.afh_nethash;
126 		rh = &nethash[hash & ROUTEHASHMASK];
127 	}
128 	rt = (struct rt_entry *)malloc(sizeof (*rt));
129 	if (rt == 0)
130 		return;
131 	rt->rt_hash = hash;
132 	rt->rt_dst = *dst;
133 	rt->rt_router = *gate;
134 	rt->rt_metric = metric;
135 	rt->rt_timer = 0;
136 	rt->rt_flags = RTF_UP | flags;
137 	rt->rt_state = state | RTS_CHANGED;
138 	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
139 	if (metric)
140 		rt->rt_flags |= RTF_GATEWAY;
141 	insque(rt, rh);
142 	TRACE_ACTION(ADD, rt);
143 	/*
144 	 * If the ioctl fails because the gateway is unreachable
145 	 * from this host, discard the entry.  This should only
146 	 * occur because of an incorrect entry in /etc/gateways.
147 	 */
148 	if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
149 		if (errno != EEXIST)
150 			perror("SIOCADDRT");
151 		if (errno == ENETUNREACH) {
152 			TRACE_ACTION(DELETE, rt);
153 			remque(rt);
154 			free((char *)rt);
155 		}
156 	}
157 }
158 
159 rtchange(rt, gate, metric)
160 	struct rt_entry *rt;
161 	struct sockaddr *gate;
162 	short metric;
163 {
164 	int doioctl = 0, metricchanged = 0;
165 	struct rtentry oldroute;
166 
167 	FIXLEN(gate);
168 	if (!equal(&rt->rt_router, gate))
169 		doioctl++;
170 	if (metric != rt->rt_metric)
171 		metricchanged++;
172 	if (doioctl || metricchanged) {
173 		TRACE_ACTION(CHANGE FROM, rt);
174 		if (doioctl) {
175 			oldroute = rt->rt_rt;
176 			rt->rt_router = *gate;
177 		}
178 		rt->rt_metric = metric;
179 		if ((rt->rt_state & RTS_INTERFACE) && metric) {
180 			rt->rt_state &= ~RTS_INTERFACE;
181 			syslog(LOG_ERR,
182 				"changing route from interface %s (timed out)",
183 				rt->rt_ifp->int_name);
184 		}
185 		if (metric)
186 			rt->rt_flags |= RTF_GATEWAY;
187 		else
188 			rt->rt_flags &= ~RTF_GATEWAY;
189 		rt->rt_state |= RTS_CHANGED;
190 		TRACE_ACTION(CHANGE TO, rt);
191 	}
192 	if (doioctl && install) {
193 #ifndef RTM_ADD
194 		if (rtioctl(ADD, &rt->rt_rt) < 0)
195 		  syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
196 		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
197 		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
198 		if (delete && rtioctl(DELETE, &oldroute) < 0)
199 			perror("rtioctl DELETE");
200 #else
201 		if (delete == 0) {
202 			if (rtioctl(ADD, &rt->rt_rt) >= 0)
203 				return;
204 		} else {
205 			if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
206 				return;
207 		}
208 	        syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
209 		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
210 		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
211 #endif
212 	}
213 }
214 
215 rtdelete(rt)
216 	struct rt_entry *rt;
217 {
218 
219 	struct sockaddr *sa = &(rt->rt_rt.rt_gateway);
220 	FIXLEN(sa);
221 #undef rt_dst
222 	sa = &(rt->rt_rt.rt_dst);
223 	FIXLEN(sa);
224 	if (rt->rt_state & RTS_INTERFACE) {
225 		syslog(LOG_ERR, "deleting route to interface %s (timed out)",
226 			rt->rt_ifp->int_name);
227 	}
228 	TRACE_ACTION(DELETE, rt);
229 	if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
230 		perror("rtioctl DELETE");
231 	remque(rt);
232 	free((char *)rt);
233 }
234 
235 rtinit()
236 {
237 	register struct rthash *rh;
238 
239 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
240 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
241 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
242 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
243 }
244 int seqno;
245 
246 rtioctl(action, ort)
247 	int action;
248 	struct ortentry *ort;
249 {
250 #ifndef RTM_ADD
251 	switch (action) {
252 
253 	case ADD:
254 		return (ioctl(s, SIOCADDRT, (char *)ort));
255 
256 	case DELETE:
257 		return (ioctl(s, SIOCDELRT, (char *)ort));
258 
259 	default:
260 		return (-1);
261 	}
262 #else /* RTM_ADD */
263 	struct {
264 		struct rt_msghdr w_rtm;
265 		struct sockaddr w_dst;
266 		struct sockaddr w_gate;
267 		struct sockaddr_ns w_netmask;
268 	} w;
269 #define rtm w.w_rtm
270 
271 	bzero((char *)&w, sizeof(w));
272 	rtm.rtm_msglen = sizeof(w);
273 	rtm.rtm_version = RTM_VERSION;
274 	rtm.rtm_type = (action == ADD ? RTM_ADD :
275 				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
276 #undef rt_flags
277 	rtm.rtm_flags = ort->rt_flags;
278 	rtm.rtm_seq = ++seqno;
279 	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
280 	bcopy((char *)&ort->rt_dst, (char *)&w.w_dst, sizeof(w.w_dst));
281 	bcopy((char *)&ort->rt_gateway, (char *)&w.w_gate, sizeof(w.w_gate));
282 	w.w_gate.sa_family = w.w_dst.sa_family = AF_NS;
283 	w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
284 	if (rtm.rtm_flags & RTF_HOST) {
285 		rtm.rtm_msglen -= sizeof(w.w_netmask);
286 	} else {
287 		w.w_netmask = ns_netmask;
288 		rtm.rtm_msglen -= 8;
289 	}
290 	errno = 0;
291 	return write(r, (char *)&w, rtm.rtm_msglen);
292 #endif  /* RTM_ADD */
293 }
294