summaryrefslogtreecommitdiff
path: root/lib/socket.c
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2014-04-09 12:08:52 +0200
committerThomas Haller <thaller@redhat.com>2014-05-06 14:34:58 +0200
commit4dd5fdd0af2c0b7ffe1dbc49313f263dbb2e906f (patch)
tree77e2bdc68404e664f27b0aded7512865b4e00493 /lib/socket.c
parent0271578987088210d7d2d68addbd5e8fe27d4383 (diff)
downloadlibnl-4dd5fdd0af2c0b7ffe1dbc49313f263dbb2e906f.tar.gz
lib/socket: retry generate local port in nl_connect on ADDRINUSE
It can easily happen that the generated local netlink port is alrady in use. In that case bind will fail with ADDRINUSE. Users of libnl3 could workaround this, by managing the local ports themselves, but sometimes these users are libraries too and they also don't know which ports might be used by other components. This patch changes that nl_socket_alloc() no longer initilizes the local port id immediately. Instead it will be initialized when the user calls nl_socket_get_local_port() the first time and thereby shows interest in the value. If bind() fails with ADDRINUSE, check if the user ever cared about the local port, i.e. whether the local port is still unset. If it is still unset, assume that libnl should choose a suitable port and retry until an unused port can be found. Signed-off-by: Thomas Haller <thaller@redhat.com>
Diffstat (limited to 'lib/socket.c')
-rw-r--r--lib/socket.c110
1 files changed, 89 insertions, 21 deletions
diff --git a/lib/socket.c b/lib/socket.c
index eb4a9785..c08f053f 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -30,6 +30,7 @@
#include "defs.h"
#include <netlink-private/netlink.h>
+#include <netlink-private/socket.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/handlers.h>
@@ -96,17 +97,59 @@ static uint32_t generate_local_port(void)
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;
+ mask = 1UL << (nr % 32);
+ nr /= 32;
nl_write_lock(&port_map_lock);
- used_ports_map[nr / 32] &= ~(1 << (nr % 32));
+ BUG_ON((used_ports_map[nr] & mask) != mask);
+ used_ports_map[nr] &= ~mask;
nl_write_unlock(&port_map_lock);
}
+/** \cond skip */
+void _nl_socket_used_ports_release_all(const uint32_t *used_ports)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (used_ports[i] != 0) {
+ nl_write_lock(&port_map_lock);
+ for (; i < 32; i++) {
+ BUG_ON((used_ports_map[i] & used_ports[i]) != used_ports[i]);
+ used_ports_map[i] &= ~(used_ports[i]);
+ }
+ nl_write_unlock(&port_map_lock);
+ return;
+ }
+ }
+}
+
+void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port)
+{
+ int nr;
+ int32_t mask;
+
+ nr = port >> 22;
+ mask = 1UL << (nr % 32);
+ nr /= 32;
+
+ /*
+ BUG_ON(port == UINT32_MAX || port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
+ BUG_ON(used_ports[nr] & mask);
+ */
+
+ used_ports[nr] |= mask;
+}
+/** \endcond */
+
/**
* @name Allocation
* @{
@@ -125,7 +168,9 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
sk->s_local.nl_family = AF_NETLINK;
sk->s_peer.nl_family = AF_NETLINK;
sk->s_seq_expect = sk->s_seq_next = time(0);
- sk->s_local.nl_pid = generate_local_port();
+
+ /* the port is 0 (unspecified), meaning NL_OWN_PORT */
+ sk->s_flags = NL_OWN_PORT;
return sk;
}
@@ -261,6 +306,26 @@ void nl_socket_enable_auto_ack(struct nl_sock *sk)
/** @} */
+/** \cond skip */
+int _nl_socket_is_local_port_unspecified(struct nl_sock *sk)
+{
+ return (sk->s_local.nl_pid == 0);
+}
+
+uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
+{
+ uint32_t port;
+
+ /* reset the port to generate_local_port(), but do not release
+ * the previously generated port. */
+
+ port = generate_local_port();
+ sk->s_flags &= ~NL_OWN_PORT;
+ sk->s_local.nl_pid = port;
+ return port;
+}
+/** \endcond */
+
/**
* @name Source Idenficiation
* @{
@@ -268,6 +333,18 @@ void nl_socket_enable_auto_ack(struct nl_sock *sk)
uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
{
+ if (sk->s_local.nl_pid == 0) {
+ /* modify the const argument sk. This is justified, because
+ * nobody ever saw the local_port from externally. So, we
+ * initilize it on first use.
+ *
+ * Note that this also means that you cannot call this function
+ * from multiple threads without synchronization. But nl_sock
+ * 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);
+ }
return sk->s_local.nl_pid;
}
@@ -276,27 +353,18 @@ uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
* @arg sk Netlink socket.
* @arg port Local port identifier
*
- * Assigns a local port identifier to the socket. If port is 0
- * a unique port identifier will be generated automatically.
+ * Assigns a local port identifier to the socket.
+ *
+ * If port is 0, the port is reset to 'unspecified' as it is after newly
+ * calling nl_socket_alloc().
+ * Unspecified means, that the port will be generated automatically later
+ * on first use (either on nl_socket_get_local_port() or nl_connect()).
*/
void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port)
{
- if (port == 0) {
- port = generate_local_port();
- /*
- * Release local port after generation of a new one to be
- * able to change local port using nl_socket_set_local_port(, 0)
- */
- if (!(sk->s_flags & NL_OWN_PORT))
- release_local_port(sk->s_local.nl_pid);
- else
- sk->s_flags &= ~NL_OWN_PORT;
- } else {
- if (!(sk->s_flags & NL_OWN_PORT))
- release_local_port(sk->s_local.nl_pid);
- sk->s_flags |= NL_OWN_PORT;
- }
-
+ if (!(sk->s_flags & NL_OWN_PORT))
+ release_local_port(sk->s_local.nl_pid);
+ sk->s_flags |= NL_OWN_PORT;
sk->s_local.nl_pid = port;
}