IPv6: fix problem with duplicate port assignment with v4-mapped addrs

In in_pcb_lport_dest(), if an IPv6 socket does not match any other IPv6
socket using in6_pcblookup_local(), and if the socket can also connect
to IPv4 (the INP_IPV4 vflag is set), check for IPv4 matches as well.
Otherwise, we can allocate a port that is used by an IPv4 socket
(possibly one created from IPv6 via the same procedure), and then
connect() can fail with EADDRINUSE, when it could have succeeded if
the bound port was not in use.

PR:		265064
Submitted by:	firk at cantconnect.ru (with modifications)
Reviewed by:	bz, melifaro
Differential Revision: https://reviews.freebsd.org/D36012
This commit is contained in:
Mike Karels 2022-07-29 09:23:23 -05:00
parent 190c4c2499
commit 637f317c6d

View File

@ -784,7 +784,7 @@ in_pcb_lport_dest(struct inpcb *inp, struct sockaddr *lsa, u_short *lportp,
}
#ifdef INET
laddr.s_addr = INADDR_ANY;
laddr.s_addr = INADDR_ANY; /* used by INET6+INET below too */
if ((inp->inp_vflag & (INP_IPV4|INP_IPV6)) == INP_IPV4) {
if (lsa != NULL)
laddr = ((struct sockaddr_in *)lsa)->sin_addr;
@ -835,9 +835,16 @@ in_pcb_lport_dest(struct inpcb *inp, struct sockaddr *lsa, u_short *lportp,
#endif
} else {
#ifdef INET6
if ((inp->inp_vflag & INP_IPV6) != 0)
if ((inp->inp_vflag & INP_IPV6) != 0) {
tmpinp = in6_pcblookup_local(pcbinfo,
&inp->in6p_laddr, lport, lookupflags, cred);
#ifdef INET
if (tmpinp == NULL &&
(inp->inp_vflag & INP_IPV4))
tmpinp = in_pcblookup_local(pcbinfo,
laddr, lport, lookupflags, cred);
#endif
}
#endif
#if defined(INET) && defined(INET6)
else