diff options
author | Alexander V. Chernikov <melifaro@FreeBSD.org> | 2021-03-08 20:27:29 +0000 |
---|---|---|
committer | Alexander V. Chernikov <melifaro@FreeBSD.org> | 2021-03-11 08:35:22 +0000 |
commit | 83cedad139721c6c150be8c078417cfcb311ee78 (patch) | |
tree | 9e20ed035d958927b4d20c9f000037dbfe576018 | |
parent | 9cd7f222d5c9d03371b8941fb9150007dfdd1638 (diff) | |
download | src-83cedad139721c6c150be8c078417cfcb311ee78.tar.gz src-83cedad139721c6c150be8c078417cfcb311ee78.zip |
Fix 'in6_purgeaddr: err=65, destination address delete failed' message.
P2P ifa may require 2 routes: one is the loopback route, another is
the "prefix" route towards its destination.
Current code marks loopback routes existence with IFA_RTSELF and
"prefix" p2p routes with IFA_ROUTE.
For historic reasons, we fill in ifa_dstaddr for loopback interfaces.
To avoid installing the same route twice, we preemptively set
IFA_RTSELF when adding "prefix" route for loopback.
However, the teardown part doesn't have this hack, so we try to
remove the same route twice.
Fix this by checking if ifa_dstaddr is different from the ifa_addr
and moving this logic into a separate function.
Reviewed By: kp
Differential Revision: https://reviews.freebsd.org/D29121
(cherry picked from commit 7634919e15f1147b6f26d55354be375bc9b198db)
-rw-r--r-- | sys/netinet6/in6.c | 32 |
1 files changed, 18 insertions, 14 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 4665a21c28fd..02cb9df7da3a 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1294,13 +1294,27 @@ in6_handle_dstaddr_rtrequest(int cmd, struct in6_ifaddr *ia) return (error); } +static bool +ifa_is_p2p(struct in6_ifaddr *ia) +{ + int plen; + + plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ + + if ((plen == 128) && (ia->ia_dstaddr.sin6_family == AF_INET6) && + !IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &ia->ia_dstaddr.sin6_addr)) + return (true); + + return (false); +} + void in6_purgeaddr(struct ifaddr *ifa) { struct ifnet *ifp = ifa->ifa_ifp; struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; struct in6_multi_mship *imm; - int plen, error; + int error; if (ifa->ifa_carp) (*carp_detach_p)(ifa, false); @@ -1328,10 +1342,7 @@ in6_purgeaddr(struct ifaddr *ifa) free(imm, M_IP6MADDR); } /* Check if we need to remove p2p route */ - plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ - if (ia->ia_dstaddr.sin6_family != AF_INET6) - plen = 0; - if ((ia->ia_flags & IFA_ROUTE) && plen == 128) { + if ((ia->ia_flags & IFA_ROUTE) && ifa_is_p2p(ia)) { error = in6_handle_dstaddr_rtrequest(RTM_DELETE, ia); if (error != 0) log(LOG_INFO, "%s: err=%d, destination address delete " @@ -1434,7 +1445,7 @@ static int in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, struct in6_aliasreq *ifra, int hostIsNew) { - int error = 0, plen, ifacount = 0; + int error = 0, ifacount = 0; struct ifaddr *ifa; struct sockaddr_in6 *pdst; char ip6buf[INET6_ADDRSTRLEN]; @@ -1487,14 +1498,7 @@ in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, * XXX: the logic below rejects assigning multiple addresses on a p2p * interface that share the same destination. */ - plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ - if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 && - ia->ia_dstaddr.sin6_family == AF_INET6) { - /* - * Handle the case for ::1 . - */ - if (ifp->if_flags & IFF_LOOPBACK) - ia->ia_flags |= IFA_RTSELF; + if (!(ia->ia_flags & IFA_ROUTE) && ifa_is_p2p(ia)) { error = in6_handle_dstaddr_rtrequest(RTM_ADD, ia); if (error) goto done; |