Page MenuHomeFreeBSD

D47590.diff
No OneTemporary

D47590.diff

diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -861,6 +861,93 @@
#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 @@
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 @@
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 @@
}
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
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -160,27 +160,157 @@
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 @@
((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) {

File Metadata

Mime Type
text/plain
Expires
Thu, Jan 16, 12:38 PM (20 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15825555
Default Alt Text
D47590.diff (15 KB)

Event Timeline