[PATCH 1/2] socket: clear port when unable to generate local port

Thomas Haller thaller at redhat.com
Fri Jul 10 05:58:50 PDT 2015


When running out of local ports, _nl_socket_generate_local_port_no_release()
would leave the socket with port UINT32_MAX. That means if nl_connect()
fails due to out-of-ports, it would leave the port id assigned to an
invalid port and the socket instance was not re-usable until the user
called nl_socket_set_local_port(). Fix that by resetting the local port
to zero.

Thereby, also change generate_local_port() to return zero when
running out of ports. zero is a more natural value for ~no port found~.
It also matches the port that _nl_socket_generate_local_port_no_release()
uses when failing to generate a port.

Also ensure that zero cannot be returned as valid port by generate_local_port().
Arguably, that would only be possible if (getpid() & 0x3FFFFF)
returns zero. Just be extra cautious.

Signed-off-by: Thomas Haller <thaller at redhat.com>
---
 lib/nl.c     |  2 +-
 lib/socket.c | 29 +++++++++++++++++++++--------
 2 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/lib/nl.c b/lib/nl.c
index f06e9a0..8c6d02f 100644
--- a/lib/nl.c
+++ b/lib/nl.c
@@ -133,7 +133,7 @@ int nl_connect(struct nl_sock *sk, int protocol)
 		while (1) {
 			port = _nl_socket_generate_local_port_no_release(sk);
 
-			if (port == UINT32_MAX) {
+			if (port == 0) {
 				NL_DBG(4, "nl_connect(%p): no more unused local ports.\n", sk);
 				_nl_socket_used_ports_release_all(used_ports);
 				err = -NLE_EXIST;
diff --git a/lib/socket.c b/lib/socket.c
index b29d1da..32be42e 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -108,7 +108,8 @@ static uint32_t generate_local_port(void)
 
 			nl_write_unlock(&port_map_lock);
 
-			return pid + (((uint32_t)n) << 22);
+			/* ensure we don't return zero. */
+			return (pid || n) ? (pid + (((uint32_t)n) << 22)) : 1024;
 		}
 	}
 
@@ -116,7 +117,7 @@ static uint32_t generate_local_port(void)
 
 	/* Out of sockets in our own PID namespace, what to do? FIXME */
 	NL_DBG(1, "Warning: Ran out of unique local port namespace\n");
-	return UINT32_MAX;
+	return 0;
 }
 
 static void release_local_port(uint32_t port)
@@ -124,9 +125,6 @@ static void release_local_port(uint32_t port)
 	int nr;
 	uint32_t mask;
 
-	if (port == UINT32_MAX)
-		return;
-
 	BUG_ON(port == 0);
 
 	nr = port >> 22;
@@ -167,7 +165,7 @@ void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port)
 	nr /= 32;
 
 	/*
-	BUG_ON(port == UINT32_MAX || port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
+	BUG_ON(port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
 	BUG_ON(used_ports[nr] & mask);
 	*/
 
@@ -345,8 +343,13 @@ uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
 	 * the previously generated port. */
 
 	port = generate_local_port();
-	sk->s_flags &= ~NL_OWN_PORT;
 	sk->s_local.nl_pid = port;
+	if (port == 0) {
+		/* failed to find an unsed port. Restore the socket to have an
+		 * unspecified port. */
+		sk->s_flags |= NL_OWN_PORT;
+	} else
+		sk->s_flags &= ~NL_OWN_PORT;
 	return port;
 }
 /** \endcond */
@@ -359,6 +362,8 @@ uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
 uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
 {
 	if (sk->s_local.nl_pid == 0) {
+		struct nl_sock *sk_mutable = (struct nl_sock *) sk;
+
 		/* modify the const argument sk. This is justified, because
 		 * nobody ever saw the local_port from externally. So, we
 		 * initilize it on first use.
@@ -368,7 +373,15 @@ uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
 		 * is not automatically threadsafe anyway, so the user is not
 		 * allowed to do that.
 		 */
-		return _nl_socket_generate_local_port_no_release((struct nl_sock *) sk);
+		sk_mutable->s_local.nl_pid = generate_local_port();
+		if (sk_mutable->s_local.nl_pid == 0) {
+			/* could not generate a local port. Assign UINT32_MAX to preserve
+			 * backward compatibility. A user who cares can clear that anyway
+			 * with nl_socket_set_local_port(). */
+			sk_mutable->s_local.nl_pid = UINT32_MAX;
+			sk_mutable->s_flags |= NL_OWN_PORT;
+		} else
+			sk_mutable->s_flags &= ~NL_OWN_PORT;
 	}
 	return sk->s_local.nl_pid;
 }
-- 
2.4.3




More information about the libnl mailing list