Annotation of XNU/bsd/netinet/in_rmx.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.