Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F107532109
D47590.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D47590.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D47590: inpcb: Factor out parts of in6_pcbbind() and in_pcbbind_setup()
Attached
Detach File
Event Timeline
Log In to Comment