Annotation of XNU/bsd/netinet/in_rmx.c, revision 1.1.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.