|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * Copyright 1994, 1995 Massachusetts Institute of Technology
24: *
25: * Permission to use, copy, modify, and distribute this software and
26: * its documentation for any purpose and without fee is hereby
27: * granted, provided that both the above copyright notice and this
28: * permission notice appear in all copies, that both the above
29: * copyright notice and this permission notice appear in all
30: * supporting documentation, and that the name of M.I.T. not be used
31: * in advertising or publicity pertaining to distribution of the
32: * software without specific, written prior permission. M.I.T. makes
33: * no representations about the suitability of this software for any
34: * purpose. It is provided "as is" without express or implied
35: * warranty.
36: *
37: * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
38: * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
39: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
41: * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48: * SUCH DAMAGE.
49: *
50: */
51:
52: /*
53: * This code does two things necessary for the enhanced TCP metrics to
54: * function in a useful manner:
55: * 1) It marks all non-host routes as `cloning', thus ensuring that
56: * every actual reference to such a route actually gets turned
57: * into a reference to a host route to the specific destination
58: * requested.
59: * 2) When such routes lose all their references, it arranges for them
60: * to be deleted in some random collection of circumstances, so that
61: * a large quantity of stale routing data is not kept in kernel memory
62: * indefinitely. See in_rtqtimo() below for the exact mechanism.
63: */
64:
65: #include <sys/param.h>
66: #include <sys/systm.h>
67: #include <sys/kernel.h>
68: #include <sys/sysctl.h>
69: #include <sys/socket.h>
70: #include <sys/mbuf.h>
71: #include <sys/syslog.h>
72:
73: #include <net/if.h>
74: #include <net/route.h>
75: #include <netinet/in.h>
76: #include <netinet/in_var.h>
77:
78: extern int in_inithead __P((void **head, int off));
79:
80: #define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */
81:
82: /*
83: * Do what we need to do when inserting a route.
84: */
85: static struct radix_node *
86: in_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
87: struct radix_node *treenodes)
88: {
89: struct rtentry *rt = (struct rtentry *)treenodes;
90: struct sockaddr_in *sin = (struct sockaddr_in *)rt_key(rt);
91: struct radix_node *ret;
92:
93: /*
94: * For IP, all unicast non-host routes are automatically cloning.
95: */
96: if(IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
97: rt->rt_flags |= RTF_MULTICAST;
98:
99: if(!(rt->rt_flags & (RTF_HOST | RTF_CLONING | RTF_MULTICAST))) {
100: rt->rt_flags |= RTF_PRCLONING;
101: }
102:
103: /*
104: * A little bit of help for both IP output and input:
105: * For host routes, we make sure that RTF_BROADCAST
106: * is set for anything that looks like a broadcast address.
107: * This way, we can avoid an expensive call to in_broadcast()
108: * in ip_output() most of the time (because the route passed
109: * to ip_output() is almost always a host route).
110: *
111: * We also do the same for local addresses, with the thought
112: * that this might one day be used to speed up ip_input().
113: *
114: * We also mark routes to multicast addresses as such, because
115: * it's easy to do and might be useful (but this is much more
116: * dubious since it's so easy to inspect the address). (This
117: * is done above.)
118: */
119: if (rt->rt_flags & RTF_HOST) {
120: if (in_broadcast(sin->sin_addr, rt->rt_ifp)) {
121: rt->rt_flags |= RTF_BROADCAST;
122: } else {
123: #define satosin(sa) ((struct sockaddr_in *)sa)
124: if (satosin(rt->rt_ifa->ifa_addr)->sin_addr.s_addr
125: == sin->sin_addr.s_addr)
126: rt->rt_flags |= RTF_LOCAL;
127: #undef satosin
128: }
129: }
130:
131: if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU)
132: && rt->rt_ifp)
133: rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
134:
135: ret = rn_addroute(v_arg, n_arg, head, treenodes);
136: if (ret == NULL && rt->rt_flags & RTF_HOST) {
137: struct rtentry *rt2;
138: /*
139: * We are trying to add a host route, but can't.
140: * Find out if it is because of an
141: * ARP entry and delete it if so.
142: */
143: rt2 = rtalloc1((struct sockaddr *)sin, 0,
144: RTF_CLONING | RTF_PRCLONING);
145: if (rt2) {
146: if (rt2->rt_flags & RTF_LLINFO &&
147: rt2->rt_flags & RTF_HOST &&
148: rt2->rt_gateway &&
149: rt2->rt_gateway->sa_family == AF_LINK) {
150: rtrequest(RTM_DELETE,
151: (struct sockaddr *)rt_key(rt2),
152: rt2->rt_gateway,
153: rt_mask(rt2), rt2->rt_flags, 0);
154: ret = rn_addroute(v_arg, n_arg, head,
155: treenodes);
156: }
157: RTFREE(rt2);
158: }
159: }
160: return ret;
161: }
162:
163: /*
164: * This code is the inverse of in_clsroute: on first reference, if we
165: * were managing the route, stop doing so and set the expiration timer
166: * back off again.
167: */
168: static struct radix_node *
169: in_matroute(void *v_arg, struct radix_node_head *head)
170: {
171: struct radix_node *rn = rn_match(v_arg, head);
172: struct rtentry *rt = (struct rtentry *)rn;
173:
174: if(rt && rt->rt_refcnt == 0) { /* this is first reference */
175: if(rt->rt_flags & RTPRF_OURS) {
176: rt->rt_flags &= ~RTPRF_OURS;
177: rt->rt_rmx.rmx_expire = 0;
178: }
179: }
180: return rn;
181: }
182:
183: static int rtq_reallyold = 60*60;
184: /* one hour is ``really old'' */
185: SYSCTL_INT(_net_inet_ip, IPCTL_RTEXPIRE, rtexpire,
186: CTLFLAG_RW, &rtq_reallyold , 0, "");
187:
188: static int rtq_minreallyold = 10;
189: /* never automatically crank down to less */
190: SYSCTL_INT(_net_inet_ip, IPCTL_RTMINEXPIRE, rtminexpire,
191: CTLFLAG_RW, &rtq_minreallyold , 0, "");
192:
193: static int rtq_toomany = 128;
194: /* 128 cached routes is ``too many'' */
195: SYSCTL_INT(_net_inet_ip, IPCTL_RTMAXCACHE, rtmaxcache,
196: CTLFLAG_RW, &rtq_toomany , 0, "");
197:
198:
199: /*
200: * On last reference drop, mark the route as belong to us so that it can be
201: * timed out.
202: */
203: static void
204: in_clsroute(struct radix_node *rn, struct radix_node_head *head)
205: {
206: struct rtentry *rt = (struct rtentry *)rn;
207:
208: if(!(rt->rt_flags & RTF_UP))
209: return; /* prophylactic measures */
210:
211: if((rt->rt_flags & (RTF_LLINFO | RTF_HOST)) != RTF_HOST)
212: return;
213:
214: if((rt->rt_flags & (RTF_WASCLONED | RTPRF_OURS))
215: != RTF_WASCLONED)
216: return;
217:
218: /*
219: * As requested by David Greenman:
220: * If rtq_reallyold is 0, just delete the route without
221: * waiting for a timeout cycle to kill it.
222: */
223: if(rtq_reallyold != 0) {
224: rt->rt_flags |= RTPRF_OURS;
225: rt->rt_rmx.rmx_expire = time_second + rtq_reallyold;
226: } else {
227: rtrequest(RTM_DELETE,
228: (struct sockaddr *)rt_key(rt),
229: rt->rt_gateway, rt_mask(rt),
230: rt->rt_flags, 0);
231: }
232: }
233:
234: struct rtqk_arg {
235: struct radix_node_head *rnh;
236: int draining;
237: int killed;
238: int found;
239: int updating;
240: time_t nextstop;
241: };
242:
243: /*
244: * Get rid of old routes. When draining, this deletes everything, even when
245: * the timeout is not expired yet. When updating, this makes sure that
246: * nothing has a timeout longer than the current value of rtq_reallyold.
247: */
248: static int
249: in_rtqkill(struct radix_node *rn, void *rock)
250: {
251: struct rtqk_arg *ap = rock;
252: struct rtentry *rt = (struct rtentry *)rn;
253: int err;
254:
255: if(rt->rt_flags & RTPRF_OURS) {
256: ap->found++;
257:
258: if(ap->draining || rt->rt_rmx.rmx_expire <= time_second) {
259: if(rt->rt_refcnt > 0)
260: panic("rtqkill route really not free");
261:
262: err = rtrequest(RTM_DELETE,
263: (struct sockaddr *)rt_key(rt),
264: rt->rt_gateway, rt_mask(rt),
265: rt->rt_flags, 0);
266: if(err) {
267: log(LOG_WARNING, "in_rtqkill: error %d\n", err);
268: } else {
269: ap->killed++;
270: }
271: } else {
272: if(ap->updating
273: && (rt->rt_rmx.rmx_expire - time_second
274: > rtq_reallyold)) {
275: rt->rt_rmx.rmx_expire = time_second
276: + rtq_reallyold;
277: }
278: ap->nextstop = lmin(ap->nextstop,
279: rt->rt_rmx.rmx_expire);
280: }
281: }
282:
283: return 0;
284: }
285:
286: #define RTQ_TIMEOUT 60*10 /* run no less than once every ten minutes */
287: static int rtq_timeout = RTQ_TIMEOUT;
288:
289: static void
290: in_rtqtimo(void *rock)
291: {
292: struct radix_node_head *rnh = rock;
293: struct rtqk_arg arg;
294: struct timeval atv;
295: static time_t last_adjusted_timeout = 0;
296: int s;
297:
298: arg.found = arg.killed = 0;
299: arg.rnh = rnh;
300: arg.nextstop = time_second + rtq_timeout;
301: arg.draining = arg.updating = 0;
302: s = splnet();
303: rnh->rnh_walktree(rnh, in_rtqkill, &arg);
304: splx(s);
305:
306: /*
307: * Attempt to be somewhat dynamic about this:
308: * If there are ``too many'' routes sitting around taking up space,
309: * then crank down the timeout, and see if we can't make some more
310: * go away. However, we make sure that we will never adjust more
311: * than once in rtq_timeout seconds, to keep from cranking down too
312: * hard.
313: */
314: if((arg.found - arg.killed > rtq_toomany)
315: && (time_second - last_adjusted_timeout >= rtq_timeout)
316: && rtq_reallyold > rtq_minreallyold) {
317: rtq_reallyold = 2*rtq_reallyold / 3;
318: if(rtq_reallyold < rtq_minreallyold) {
319: rtq_reallyold = rtq_minreallyold;
320: }
321:
322: last_adjusted_timeout = time_second;
323: #if DIAGNOSTIC
324: log(LOG_DEBUG, "in_rtqtimo: adjusted rtq_reallyold to %d\n",
325: rtq_reallyold);
326: #endif
327: arg.found = arg.killed = 0;
328: arg.updating = 1;
329: s = splnet();
330: rnh->rnh_walktree(rnh, in_rtqkill, &arg);
331: splx(s);
332: }
333:
334: atv.tv_usec = 0;
335: atv.tv_sec = arg.nextstop - time_second;
336: timeout(in_rtqtimo, rock, tvtohz(&atv));
337: }
338:
339: void
340: in_rtqdrain(void)
341: {
342: struct radix_node_head *rnh = rt_tables[AF_INET];
343: struct rtqk_arg arg;
344: int s;
345: arg.found = arg.killed = 0;
346: arg.rnh = rnh;
347: arg.nextstop = 0;
348: arg.draining = 1;
349: arg.updating = 0;
350: s = splnet();
351: rnh->rnh_walktree(rnh, in_rtqkill, &arg);
352: splx(s);
353: }
354:
355: /*
356: * Initialize our routing tree.
357: */
358: int
359: in_inithead(void **head, int off)
360: {
361: struct radix_node_head *rnh;
362:
363: if (*head)
364: return 1;
365:
366: if(!rn_inithead(head, off))
367: return 0;
368:
369: if(head != (void **)&rt_tables[AF_INET]) /* BOGUS! */
370: return 1; /* only do this for the real routing table */
371:
372: rnh = *head;
373: rnh->rnh_addaddr = in_addroute;
374: rnh->rnh_matchaddr = in_matroute;
375: rnh->rnh_close = in_clsroute;
376: in_rtqtimo(rnh); /* kick off timeout first time */
377: return 1;
378: }
379:
380:
381: /*
382: * This zaps old routes when the interface goes down.
383: * Currently it doesn't delete static routes; there are
384: * arguments one could make for both behaviors. For the moment,
385: * we will adopt the Principle of Least Surprise and leave them
386: * alone (with the knowledge that this will not be enough for some
387: * people). The ones we really want to get rid of are things like ARP
388: * entries, since the user might down the interface, walk over to a completely
389: * different network, and plug back in.
390: */
391: struct in_ifadown_arg {
392: struct radix_node_head *rnh;
393: struct ifaddr *ifa;
394: };
395:
396: static int
397: in_ifadownkill(struct radix_node *rn, void *xap)
398: {
399: struct in_ifadown_arg *ap = xap;
400: struct rtentry *rt = (struct rtentry *)rn;
401: int err;
402:
403: if (rt->rt_ifa == ap->ifa && !(rt->rt_flags & RTF_STATIC)) {
404: /*
405: * We need to disable the automatic prune that happens
406: * in this case in rtrequest() because it will blow
407: * away the pointers that rn_walktree() needs in order
408: * continue our descent. We will end up deleting all
409: * the routes that rtrequest() would have in any case,
410: * so that behavior is not needed there.
411: */
412: rt->rt_flags &= ~RTF_PRCLONING;
413: err = rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt),
414: rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
415: if (err) {
416: log(LOG_WARNING, "in_ifadownkill: error %d\n", err);
417: }
418: }
419: return 0;
420: }
421:
422: int
423: in_ifadown(struct ifaddr *ifa)
424: {
425: struct in_ifadown_arg arg;
426: struct radix_node_head *rnh;
427:
428: if (ifa->ifa_addr->sa_family != AF_INET)
429: return 1;
430:
431: arg.rnh = rnh = rt_tables[AF_INET];
432: arg.ifa = ifa;
433: rnh->rnh_walktree(rnh, in_ifadownkill, &arg);
434: ifa->ifa_flags &= ~IFA_ROUTE;
435: return 0;
436: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.