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 */