]> git.saurik.com Git - apple/network_cmds.git/blame - routed.tproj/tables.c
network_cmds-76.tar.gz
[apple/network_cmds.git] / routed.tproj / tables.c
CommitLineData
b7080c8e
A
1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24/*
25 * Copyright (c) 1983, 1988, 1993
26 * The Regents of the University of California. All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgment:
38 * This product includes software developed by the University of
39 * California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 *
56 * @(#)defs.h 8.1 (Berkeley) 6/5/93
57 */
58
59
60/*
61 * Routing Table Management Daemon
62 */
63#include "defs.h"
64#include <sys/ioctl.h>
65#include <errno.h>
66#include <sys/syslog.h>
67
68#ifndef DEBUG
69#define DEBUG 0
70#endif
71
72#ifdef RTM_ADD
73#define FIXLEN(s) {if ((s)->sa_len == 0) (s)->sa_len = sizeof *(s);}
74#else
75#define FIXLEN(s) { }
76#endif
77
78int install = !DEBUG; /* if 1 call kernel */
79
80/*
81 * Lookup dst in the tables for an exact match.
82 */
83struct rt_entry *
84rtlookup(dst)
85 struct sockaddr *dst;
86{
87 register struct rt_entry *rt;
88 register struct rthash *rh;
89 register u_int hash;
90 struct afhash h;
91 int doinghost = 1;
92
93 if (dst->sa_family >= af_max)
94 return (0);
95 (*afswitch[dst->sa_family].af_hash)(dst, &h);
96 hash = h.afh_hosthash;
97 rh = &hosthash[hash & ROUTEHASHMASK];
98again:
99 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
100 if (rt->rt_hash != hash)
101 continue;
102 if (equal(&rt->rt_dst, dst))
103 return (rt);
104 }
105 if (doinghost) {
106 doinghost = 0;
107 hash = h.afh_nethash;
108 rh = &nethash[hash & ROUTEHASHMASK];
109 goto again;
110 }
111 return (0);
112}
113
114struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
115
116/*
117 * Find a route to dst as the kernel would.
118 */
119struct rt_entry *
120rtfind(dst)
121 struct sockaddr *dst;
122{
123 register struct rt_entry *rt;
124 register struct rthash *rh;
125 register u_int hash;
126 struct afhash h;
127 int af = dst->sa_family;
128 int doinghost = 1, (*match)();
129
130 if (af >= af_max)
131 return (0);
132 (*afswitch[af].af_hash)(dst, &h);
133 hash = h.afh_hosthash;
134 rh = &hosthash[hash & ROUTEHASHMASK];
135
136again:
137 for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
138 if (rt->rt_hash != hash)
139 continue;
140 if (doinghost) {
141 if (equal(&rt->rt_dst, dst))
142 return (rt);
143 } else {
144 if (rt->rt_dst.sa_family == af &&
145 (*match)(&rt->rt_dst, dst))
146 return (rt);
147 }
148 }
149 if (doinghost) {
150 doinghost = 0;
151 hash = h.afh_nethash;
152 rh = &nethash[hash & ROUTEHASHMASK];
153 match = afswitch[af].af_netmatch;
154 goto again;
155 }
156#ifdef notyet
157 /*
158 * Check for wildcard gateway, by convention network 0.
159 */
160 if (dst != &wildcard) {
161 dst = &wildcard, hash = 0;
162 goto again;
163 }
164#endif
165 return (0);
166}
167
168rtadd(dst, gate, metric, state)
169 struct sockaddr *dst, *gate;
170 int metric, state;
171{
172 struct afhash h;
173 register struct rt_entry *rt;
174 struct rthash *rh;
175 int af = dst->sa_family, flags;
176 u_int hash;
177
178 if (af >= af_max)
179 return;
180 (*afswitch[af].af_hash)(dst, &h);
181 flags = (*afswitch[af].af_rtflags)(dst);
182 /*
183 * Subnet flag isn't visible to kernel, move to state. XXX
184 */
185 FIXLEN(dst);
186 FIXLEN(gate);
187 if (flags & RTF_SUBNET) {
188 state |= RTS_SUBNET;
189 flags &= ~RTF_SUBNET;
190 }
191 if (flags & RTF_HOST) {
192 hash = h.afh_hosthash;
193 rh = &hosthash[hash & ROUTEHASHMASK];
194 } else {
195 hash = h.afh_nethash;
196 rh = &nethash[hash & ROUTEHASHMASK];
197 }
198 rt = (struct rt_entry *)malloc(sizeof (*rt));
199 if (rt == 0)
200 return;
201 rt->rt_hash = hash;
202 rt->rt_dst = *dst;
203 rt->rt_router = *gate;
204 rt->rt_timer = 0;
205 rt->rt_flags = RTF_UP | flags;
206 rt->rt_state = state | RTS_CHANGED;
207 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst);
208 if (rt->rt_ifp == 0)
209 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
210 if ((state & RTS_INTERFACE) == 0)
211 rt->rt_flags |= RTF_GATEWAY;
212 rt->rt_metric = metric;
213 insque(rt, rh);
214 TRACE_ACTION("ADD", rt);
215 /*
216 * If the ioctl fails because the gateway is unreachable
217 * from this host, discard the entry. This should only
218 * occur because of an incorrect entry in /etc/gateways.
219 */
220 if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
221 rtioctl(ADD, &rt->rt_rt) < 0) {
222 if (errno != EEXIST && gate->sa_family < af_max)
223 syslog(LOG_ERR,
224 "adding route to net/host %s through gateway %s: %m\n",
225 (*afswitch[dst->sa_family].af_format)(dst),
226 (*afswitch[gate->sa_family].af_format)(gate));
227 perror("ADD ROUTE");
228 if (errno == ENETUNREACH) {
229 TRACE_ACTION("DELETE", rt);
230 remque(rt);
231 free((char *)rt);
232 }
233 }
234}
235
236rtchange(rt, gate, metric)
237 struct rt_entry *rt;
238 struct sockaddr *gate;
239 short metric;
240{
241 int add = 0, delete = 0, newgateway = 0;
242 struct rtuentry oldroute;
243
244 FIXLEN(gate);
245 FIXLEN(&(rt->rt_router));
246 FIXLEN(&(rt->rt_dst));
247 if (!equal(&rt->rt_router, gate)) {
248 newgateway++;
249 TRACE_ACTION("CHANGE FROM ", rt);
250 } else if (metric != rt->rt_metric)
251 TRACE_NEWMETRIC(rt, metric);
252 if ((rt->rt_state & RTS_INTERNAL) == 0) {
253 /*
254 * If changing to different router, we need to add
255 * new route and delete old one if in the kernel.
256 * If the router is the same, we need to delete
257 * the route if has become unreachable, or re-add
258 * it if it had been unreachable.
259 */
260 if (newgateway) {
261 add++;
262 if (rt->rt_metric != HOPCNT_INFINITY)
263 delete++;
264 } else if (metric == HOPCNT_INFINITY)
265 delete++;
266 else if (rt->rt_metric == HOPCNT_INFINITY)
267 add++;
268 }
269 if (delete)
270 oldroute = rt->rt_rt;
271 if ((rt->rt_state & RTS_INTERFACE) && delete) {
272 rt->rt_state &= ~RTS_INTERFACE;
273 rt->rt_flags |= RTF_GATEWAY;
274 if (metric > rt->rt_metric && delete)
275 syslog(LOG_ERR, "%s route to interface %s (timed out)",
276 add? "changing" : "deleting",
277 rt->rt_ifp ? rt->rt_ifp->int_name : "?");
278 }
279 if (add) {
280 rt->rt_router = *gate;
281 rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
282 if (rt->rt_ifp == 0)
283 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
284 }
285 rt->rt_metric = metric;
286 rt->rt_state |= RTS_CHANGED;
287 if (newgateway)
288 TRACE_ACTION("CHANGE TO ", rt);
289#ifndef RTM_ADD
290 if (add && rtioctl(ADD, &rt->rt_rt) < 0)
291 perror("ADD ROUTE");
292 if (delete && rtioctl(DELETE, &oldroute) < 0)
293 perror("DELETE ROUTE");
294#else
295 if (delete && !add) {
296 if (rtioctl(DELETE, &oldroute) < 0)
297 perror("DELETE ROUTE");
298 } else if (!delete && add) {
299 if (rtioctl(ADD, &rt->rt_rt) < 0)
300 perror("ADD ROUTE");
301 } else if (delete && add) {
302 if (rtioctl(CHANGE, &rt->rt_rt) < 0)
303 perror("CHANGE ROUTE");
304 }
305#endif
306}
307
308rtdelete(rt)
309 struct rt_entry *rt;
310{
311
312 TRACE_ACTION("DELETE", rt);
313 FIXLEN(&(rt->rt_router));
314 FIXLEN(&(rt->rt_dst));
315 if (rt->rt_metric < HOPCNT_INFINITY) {
316 if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE)
317 syslog(LOG_ERR,
318 "deleting route to interface %s? (timed out?)",
319 rt->rt_ifp->int_name);
320 if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
321 rtioctl(DELETE, &rt->rt_rt) < 0)
322 perror("rtdelete");
323 }
324 remque(rt);
325 free((char *)rt);
326}
327
328rtdeleteall(sig)
329 int sig;
330{
331 register struct rthash *rh;
332 register struct rt_entry *rt;
333 struct rthash *base = hosthash;
334 int doinghost = 1;
335
336again:
337 for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
338 rt = rh->rt_forw;
339 for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
340 if (rt->rt_state & RTS_INTERFACE ||
341 rt->rt_metric >= HOPCNT_INFINITY)
342 continue;
343 TRACE_ACTION("DELETE", rt);
344 if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 &&
345 rtioctl(DELETE, &rt->rt_rt) < 0)
346 perror("rtdeleteall");
347 }
348 }
349 if (doinghost) {
350 doinghost = 0;
351 base = nethash;
352 goto again;
353 }
354 exit(sig);
355}
356
357/*
358 * If we have an interface to the wide, wide world,
359 * add an entry for an Internet default route (wildcard) to the internal
360 * tables and advertise it. This route is not added to the kernel routes,
361 * but this entry prevents us from listening to other people's defaults
362 * and installing them in the kernel here.
363 */
364rtdefault()
365{
366 extern struct sockaddr inet_default;
367
368 rtadd(&inet_default, &inet_default, 1,
369 RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
370}
371
372rtinit()
373{
374 register struct rthash *rh;
375
376 for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
377 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
378 for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
379 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
380}
381
382rtioctl(action, ort)
383 int action;
384 struct rtuentry *ort;
385{
386#ifndef RTM_ADD
387 if (install == 0)
388 return (errno = 0);
389 ort->rtu_rtflags = ort->rtu_flags;
390 switch (action) {
391
392 case ADD:
393 return (ioctl(s, SIOCADDRT, (char *)ort));
394
395 case DELETE:
396 return (ioctl(s, SIOCDELRT, (char *)ort));
397
398 default:
399 return (-1);
400 }
401#else /* RTM_ADD */
402 struct {
403 struct rt_msghdr w_rtm;
404 struct sockaddr_in w_dst;
405 struct sockaddr w_gate;
406 struct sockaddr_in w_netmask;
407 } w;
408#define rtm w.w_rtm
409
410 memset(&w, 0, sizeof(w));
411 rtm.rtm_msglen = sizeof(w);
412 rtm.rtm_version = RTM_VERSION;
413 rtm.rtm_type = (action == ADD ? RTM_ADD :
414 (action == DELETE ? RTM_DELETE : RTM_CHANGE));
415#undef rt_dst
416 rtm.rtm_flags = ort->rtu_flags;
417 rtm.rtm_seq = ++seqno;
418 rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
419 memmove(&w.w_dst, &ort->rtu_dst, sizeof(w.w_dst));
420 memmove(&w.w_gate, &ort->rtu_router, sizeof(w.w_gate));
421 w.w_dst.sin_family = AF_INET;
422 w.w_dst.sin_len = sizeof(w.w_dst);
423 w.w_gate.sa_family = AF_INET;
424 w.w_gate.sa_len = sizeof(w.w_gate);
425 if (rtm.rtm_flags & RTF_HOST) {
426 rtm.rtm_msglen -= sizeof(w.w_netmask);
427 } else {
428 register char *cp;
429 int len;
430
431 rtm.rtm_addrs |= RTA_NETMASK;
432 w.w_netmask.sin_addr.s_addr =
433 inet_maskof(w.w_dst.sin_addr.s_addr);
434 for (cp = (char *)(1 + &w.w_netmask.sin_addr);
435 --cp > (char *) &w.w_netmask; )
436 if (*cp)
437 break;
438 len = cp - (char *)&w.w_netmask;
439 if (len) {
440 len++;
441 w.w_netmask.sin_len = len;
442 len = 1 + ((len - 1) | (sizeof(long) - 1));
443 } else
444 len = sizeof(long);
445 rtm.rtm_msglen -= (sizeof(w.w_netmask) - len);
446 }
447 errno = 0;
448 return (install ? write(r, (char *)&w, rtm.rtm_msglen) : (errno = 0));
449#endif /* RTM_ADD */
450}