Remove per-packet ifa refcounting from IPv6 fast path.

Currently ip6_input() calls in6ifa_ifwithaddr() for
 every local packet, in order to check if the target ip
 belongs to the local ifa in proper state and increase
 its counters.

in6ifa_ifwithaddr() references found ifa.
With epoch changes, both `ip6_input()` and all other current callers
 of `in6ifa_ifwithaddr()` do not need this reference
 anymore, as epoch provides stability guarantee.

Given that, update `in6ifa_ifwithaddr()` to allow
 it to return ifa without referencing it, while preserving
 option for getting referenced ifa if so desired.

MFC after:		1 week
Differential Revision:	https://reviews.freebsd.org/D28648
This commit is contained in:
Alexander V. Chernikov 2021-02-15 21:59:21 +00:00
parent 605284b894
commit 8268d82cff
9 changed files with 22 additions and 39 deletions

View File

@ -592,16 +592,13 @@ tcp6_input(struct mbuf **mp, int *offp, int proto)
* better place to put this in? * better place to put this in?
*/ */
ip6 = mtod(m, struct ip6_hdr *); ip6 = mtod(m, struct ip6_hdr *);
ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */, false);
if (ia6 && (ia6->ia6_flags & IN6_IFF_ANYCAST)) { if (ia6 && (ia6->ia6_flags & IN6_IFF_ANYCAST)) {
ifa_free(&ia6->ia_ifa);
icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR,
(caddr_t)&ip6->ip6_dst - (caddr_t)ip6); (caddr_t)&ip6->ip6_dst - (caddr_t)ip6);
*mp = NULL; *mp = NULL;
return (IPPROTO_DONE); return (IPPROTO_DONE);
} }
if (ia6)
ifa_free(&ia6->ia_ifa);
*mp = m; *mp = m;
return (tcp_input(mp, offp, proto)); return (tcp_input(mp, offp, proto));
@ -1249,10 +1246,9 @@ tfo_socket_result:
if (isipv6 && !V_ip6_use_deprecated) { if (isipv6 && !V_ip6_use_deprecated) {
struct in6_ifaddr *ia6; struct in6_ifaddr *ia6;
ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */, false);
if (ia6 != NULL && if (ia6 != NULL &&
(ia6->ia6_flags & IN6_IFF_DEPRECATED)) { (ia6->ia6_flags & IN6_IFF_DEPRECATED)) {
ifa_free(&ia6->ia_ifa);
if ((s = tcp_log_addrs(&inc, th, NULL, NULL))) if ((s = tcp_log_addrs(&inc, th, NULL, NULL)))
log(LOG_DEBUG, "%s; %s: Listen socket: " log(LOG_DEBUG, "%s; %s: Listen socket: "
"Connection attempt to deprecated " "Connection attempt to deprecated "
@ -1261,8 +1257,6 @@ tfo_socket_result:
rstreason = BANDLIM_RST_OPENPORT; rstreason = BANDLIM_RST_OPENPORT;
goto dropwithreset; goto dropwithreset;
} }
if (ia6)
ifa_free(&ia6->ia_ifa);
} }
#endif /* INET6 */ #endif /* INET6 */
/* /*

View File

@ -396,11 +396,9 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
dstifp = NULL; dstifp = NULL;
/* Find the destination interface of the packet. */ /* Find the destination interface of the packet. */
ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */, false);
if (ia6 != NULL) { if (ia6 != NULL)
dstifp = ia6->ia_ifp; dstifp = ia6->ia_ifp;
ifa_free(&ia6->ia_ifa);
}
/* Jumbo payload cannot contain a fragment header. */ /* Jumbo payload cannot contain a fragment header. */
if (ip6->ip6_plen == 0) { if (ip6->ip6_plen == 0) {

View File

@ -1221,19 +1221,17 @@ ni6_input(struct mbuf *m, int off, struct prison *pr)
goto bad; goto bad;
/* else it's a link-local multicast, fine */ /* else it's a link-local multicast, fine */
} else { /* unicast or anycast */ } else { /* unicast or anycast */
ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); ia6 = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */, false);
if (ia6 == NULL) if (ia6 == NULL)
goto bad; /* XXX impossible */ goto bad; /* XXX impossible */
if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) && if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) &&
!(V_icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK)) { !(V_icmp6_nodeinfo & ICMP6_NODEINFO_TMPADDROK)) {
ifa_free(&ia6->ia_ifa);
nd6log((LOG_DEBUG, "ni6_input: ignore node info to " nd6log((LOG_DEBUG, "ni6_input: ignore node info to "
"a temporary address in %s:%d", "a temporary address in %s:%d",
__FILE__, __LINE__)); __FILE__, __LINE__));
goto bad; goto bad;
} }
ifa_free(&ia6->ia_ifa);
} }
/* validate query Subject field. */ /* validate query Subject field. */
@ -2104,7 +2102,7 @@ icmp6_reflect(struct mbuf *m, size_t off)
* destined to a duplicated address of ours. * destined to a duplicated address of ours.
*/ */
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
ia = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); ia = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */, false);
if (ia != NULL && !(ia->ia6_flags & if (ia != NULL && !(ia->ia6_flags &
(IN6_IFF_ANYCAST|IN6_IFF_NOTREADY))) { (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY))) {
src6 = ia->ia_addr.sin6_addr; src6 = ia->ia_addr.sin6_addr;
@ -2116,8 +2114,6 @@ icmp6_reflect(struct mbuf *m, size_t off)
} else } else
hlim = V_ip6_defhlim; hlim = V_ip6_defhlim;
} }
if (ia != NULL)
ifa_free(&ia->ia_ifa);
} }
if (srcp == NULL) { if (srcp == NULL) {

View File

@ -1550,10 +1550,10 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags)
/* /*
* find the interface address corresponding to a given IPv6 address. * find the interface address corresponding to a given IPv6 address.
* ifaddr is returned referenced. * ifaddr is returned referenced if @referenced flag is set.
*/ */
struct in6_ifaddr * struct in6_ifaddr *
in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid) in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid, bool referenced)
{ {
struct rm_priotracker in6_ifa_tracker; struct rm_priotracker in6_ifa_tracker;
struct in6_ifaddr *ia; struct in6_ifaddr *ia;
@ -1564,7 +1564,8 @@ in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid)
if (zoneid != 0 && if (zoneid != 0 &&
zoneid != ia->ia_addr.sin6_scope_id) zoneid != ia->ia_addr.sin6_scope_id)
continue; continue;
ifa_ref(&ia->ia_ifa); if (referenced)
ifa_ref(&ia->ia_ifa);
break; break;
} }
} }

