diff options
-rw-r--r-- | lib/nl.c | 29 | ||||
-rw-r--r-- | lib/socket.c | 33 |
2 files changed, 40 insertions, 22 deletions
@@ -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; } |