1 /*
2 * Copyright (c) 1983, 1988, 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.2 (Berkeley) 04/28/95";
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 #include <sys/syslog.h>
19
20 #ifndef DEBUG
21 #define DEBUG 0
22 #endif
23
24 #ifdef RTM_ADD
25 #define FIXLEN(s) {if ((s)->sa_len == 0) (s)->sa_len = sizeof *(s);}
26 #else
27 #define FIXLEN(s) { }
28 #endif
29
30 int install = !DEBUG; /* if 1 call kernel */
31
32 /*
33 * Lookup dst in the tables for an exact match.
34 */
35 struct rt_entry *
rtlookup(dst)36 rtlookup(dst)
37 struct sockaddr *dst;
38 {
39 register struct rt_entry *rt;
40 register struct rthash *rh;
41 register u_int hash;
42 struct afhash h;
43 int doinghost = 1;
44
45 if (dst->sa_family >= af_max)
46 return (0);
47 (*afswitch[dst->sa_family].af_hash)(dst, &h);
48 hash = h.afh_hosthash;
49 rh = &hosthash[hash & ROUTEHASHMASK];
50 again:
51 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
52 if (rt->rt_hash != hash)
53 continue;
54 if (equal(&rt->rt_dst, dst))
55 return (rt);
56 }
57 if (doinghost) {
58 doinghost = 0;
59 hash = h.afh_nethash;
60 rh = &nethash[hash & ROUTEHASHMASK];
61 goto again;
62 }
63 return (0);
64 }
65
66 struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
67
68 /*
69 * Find a route to dst as the kernel would.
70 */
71 struct rt_entry *
rtfind(dst)72 rtfind(dst)
73 struct sockaddr *dst;
74 {
75 register struct rt_entry *rt;
76 register struct rthash *rh;
77 register u_int hash;
78 struct afhash h;
79 int af = dst->sa_family;
80 int doinghost = 1, (*match)();
81
82 if (af >= af_max)
83 return (0);
84 (*afswitch[af].af_hash)(dst, &h);
85 hash = h.afh_hosthash;
86 rh = &hosthash[hash & ROUTEHASHMASK];
87
88 again:
89 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
90 if (rt->rt_hash != hash)
91 continue;
92 if (doinghost) {
93 if (equal(&rt->rt_dst, dst))
94 return (rt);
95 } else {
96 if (rt->rt_dst.sa_family == af &&
97 (*match)(&rt->rt_dst, dst))
98 return (rt);
99 }
100 }
101 if (doinghost) {
102 doinghost = 0;
103 hash = h.afh_nethash;
104 rh = &nethash[hash & ROUTEHASHMASK];
105 match = afswitch[af].af_netmatch;
106 goto again;
107 }
108 #ifdef notyet
109 /*
110 * Check for wildcard gateway, by convention network 0.
111 */
112 if (dst != &wildcard) {
113 dst = &wildcard, hash = 0;
114 goto again;
115 }
116 #endif
117 return (0);
118 }
119
120 rtadd(dst, gate, metric, state)
121 struct sockaddr *dst, *gate;
122 int metric, state;
123 {
124 struct afhash h;
125 register struct rt_entry *rt;
126 struct rthash *rh;
127 int af = dst->sa_family, flags;
128 u_int hash;
129
130 if (af >= af_max)
131 return;
132 (*afswitch[af].af_hash)(dst, &h);
133 flags = (*afswitch[af].af_rtflags)(dst);
134 /*
135 * Subnet flag isn't visible to kernel, move to state. XXX
136 */
137 FIXLEN(dst);
138 FIXLEN(gate);
139 if (flags & RTF_SUBNET) {
140 state |= RTS_SUBNET;
141 flags &= ~RTF_SUBNET;
142 }
143 if (flags & RTF_HOST) {
144 hash = h.afh_hosthash;
145 rh = &hosthash[hash & ROUTEHASHMASK];
146 } else {
147 hash = h.afh_nethash;
148 rh = &nethash[hash & ROUTEHASHMASK];
149 }
150 rt = (struct rt_entry *)malloc(sizeof (*rt));
151 if (rt == 0)
152 return;
153 rt->rt_hash = hash;
154 rt->rt_dst = *dst;
155 rt->rt_router = *gate;
156 rt->rt_timer = 0;
157 rt->rt_flags = RTF_UP | flags;
158 rt->rt_state = state | RTS_CHANGED;
159 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst);
160 if (rt->rt_ifp == 0)
161 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
162 if ((state & RTS_INTERFACE) == 0)
163 rt->rt_flags |= RTF_GATEWAY;
164 rt->rt_metric = metric;
165 insque(rt, rh);
166 TRACE_ACTION("ADD", rt);
167 /*
168 * If the ioctl fails because the gateway is unreachable
169 * from this host, discard the entry. This should only
170 * occur because of an incorrect entry in /etc/gateways.
171 */
172 if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
173 rtioctl(ADD, &rt->rt_rt) < 0) {
174 if (errno != EEXIST && gate->sa_family < af_max)
175 syslog(LOG_ERR,
176 "adding route to net/host %s through gateway %s: %m\n",
177 (*afswitch[dst->sa_family].af_format)(dst),
178 (*afswitch[gate->sa_family].af_format)(gate));
179 perror("ADD ROUTE");
180 if (errno == ENETUNREACH) {
181 TRACE_ACTION("DELETE", rt);
182 remque(rt);
183 free((char *)rt);
184 }
185 }
186 }
187
188 rtchange(rt, gate, metric)
189 struct rt_entry *rt;
190 struct sockaddr *gate;
191 short metric;
192 {
193 int add = 0, delete = 0, newgateway = 0;
194 struct rtuentry oldroute;
195
196 FIXLEN(gate);
197 FIXLEN(&(rt->rt_router));
198 FIXLEN(&(rt->rt_dst));
199 if (!equal(&rt->rt_router, gate)) {
200 newgateway++;
201 TRACE_ACTION("CHANGE FROM ", rt);
202 } else if (metric != rt->rt_metric)
203 TRACE_NEWMETRIC(rt, metric);
204 if ((rt->rt_state & RTS_INTERNAL) == 0) {
205 /*
206 * If changing to different router, we need to add
207 * new route and delete old one if in the kernel.
208 * If the router is the same, we need to delete
209 * the route if has become unreachable, or re-add
210 * it if it had been unreachable.
211 */
212 if (newgateway) {
213 add++;
214 if (rt->rt_metric != HOPCNT_INFINITY)
215 delete++;
216 } else if (metric == HOPCNT_INFINITY)
217 delete++;
218 else if (rt->rt_metric == HOPCNT_INFINITY)
219 add++;
220 }
221 if (delete)
222 oldroute = rt->rt_rt;
223 if ((rt->rt_state & RTS_INTERFACE) && delete) {
224 rt->rt_state &= ~RTS_INTERFACE;
225 rt->rt_flags |= RTF_GATEWAY;
226 if (metric > rt->rt_metric && delete)
227 syslog(LOG_ERR, "%s route to interface %s (timed out)",
228 add? "changing" : "deleting",
229 rt->rt_ifp ? rt->rt_ifp->int_name : "?");
230 }
231 if (add) {
232 rt->rt_router = *gate;
233 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
234 if (rt->rt_ifp == 0)
235 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
236 }
237 rt->rt_metric = metric;
238 rt->rt_state |= RTS_CHANGED;
239 if (newgateway)
240 TRACE_ACTION("CHANGE TO ", rt);
241 #ifndef RTM_ADD
242 if (add && rtioctl(ADD, &rt->rt_rt) < 0)
243 perror("ADD ROUTE");
244 if (delete && rtioctl(DELETE, &oldroute) < 0)
245 perror("DELETE ROUTE");
246 #else
247 if (delete && !add) {
248 if (rtioctl(DELETE, &oldroute) < 0)
249 perror("DELETE ROUTE");
250 } else if (!delete && add) {
251 if (rtioctl(ADD, &rt->rt_rt) < 0)
252 perror("ADD ROUTE");
253 } else if (delete && add) {
254 if (rtioctl(CHANGE, &rt->rt_rt) < 0)
255 perror("CHANGE ROUTE");
256 }
257 #endif
258 }
259
260 rtdelete(rt)
261 struct rt_entry *rt;
262 {
263
264 TRACE_ACTION("DELETE", rt);
265 FIXLEN(&(rt->rt_router));
266 FIXLEN(&(rt->rt_dst));
267 if (rt->rt_metric < HOPCNT_INFINITY) {
268 if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE)
269 syslog(LOG_ERR,
270 "deleting route to interface %s? (timed out?)",
271 rt->rt_ifp->int_name);
272 if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
273 rtioctl(DELETE, &rt->rt_rt) < 0)
274 perror("rtdelete");
275 }
276 remque(rt);
277 free((char *)rt);
278 }
279
rtdeleteall(sig)280 rtdeleteall(sig)
281 int sig;
282 {
283 register struct rthash *rh;
284 register struct rt_entry *rt;
285 struct rthash *base = hosthash;
286 int doinghost = 1;
287
288 again:
289 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
290 rt = rh->rt_forw;
291 for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
292 if (rt->rt_state & RTS_INTERFACE ||
293 rt->rt_metric >= HOPCNT_INFINITY)
294 continue;
295 TRACE_ACTION("DELETE", rt);
296 if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 &&
297 rtioctl(DELETE, &rt->rt_rt) < 0)
298 perror("rtdeleteall");
299 }
300 }
301 if (doinghost) {
302 doinghost = 0;
303 base = nethash;
304 goto again;
305 }
306 exit(sig);
307 }
308
309 /*
310 * If we have an interface to the wide, wide world,
311 * add an entry for an Internet default route (wildcard) to the internal
312 * tables and advertise it. This route is not added to the kernel routes,
313 * but this entry prevents us from listening to other people's defaults
314 * and installing them in the kernel here.
315 */
rtdefault()316 rtdefault()
317 {
318 extern struct sockaddr inet_default;
319
320 rtadd(&inet_default, &inet_default, 1,
321 RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
322 }
323
rtinit()324 rtinit()
325 {
326 register struct rthash *rh;
327
328 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
329 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
330 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
331 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
332 }
333
rtioctl(action,ort)334 rtioctl(action, ort)
335 int action;
336 struct rtuentry *ort;
337 {
338 #ifndef RTM_ADD
339 if (install == 0)
340 return (errno = 0);
341 ort->rtu_rtflags = ort->rtu_flags;
342 switch (action) {
343
344 case ADD:
345 return (ioctl(s, SIOCADDRT, (char *)ort));
346
347 case DELETE:
348 return (ioctl(s, SIOCDELRT, (char *)ort));
349
350 default:
351 return (-1);
352 }
353 #else /* RTM_ADD */
354 struct {
355 struct rt_msghdr w_rtm;
356 struct sockaddr_in w_dst;
357 struct sockaddr w_gate;
358 struct sockaddr_in w_netmask;
359 } w;
360 #define rtm w.w_rtm
361
362 memset(&w, 0, sizeof(w));
363 rtm.rtm_msglen = sizeof(w);
364 rtm.rtm_version = RTM_VERSION;
365 rtm.rtm_type = (action == ADD ? RTM_ADD :
366 (action == DELETE ? RTM_DELETE : RTM_CHANGE));
367 #undef rt_dst
368 rtm.rtm_flags = ort->rtu_flags;
369 rtm.rtm_seq = ++seqno;
370 rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
371 memmove(&w.w_dst, &ort->rtu_dst, sizeof(w.w_dst));
372 memmove(&w.w_gate, &ort->rtu_router, sizeof(w.w_gate));
373 w.w_dst.sin_family = AF_INET;
374 w.w_dst.sin_len = sizeof(w.w_dst);
375 w.w_gate.sa_family = AF_INET;
376 w.w_gate.sa_len = sizeof(w.w_gate);
377 if (rtm.rtm_flags & RTF_HOST) {
378 rtm.rtm_msglen -= sizeof(w.w_netmask);
379 } else {
380 register char *cp;
381 int len;
382
383 rtm.rtm_addrs |= RTA_NETMASK;
384 w.w_netmask.sin_addr.s_addr =
385 inet_maskof(w.w_dst.sin_addr.s_addr);
386 for (cp = (char *)(1 + &w.w_netmask.sin_addr);
387 --cp > (char *) &w.w_netmask; )
388 if (*cp)
389 break;
390 len = cp - (char *)&w.w_netmask;
391 if (len) {
392 len++;
393 w.w_netmask.sin_len = len;
394 len = 1 + ((len - 1) | (sizeof(long) - 1));
395 } else
396 len = sizeof(long);
397 rtm.rtm_msglen -= (sizeof(w.w_netmask) - len);
398 }
399 errno = 0;
400 return (install ? write(r, (char *)&w, rtm.rtm_msglen) : (errno = 0));
401 #endif /* RTM_ADD */
402 }
403