1 /*
2 * Copyright (c) 1983, 1988 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 static char sccsid[] = "@(#)tables.c 5.17 (Berkeley) 6/1/90";
36 #endif /* not lint */
37
38 /*
39 * Routing Table Management Daemon
40 */
41 #include "defs.h"
42 #include <sys/ioctl.h>
43 #include <errno.h>
44 #include <sys/syslog.h>
45
46 #ifndef DEBUG
47 #define DEBUG 0
48 #endif
49
50 #ifdef RTM_ADD
51 #define FIXLEN(s) {if ((s)->sa_len == 0) (s)->sa_len = sizeof *(s);}
52 #else
53 #define FIXLEN(s) { }
54 #endif
55
56 int install = !DEBUG; /* if 1 call kernel */
57
58 /*
59 * Lookup dst in the tables for an exact match.
60 */
61 struct rt_entry *
rtlookup(dst)62 rtlookup(dst)
63 struct sockaddr *dst;
64 {
65 register struct rt_entry *rt;
66 register struct rthash *rh;
67 register u_int hash;
68 struct afhash h;
69 int doinghost = 1;
70
71 if (dst->sa_family >= af_max)
72 return (0);
73 (*afswitch[dst->sa_family].af_hash)(dst, &h);
74 hash = h.afh_hosthash;
75 rh = &hosthash[hash & ROUTEHASHMASK];
76 again:
77 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
78 if (rt->rt_hash != hash)
79 continue;
80 if (equal(&rt->rt_dst, dst))
81 return (rt);
82 }
83 if (doinghost) {
84 doinghost = 0;
85 hash = h.afh_nethash;
86 rh = &nethash[hash & ROUTEHASHMASK];
87 goto again;
88 }
89 return (0);
90 }
91
92 struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
93
94 /*
95 * Find a route to dst as the kernel would.
96 */
97 struct rt_entry *
rtfind(dst)98 rtfind(dst)
99 struct sockaddr *dst;
100 {
101 register struct rt_entry *rt;
102 register struct rthash *rh;
103 register u_int hash;
104 struct afhash h;
105 int af = dst->sa_family;
106 int doinghost = 1, (*match)();
107
108 if (af >= af_max)
109 return (0);
110 (*afswitch[af].af_hash)(dst, &h);
111 hash = h.afh_hosthash;
112 rh = &hosthash[hash & ROUTEHASHMASK];
113
114 again:
115 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
116 if (rt->rt_hash != hash)
117 continue;
118 if (doinghost) {
119 if (equal(&rt->rt_dst, dst))
120 return (rt);
121 } else {
122 if (rt->rt_dst.sa_family == af &&
123 (*match)(&rt->rt_dst, dst))
124 return (rt);
125 }
126 }
127 if (doinghost) {
128 doinghost = 0;
129 hash = h.afh_nethash;
130 rh = &nethash[hash & ROUTEHASHMASK];
131 match = afswitch[af].af_netmatch;
132 goto again;
133 }
134 /*
135 * Check for wildcard gateway, by convention network 0.
136 */
137 if (dst != &wildcard) {
138 dst = &wildcard, hash = 0;
139 goto again;
140 }
141 return (0);
142 }
143
144 rtadd(dst, gate, metric, state)
145 struct sockaddr *dst, *gate;
146 int metric, state;
147 {
148 struct afhash h;
149 register struct rt_entry *rt;
150 struct rthash *rh;
151 int af = dst->sa_family, flags;
152 u_int hash;
153
154 if (af >= af_max)
155 return;
156 (*afswitch[af].af_hash)(dst, &h);
157 flags = (*afswitch[af].af_rtflags)(dst);
158 /*
159 * Subnet flag isn't visible to kernel, move to state. XXX
160 */
161 FIXLEN(dst);
162 FIXLEN(gate);
163 if (flags & RTF_SUBNET) {
164 state |= RTS_SUBNET;
165 flags &= ~RTF_SUBNET;
166 }
167 if (flags & RTF_HOST) {
168 hash = h.afh_hosthash;
169 rh = &hosthash[hash & ROUTEHASHMASK];
170 } else {
171 hash = h.afh_nethash;
172 rh = &nethash[hash & ROUTEHASHMASK];
173 }
174 rt = (struct rt_entry *)malloc(sizeof (*rt));
175 if (rt == 0)
176 return;
177 rt->rt_hash = hash;
178 rt->rt_dst = *dst;
179 rt->rt_router = *gate;
180 rt->rt_timer = 0;
181 rt->rt_flags = RTF_UP | flags;
182 rt->rt_state = state | RTS_CHANGED;
183 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst);
184 if (rt->rt_ifp == 0)
185 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
186 if ((state & RTS_INTERFACE) == 0)
187 rt->rt_flags |= RTF_GATEWAY;
188 rt->rt_metric = metric;
189 insque(rt, rh);
190 TRACE_ACTION("ADD", rt);
191 /*
192 * If the ioctl fails because the gateway is unreachable
193 * from this host, discard the entry. This should only
194 * occur because of an incorrect entry in /etc/gateways.
195 */
196 if (install && (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
197 ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) {
198 if (errno != EEXIST && gate->sa_family < af_max)
199 syslog(LOG_ERR,
200 "adding route to net/host %s through gateway %s: %m\n",
201 (*afswitch[dst->sa_family].af_format)(dst),
202 (*afswitch[gate->sa_family].af_format)(gate));
203 perror("SIOCADDRT");
204 if (errno == ENETUNREACH) {
205 TRACE_ACTION("DELETE", rt);
206 remque(rt);
207 free((char *)rt);
208 }
209 }
210 }
211
212 rtchange(rt, gate, metric)
213 struct rt_entry *rt;
214 struct sockaddr *gate;
215 short metric;
216 {
217 int add = 0, delete = 0, newgateway = 0;
218 struct rtentry oldroute;
219
220 FIXLEN(gate);
221 FIXLEN(&(rt->rt_router));
222 FIXLEN(&(rt->rt_dst));
223 if (!equal(&rt->rt_router, gate)) {
224 newgateway++;
225 TRACE_ACTION("CHANGE FROM ", rt);
226 } else if (metric != rt->rt_metric)
227 TRACE_NEWMETRIC(rt, metric);
228 if ((rt->rt_state & RTS_INTERNAL) == 0) {
229 /*
230 * If changing to different router, we need to add
231 * new route and delete old one if in the kernel.
232 * If the router is the same, we need to delete
233 * the route if has become unreachable, or re-add
234 * it if it had been unreachable.
235 */
236 if (newgateway) {
237 add++;
238 if (rt->rt_metric != HOPCNT_INFINITY)
239 delete++;
240 } else if (metric == HOPCNT_INFINITY)
241 delete++;
242 else if (rt->rt_metric == HOPCNT_INFINITY)
243 add++;
244 }
245 if (delete)
246 oldroute = rt->rt_rt;
247 if ((rt->rt_state & RTS_INTERFACE) && delete) {
248 rt->rt_state &= ~RTS_INTERFACE;
249 rt->rt_flags |= RTF_GATEWAY;
250 if (metric > rt->rt_metric && delete)
251 syslog(LOG_ERR, "%s route to interface %s (timed out)",
252 add? "changing" : "deleting",
253 rt->rt_ifp->int_name);
254 }
255 if (add) {
256 rt->rt_router = *gate;
257 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
258 if (rt->rt_ifp == 0)
259 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
260 }
261 rt->rt_metric = metric;
262 rt->rt_state |= RTS_CHANGED;
263 if (newgateway)
264 TRACE_ACTION("CHANGE TO ", rt);
265 #ifndef RTM_ADD
266 if (add && install)
267 if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
268 perror("SIOCADDRT");
269 if (delete && install)
270 if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
271 perror("SIOCDELRT");
272 #else
273 if (delete && install)
274 if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
275 perror("SIOCDELRT");
276 if (add && install) {
277 if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
278 perror("SIOCADDRT");
279 }
280 #endif
281 }
282
283 rtdelete(rt)
284 struct rt_entry *rt;
285 {
286
287 TRACE_ACTION("DELETE", rt);
288 FIXLEN(&(rt->rt_router));
289 FIXLEN(&(rt->rt_dst));
290 if (rt->rt_metric < HOPCNT_INFINITY) {
291 if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE)
292 syslog(LOG_ERR,
293 "deleting route to interface %s? (timed out?)",
294 rt->rt_ifp->int_name);
295 if (install &&
296 (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
297 ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
298 perror("SIOCDELRT");
299 }
300 remque(rt);
301 free((char *)rt);
302 }
303
rtdeleteall(sig)304 rtdeleteall(sig)
305 int sig;
306 {
307 register struct rthash *rh;
308 register struct rt_entry *rt;
309 struct rthash *base = hosthash;
310 int doinghost = 1;
311
312 again:
313 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
314 rt = rh->rt_forw;
315 for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
316 if (rt->rt_state & RTS_INTERFACE ||
317 rt->rt_metric >= HOPCNT_INFINITY)
318 continue;
319 TRACE_ACTION("DELETE", rt);
320 if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 &&
321 ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
322 perror("SIOCDELRT");
323 }
324 }
325 if (doinghost) {
326 doinghost = 0;
327 base = nethash;
328 goto again;
329 }
330 exit(sig);
331 }
332
333 /*
334 * If we have an interface to the wide, wide world,
335 * add an entry for an Internet default route (wildcard) to the internal
336 * tables and advertise it. This route is not added to the kernel routes,
337 * but this entry prevents us from listening to other people's defaults
338 * and installing them in the kernel here.
339 */
rtdefault()340 rtdefault()
341 {
342 extern struct sockaddr inet_default;
343
344 rtadd(&inet_default, &inet_default, 1,
345 RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
346 }
347
rtinit()348 rtinit()
349 {
350 register struct rthash *rh;
351
352 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
353 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
354 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
355 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
356 }
357
358
359 /* ffrom /sys/i386/i386/machdep.c */
360 /*
361 * insert an element into a queue
362 */
insque(element,head)363 insque(element, head)
364 register struct rthash *element, *head;
365 {
366 element->rt_forw = head->rt_forw;
367 head->rt_forw = (struct rt_entry *)element;
368 element->rt_back = (struct rt_entry *)head;
369 ((struct rthash *)(element->rt_forw))->rt_back=(struct rt_entry *)element;
370 }
371
372 /*
373 * remove an element from a queue
374 */
remque(element)375 remque(element)
376 register struct rthash *element;
377 {
378 ((struct rthash *)(element->rt_forw))->rt_back = element->rt_back;
379 ((struct rthash *)(element->rt_back))->rt_forw = element->rt_forw;
380 element->rt_back = (struct rt_entry *)0;
381 }
382