svn commit: r252710 - in head/sys: netinet netinet6

Mikolaj Golub trociny at FreeBSD.org
Thu Jul 4 18:38:02 UTC 2013


Author: trociny
Date: Thu Jul  4 18:38:00 2013
New Revision: 252710
URL: http://svnweb.freebsd.org/changeset/base/252710

Log:
  In r227207, to fix the issue with possible NULL inp_socket pointer
  dereferencing, when checking for SO_REUSEPORT option (and SO_REUSEADDR
  for multicast), INP_REUSEPORT flag was introduced to cache the socket
  option.  It was decided then that one flag would be enough to cache
  both SO_REUSEPORT and SO_REUSEADDR: when processing SO_REUSEADDR
  setsockopt(2), it was checked if it was called for a multicast address
  and INP_REUSEPORT was set accordingly.
  
  Unfortunately that approach does not work when setsockopt(2) is called
  before binding to a multicast address: the multicast check fails and
  INP_REUSEPORT is not set.
  
  Fix this by adding INP_REUSEADDR flag to unconditionally cache
  SO_REUSEADDR.
  
  PR:		179901
  Submitted by:	Michael Gmelin freebsd grem.de (initial version)
  Reviewed by:	rwatson
  MFC after:	1 week

Modified:
  head/sys/netinet/in_pcb.c
  head/sys/netinet/in_pcb.h
  head/sys/netinet/ip_output.c
  head/sys/netinet6/in6_pcb.c
  head/sys/netinet6/ip6_output.c

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c	Thu Jul  4 18:00:27 2013	(r252709)
+++ head/sys/netinet/in_pcb.c	Thu Jul  4 18:38:00 2013	(r252710)
@@ -467,6 +467,23 @@ in_pcb_lport(struct inpcb *inp, struct i
 
 	return (0);
 }
+
+/*
+ * Return cached socket options.
+ */
+short
+inp_so_options(const struct inpcb *inp)
+{
+   short so_options;
+
+   so_options = 0;
+
+   if ((inp->inp_flags2 & INP_REUSEPORT) != 0)
+	   so_options |= SO_REUSEPORT;
+   if ((inp->inp_flags2 & INP_REUSEADDR) != 0)
+	   so_options |= SO_REUSEADDR;
+   return (so_options);
+}
 #endif /* INET || INET6 */
 
 #ifdef INET
