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