Patch from Michael Galassi (nerd@percy.rain.com):
[unix-history] / usr.sbin / routed / tables.c
CommitLineData
15637ed4
RG
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
35static 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
56int install = !DEBUG; /* if 1 call kernel */
57
58/*
59 * Lookup dst in the tables for an exact match.
60 */
61struct rt_entry *
62rtlookup(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];
76again:
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
92struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
93
94/*
95 * Find a route to dst as the kernel would.
96 */
97struct rt_entry *
98rtfind(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
114again:
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#ifdef notyet
135 /*
136 * Check for wildcard gateway, by convention network 0.
137 */
138 if (dst != &wildcard) {
139 dst = &wildcard, hash = 0;
140 goto again;
141 }
142#endif
143 return (0);
144}
145
146rtadd(dst, gate, metric, state)
147 struct sockaddr *dst, *gate;
148 int metric, state;
149{
150 struct afhash h;
151 register struct rt_entry *rt;
152 struct rthash *rh;
153 int af = dst->sa_family, flags;
154 u_int hash;
155
156 if (af >= af_max)
157 return;
158 (*afswitch[af].af_hash)(dst, &h);
159 flags = (*afswitch[af].af_rtflags)(dst);
160 /*
161 * Subnet flag isn't visible to kernel, move to state. XXX
162 */
163 FIXLEN(dst);
164 FIXLEN(gate);
165 if (flags & RTF_SUBNET) {
166 state |= RTS_SUBNET;
167 flags &= ~RTF_SUBNET;
168 }
169 if (flags & RTF_HOST) {
170 hash = h.afh_hosthash;
171 rh = &hosthash[hash & ROUTEHASHMASK];
172 } else {
173 hash = h.afh_nethash;
174 rh = &nethash[hash & ROUTEHASHMASK];
175 }
176 rt = (struct rt_entry *)malloc(sizeof (*rt));
177 if (rt == 0)
178 return;
179 rt->rt_hash = hash;
180 rt->rt_dst = *dst;
181 rt->rt_router = *gate;
182 rt->rt_timer = 0;
183 rt->rt_flags = RTF_UP | flags;
184 rt->rt_state = state | RTS_CHANGED;
185 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst);
57742e6e 186 if (rt->rt_ifp == 0) {
15637ed4 187 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
57742e6e
DG
188 /*
189 * seems like we can't figure out the interface for the
190 * IP address of the local side of a point to point
191 * connection, we just don't add that entry in the
192 * table. (it seems to already be there anyway)
193 */
194 if (rt->rt_ifp == 0) {
195 syslog(LOG_DEBUG,
196 "rtadd: can't get interface for %s",
197 (*afswitch[dst->sa_family].af_format)(dst));
198 return;
199 }
200 }
15637ed4
RG
201 if ((state & RTS_INTERFACE) == 0)
202 rt->rt_flags |= RTF_GATEWAY;
203 rt->rt_metric = metric;
204 insque(rt, rh);
205 TRACE_ACTION("ADD", rt);
206 /*
207 * If the ioctl fails because the gateway is unreachable
208 * from this host, discard the entry. This should only
209 * occur because of an incorrect entry in /etc/gateways.
210 */
211 if (install && (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
212 ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) {
213 if (errno != EEXIST && gate->sa_family < af_max)
214 syslog(LOG_ERR,
215 "adding route to net/host %s through gateway %s: %m\n",
216 (*afswitch[dst->sa_family].af_format)(dst),
217 (*afswitch[gate->sa_family].af_format)(gate));
218 perror("SIOCADDRT");
219 if (errno == ENETUNREACH) {
220 TRACE_ACTION("DELETE", rt);
221 remque(rt);
222 free((char *)rt);
223 }
224 }
225}
226
227rtchange(rt, gate, metric)
228 struct rt_entry *rt;
229 struct sockaddr *gate;
230 short metric;
231{
232 int add = 0, delete = 0, newgateway = 0;
233 struct rtentry oldroute;
234
235 FIXLEN(gate);
236 FIXLEN(&(rt->rt_router));
237 FIXLEN(&(rt->rt_dst));
238 if (!equal(&rt->rt_router, gate)) {
239 newgateway++;
240 TRACE_ACTION("CHANGE FROM ", rt);
241 } else if (metric != rt->rt_metric)
242 TRACE_NEWMETRIC(rt, metric);
243 if ((rt->rt_state & RTS_INTERNAL) == 0) {
244 /*
245 * If changing to different router, we need to add
246 * new route and delete old one if in the kernel.
247 * If the router is the same, we need to delete
248 * the route if has become unreachable, or re-add
249 * it if it had been unreachable.
250 */
251 if (newgateway) {
252 add++;
253 if (rt->rt_metric != HOPCNT_INFINITY)
254 delete++;
255 } else if (metric == HOPCNT_INFINITY)
256 delete++;
257 else if (rt->rt_metric == HOPCNT_INFINITY)
258 add++;
259 }
260 if (delete)
261 oldroute = rt->rt_rt;
262 if ((rt->rt_state & RTS_INTERFACE) && delete) {
263 rt->rt_state &= ~RTS_INTERFACE;
264 rt->rt_flags |= RTF_GATEWAY;
265 if (metric > rt->rt_metric && delete)
266 syslog(LOG_ERR, "%s route to interface %s (timed out)",
267 add? "changing" : "deleting",
268 rt->rt_ifp->int_name);
269 }
270 if (add) {
271 rt->rt_router = *gate;
272 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
57742e6e 273 if (rt->rt_ifp == 0) {
15637ed4 274 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
57742e6e
DG
275 /*
276 * seems like we can't figure out the interface for the
277 * IP address of the local side of a point to point
278 * connection, we just don't add that entry in the
279 * table. (it seems to already be there anyway)
280 */
281 if (rt->rt_ifp == 0) {
282 struct sockaddr *dst = &(rt->rt_dst);
283 syslog(LOG_DEBUG,
284 "rtchange: can't get interface for %s",
285 (*afswitch[dst->sa_family].af_format)(dst));
286 return;
287 }
288 }
15637ed4
RG
289 }
290 rt->rt_metric = metric;
291 rt->rt_state |= RTS_CHANGED;
292 if (newgateway)
293 TRACE_ACTION("CHANGE TO ", rt);
294#ifndef RTM_ADD
295 if (add && install)
296 if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
297 perror("SIOCADDRT");
298 if (delete && install)
299 if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
300 perror("SIOCDELRT");
301#else
302 if (delete && install)
303 if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
304 perror("SIOCDELRT");
305 if (add && install) {
306 if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
307 perror("SIOCADDRT");
308 }
309#endif
310}
311
312rtdelete(rt)
313 struct rt_entry *rt;
314{
315
316 TRACE_ACTION("DELETE", rt);
317 FIXLEN(&(rt->rt_router));
318 FIXLEN(&(rt->rt_dst));
319 if (rt->rt_metric < HOPCNT_INFINITY) {
320 if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE)
321 syslog(LOG_ERR,
322 "deleting route to interface %s? (timed out?)",
323 rt->rt_ifp->int_name);
324 if (install &&
325 (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
326 ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
327 perror("SIOCDELRT");
328 }
329 remque(rt);
330 free((char *)rt);
331}
332
333rtdeleteall(sig)
334 int sig;
335{
336 register struct rthash *rh;
337 register struct rt_entry *rt;
338 struct rthash *base = hosthash;
339 int doinghost = 1;
340
341again:
342 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
343 rt = rh->rt_forw;
344 for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
345 if (rt->rt_state & RTS_INTERFACE ||
346 rt->rt_metric >= HOPCNT_INFINITY)
347 continue;
348 TRACE_ACTION("DELETE", rt);
349 if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 &&
350 ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
351 perror("SIOCDELRT");
352 }
353 }
354 if (doinghost) {
355 doinghost = 0;
356 base = nethash;
357 goto again;
358 }
359 exit(sig);
360}
361
362/*
363 * If we have an interface to the wide, wide world,
364 * add an entry for an Internet default route (wildcard) to the internal
365 * tables and advertise it. This route is not added to the kernel routes,
366 * but this entry prevents us from listening to other people's defaults
367 * and installing them in the kernel here.
368 */
369rtdefault()
370{
371 extern struct sockaddr inet_default;
372
373 rtadd(&inet_default, &inet_default, 1,
374 RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
375}
376
377rtinit()
378{
379 register struct rthash *rh;
380
381 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
382 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
383 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
384 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
385}
386
387
388/* ffrom /sys/i386/i386/machdep.c */
389/*
390 * insert an element into a queue
391 */
392insque(element, head)
393 register struct rthash *element, *head;
394{
395 element->rt_forw = head->rt_forw;
396 head->rt_forw = (struct rt_entry *)element;
397 element->rt_back = (struct rt_entry *)head;
398 ((struct rthash *)(element->rt_forw))->rt_back=(struct rt_entry *)element;
399}
400
401/*
402 * remove an element from a queue
403 */
404remque(element)
405 register struct rthash *element;
406{
407 ((struct rthash *)(element->rt_forw))->rt_back = element->rt_back;
408 ((struct rthash *)(element->rt_back))->rt_forw = element->rt_forw;
409 element->rt_back = (struct rt_entry *)0;
410}