@@ -595,8 +612,7 @@ in_pcbbind_setup(struct inpcb *inp, stru
 				if (tw == NULL ||
 				    (reuseport & tw->tw_so_options) == 0)
 					return (EADDRINUSE);
-			} else if (t && (reuseport == 0 ||
-			    (t->inp_flags2 & INP_REUSEPORT) == 0)) {
+			} else if (t && (reuseport & inp_so_options(t)) == 0) {
 #ifdef INET6
 				if (ntohl(sin->sin_addr.s_addr) !=
 				    INADDR_ANY ||

Modified: head/sys/netinet/in_pcb.h
==============================================================================
--- head/sys/netinet/in_pcb.h	Thu Jul  4 18:00:27 2013	(r252709)
+++ head/sys/netinet/in_pcb.h	Thu Jul  4 18:38:00 2013	(r252710)
@@ -442,6 +442,7 @@ struct tcpcb *
 	inp_inpcbtotcpcb(struct inpcb *inp);
 void 	inp_4tuple_get(struct inpcb *inp, uint32_t *laddr, uint16_t *lp,
 		uint32_t *faddr, uint16_t *fp);
+short	inp_so_options(const struct inpcb *inp);
 
 #endif /* _KERNEL */
 
@@ -543,6 +544,7 @@ void 	inp_4tuple_get(struct inpcb *inp, 
 #define	INP_PCBGROUPWILD	0x00000004 /* in pcbgroup wildcard list */
 #define	INP_REUSEPORT		0x00000008 /* SO_REUSEPORT option is set */
 #define	INP_FREED		0x00000010 /* inp itself is not valid */
+#define	INP_REUSEADDR		0x00000020 /* SO_REUSEADDR option is set */
 
 /*
  * Flags passed to in_pcblookup*() functions.

Modified: head/sys/netinet/ip_output.c
==============================================================================
--- head/sys/netinet/ip_output.c	Thu Jul  4 18:00:27 2013	(r252709)
+++ head/sys/netinet/ip_output.c	Thu Jul  4 18:38:00 2013	(r252710)
@@ -900,13 +900,10 @@ ip_ctloutput(struct socket *so, struct s
 			switch (sopt->sopt_name) {
 			case SO_REUSEADDR:
 				INP_WLOCK(inp);
-				if (IN_MULTICAST(ntohl(inp->inp_laddr.s_addr))) {
-					if ((so->so_options &
-					    (SO_REUSEADDR | SO_REUSEPORT)) != 0)
-						inp->inp_flags2 |= INP_REUSEPORT;
-					else
-						inp->inp_flags2 &= ~INP_REUSEPORT;
-				}
+				if ((so->so_options & SO_REUSEADDR) != 0)
+					inp->inp_flags2 |= INP_REUSEADDR;
+				else
+					inp->inp_flags2 &= ~INP_REUSEADDR;
 				INP_WUNLOCK(inp);
 				error = 0;
 				break;

Modified: head/sys/netinet6/in6_pcb.c
==============================================================================
--- head/sys/netinet6/in6_pcb.c	Thu Jul  4 18:00:27 2013	(r252709)
+++ head/sys/netinet6/in6_pcb.c	Thu Jul  4 18:38:00 2013	(r252710)
@@ -243,8 +243,7 @@ in6_pcbbind(register struct inpcb *inp, 
 				if (tw == NULL ||
 				    (reuseport & tw->tw_so_options) == 0)
 					return (EADDRINUSE);
-			} else if (t && (reuseport == 0 ||
-			    (t->inp_flags2 & INP_REUSEPORT) == 0)) {
+			} else if (t && (reuseport & inp_so_options(t)) == 0) {
 				return (EADDRINUSE);
 			}
 #ifdef INET
@@ -265,8 +264,8 @@ in6_pcbbind(register struct inpcb *inp, 
 					     INP_IPV6PROTO) ==
 					     (t->inp_vflag & INP_IPV6PROTO))))
 						return (EADDRINUSE);
-				} else if (t && (reuseport == 0 ||
-				    (t->inp_flags2 & INP_REUSEPORT) == 0) &&
+				} else if (t &&
+				    (reuseport & inp_so_options(t)) == 0 &&
 				    (ntohl(t->inp_laddr.s_addr) != INADDR_ANY ||
 				    (t->inp_vflag & INP_IPV6PROTO) != 0))
 					return (EADDRINUSE);

Modified: head/sys/netinet6/ip6_output.c
==============================================================================
--- head/sys/netinet6/ip6_output.c	Thu Jul  4 18:00:27 2013	(r252709)
+++ head/sys/netinet6/ip6_output.c	Thu Jul  4 18:38:00 2013	(r252710)
@@ -1477,13 +1477,10 @@ ip6_ctloutput(struct socket *so, struct 
 			switch (sopt->sopt_name) {
 			case SO_REUSEADDR:
 				INP_WLOCK(in6p);
-				if (IN_MULTICAST(ntohl(in6p->inp_laddr.s_addr))) {
-					if ((so->so_options &
-					    (SO_REUSEADDR | SO_REUSEPORT)) != 0)
-						in6p->inp_flags2 |= INP_REUSEPORT;
-					else
-						in6p->inp_flags2 &= ~INP_REUSEPORT;
-				}
+				if ((so->so_options & SO_REUSEADDR) != 0)
+					in6p->inp_flags2 |= INP_REUSEADDR;
+				else
+					in6p->inp_flags2 &= ~INP_REUSEADDR;
 				INP_WUNLOCK(in6p);
 				error = 0;
 				break;


More information about the svn-src-all mailing list