diff options
author | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2001-07-23 14:23:22 +0000 |
---|---|---|
committer | Jun-ichiro itojun Hagino <itojun@cvs.openbsd.org> | 2001-07-23 14:23:22 +0000 |
commit | d4ddf0449cde9c08ec0e21a98807e1b122af7bbf (patch) | |
tree | 6ae2817a9091e599a8293dcf9bd1e1723501abb4 | |
parent | e91cd82f986e9be5abbb8a20540e8efacb22bf6a (diff) |
improve IFA_ROUTE handling. do not lose connected route on primary
interface address removals.
-rw-r--r-- | sys/netinet/in.c | 180 |
1 files changed, 149 insertions, 31 deletions
diff --git a/sys/netinet/in.c b/sys/netinet/in.c index e47ef8e6638..b9227c0f1bb 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -1,7 +1,35 @@ -/* $OpenBSD: in.c,v 1.20 2001/06/08 03:53:45 angelos Exp $ */ +/* $OpenBSD: in.c,v 1.21 2001/07/23 14:23:21 itojun Exp $ */ /* $NetBSD: in.c,v 1.26 1996/02/13 23:41:39 christos Exp $ */ /* + * Copyright (C) 2001 WIDE Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * @@ -63,6 +91,9 @@ static void in_len2mask __P((struct in_addr *, int)); static int in_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *)); +static int in_addprefix __P((struct in_ifaddr *, int)); +static int in_scrubprefix __P((struct in_ifaddr *)); + #ifndef SUBNETSARELOCAL #define SUBNETSARELOCAL 0 #endif @@ -329,23 +360,6 @@ in_control(so, cmd, data, ifp) case SIOCSIFADDR: error = in_ifinit(ifp, ia, satosin(&ifr->ifr_addr), 1); -#if 0 - /* - * the code chokes if we are to assign multiple addresses with - * the same address prefix (rtinit() will return EEXIST, which - * is not fatal actually). we will get memory leak if we - * don't do it. - * -> we may want to hide EEXIST from rtinit(). - */ - undo: - if (error && newifaddr){ - TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); - TAILQ_REMOVE(&in_ifaddr, ia, ia_list); - FREE(ia, M_IFADDR); - if ((ifp->if_flags & IFF_LOOPBACK) == 0) - in_interfaces--; - } -#endif return error; case SIOCSIFNETMASK: @@ -380,10 +394,6 @@ in_control(so, cmd, data, ifp) if (ifra->ifra_addr.sin_family == AF_INET && (hostIsNew || maskIsNew)) { error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); -#if 0 - if (error) - goto undo; -#endif } if ((ifp->if_flags & IFF_BROADCAST) && (ifra->ifra_broadaddr.sin_family == AF_INET)) @@ -604,13 +614,7 @@ in_ifscrub(ifp, ia) register struct in_ifaddr *ia; { - if ((ia->ia_flags & IFA_ROUTE) == 0) - return; - if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); - else - rtinit(&(ia->ia_ifa), (int)RTM_DELETE, 0); - ia->ia_flags &= ~IFA_ROUTE; + in_scrubprefix(ia); } /* @@ -683,8 +687,7 @@ in_ifinit(ifp, ia, sin, scrub) return (0); flags |= RTF_HOST; } - if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) - ia->ia_flags |= IFA_ROUTE; + error = in_addprefix(ia, flags); /* * If the interface supports multicast, join the "all hosts" * multicast group on that interface. @@ -698,6 +701,121 @@ in_ifinit(ifp, ia, sin, scrub) return (error); } +#define rtinitflags(x) \ + (((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) && \ + (x)->ia_dstaddr.sin_family == AF_INET) ? RTF_HOST : 0) + +/* + * add a route to prefix ("connected route" in cisco terminology). + * does nothing if there's some interface address with the same prefix already. + */ +static int +in_addprefix(target, flags) + struct in_ifaddr *target; + int flags; +{ + struct in_ifaddr *ia; + struct in_addr prefix, mask, p; + int error; + + if ((flags & RTF_HOST) != 0) + prefix = target->ia_dstaddr.sin_addr; + else + prefix = target->ia_addr.sin_addr; + mask = target->ia_sockmask.sin_addr; + prefix.s_addr &= mask.s_addr; + + for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) { + /* easy one first */ + if (mask.s_addr != ia->ia_sockmask.sin_addr.s_addr) + continue; + + if (rtinitflags(ia)) + p = ia->ia_dstaddr.sin_addr; + else + p = ia->ia_addr.sin_addr; + p.s_addr &= ia->ia_sockmask.sin_addr.s_addr; + if (prefix.s_addr != p.s_addr) + continue; + + /* + * if we got a matching prefix route inserted by other + * interface adderss, we don't need to bother + */ + if (ia->ia_flags & IFA_ROUTE) + return 0; + } + + /* + * noone seem to have prefix route. insert it. + */ + error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags); + if (!error) + target->ia_flags |= IFA_ROUTE; + return error; +} + +/* + * remove a route to prefix ("connected route" in cisco terminology). + * re-installs the route by using another interface address, if there's one + * with the same prefix (otherwise we lose the route mistakenly). + */ +static int +in_scrubprefix(target) + struct in_ifaddr *target; +{ + struct in_ifaddr *ia; + struct in_addr prefix, mask, p; + int error; + + if ((target->ia_flags & IFA_ROUTE) == 0) + return 0; + + if (rtinitflags(target)) + prefix = target->ia_dstaddr.sin_addr; + else + prefix = target->ia_addr.sin_addr; + mask = target->ia_sockmask.sin_addr; + prefix.s_addr &= mask.s_addr; + + for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) { + /* easy one first */ + if (mask.s_addr != ia->ia_sockmask.sin_addr.s_addr) + continue; + + if (rtinitflags(ia)) + p = ia->ia_dstaddr.sin_addr; + else + p = ia->ia_addr.sin_addr; + p.s_addr &= ia->ia_sockmask.sin_addr.s_addr; + if (prefix.s_addr != p.s_addr) + continue; + + /* + * if we got a matching prefix route, move IFA_ROUTE to him + */ + if ((ia->ia_flags & IFA_ROUTE) == 0) { + rtinit(&(target->ia_ifa), (int)RTM_DELETE, + rtinitflags(target)); + target->ia_flags &= ~IFA_ROUTE; + + error = rtinit(&ia->ia_ifa, (int)RTM_ADD, + rtinitflags(ia) | RTF_UP); + if (error == 0) + ia->ia_flags |= IFA_ROUTE; + return error; + } + } + + /* + * noone seem to have prefix route. remove it. + */ + rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target)); + target->ia_flags &= ~IFA_ROUTE; + return 0; +} + +#undef rtinitflags /* * Return 1 if the address might be a local broadcast address. |