Re: git: 01f8ce83242d - main - inpcb: Factor out parts of in6_pcbbind() and in_pcbbind_setup()

From: FreeBSD User <freebsd_at_walstatt-de.de>
Date: Thu, 05 Dec 2024 16:35:23 UTC
On Thu, 5 Dec 2024 15:20:48 GMT
Mark Johnston <markj@FreeBSD.org> wrote:


This commit breaks buildkernel:

[...]
--- in6_pcb.o ---
/usr/src/sys/netinet6/in6_pcb.c:301:20: error: unused variable 'pcbinfo'
[-Werror,-Wunused-variable] 301 |         struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;


> The branch main has been updated by markj:
> 
> URL: https://cgit.FreeBSD.org/src/commit/?id=01f8ce83242d7a8e599cf6a78b6277161d79edd4
> 
> commit 01f8ce83242d7a8e599cf6a78b6277161d79edd4
> Author:     Mark Johnston <markj@FreeBSD.org>
> AuthorDate: 2024-12-05 15:00:13 +0000
> Commit:     Mark Johnston <markj@FreeBSD.org>
> CommitDate: 2024-12-05 15:19:57 +0000
> 
>     inpcb: Factor out parts of in6_pcbbind() and in_pcbbind_setup()
>     
>     A large portion of these functions just determines whether the inpcb can
>     bind to the address/port.  This portion has no side effects, so is a
>     good candidate to move into its own helper function.  This patch does
>     so, making the callers less complicated and reducing indentation.
>     
>     While moving this code, also make some changes:
>     - Load socket options (SO_REUSEADDR etc.) only once.  There is nothing
>       preventing another thread from toggling the socket options, so make
>       this function easier to reason about by avoiding races.
>     - When checking whether the bind address is an interface address, make a
>       separate sockaddr rather than temporarily modifying the one passed to
>       in_pcbbind().
>     
>     Reviewed by:    ae, glebius
>     MFC after:      1 month
>     Sponsored by:   Klara, Inc.
>     Sponsored by:   Stormshield
>     Differential Revision:  https://reviews.freebsd.org/D47590
> ---
>  sys/netinet/in_pcb.c   | 172 ++++++++++++++++++--------------
>  sys/netinet6/in6_pcb.c | 262 +++++++++++++++++++++++++++----------------------
>  2 files changed, 242 insertions(+), 192 deletions(-)
> 
> diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
> index 7c881817ac6e..94fb406bcd2b 100644
> --- a/sys/netinet/in_pcb.c
> +++ b/sys/netinet/in_pcb.c
> @@ -861,6 +861,93 @@ in_pcb_lport(struct inpcb *inp, struct in_addr *laddrp, u_short
> *lportp, #endif /* INET || INET6 */
>  
>  #ifdef INET
> +/*
> + * Determine whether the inpcb can be bound to the specified address/port tuple.
> + */
> +static int
> +in_pcbbind_avail(struct inpcb *inp, const struct in_addr laddr,
> +    const u_short lport, int sooptions, int lookupflags, struct ucred *cred)
> +{
> +	int reuseport, reuseport_lb;
> +
> +	INP_LOCK_ASSERT(inp);
> +	INP_HASH_LOCK_ASSERT(inp->inp_pcbinfo);
> +
> +	reuseport = (sooptions & SO_REUSEPORT);
> +	reuseport_lb = (sooptions & SO_REUSEPORT_LB);
> +
> +	if (IN_MULTICAST(ntohl(laddr.s_addr))) {
> +		/*
> +		 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
> +		 * allow complete duplication of binding if
> +		 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
> +		 * and a multicast address is bound on both
> +		 * new and duplicated sockets.
> +		 */
> +		if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT)) != 0)
> +			reuseport = SO_REUSEADDR | SO_REUSEPORT;
> +		/*
> +		 * XXX: How to deal with SO_REUSEPORT_LB here?
> +		 * Treat same as SO_REUSEPORT for now.
> +		 */
> +		if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT_LB)) != 0)
> +			reuseport_lb = SO_REUSEADDR | SO_REUSEPORT_LB;
> +	} else if (!in_nullhost(laddr)) {
> +		struct sockaddr_in sin;
> +
> +		memset(&sin, 0, sizeof(sin));
> +		sin.sin_family = AF_INET;
> +		sin.sin_len = sizeof(sin);
> +		sin.sin_addr = laddr;
> +
> +		/*
> +		 * Is the address a local IP address?
> +		 * If INP_BINDANY is set, then the socket may be bound
> +		 * to any endpoint address, local or not.
> +		 */
> +		if ((inp->inp_flags & INP_BINDANY) == 0 &&
> +		    ifa_ifwithaddr_check((const struct sockaddr *)&sin) == 0)
> +			return (EADDRNOTAVAIL);
> +	}
> +
> +	if (lport != 0) {
> +		struct inpcb *t;
> +
> +		if (ntohs(lport) <= V_ipport_reservedhigh &&
> +		    ntohs(lport) >= V_ipport_reservedlow &&
> +		    priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT))
> +			return (EACCES);
> +
> +		if (!IN_MULTICAST(ntohl(laddr.s_addr)) &&
> +		    priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT) != 0) {
> +			t = in_pcblookup_local(inp->inp_pcbinfo, laddr, lport,
> +			    INPLOOKUP_WILDCARD, cred);
> +			if (t != NULL &&
> +			    (inp->inp_socket->so_type != SOCK_STREAM ||
> +			     in_nullhost(t->inp_faddr)) &&
> +			    (!in_nullhost(laddr) ||
> +			     !in_nullhost(t->inp_laddr) ||
> +			     (t->inp_socket->so_options & SO_REUSEPORT) ||
> +			     (t->inp_socket->so_options & SO_REUSEPORT_LB) == 0) &&
> +			    (inp->inp_cred->cr_uid != t->inp_cred->cr_uid))
> +				return (EADDRINUSE);
> +		}
> +		t = in_pcblookup_local(inp->inp_pcbinfo, laddr, lport,
> +		    lookupflags, cred);
> +		if (t != NULL && ((reuseport | reuseport_lb) &
> +		    t->inp_socket->so_options) == 0) {
> +#ifdef INET6
> +			if (!in_nullhost(laddr) ||
> +			    !in_nullhost(t->inp_laddr) ||
> +			    (inp->inp_vflag & INP_IPV6PROTO) == 0 ||
> +			    (t->inp_vflag & INP_IPV6PROTO) == 0)
> +#endif
> +				return (EADDRINUSE);
> +		}
> +	}
> +	return (0);
> +}
> +
>  /*
>   * Set up a bind operation on a PCB, performing port allocation
>   * as required, but do not actually modify the PCB. Callers can
> @@ -878,15 +965,9 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin,
> in_addr_t *laddrp, struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
>  	struct in_addr laddr;
>  	u_short lport = 0;
> -	int lookupflags = 0, reuseport = (so->so_options & SO_REUSEPORT);
> +	int lookupflags, sooptions;
>  	int error;
>  
> -	/*
> -	 * XXX: Maybe we could let SO_REUSEPORT_LB set SO_REUSEPORT bit here
> -	 * so that we don't have to add to the (already messy) code below.
> -	 */
> -	int reuseport_lb = (so->so_options & SO_REUSEPORT_LB);
> -
>  	/*
>  	 * No state changes, so read locks are sufficient here.
>  	 */
> @@ -896,7 +977,10 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin,
> in_addr_t *laddrp, laddr.s_addr = *laddrp;
>  	if (sin != NULL && laddr.s_addr != INADDR_ANY)
>  		return (EINVAL);
> -	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT|SO_REUSEPORT_LB)) == 0)
> +
> +	lookupflags = 0;
> +	sooptions = atomic_load_int(&so->so_options);
> +	if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT | SO_REUSEPORT_LB)) == 0)
>  		lookupflags = INPLOOKUP_WILDCARD;
>  	if (sin == NULL) {
>  		if ((error = prison_local_ip4(cred, &laddr)) != 0)
> @@ -918,73 +1002,11 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin,
> in_addr_t *laddrp, }
>  		laddr = sin->sin_addr;
>  
> -		/* NB: lport is left as 0 if the port isn't being changed. */
> -		if (IN_MULTICAST(ntohl(laddr.s_addr))) {
> -			/*
> -			 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
> -			 * allow complete duplication of binding if
> -			 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
> -			 * and a multicast address is bound on both
> -			 * new and duplicated sockets.
> -			 */
> -			if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) != 0)
> -				reuseport = SO_REUSEADDR|SO_REUSEPORT;
> -			/*
> -			 * XXX: How to deal with SO_REUSEPORT_LB here?
> -			 * Treat same as SO_REUSEPORT for now.
> -			 */
> -			if ((so->so_options &
> -			    (SO_REUSEADDR|SO_REUSEPORT_LB)) != 0)
> -				reuseport_lb = SO_REUSEADDR|SO_REUSEPORT_LB;
> -		} else if (!in_nullhost(laddr)) {
> -			sin->sin_port = 0;		/* yech... */
> -			bzero(&sin->sin_zero, sizeof(sin->sin_zero));
> -			/*
> -			 * Is the address a local IP address?
> -			 * If INP_BINDANY is set, then the socket may be bound
> -			 * to any endpoint address, local or not.
> -			 */
> -			if ((inp->inp_flags & INP_BINDANY) == 0 &&
> -			    ifa_ifwithaddr_check(
> -			    (const struct sockaddr *)sin) == 0)
> -				return (EADDRNOTAVAIL);
> -		}
> -		if (lport) {
> -			struct inpcb *t;
> -
> -			if (ntohs(lport) <= V_ipport_reservedhigh &&
> -			    ntohs(lport) >= V_ipport_reservedlow &&
> -			    priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT))
> -				return (EACCES);
> -
> -			if (!IN_MULTICAST(ntohl(laddr.s_addr)) &&
> -			    priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT) !=
> 0) {
> -				t = in_pcblookup_local(pcbinfo, laddr, lport,
> -				    INPLOOKUP_WILDCARD, cred);
> -				if (t != NULL &&
> -				    (so->so_type != SOCK_STREAM ||
> -				     in_nullhost(t->inp_faddr)) &&
> -				    (!in_nullhost(laddr) ||
> -				     !in_nullhost(t->inp_laddr) ||
> -				     (t->inp_socket->so_options & SO_REUSEPORT) ||
> -				     (t->inp_socket->so_options & SO_REUSEPORT_LB) ==
> 0) &&
> -				    (inp->inp_cred->cr_uid !=
> -				     t->inp_cred->cr_uid))
> -					return (EADDRINUSE);
> -			}
> -			t = in_pcblookup_local(pcbinfo, laddr, lport,
> -			    lookupflags, cred);
> -			if (t != NULL && ((reuseport | reuseport_lb) &
> -			    t->inp_socket->so_options) == 0) {
> -#ifdef INET6
> -				if (!in_nullhost(laddr) ||
> -				    !in_nullhost(t->inp_laddr) ||
> -				    (inp->inp_vflag & INP_IPV6PROTO) == 0 ||
> -				    (t->inp_vflag & INP_IPV6PROTO) == 0)
> -#endif
> -					return (EADDRINUSE);
> -			}
> -		}
> +		/* See if this address/port combo is available. */
> +		error = in_pcbbind_avail(inp, laddr, lport, sooptions,
> +		    lookupflags, cred);
> +		if (error != 0)
> +			return (error);
>  	}
>  	if (*lportp != 0)
>  		lport = *lportp;
> diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
> index 639ec68e19e2..6b5a9d3706be 100644
> --- a/sys/netinet6/in6_pcb.c
> +++ b/sys/netinet6/in6_pcb.c
> @@ -160,27 +160,157 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct
> ucred *cred) return (0);
>  }
>  
> +/*
> + * Determine whether the inpcb can be bound to the specified address/port tuple.
> + */
> +static int
> +in6_pcbbind_avail(struct inpcb *inp, const struct sockaddr_in6 *sin6,
> +    int sooptions, int lookupflags, struct ucred *cred)
> +{
> +	const struct in6_addr *laddr;
> +	int reuseport, reuseport_lb;
> +	u_short lport;
> +
> +	INP_LOCK_ASSERT(inp);
> +	INP_HASH_LOCK_ASSERT(inp->inp_pcbinfo);
> +
> +	laddr = &sin6->sin6_addr;
> +	lport = sin6->sin6_port;
> +
> +	reuseport = (sooptions & SO_REUSEPORT);
> +	reuseport_lb = (sooptions & SO_REUSEPORT_LB);
> +
> +	if (IN6_IS_ADDR_MULTICAST(laddr)) {
> +		/*
> +		 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
> +		 * allow compepte duplication of binding if
> +		 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
> +		 * and a multicast address is bound on both
> +		 * new and duplicated sockets.
> +		 */
> +		if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT)) != 0)
> +			reuseport = SO_REUSEADDR | SO_REUSEPORT;
> +		/*
> +		 * XXX: How to deal with SO_REUSEPORT_LB here?
> +		 * Treat same as SO_REUSEPORT for now.
> +		 */
> +		if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT_LB)) != 0)
> +			reuseport_lb = SO_REUSEADDR | SO_REUSEPORT_LB;
> +	} else if (!IN6_IS_ADDR_UNSPECIFIED(laddr)) {
> +		struct sockaddr_in6 sin6;
> +		struct epoch_tracker et;
> +		struct ifaddr *ifa;
> +
> +		memset(&sin6, 0, sizeof(sin6));
> +		sin6.sin6_family = AF_INET6;
> +		sin6.sin6_len = sizeof(sin6);
> +		sin6.sin6_addr = *laddr;
> +
> +		NET_EPOCH_ENTER(et);
> +		if ((ifa = ifa_ifwithaddr((const struct sockaddr *)&sin6)) ==
> +		    NULL && (inp->inp_flags & INP_BINDANY) == 0) {
> +			NET_EPOCH_EXIT(et);
> +			return (EADDRNOTAVAIL);
> +		}
> +
> +		/*
> +		 * XXX: bind to an anycast address might accidentally
> +		 * cause sending a packet with anycast source address.
> +		 * We should allow to bind to a deprecated address, since
> +		 * the application dares to use it.
> +		 */
> +		if (ifa != NULL &&
> +		    ((struct in6_ifaddr *)ifa)->ia6_flags &
> +		    (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY | IN6_IFF_DETACHED)) {
> +			NET_EPOCH_EXIT(et);
> +			return (EADDRNOTAVAIL);
> +		}
> +		NET_EPOCH_EXIT(et);
> +	}
> +
> +	if (lport != 0) {
> +		struct inpcb *t;
> +
> +		if (ntohs(lport) <= V_ipport_reservedhigh &&
> +		    ntohs(lport) >= V_ipport_reservedlow &&
> +		    priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT))
> +			return (EACCES);
> +
> +		if (!IN6_IS_ADDR_MULTICAST(laddr) &&
> +		    priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT) !=
> +		    0) {
> +			t = in6_pcblookup_local(inp->inp_pcbinfo, laddr, lport,
> +			    INPLOOKUP_WILDCARD, cred);
> +			if (t != NULL &&
> +			    (inp->inp_socket->so_type != SOCK_STREAM ||
> +			     IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) &&
> +			    (!IN6_IS_ADDR_UNSPECIFIED(laddr) ||
> +			     !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) ||
> +			     (t->inp_socket->so_options & SO_REUSEPORT) ||
> +			     (t->inp_socket->so_options & SO_REUSEPORT_LB) == 0) &&
> +			    (inp->inp_cred->cr_uid != t->inp_cred->cr_uid))
> +				return (EADDRINUSE);
> +
> +#ifdef INET
> +			if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
> +			    IN6_IS_ADDR_UNSPECIFIED(laddr)) {
> +				struct sockaddr_in sin;
> +
> +				in6_sin6_2_sin(&sin, sin6);
> +				t = in_pcblookup_local(inp->inp_pcbinfo,
> +				    sin.sin_addr, lport, INPLOOKUP_WILDCARD,
> +				    cred);
> +				if (t != NULL &&
> +				    (inp->inp_socket->so_type != SOCK_STREAM ||
> +				     in_nullhost(t->inp_faddr)) &&
> +				    (inp->inp_cred->cr_uid !=
> +				     t->inp_cred->cr_uid))
> +					return (EADDRINUSE);
> +			}
> +#endif
> +		}
> +		t = in6_pcblookup_local(inp->inp_pcbinfo, laddr, lport,
> +		    lookupflags, cred);
> +		if (t != NULL && ((reuseport | reuseport_lb) &
> +		    t->inp_socket->so_options) == 0)
> +			return (EADDRINUSE);
> +#ifdef INET
> +		if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
> +		    IN6_IS_ADDR_UNSPECIFIED(laddr)) {
> +			struct sockaddr_in sin;
> +
> +			in6_sin6_2_sin(&sin, sin6);
> +			t = in_pcblookup_local(inp->inp_pcbinfo, sin.sin_addr,
> +			   lport, lookupflags, cred);
> +			if (t != NULL && ((reuseport | reuseport_lb) &
> +			    t->inp_socket->so_options) == 0 &&
> +			    (!in_nullhost(t->inp_laddr) ||
> +			     (t->inp_vflag & INP_IPV6PROTO) != 0)) {
> +				return (EADDRINUSE);
> +			}
> +		}
> +#endif
> +	}
> +	return (0);
> +}
> +
>  int
>  in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, struct ucred *cred)
>  {
>  	struct socket *so = inp->inp_socket;
>  	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
>  	u_short	lport = 0;
> -	int error, lookupflags = 0;
> -	int reuseport = (so->so_options & SO_REUSEPORT);
> -
> -	/*
> -	 * XXX: Maybe we could let SO_REUSEPORT_LB set SO_REUSEPORT bit here
> -	 * so that we don't have to add to the (already messy) code below.
> -	 */
> -	int reuseport_lb = (so->so_options & SO_REUSEPORT_LB);
> +	int error, lookupflags, sooptions;
>  
>  	INP_WLOCK_ASSERT(inp);
>  	INP_HASH_WLOCK_ASSERT(pcbinfo);
>  
>  	if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
>  		return (EINVAL);
> -	if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT|SO_REUSEPORT_LB)) == 0)
> +
> +	lookupflags = 0;
> +	sooptions = atomic_load_int(&so->so_options);
> +	if ((sooptions & (SO_REUSEADDR | SO_REUSEPORT | SO_REUSEPORT_LB)) == 0)
>  		lookupflags = INPLOOKUP_WILDCARD;
>  	if (sin6 == NULL) {
>  		if ((error = prison_local_ip6(cred, &inp->in6p_laddr,
> @@ -199,115 +329,13 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, struct
> ucred *cred) ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0))) != 0)
>  			return (error);
>  
> -		lport = sin6->sin6_port;
> -		if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
> -			/*
> -			 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
> -			 * allow compepte duplication of binding if
> -			 * SO_REUSEPORT is set, or if SO_REUSEADDR is set
> -			 * and a multicast address is bound on both
> -			 * new and duplicated sockets.
> -			 */
> -			if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) != 0)
> -				reuseport = SO_REUSEADDR|SO_REUSEPORT;
> -			/*
> -			 * XXX: How to deal with SO_REUSEPORT_LB here?
> -			 * Treat same as SO_REUSEPORT for now.
> -			 */
> -			if ((so->so_options &
> -			    (SO_REUSEADDR|SO_REUSEPORT_LB)) != 0)
> -				reuseport_lb = SO_REUSEADDR|SO_REUSEPORT_LB;
> -		} else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
> -			struct epoch_tracker et;
> -			struct ifaddr *ifa;
> -
> -			sin6->sin6_port = 0;		/* yech... */
> -			NET_EPOCH_ENTER(et);
> -			if ((ifa = ifa_ifwithaddr((struct sockaddr *)sin6)) ==
> -			    NULL &&
> -			    (inp->inp_flags & INP_BINDANY) == 0) {
> -				NET_EPOCH_EXIT(et);
> -				return (EADDRNOTAVAIL);
> -			}
> -
> -			/*
> -			 * XXX: bind to an anycast address might accidentally
> -			 * cause sending a packet with anycast source address.
> -			 * We should allow to bind to a deprecated address, since
> -			 * the application dares to use it.
> -			 */
> -			if (ifa != NULL &&
> -			    ((struct in6_ifaddr *)ifa)->ia6_flags &
> -			    (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) {
> -				NET_EPOCH_EXIT(et);
> -				return (EADDRNOTAVAIL);
> -			}
> -			NET_EPOCH_EXIT(et);
> -		}
> -		if (lport) {
> -			struct inpcb *t;
> -
> -			if (ntohs(lport) <= V_ipport_reservedhigh &&
> -			    ntohs(lport) >= V_ipport_reservedlow &&
> -			    priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT))
> -				return (EACCES);
> -
> -			if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) &&
> -			    priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT) !=
> 0) {
> -				t = in6_pcblookup_local(pcbinfo,
> -				    &sin6->sin6_addr, lport,
> -				    INPLOOKUP_WILDCARD, cred);
> -				if (t != NULL &&
> -				    (so->so_type != SOCK_STREAM ||
> -				     IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) &&
> -				    (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
> -				     !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) ||
> -				     (t->inp_socket->so_options & SO_REUSEPORT) ||
> -				     (t->inp_socket->so_options & SO_REUSEPORT_LB) ==
> 0) &&
> -				    (inp->inp_cred->cr_uid !=
> -				     t->inp_cred->cr_uid))
> -					return (EADDRINUSE);
> -
> -#ifdef INET
> -				if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
> -				    IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
> -					struct sockaddr_in sin;
> -
> -					in6_sin6_2_sin(&sin, sin6);
> -					t = in_pcblookup_local(pcbinfo,
> -					    sin.sin_addr, lport,
> -					    INPLOOKUP_WILDCARD, cred);
> -					if (t != NULL &&
> -					    (so->so_type != SOCK_STREAM ||
> -					     in_nullhost(t->inp_faddr)) &&
> -					    (inp->inp_cred->cr_uid !=
> -					     t->inp_cred->cr_uid))
> -						return (EADDRINUSE);
> -				}
> -#endif
> -			}
> -			t = in6_pcblookup_local(pcbinfo, &sin6->sin6_addr,
> -			    lport, lookupflags, cred);
> -			if (t != NULL && ((reuseport | reuseport_lb) &
> -			    t->inp_socket->so_options) == 0)
> -				return (EADDRINUSE);
> -#ifdef INET
> -			if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
> -			    IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
> -				struct sockaddr_in sin;
> +		/* See if this address/port combo is available. */
> +		error = in6_pcbbind_avail(inp, sin6, sooptions, lookupflags,
> +		    cred);
> +		if (error != 0)
> +			return (error);
>  
> -				in6_sin6_2_sin(&sin, sin6);
> -				t = in_pcblookup_local(pcbinfo, sin.sin_addr,
> -				   lport, lookupflags, cred);
> -				if (t != NULL && ((reuseport | reuseport_lb) &
> -				    t->inp_socket->so_options) == 0 &&
> -				    (!in_nullhost(t->inp_laddr) ||
> -				     (t->inp_vflag & INP_IPV6PROTO) != 0)) {
> -					return (EADDRINUSE);
> -				}
> -			}
> -#endif
> -		}
> +		lport = sin6->sin6_port;
>  		inp->in6p_laddr = sin6->sin6_addr;
>  	}
>  	if (lport == 0) {
>