View File

@ -713,11 +713,9 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
/* /*
* check that loopback address doesn't exist yet. * check that loopback address doesn't exist yet.
*/ */
ia = in6ifa_ifwithaddr(&in6addr_loopback, 0); ia = in6ifa_ifwithaddr(&in6addr_loopback, 0, false);
if (ia == NULL) if (ia == NULL)
in6_ifattach_loopback(ifp); in6_ifattach_loopback(ifp);
else
ifa_free(&ia->ia_ifa);
} }
/* /*

View File

@ -252,15 +252,11 @@ in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock,
* ancillary data. * ancillary data.
*/ */
if ((inp->inp_flags & INP_BINDANY) == 0) { if ((inp->inp_flags & INP_BINDANY) == 0) {
ia = in6ifa_ifwithaddr(&tmp, 0 /* XXX */); ia = in6ifa_ifwithaddr(&tmp, 0 /* XXX */, false);
if (ia == NULL || (ia->ia6_flags & (IN6_IFF_ANYCAST | if (ia == NULL || (ia->ia6_flags & (IN6_IFF_ANYCAST |
IN6_IFF_NOTREADY))) { IN6_IFF_NOTREADY)))
if (ia != NULL)
ifa_free(&ia->ia_ifa);
return (EADDRNOTAVAIL); return (EADDRNOTAVAIL);
}
bcopy(&ia->ia_addr.sin6_addr, srcp, sizeof(*srcp)); bcopy(&ia->ia_addr.sin6_addr, srcp, sizeof(*srcp));
ifa_free(&ia->ia_ifa);
} else } else
bcopy(&tmp, srcp, sizeof(*srcp)); bcopy(&tmp, srcp, sizeof(*srcp));
pi->ipi6_addr = tmp; /* XXX: this overrides pi */ pi->ipi6_addr = tmp; /* XXX: this overrides pi */

View File

@ -899,7 +899,7 @@ void in6_setmaxmtu(void);
int in6_if2idlen(struct ifnet *); int in6_if2idlen(struct ifnet *);
struct in6_ifaddr *in6ifa_ifpforlinklocal(struct ifnet *, int); struct in6_ifaddr *in6ifa_ifpforlinklocal(struct ifnet *, int);
struct in6_ifaddr *in6ifa_ifpwithaddr(struct ifnet *, const struct in6_addr *); struct in6_ifaddr *in6ifa_ifpwithaddr(struct ifnet *, const struct in6_addr *);
struct in6_ifaddr *in6ifa_ifwithaddr(const struct in6_addr *, uint32_t); struct in6_ifaddr *in6ifa_ifwithaddr(const struct in6_addr *, uint32_t, bool);
struct in6_ifaddr *in6ifa_llaonifp(struct ifnet *); struct in6_ifaddr *in6ifa_llaonifp(struct ifnet *);
int in6_addr2zoneid(struct ifnet *, struct in6_addr *, u_int32_t *); int in6_addr2zoneid(struct ifnet *, struct in6_addr *, u_int32_t *);
int in6_matchlen(struct in6_addr *, struct in6_addr *); int in6_matchlen(struct in6_addr *, struct in6_addr *);

