mirror of
https://github.com/opnsense/src.git
synced 2026-06-07 07:42:26 -04:00
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
This commit is contained in:
parent
fcb5e8d0c1
commit
01f8ce8324
2 changed files with 242 additions and 192 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/* See if this address/port combo is available. */
|
||||
error = in6_pcbbind_avail(inp, sin6, sooptions, lookupflags,
|
||||
cred);
|
||||
if (error != 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;
|
||||
|
||||
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
|
||||
}
|
||||
inp->in6p_laddr = sin6->sin6_addr;
|
||||
}
|
||||
if (lport == 0) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue