source: rtems/cpukit/libnetworking/net/route.c @ 39bad7e8

4.104.115
Last change on this file since 39bad7e8 was 42e10fa2, checked in by Ralf Corsepius <ralf.corsepius@…>, on 09/01/08 at 04:56:33

Stop using old-style function definitions.

  • Property mode set to 100644
File size: 24.8 KB
Line 
1/*
2 * Copyright (c) 1980, 1986, 1991, 1993
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *      @(#)route.c     8.2 (Berkeley) 11/15/93
30 *      $Id$
31 */
32
33#include "opt_mrouting.h"
34
35#include <sys/param.h>
36#include <rtems/bsd/sys/queue.h>
37#include <sys/systm.h>
38#include <sys/kernel.h>
39#include <sys/proc.h>
40#include <sys/mbuf.h>
41#include <sys/socket.h>
42#include <sys/socketvar.h>
43#include <sys/domain.h>
44#include <sys/protosw.h>
45#include <sys/ioctl.h>
46
47#include <net/if.h>
48#include <net/route.h>
49#include <net/raw_cb.h>
50
51#include <netinet/in.h>
52#include <netinet/in_var.h>
53#include <netinet/ip_mroute.h>
54
55#define SA(p) ((struct sockaddr *)(p))
56
57struct route_cb route_cb;
58static struct rtstat rtstat;
59struct radix_node_head *rt_tables[AF_MAX+1];
60
61static int      rttrash;                /* routes not in table but not freed */
62
63static void rt_maskedcopy(struct sockaddr *,
64            struct sockaddr *, struct sockaddr *);
65static void rtable_init(struct radix_node_head **);
66
67/* compare two sockaddr structures */
68#define sa_equal(a1, a2) (bcmp((a1), (a2), (a1)->sa_len) == 0)
69
70static void
71rtable_init(struct radix_node_head **table)
72{
73        struct domain *dom;
74        for (dom = domains; dom; dom = dom->dom_next)
75                if (dom->dom_rtattach)
76                        dom->dom_rtattach((void *)&table[dom->dom_family],
77                            dom->dom_rtoffset);
78}
79
80void
81route_init(void)
82{
83        rn_init();      /* initialize all zeroes, all ones, mask table */
84        rtable_init(rt_tables);
85}
86
87/*
88 * Packet routing routines.
89 */
90void
91rtalloc(struct route *ro)
92{
93        if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
94                return;                          /* XXX */
95        ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL);
96}
97
98void
99rtalloc_ign(struct route *ro, u_long ignore)
100{
101        if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
102                return;                          /* XXX */
103        ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore);
104}
105
106/*
107 * Look up the route that matches the address given
108 * Or, at least try.. Create a cloned route if needed.
109 */
110struct rtentry *
111rtalloc1(struct sockaddr *dst, int report, u_long ignflags)
112{
113        struct radix_node_head *rnh = rt_tables[dst->sa_family];
114        struct rtentry *rt;
115        struct radix_node *rn;
116        struct rtentry *newrt;
117        struct rt_addrinfo info;
118        u_long nflags;
119        int  s = splnet();
120        int err = 0, msgtype = RTM_MISS;
121
122        newrt = NULL;
123        /*
124         * Look up the address in the table for that Address Family
125         */
126        if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) &&
127            ((rn->rn_flags & RNF_ROOT) == 0)) {
128                /*
129                 * If we find it and it's not the root node, then
130                 * get a refernce on the rtentry associated.
131                 */
132                newrt = rt = (struct rtentry *)rn;
133                nflags = rt->rt_flags & ~ignflags;
134                if (report && (nflags & (RTF_CLONING | RTF_PRCLONING))) {
135                        /*
136                         * We are apparently adding (report = 0 in delete).
137                         * If it requires that it be cloned, do so.
138                         * (This implies it wasn't a HOST route.)
139                         */
140                        err = rtrequest(RTM_RESOLVE, dst, SA(0),
141                                              SA(0), 0, &newrt);
142                        if (err) {
143                                /*
144                                 * If the cloning didn't succeed, maybe
145                                 * what we have will do. Return that.
146                                 */
147                                newrt = rt;
148                                rt->rt_refcnt++;
149                                goto miss;
150                        }
151                        if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) {
152                                /*
153                                 * If the new route specifies it be
154                                 * externally resolved, then go do that.
155                                 */
156                                msgtype = RTM_RESOLVE;
157                                goto miss;
158                        }
159                } else
160                        rt->rt_refcnt++;
161        } else {
162                /*
163                 * Either we hit the root or couldn't find any match,
164                 * Which basically means
165                 * "caint get there frm here"
166                 */
167                rtstat.rts_unreach++;
168        miss:   if (report) {
169                        /*
170                         * If required, report the failure to the supervising
171                         * Authorities.
172                         * For a delete, this is not an error. (report == 0)
173                         */
174                        bzero(&info, sizeof(info));
175                        info.rti_info[RTAX_DST] = dst;
176                        rt_missmsg(msgtype, &info, 0, err);
177                }
178        }
179        splx(s);
180        return (newrt);
181}
182
183/*
184 * Remove a reference count from an rtentry.
185 * If the count gets low enough, take it out of the routing table
186 */
187void
188rtfree(struct rtentry *rt)
189{
190        register struct radix_node_head *rnh =
191                rt_tables[rt_key(rt)->sa_family];
192        register struct ifaddr *ifa;
193
194        if (rt == 0 || rnh == 0)
195                panic("rtfree");
196        rt->rt_refcnt--;
197        if(rnh->rnh_close && rt->rt_refcnt == 0) {
198                rnh->rnh_close((struct radix_node *)rt, rnh);
199        }
200        if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) {
201                if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT))
202                        panic ("rtfree 2");
203                rttrash--;
204                if (rt->rt_refcnt < 0) {
205                        printf("rtfree: %p not freed (neg refs)\n", rt);
206                        return;
207                }
208                ifa = rt->rt_ifa;
209                IFAFREE(ifa);
210                if (rt->rt_parent) {
211                        RTFREE(rt->rt_parent);
212                }
213                Free(rt_key(rt));
214                Free(rt);
215        }
216}
217
218void
219ifafree(struct ifaddr *ifa)
220{
221        if (ifa == NULL)
222                panic("ifafree");
223        if (ifa->ifa_refcnt == 0)
224                free(ifa, M_IFADDR);
225        else
226                ifa->ifa_refcnt--;
227}
228
229/*
230 * Force a routing table entry to the specified
231 * destination to go through the given gateway.
232 * Normally called as a result of a routing redirect
233 * message from the network layer.
234 *
235 * N.B.: must be called at splnet
236 *
237 */
238void
239rtredirect(struct sockaddr *dst, struct sockaddr *gateway,
240    struct sockaddr *netmask, int flags, struct sockaddr *src,
241    struct rtentry **rtp)
242{
243        struct rtentry *rt;
244        int error = 0;
245        short *stat = NULL;
246        struct rt_addrinfo info;
247        struct ifaddr *ifa;
248
249        /* verify the gateway is directly reachable */
250        if ((ifa = ifa_ifwithnet(gateway)) == NULL) {
251                error = ENETUNREACH;
252                goto out;
253        }
254        rt = rtalloc1(dst, 0, 0UL);
255        /*
256         * If the redirect isn't from our current router for this dst,
257         * it's either old or wrong.  If it redirects us to ourselves,
258         * we have a routing loop, perhaps as a result of an interface
259         * going down recently.
260         */
261#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
262        if (!(flags & RTF_DONE) && rt &&
263             (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
264                error = EINVAL;
265        else if (ifa_ifwithaddr(gateway))
266                error = EHOSTUNREACH;
267        if (error)
268                goto done;
269        /*
270         * Create a new entry if we just got back a wildcard entry
271         * or the the lookup failed.  This is necessary for hosts
272         * which use routing redirects generated by smart gateways
273         * to dynamically build the routing tables.
274         */
275        if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
276                goto create;
277        /*
278         * Don't listen to the redirect if it's
279         * for a route to an interface.
280         */
281        if (rt->rt_flags & RTF_GATEWAY) {
282                if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
283                        /*
284                         * Changing from route to net => route to host.
285                         * Create new route, rather than smashing route to net.
286                         */
287                create:
288                        flags |=  RTF_GATEWAY | RTF_DYNAMIC;
289                        error = rtrequest((int)RTM_ADD, dst, gateway,
290                                    netmask, flags,
291                                    (struct rtentry **)0);
292                        stat = &rtstat.rts_dynamic;
293                } else {
294                        /*
295                         * Smash the current notion of the gateway to
296                         * this destination.  Should check about netmask!!!
297                         */
298                        rt->rt_flags |= RTF_MODIFIED;
299                        flags |= RTF_MODIFIED;
300                        stat = &rtstat.rts_newgateway;
301                        rt_setgate(rt, rt_key(rt), gateway);
302                }
303        } else
304                error = EHOSTUNREACH;
305done:
306        if (rt) {
307                if (rtp && !error)
308                        *rtp = rt;
309                else
310                        rtfree(rt);
311        }
312out:
313        if (error)
314                rtstat.rts_badredirect++;
315        else if (stat != NULL)
316                (*stat)++;
317        bzero(&info, sizeof(info));
318        info.rti_info[RTAX_DST] = dst;
319        info.rti_info[RTAX_GATEWAY] = gateway;
320        info.rti_info[RTAX_NETMASK] = netmask;
321        info.rti_info[RTAX_AUTHOR] = src;
322        rt_missmsg(RTM_REDIRECT, &info, flags, error);
323}
324
325/*
326* Routing table ioctl interface.
327*/
328int
329rtioctl(int req, caddr_t data, struct proc *p)
330{
331#ifdef INET
332        /* Multicast goop, grrr... */
333#ifdef MROUTING
334        return mrt_ioctl(req, data);
335#else
336        return mrt_ioctl(req, data, p);
337#endif
338#else /* INET */
339        return ENXIO;
340#endif /* INET */
341}
342
343struct ifaddr *
344ifa_ifwithroute(int flags, struct sockaddr *dst, struct sockaddr *gateway)
345{
346        register struct ifaddr *ifa;
347        if ((flags & RTF_GATEWAY) == 0) {
348                /*
349                 * If we are adding a route to an interface,
350                 * and the interface is a pt to pt link
351                 * we should search for the destination
352                 * as our clue to the interface.  Otherwise
353                 * we can use the local address.
354                 */
355                ifa = NULL;
356                if (flags & RTF_HOST) {
357                        ifa = ifa_ifwithdstaddr(dst);
358                }
359                if (ifa == NULL)
360                        ifa = ifa_ifwithaddr(gateway);
361        } else {
362                /*
363                 * If we are adding a route to a remote net
364                 * or host, the gateway may still be on the
365                 * other end of a pt to pt link.
366                 */
367                ifa = ifa_ifwithdstaddr(gateway);
368        }
369        if (ifa == 0)
370                ifa = ifa_ifwithnet(gateway);
371        if (ifa == 0) {
372                struct rtentry *rt = rtalloc1(dst, 0, 0UL);
373                if (rt == 0)
374                        return (0);
375                rt->rt_refcnt--;
376                if ((ifa = rt->rt_ifa) == 0)
377                        return (0);
378        }
379        if (ifa->ifa_addr->sa_family != dst->sa_family) {
380                struct ifaddr *oifa = ifa;
381                ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
382                if (ifa == 0)
383                        ifa = oifa;
384        }
385        return (ifa);
386}
387
388#define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
389
390static int rt_fixdelete(struct radix_node *, void *);
391static int rt_fixchange(struct radix_node *, void *);
392
393struct rtfc_arg {
394        struct rtentry *rt0;
395        struct radix_node_head *rnh;
396};
397
398/*
399 * Do appropriate manipulations of a routing tree given
400 * all the bits of info needed
401 */
402int
403rtrequest(int req, struct sockaddr *dst, struct sockaddr *gateway,
404    struct sockaddr *netmask, int flags, struct rtentry **ret_nrt)
405{
406        int s = splnet(); int error = 0;
407        register struct rtentry *rt;
408        register struct radix_node *rn;
409        register struct radix_node_head *rnh;
410        struct ifaddr *ifa;
411        struct sockaddr *ndst;
412#define senderr(x) { error = x ; goto bad; }
413
414        /*
415         * Find the correct routing tree to use for this Address Family
416         */
417        if ((rnh = rt_tables[dst->sa_family]) == 0)
418                senderr(ESRCH);
419        /*
420         * If we are adding a host route then we don't want to put
421         * a netmask in the tree
422         */
423        if (flags & RTF_HOST)
424                netmask = 0;
425        switch (req) {
426        case RTM_DELETE:
427                /*
428                 * Remove the item from the tree and return it.
429                 * Complain if it is not there and do no more processing.
430                 */
431                if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == 0)
432                        senderr(ESRCH);
433                if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
434                        panic ("rtrequest delete");
435                rt = (struct rtentry *)rn;
436
437                /*
438                 * Now search what's left of the subtree for any cloned
439                 * routes which might have been formed from this node.
440                 */
441                if ((rt->rt_flags & RTF_PRCLONING) && netmask) {
442                        rnh->rnh_walktree_from(rnh, dst, netmask,
443                                               rt_fixdelete, rt);
444                }
445
446                /*
447                 * Remove any external references we may have.
448                 * This might result in another rtentry being freed if
449                 * we held it's last reference.
450                 */
451                if (rt->rt_gwroute) {
452                        rt = rt->rt_gwroute;
453                        RTFREE(rt);
454                        (rt = (struct rtentry *)rn)->rt_gwroute = 0;
455                }
456
457                /*
458                 * NB: RTF_UP must be set during the search above,
459                 * because we might delete the last ref, causing
460                 * rt to get freed prematurely.
461                 */
462                rt->rt_flags &= ~RTF_UP;
463
464                /*
465                 * If there is llinfo or similar associated with the
466                 * route, give the interface a chance to deal with it..
467                 */
468                if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest)
469                        ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0));
470                rttrash++;
471                /*
472                 * If the caller wants it, then it can have it, but it's up to it
473                 * to free the rtentry as we won't be doing it.
474                 */
475                if (ret_nrt)
476                        *ret_nrt = rt;
477                else if (rt->rt_refcnt <= 0) {
478                        rt->rt_refcnt++; /* make a 1->0 transition */
479                        rtfree(rt);
480                }
481                break;
482
483        case RTM_RESOLVE:
484                if (ret_nrt == 0 || (rt = *ret_nrt) == 0)
485                        senderr(EINVAL);
486                ifa = rt->rt_ifa;
487                flags = rt->rt_flags &
488                    ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC);
489                flags |= RTF_WASCLONED;
490                gateway = rt->rt_gateway;
491                if ((netmask = rt->rt_genmask) == 0)
492                        flags |= RTF_HOST;
493                goto makeroute;
494
495        case RTM_ADD:
496                if ((flags & RTF_GATEWAY) && !gateway)
497                        panic("rtrequest: GATEWAY but no gateway");
498
499                if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0)
500                        senderr(ENETUNREACH);
501
502        makeroute:
503                R_Malloc(rt, struct rtentry *, sizeof(*rt));
504                if (rt == 0)
505                        senderr(ENOBUFS);
506                Bzero(rt, sizeof(*rt));
507                rt->rt_flags = RTF_UP | flags;
508                if ((error = rt_setgate(rt, dst, gateway))) {
509                        Free(rt);
510                        senderr(error);
511                }
512                ndst = rt_key(rt);
513                if (netmask) {
514                        rt_maskedcopy(dst, ndst, netmask);
515                } else
516                        Bcopy(dst, ndst, dst->sa_len);
517
518                /*
519                 * This moved from below so that rnh->rnh_addaddr() can
520                 * examine the ifa and ifp if it so desires.
521                 */
522                ifa->ifa_refcnt++;
523                rt->rt_ifa = ifa;
524                rt->rt_ifp = ifa->ifa_ifp;
525
526                rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask,
527                                        rnh, rt->rt_nodes);
528                if (rn == 0) {
529                        struct rtentry *rt2;
530                        /*
531                         * Uh-oh, we already have one of these in the tree.
532                         * We do a special hack: if the route that's already
533                         * there was generated by the protocol-cloning
534                         * mechanism, then we just blow it away and retry
535                         * the insertion of the new one.
536                         */
537                        rt2 = rtalloc1(dst, 0, RTF_PRCLONING);
538                        if (rt2 && rt2->rt_parent) {
539                                rtrequest(RTM_DELETE,
540                                          (struct sockaddr *)rt_key(rt2),
541                                          rt2->rt_gateway,
542                                          rt_mask(rt2), rt2->rt_flags, 0);
543                                RTFREE(rt2);
544                                rn = rnh->rnh_addaddr((caddr_t)ndst,
545                                                      (caddr_t)netmask,
546                                                      rnh, rt->rt_nodes);
547                        } else if (rt2) {
548                                RTFREE(rt2);
549                        }
550                }
551
552                if (rn == 0) {
553                        if (rt->rt_gwroute)
554                                rtfree(rt->rt_gwroute);
555                        if (rt->rt_ifa) {
556                                IFAFREE(rt->rt_ifa);
557                        }
558                        Free(rt_key(rt));
559                        Free(rt);
560                        senderr(EEXIST);
561                }
562                rt->rt_parent = 0;
563
564                if (req == RTM_RESOLVE) {
565                        rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
566                        if ((*ret_nrt)->rt_flags & RTF_PRCLONING) {
567                                rt->rt_parent = (*ret_nrt);
568                                (*ret_nrt)->rt_refcnt++;
569                        }
570                }
571                if (ifa->ifa_rtrequest)
572                        ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0));
573                /*
574                 * We repeat the same procedure from rt_setgate() here because
575                 * it doesn't fire when we call it there because the node
576                 * hasn't been added to the tree yet.
577                 */
578                if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) {
579                        struct rtfc_arg arg;
580                        arg.rnh = rnh;
581                        arg.rt0 = rt;
582                        rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt),
583                                               rt_fixchange, &arg);
584                }
585
586                if (ret_nrt) {
587                        *ret_nrt = rt;
588                        rt->rt_refcnt++;
589                }
590                break;
591        }
592bad:
593        splx(s);
594        return (error);
595}
596
597/*
598 * Called from rtrequest(RTM_DELETE, ...) to fix up the route's ``family''
599 * (i.e., the routes related to it by the operation of cloning).  This
600 * routine is iterated over all potential former-child-routes by way of
601 * rnh->rnh_walktree_from() above, and those that actually are children of
602 * the late parent (passed in as VP here) are themselves deleted.
603 */
604static int
605rt_fixdelete(struct radix_node *rn, void *vp)
606{
607        struct rtentry *rt = (struct rtentry *)rn;
608        struct rtentry *rt0 = vp;
609
610        if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) {
611                return rtrequest(RTM_DELETE, rt_key(rt),
612                                 (struct sockaddr *)0, rt_mask(rt),
613                                 rt->rt_flags, (struct rtentry **)0);
614        }
615        return 0;
616}
617
618/*
619 * This routine is called from rt_setgate() to do the analogous thing for
620 * adds and changes.  There is the added complication in this case of a
621 * middle insert; i.e., insertion of a new network route between an older
622 * network route and (cloned) host routes.  For this reason, a simple check
623 * of rt->rt_parent is insufficient; each candidate route must be tested
624 * against the (mask, value) of the new route (passed as before in vp)
625 * to see if the new route matches it.  Unfortunately, this has the obnoxious
626 * property of also triggering for insertion /above/ a pre-existing network
627 * route and clones.  Sigh.  This may be fixed some day.
628 *
629 * XXX - it may be possible to do fixdelete() for changes and reserve this
630 * routine just for adds.  I'm not sure why I thought it was necessary to do
631 * changes this way.
632 */
633#ifdef DEBUG
634int rtfcdebug = 0;
635#endif
636
637static int
638rt_fixchange(struct radix_node *rn, void *vp)
639{
640        struct rtentry *rt = (struct rtentry *)rn;
641        struct rtfc_arg *ap = vp;
642        struct rtentry *rt0 = ap->rt0;
643        struct radix_node_head *rnh = ap->rnh;
644        u_char *xk1, *xm1, *xk2;
645        int i, len;
646
647#ifdef DEBUG
648        if (rtfcdebug)
649                printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0);
650#endif
651
652        if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) {
653#ifdef DEBUG
654                if(rtfcdebug) printf("no parent or pinned\n");
655#endif
656                return 0;
657        }
658
659        if (rt->rt_parent == rt0) {
660#ifdef DEBUG
661                if(rtfcdebug) printf("parent match\n");
662#endif
663                return rtrequest(RTM_DELETE, rt_key(rt),
664                                 (struct sockaddr *)0, rt_mask(rt),
665                                 rt->rt_flags, (struct rtentry **)0);
666        }
667
668        /*
669         * There probably is a function somewhere which does this...
670         * if not, there should be.
671         */
672        len = imin(((struct sockaddr *)rt_key(rt0))->sa_len,
673                   ((struct sockaddr *)rt_key(rt))->sa_len);
674
675        xk1 = (u_char *)rt_key(rt0);
676        xm1 = (u_char *)rt_mask(rt0);
677        xk2 = (u_char *)rt_key(rt);
678
679        for (i = rnh->rnh_treetop->rn_offset; i < len; i++) {
680                if ((xk2[i] & xm1[i]) != xk1[i]) {
681#ifdef DEBUG
682                        if(rtfcdebug) printf("no match\n");
683#endif
684                        return 0;
685                }
686        }
687
688        /*
689         * OK, this node is a clone, and matches the node currently being
690         * changed/added under the node's mask.  So, get rid of it.
691         */
692#ifdef DEBUG
693        if(rtfcdebug) printf("deleting\n");
694#endif
695        return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0,
696                         rt_mask(rt), rt->rt_flags, (struct rtentry **)0);
697}
698
699int
700rt_setgate(struct rtentry *rt0, struct sockaddr *dst, struct sockaddr *gate)
701{
702        caddr_t new, old;
703        int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len);
704        register struct rtentry *rt = rt0;
705        struct radix_node_head *rnh = rt_tables[dst->sa_family];
706
707        /*
708         * A host route with the destination equal to the gateway
709         * will interfere with keeping LLINFO in the routing
710         * table, so disallow it.
711         */
712        if (((rt0->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) ==
713                                        (RTF_HOST|RTF_GATEWAY)) &&
714            (dst->sa_len == gate->sa_len) &&
715            (bcmp(dst, gate, dst->sa_len) == 0)) {
716                /*
717                 * The route might already exist if this is an RTM_CHANGE
718                 * or a routing redirect, so try to delete it.
719                 */
720                if (rt_key(rt0))
721                        rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt0),
722                            rt0->rt_gateway, rt_mask(rt0), rt0->rt_flags, 0);
723                return EADDRNOTAVAIL;
724        }
725
726        if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) {
727                old = (caddr_t)rt_key(rt);
728                R_Malloc(new, caddr_t, dlen + glen);
729                if (new == 0)
730                        return ENOBUFS;
731                rt->rt_nodes->rn_key = new;
732        } else {
733                new = rt->rt_nodes->rn_key;
734                old = 0;
735        }
736        Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen);
737        if (old) {
738                Bcopy(dst, new, dlen);
739                Free(old);
740        }
741        if (rt->rt_gwroute) {
742                rt = rt->rt_gwroute; RTFREE(rt);
743                rt = rt0; rt->rt_gwroute = 0;
744        }
745        /*
746         * Cloning loop avoidance:
747         * In the presence of protocol-cloning and bad configuration,
748         * it is possible to get stuck in bottomless mutual recursion
749         * (rtrequest rt_setgate rtalloc1).  We avoid this by not allowing
750         * protocol-cloning to operate for gateways (which is probably the
751         * correct choice anyway), and avoid the resulting reference loops
752         * by disallowing any route to run through itself as a gateway.
753         * This is obviuosly mandatory when we get rt->rt_output().
754         */
755        if (rt->rt_flags & RTF_GATEWAY) {
756                rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING);
757                if (rt->rt_gwroute == rt) {
758                        RTFREE(rt->rt_gwroute);
759                        rt->rt_gwroute = 0;
760                        return EDQUOT; /* failure */
761                }
762        }
763
764        /*
765         * This isn't going to do anything useful for host routes, so
766         * don't bother.  Also make sure we have a reasonable mask
767         * (we don't yet have one during adds).
768         */
769        if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) {
770                struct rtfc_arg arg;
771                arg.rnh = rnh;
772                arg.rt0 = rt;
773                rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt),
774                                       rt_fixchange, &arg);
775        }
776
777        return 0;
778}
779
780static void
781rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst,
782    struct sockaddr *netmask)
783{
784        register u_char *cp1 = (u_char *)src;
785        register u_char *cp2 = (u_char *)dst;
786        register u_char *cp3 = (u_char *)netmask;
787        u_char *cplim = cp2 + *cp3;
788        u_char *cplim2 = cp2 + *cp1;
789
790        *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
791        cp3 += 2;
792        if (cplim > cplim2)
793                cplim = cplim2;
794        while (cp2 < cplim)
795                *cp2++ = *cp1++ & *cp3++;
796        if (cp2 < cplim2)
797                bzero(cp2, (unsigned)(cplim2 - cp2));
798}
799
800/*
801 * Set up a routing table entry, normally
802 * for an interface.
803 */
804int
805rtinit(struct ifaddr *ifa, int cmd, int flags)
806{
807        register struct rtentry *rt;
808        register struct sockaddr *dst;
809        register struct sockaddr *deldst;
810        struct mbuf *m = 0;
811        struct rtentry *nrt = 0;
812        int error;
813
814        dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
815        /*
816         * If it's a delete, check that if it exists, it's on the correct
817         * interface or we might scrub a route to another ifa which would
818         * be confusing at best and possibly worse.
819         */
820        if (cmd == RTM_DELETE) {
821                /*
822                 * It's a delete, so it should already exist..
823                 * If it's a net, mask off the host bits
824                 * (Assuming we have a mask)
825                 */
826                if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
827                        m = m_get(M_WAIT, MT_SONAME);
828                        deldst = mtod(m, struct sockaddr *);
829                        rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
830                        dst = deldst;
831                }
832                /*
833                 * Get an rtentry that is in the routing tree and
834                 * contains the correct info. (if this fails we can't get there).
835                 * We set "report" to FALSE so that if it doesn't exist,
836                 * it doesn't report an error or clone a route, etc. etc.
837                 */
838                rt = rtalloc1(dst, 0, 0UL);
839                if (rt) {
840                        /*
841                         * Ok so we found the rtentry. it has an extra reference
842                         * for us at this stage. we won't need that so
843                         * lop that off now.
844                         */
845                        rt->rt_refcnt--;
846                        if (rt->rt_ifa != ifa) {
847                                /*
848                                 * If the interface in the rtentry doesn't match
849                                 * the interface we are using, then we don't
850                                 * want to delete it, so return an error.
851                                 * This seems to be the only point of
852                                 * this whole RTM_DELETE clause.
853                                 */
854                                if (m)
855                                        (void) m_free(m);
856                                return (flags & RTF_HOST ? EHOSTUNREACH
857                                                        : ENETUNREACH);
858                        }
859                }
860                /* XXX */
861#if 0
862                else {
863                        /*
864                         * One would think that as we are deleting, and we know
865                         * it doesn't exist, we could just return at this point
866                         * with an "ELSE" clause, but apparently not..
867                         */
868                        return (flags & RTF_HOST ? EHOSTUNREACH
869                                                        : ENETUNREACH);
870                }
871#endif
872        }
873        /*
874         * Do the actual request
875         */
876        error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
877                        flags | ifa->ifa_flags, &nrt);
878        if (m)
879                (void) m_free(m);
880        /*
881         * If we are deleting, and we found an entry, then
882         * it's been removed from the tree.. now throw it away.
883         */
884        if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) {
885                /*
886                 * notify any listenning routing agents of the change
887                 */
888                rt_newaddrmsg(cmd, ifa, error, nrt);
889                if (rt->rt_refcnt <= 0) {
890                        rt->rt_refcnt++; /* need a 1->0 transition to free */
891                        rtfree(rt);
892                }
893        }
894
895        /*
896         * We are adding, and we have a returned routing entry.
897         * We need to sanity check the result.
898         */
899        if (cmd == RTM_ADD && error == 0 && (rt = nrt)) {
900                /*
901                 * We just wanted to add it.. we don't actually need a reference
902                 */
903                rt->rt_refcnt--;
904                /*
905                 * If it came back with an unexpected interface, then it must
906                 * have already existed or something. (XXX)
907                 */
908                if (rt->rt_ifa != ifa) {
909                        printf("rtinit: wrong ifa (%p) was (%p)\n", ifa,
910                                rt->rt_ifa);
911                        /*
912                         * Ask that the route we got back be removed
913                         * from the routing tables as we are trying
914                         * to supersede it.
915                         */
916                        if (rt->rt_ifa->ifa_rtrequest)
917                            rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0));
918                        /*
919                         * Remove the referenve to the it's ifaddr.
920                         */
921                        IFAFREE(rt->rt_ifa);
922                        /*
923                         * And substitute in references to the ifaddr
924                         * we are adding.
925                         */
926                        rt->rt_ifa = ifa;
927                        rt->rt_ifp = ifa->ifa_ifp;
928                        ifa->ifa_refcnt++;
929                        /*
930                         * Now add it to the routing table
931                         * XXX could we have just left it?
932                         * as it might have been in the right place..
933                         */
934                        if (ifa->ifa_rtrequest)
935                            ifa->ifa_rtrequest(RTM_ADD, rt, SA(0));
936                }
937                /*
938                 * notify any listenning routing agents of the change
939                 */
940                rt_newaddrmsg(cmd, ifa, error, nrt);
941        }
942        return (error);
943}
Note: See TracBrowser for help on using the repository browser.