summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/nl.c29
-rw-r--r--lib/socket.c33
2 files changed, 40 insertions, 22 deletions
diff --git a/lib/nl.c b/lib/nl.c
index 1e1b1ed..737ebaa 100644
--- a/lib/nl.c
+++ b/lib/nl.c
@@ -106,6 +106,7 @@ int nl_connect(struct nl_sock *sk, int protocol)
socklen_t addrlen;
struct sockaddr_nl local = { 0 };
char buf[64];
+ int try_bind = 1;
#ifdef SOCK_CLOEXEC
flags |= SOCK_CLOEXEC;
@@ -130,20 +131,26 @@ int nl_connect(struct nl_sock *sk, int protocol)
if (_nl_socket_is_local_port_unspecified (sk)) {
uint32_t port;
uint32_t used_ports[32] = { 0 };
+ int ntries = 0;
while (1) {
+ if (ntries++ > 5) {
+ /* try only a few times. We hit this only if many ports are already in
+ * use but allocated *outside* libnl/generate_local_port(). */
+ nl_socket_set_local_port (sk, 0);
+ break;
+ }
+
port = _nl_socket_generate_local_port_no_release(sk);
+ if (port == 0)
+ break;
- if (port == UINT32_MAX) {
- NL_DBG(4, "nl_connect(%p): no more unused local ports.\n", sk);
- _nl_socket_used_ports_release_all(used_ports);
- err = -NLE_EXIST;
- goto errout;
- }
err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
sizeof(sk->s_local));
- if (err == 0)
+ if (err == 0) {
+ try_bind = 0;
break;
+ }
errsv = errno;
if (errsv == EADDRINUSE) {
@@ -158,7 +165,8 @@ int nl_connect(struct nl_sock *sk, int protocol)
}
}
_nl_socket_used_ports_release_all(used_ports);
- } else {
+ }
+ if (try_bind) {
err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
sizeof(sk->s_local));
if (err != 0) {
@@ -191,9 +199,8 @@ int nl_connect(struct nl_sock *sk, int protocol)
}
if (sk->s_local.nl_pid != local.nl_pid) {
- /* strange, the port id is not as expected. Set the local
- * port id to release a possibly generated port and un-own
- * it. */
+ /* The port id is different. That can happen if the port id was zero
+ * and kernel assigned a local port. */
nl_socket_set_local_port (sk, local.nl_pid);
}
sk->s_local = local;
diff --git a/lib/socket.c b/lib/socket.c
index b29d1da..eef07f0 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -108,15 +108,14 @@ 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. */
+ pid = pid + (((uint32_t)n) << 22);
+ return pid ? pid : 1024;
}
}
nl_write_unlock(&port_map_lock);
-
- /* 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 +123,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 +163,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 +341,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 +360,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 +371,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;
}