From c7ab66020fb183062ded58a617a6b4979fdd9e63 Mon Sep 17 00:00:00 2001 From: Qing Li Date: Wed, 30 Dec 2009 21:35:34 +0000 Subject: [PATCH] The proxy arp entries could not be added into the system over the IFF_POINTOPOINT link types. The reason was due to the routing entry returned from the kernel covering the remote end is of an interface type that does not support ARP. This patch fixes this problem by providing a hint to the kernel routing code, which indicates the prefix route instead of the PPP host route should be returned to the caller. Since a host route to the local end point is also added into the routing table, and there could be multiple such instantiations due to multiple PPP links can be created with the same local end IP address, this patch also fixes the loopback route installation failure problem observed prior to this patch. The reference count of loopback route to local end would be either incremented or decremented. The first instantiation would create the entry and the last removal would delete the route entry. MFC after: 5 days --- sys/net/if_llatbl.c | 4 ++- sys/net/if_llatbl.h | 2 +- sys/net/if_var.h | 2 ++ sys/net/route.c | 4 +-- sys/net/route.h | 1 + sys/net/rtsock.c | 22 ++++++++++++++++ sys/netinet/in.c | 52 ++++++++++++++++++++++++++++++------ sys/netinet6/in6.c | 16 +++++++++--- usr.sbin/arp/arp.c | 64 +++++++++++++++++++++++++-------------------- 9 files changed, 121 insertions(+), 46 deletions(-) diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c index 4991c81d92c6..5992f6d82e0c 100644 --- a/sys/net/if_llatbl.c +++ b/sys/net/if_llatbl.c @@ -274,7 +274,9 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) #ifdef INET if (dst->sa_family == AF_INET && ((struct sockaddr_inarp *)dst)->sin_other != 0) { - struct rtentry *rt = rtalloc1(dst, 0, 0); + struct rtentry *rt; + ((struct sockaddr_inarp *)dst)->sin_other = 0; + rt = rtalloc1(dst, 0, 0); if (rt == NULL || !(rt->rt_flags & RTF_HOST)) { log(LOG_INFO, "%s: RTM_ADD publish " "(proxy only) is invalid\n", diff --git a/sys/net/if_llatbl.h b/sys/net/if_llatbl.h index f54c78ad8a28..21357ebaef6e 100644 --- a/sys/net/if_llatbl.h +++ b/sys/net/if_llatbl.h @@ -159,7 +159,7 @@ struct lltable { const struct sockaddr *mask); struct llentry * (*llt_lookup)(struct lltable *, u_int flags, const struct sockaddr *l3addr); - int (*llt_rtcheck)(struct ifnet *, + int (*llt_rtcheck)(struct ifnet *, u_int flags, const struct sockaddr *); int (*llt_dump)(struct lltable *, struct sysctl_req *); diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 519e6aaa1fb6..148d72c403fa 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -710,6 +710,7 @@ struct ifaddr { struct mtx ifa_mtx; }; #define IFA_ROUTE RTF_UP /* route installed */ +#define IFA_RTSELF RTF_HOST /* loopback route to self installed */ /* for compatibility with other BSDs */ #define ifa_list ifa_link @@ -843,6 +844,7 @@ void if_ref(struct ifnet *); void if_rele(struct ifnet *); int if_setlladdr(struct ifnet *, const u_char *, int); void if_up(struct ifnet *); +/*void ifinit(void);*/ /* declared in systm.h for main() */ int ifioctl(struct socket *, u_long, caddr_t, struct thread *); int ifpromisc(struct ifnet *, int); struct ifnet *ifunit(const char *); diff --git a/sys/net/route.c b/sys/net/route.c index 027772bec8ce..a938c9c1b7fb 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -98,8 +98,6 @@ VNET_DEFINE(struct rtstat, rtstat); #define V_rttrash VNET(rttrash) #define V_rtstat VNET(rtstat) -static void rt_maskedcopy(struct sockaddr *, - struct sockaddr *, struct sockaddr *); /* compare two sockaddr structures */ #define sa_equal(a1, a2) (bcmp((a1), (a2), (a1)->sa_len) == 0) @@ -1322,7 +1320,7 @@ rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate) return (0); } -static void +void rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) { register u_char *cp1 = (u_char *)src; diff --git a/sys/net/route.h b/sys/net/route.h index 9a0bc63b69ad..a8ae867f526e 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -384,6 +384,7 @@ void rt_missmsg(int, struct rt_addrinfo *, int, int); void rt_newaddrmsg(int, struct ifaddr *, int, struct rtentry *); void rt_newmaddrmsg(int, struct ifmultiaddr *); int rt_setgate(struct rtentry *, struct sockaddr *, struct sockaddr *); +void rt_maskedcopy(struct sockaddr *, struct sockaddr *, struct sockaddr *); /* * Note the following locking behavior: diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index a0677ec1dfc1..df4f9ae762b6 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -60,6 +60,7 @@ #include #include +#include #ifdef INET6 #include #endif @@ -622,6 +623,27 @@ route_output(struct mbuf *m, struct socket *so) } } #endif + /* + * If performing proxied L2 entry insertion, and + * the actual PPP host entry is found, perform + * another search to retrieve the prefix route of + * the local end point of the PPP link. + */ + if ((rtm->rtm_flags & RTF_ANNOUNCE) && + (rt->rt_ifp->if_flags & IFF_POINTOPOINT)) { + struct sockaddr laddr; + rt_maskedcopy(rt->rt_ifa->ifa_addr, + &laddr, + rt->rt_ifa->ifa_netmask); + /* + * refactor rt and no lock operation necessary + */ + rt = (struct rtentry *)rnh->rnh_matchaddr(&laddr, rnh); + if (rt == NULL) { + RADIX_NODE_HEAD_RUNLOCK(rnh); + senderr(ESRCH); + } + } RT_LOCK(rt); RT_ADDREF(rt); RADIX_NODE_HEAD_RUNLOCK(rnh); diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 09f2f449b19d..c5317e5529ff 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -924,9 +924,25 @@ in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin, /* * add a loopback route to self */ - if (V_useloopback && !(ifp->if_flags & IFF_LOOPBACK)) - error = ifa_add_loopback_route((struct ifaddr *)ia, + if (V_useloopback && !(ifp->if_flags & IFF_LOOPBACK)) { + struct route ia_ro; + + bzero(&ia_ro, sizeof(ia_ro)); + *((struct sockaddr_in *)(&ia_ro.ro_dst)) = ia->ia_addr; + rtalloc_ign_fib(&ia_ro, 0, 0); + if ((ia_ro.ro_rt != NULL) && (ia_ro.ro_rt->rt_ifp != NULL) && + (ia_ro.ro_rt->rt_ifp == V_loif)) { + RT_LOCK(ia_ro.ro_rt); + RT_ADDREF(ia_ro.ro_rt); + RTFREE_LOCKED(ia_ro.ro_rt); + } else + error = ifa_add_loopback_route((struct ifaddr *)ia, (struct sockaddr *)&ia->ia_addr); + if (error == 0) + ia->ia_flags |= IFA_RTSELF; + if (ia_ro.ro_rt != NULL) + RTFREE(ia_ro.ro_rt); + } return (error); } @@ -1043,7 +1059,7 @@ in_scrubprefix(struct in_ifaddr *target) { struct in_ifaddr *ia; struct in_addr prefix, mask, p; - int error; + int error = 0; struct sockaddr_in prefix0, mask0; /* @@ -1057,9 +1073,28 @@ in_scrubprefix(struct in_ifaddr *target) * deletion is unconditional. */ if ((target->ia_addr.sin_addr.s_addr != INADDR_ANY) && - !(target->ia_ifp->if_flags & IFF_LOOPBACK)) { - error = ifa_del_loopback_route((struct ifaddr *)target, + !(target->ia_ifp->if_flags & IFF_LOOPBACK) && + (target->ia_flags & IFA_RTSELF)) { + struct route ia_ro; + int freeit = 0; + + bzero(&ia_ro, sizeof(ia_ro)); + *((struct sockaddr_in *)(&ia_ro.ro_dst)) = target->ia_addr; + rtalloc_ign_fib(&ia_ro, 0, 0); + if ((ia_ro.ro_rt != NULL) && (ia_ro.ro_rt->rt_ifp != NULL) && + (ia_ro.ro_rt->rt_ifp == V_loif)) { + RT_LOCK(ia_ro.ro_rt); + if (ia_ro.ro_rt->rt_refcnt <= 1) + freeit = 1; + else + RT_REMREF(ia_ro.ro_rt); + RTFREE_LOCKED(ia_ro.ro_rt); + } + if (freeit) + error = ifa_del_loopback_route((struct ifaddr *)target, (struct sockaddr *)&target->ia_addr); + if (error == 0) + target->ia_flags &= ~IFA_RTSELF; /* remove arp cache */ arp_ifscrub(target->ia_ifp, IA_SIN(target)->sin_addr.s_addr); } @@ -1317,7 +1352,7 @@ in_lltable_prefix_free(struct lltable *llt, static int -in_lltable_rtcheck(struct ifnet *ifp, const struct sockaddr *l3addr) +in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr) { struct rtentry *rt; @@ -1326,7 +1361,8 @@ in_lltable_rtcheck(struct ifnet *ifp, const struct sockaddr *l3addr) /* XXX rtalloc1 should take a const param */ rt = rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0); - if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) { + if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || + ((rt->rt_ifp != ifp) && !(flags & LLE_PUB))) { #ifdef DIAGNOSTIC log(LOG_INFO, "IPv4 address: \"%s\" is not on the network\n", inet_ntoa(((const struct sockaddr_in *)l3addr)->sin_addr)); @@ -1378,7 +1414,7 @@ in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3add * verify this. */ if (!(flags & LLE_IFADDR) && - in_lltable_rtcheck(ifp, l3addr) != 0) + in_lltable_rtcheck(ifp, flags, l3addr) != 0) goto done; lle = in_lltable_new(l3addr, flags); diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 7254c4d0eb08..c839efdc8df5 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1200,8 +1200,12 @@ in6_purgeaddr(struct ifaddr *ifa) * The check for the current setting of "nd6_useloopback" * is not needed. */ - error = ifa_del_loopback_route((struct ifaddr *)ia, - (struct sockaddr *)&ia->ia_addr); + if (ia->ia_flags & IFA_RTSELF) { + error = ifa_del_loopback_route((struct ifaddr *)ia, + (struct sockaddr *)&ia->ia_addr); + if (error == 0) + ia->ia_flags &= ~IFA_RTSELF; + } /* stop DAD processing */ nd6_dad_stop(ifa); @@ -1762,6 +1766,8 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, || (ifp->if_flags & IFF_LOOPBACK))) { error = ifa_add_loopback_route((struct ifaddr *)ia, (struct sockaddr *)&ia->ia_addr); + if (error == 0) + ia->ia_flags |= IFA_RTSELF; } /* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */ @@ -2347,7 +2353,9 @@ in6_lltable_prefix_free(struct lltable *llt, } static int -in6_lltable_rtcheck(struct ifnet *ifp, const struct sockaddr *l3addr) +in6_lltable_rtcheck(struct ifnet *ifp, + u_int flags, + const struct sockaddr *l3addr) { struct rtentry *rt; char ip6buf[INET6_ADDRSTRLEN]; @@ -2415,7 +2423,7 @@ in6_lltable_lookup(struct lltable *llt, u_int flags, * verify this. */ if (!(flags & LLE_IFADDR) && - in6_lltable_rtcheck(ifp, l3addr) != 0) + in6_lltable_rtcheck(ifp, flags, l3addr) != 0) return NULL; lle = in6_lltable_new(l3addr, flags); diff --git a/usr.sbin/arp/arp.c b/usr.sbin/arp/arp.c index 2982f48bec41..61427d34c81f 100644 --- a/usr.sbin/arp/arp.c +++ b/usr.sbin/arp/arp.c @@ -326,7 +326,6 @@ set(int argc, char **argv) doing_proxy = 1; if (argc && strncmp(argv[1], "only", 3) == 0) { proxy_only = 1; - dst->sin_other = SIN_PROXY; argc--; argv++; } } else if (strncmp(argv[0], "blackhole", 9) == 0) { @@ -365,33 +364,30 @@ set(int argc, char **argv) sdl_m.sdl_alen = ETHER_ADDR_LEN; } } - for (;;) { /* try at most twice */ - rtm = rtmsg(RTM_GET, dst, &sdl_m); - if (rtm == NULL) { - warn("%s", host); - return (1); - } - addr = (struct sockaddr_inarp *)(rtm + 1); - sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); - if (addr->sin_addr.s_addr != dst->sin_addr.s_addr) - break; - if (sdl->sdl_family == AF_LINK && - !(rtm->rtm_flags & RTF_GATEWAY) && - valid_type(sdl->sdl_type) ) - break; - if (doing_proxy == 0) { - printf("set: can only proxy for %s\n", host); - return (1); - } - if (dst->sin_other & SIN_PROXY) { - printf("set: proxy entry exists for non 802 device\n"); - return (1); - } - dst->sin_other = SIN_PROXY; - proxy_only = 1; + + /* + * In the case a proxy-arp entry is being added for + * a remote end point, the RTF_ANNOUNCE flag in the + * RTM_GET command is an indication to the kernel + * routing code that the interface associated with + * the prefix route covering the local end of the + * PPP link should be returned, on which ARP applies. + */ + rtm = rtmsg(RTM_GET, dst, &sdl_m); + if (rtm == NULL) { + warn("%s", host); + return (1); + } + addr = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); + if (addr->sin_addr.s_addr == dst->sin_addr.s_addr) { + printf("set: proxy entry exists for non 802 device\n"); + return (1); } - if (sdl->sdl_family != AF_LINK) { + if ((sdl->sdl_family != AF_LINK) || + (rtm->rtm_flags & RTF_GATEWAY) || + !valid_type(sdl->sdl_type)) { printf("cannot intuit interface index and type for %s\n", host); return (1); } @@ -436,7 +432,11 @@ delete(char *host, int do_proxy) dst = getaddr(host); if (dst == NULL) return (1); - dst->sin_other = do_proxy; + + /* + * Perform a regular entry delete first. + */ + flags &= ~RTF_ANNOUNCE; /* * setup the data structure to notify the kernel @@ -471,11 +471,16 @@ delete(char *host, int do_proxy) break; } - if (dst->sin_other & SIN_PROXY) { + /* + * Regualar entry delete failed, now check if there + * is a proxy-arp entry to remove. + */ + if (flags & RTF_ANNOUNCE) { fprintf(stderr, "delete: cannot locate %s\n",host); return (1); } - dst->sin_other = SIN_PROXY; + + flags |= RTF_ANNOUNCE; } rtm->rtm_flags |= RTF_LLDATA; if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { @@ -485,6 +490,7 @@ delete(char *host, int do_proxy) return (1); } + /* * Search the arp table and do some action on matching entries */