View File

@ -803,7 +803,7 @@ passin:
* XXX: For now we keep link-local IPv6 addresses with embedded * XXX: For now we keep link-local IPv6 addresses with embedded
* scope zone id, therefore we use zero zoneid here. * scope zone id, therefore we use zero zoneid here.
*/ */
ia = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */); ia = in6ifa_ifwithaddr(&ip6->ip6_dst, 0 /* XXX */, false);
if (ia != NULL) { if (ia != NULL) {
if (ia->ia6_flags & IN6_IFF_NOTREADY) { if (ia->ia6_flags & IN6_IFF_NOTREADY) {
char ip6bufs[INET6_ADDRSTRLEN]; char ip6bufs[INET6_ADDRSTRLEN];
@ -813,13 +813,11 @@ passin:
"ip6_input: packet to an unready address %s->%s\n", "ip6_input: packet to an unready address %s->%s\n",
ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufs, &ip6->ip6_src),
ip6_sprintf(ip6bufd, &ip6->ip6_dst))); ip6_sprintf(ip6bufd, &ip6->ip6_dst)));
ifa_free(&ia->ia_ifa);
goto bad; goto bad;
} }
/* Count the packet in the ip address stats */ /* Count the packet in the ip address stats */
counter_u64_add(ia->ia_ifa.ifa_ipackets, 1); counter_u64_add(ia->ia_ifa.ifa_ipackets, 1);
counter_u64_add(ia->ia_ifa.ifa_ibytes, m->m_pkthdr.len); counter_u64_add(ia->ia_ifa.ifa_ibytes, m->m_pkthdr.len);
ifa_free(&ia->ia_ifa);
ours = 1; ours = 1;
goto hbhcheck; goto hbhcheck;
} }

View File

@ -117,6 +117,7 @@ send_output(struct mbuf *m, struct ifnet *ifp, int direction)
struct icmp6_hdr *icmp6; struct icmp6_hdr *icmp6;
struct epoch_tracker et; struct epoch_tracker et;
int icmp6len; int icmp6len;
int error;
/* /*
* Receive incoming (SeND-protected) or outgoing traffic * Receive incoming (SeND-protected) or outgoing traffic
@ -143,17 +144,17 @@ send_output(struct mbuf *m, struct ifnet *ifp, int direction)
ip6 = mtod(m, struct ip6_hdr *); ip6 = mtod(m, struct ip6_hdr *);
icmp6 = (struct icmp6_hdr *)(ip6 + 1); icmp6 = (struct icmp6_hdr *)(ip6 + 1);
error = 0;
/* /*
* Output the packet as icmp6.c:icpm6_input() would do. * Output the packet as icmp6.c:icpm6_input() would do.
* The mbuf is always consumed, so we do not have to * The mbuf is always consumed, so we do not have to
* care about that. * care about that.
*/ */
NET_EPOCH_ENTER(et);
switch (icmp6->icmp6_type) { switch (icmp6->icmp6_type) {
case ND_NEIGHBOR_SOLICIT: case ND_NEIGHBOR_SOLICIT:
NET_EPOCH_ENTER(et);
nd6_ns_input(m, sizeof(struct ip6_hdr), icmp6len); nd6_ns_input(m, sizeof(struct ip6_hdr), icmp6len);
NET_EPOCH_EXIT(et);
break; break;
case ND_NEIGHBOR_ADVERT: case ND_NEIGHBOR_ADVERT:
nd6_na_input(m, sizeof(struct ip6_hdr), icmp6len); nd6_na_input(m, sizeof(struct ip6_hdr), icmp6len);
@ -169,9 +170,11 @@ send_output(struct mbuf *m, struct ifnet *ifp, int direction)
break; break;
default: default:
m_freem(m); m_freem(m);
return (ENOSYS); error = ENOSYS;
} }
return (0); NET_EPOCH_EXIT(et);
return (error);
case SND_OUT: case SND_OUT:
if (m->m_len < sizeof(struct ip6_hdr)) { if (m->m_len < sizeof(struct ip6_hdr)) {
@ -199,7 +202,6 @@ send_output(struct mbuf *m, struct ifnet *ifp, int direction)
* XXX-BZ as we added data, what about fragmenting, * XXX-BZ as we added data, what about fragmenting,
* if now needed? * if now needed?
*/ */
int error;
error = ((*ifp->if_output)(ifp, m, (struct sockaddr *)&dst, error = ((*ifp->if_output)(ifp, m, (struct sockaddr *)&dst,
NULL)); NULL));
if (error) if (error)