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 *
rtlookup(dst)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 *
rtfind(dst)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
rtinit()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
rtioctl(action,ort)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