[libnl3/f19] backport retry on local port in use (rh #1097175) (3.2.22-3)
thaller
thaller at fedoraproject.org
Wed May 21 14:54:56 UTC 2014
commit ec4d19c3764fd43e2972bbd37e0a5dc4821de977
Author: Thomas Haller <thaller at redhat.com>
Date: Wed May 21 14:42:50 2014 +0200
backport retry on local port in use (rh #1097175) (3.2.22-3)
0002-rh1097175-nl-has-capability.patch | 89 +++++
0003-rh1097175-retry-local-port.patch | 632 ++++++++++++++++++++++++++++++++
libnl3.spec | 13 +-
3 files changed, 732 insertions(+), 2 deletions(-)
---
diff --git a/0002-rh1097175-nl-has-capability.patch b/0002-rh1097175-nl-has-capability.patch
new file mode 100644
index 0000000..74b1ae3
--- /dev/null
+++ b/0002-rh1097175-nl-has-capability.patch
@@ -0,0 +1,89 @@
+From 4780325418e9d34a4c914ab42e46585012527a82 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Thu, 13 Mar 2014 13:16:05 +0100
+Subject: [PATCH 1/1] utils: add nl_has_capability() function
+
+Acked-by: Thomas Graf <tgraf at suug.ch>
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+(cherry picked from commit 68d6bd7f37dc9a0c004b4355770e9c475fb964cd)
+---
+ include/netlink/utils.h | 8 ++++++++
+ lib/utils.c | 43 +++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 51 insertions(+)
+
+diff --git a/include/netlink/utils.h b/include/netlink/utils.h
+index 502341a..da46a55 100644
+--- a/include/netlink/utils.h
++++ b/include/netlink/utils.h
+@@ -79,6 +79,14 @@ extern void nl_new_line(struct nl_dump_params *);
+ extern void nl_dump(struct nl_dump_params *, const char *, ...);
+ extern void nl_dump_line(struct nl_dump_params *, const char *, ...);
+
++enum {
++ NL_CAPABILITY_NONE,
++
++ __NL_CAPABILITY_MAX
++#define NL_CAPABILITY_MAX (__NL_CAPABILITY_MAX - 1)
++};
++int nl_has_capability (int capability);
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/lib/utils.c b/lib/utils.c
+index 4457b1f..a8bfda4 100644
+--- a/lib/utils.c
++++ b/lib/utils.c
+@@ -1101,6 +1101,49 @@ void dump_from_ops(struct nl_object *obj, struct nl_dump_params *params)
+ obj->ce_ops->oo_dump[type](obj, params);
+ }
+
++/**
++ * Check for library capabilities
++ *
++ * @arg capability capability identifier
++ *
++ * Check whether the loaded libnl library supports a certain capability.
++ * This is useful so that applications can workaround known issues of
++ * libnl that are fixed in newer library versions, without
++ * having a hard dependency on the new version. It is also useful, for
++ * capabilities that cannot easily be detected using autoconf tests.
++ * The capabilities are integer constants with name NL_CAPABILITY_*.
++ *
++ * As this function is intended to detect capabilities at runtime,
++ * you might not want to depend during compile time on the NL_CAPABILITY_*
++ * names. Instead you can use their numeric values which are guaranteed not to
++ * change meaning.
++ *
++ * @return non zero if libnl supports a certain capability, 0 otherwise.
++ **/
++int nl_has_capability (int capability)
++{
++ static const uint8_t caps[ ( NL_CAPABILITY_MAX + 7 ) / 8 ] = {
++#define _NL_ASSERT(expr) ( 0 * sizeof(struct { unsigned int x: ( (!!(expr)) ? 1 : -1 ); }) )
++#define _NL_SETV(i, r, v) \
++ ( _NL_ASSERT( (v) == 0 || (i) * 8 + (r) == (v) - 1 ) + \
++ ( (v) == 0 ? 0 : (1 << (r)) ) )
++#define _NL_SET(i, v0, v1, v2, v3, v4, v5, v6, v7) \
++ [(i)] = ( \
++ _NL_SETV((i), 0, (v0)) | _NL_SETV((i), 4, (v4)) | \
++ _NL_SETV((i), 1, (v1)) | _NL_SETV((i), 5, (v5)) | \
++ _NL_SETV((i), 2, (v2)) | _NL_SETV((i), 6, (v6)) | \
++ _NL_SETV((i), 3, (v3)) | _NL_SETV((i), 7, (v7)) )
++#undef _NL_SET
++#undef _NL_SETV
++#undef _NL_ASSERT
++ };
++
++ if (capability <= 0 || capability > NL_CAPABILITY_MAX)
++ return 0;
++ capability--;
++ return (caps[capability / 8] & (1 << (capability % 8))) != 0;
++}
++
+ /** @endcond */
+
+ /** @} */
+--
+1.9.0
+
diff --git a/0003-rh1097175-retry-local-port.patch b/0003-rh1097175-retry-local-port.patch
new file mode 100644
index 0000000..018bba1
--- /dev/null
+++ b/0003-rh1097175-retry-local-port.patch
@@ -0,0 +1,632 @@
+From fe01b8509be5b64f3a6cde20e30fe3f72226272f Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Wed, 9 Apr 2014 12:08:50 +0200
+Subject: [PATCH 1/5] lib/socket: use proper typed constant UINT32_MAX for
+ uint32_t typed port
+
+This was a bug on architectures with native int type less then 32 bit.
+
+Acked-by: Thomas Graf <tgraf at suug.ch>
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+(cherry picked from commit 0fd510b3673f479637a6376db3d66dcb9f8911d0)
+
+Conflicts:
+ lib/socket.c
+---
+ lib/socket.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/lib/socket.c b/lib/socket.c
+index 1ca7783..959b122 100644
+--- a/lib/socket.c
++++ b/lib/socket.c
+@@ -82,21 +82,22 @@ static uint32_t generate_local_port(void)
+
+ nl_write_unlock(&port_map_lock);
+
+- return pid + (n << 22);
++ return pid + (((uint32_t)n) << 22);
+ }
+ }
+
+ nl_write_unlock(&port_map_lock);
+
+ /* Out of sockets in our own PID namespace, what to do? FIXME */
+- return UINT_MAX;
++ NL_DBG(1, "Warning: Ran out of unique local port namespace\n");
++ return UINT32_MAX;
+ }
+
+ static void release_local_port(uint32_t port)
+ {
+ int nr;
+
+- if (port == UINT_MAX)
++ if (port == UINT32_MAX)
+ return;
+
+ nr = port >> 22;
+@@ -125,7 +126,7 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
+ 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();
+- if (sk->s_local.nl_pid == UINT_MAX) {
++ if (sk->s_local.nl_pid == UINT32_MAX) {
+ nl_socket_free(sk);
+ return NULL;
+ }
+--
+1.9.0
+
+
+From 021d8ed60d6cfdc2f5a185c6aeb9135f8032dd1b Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Wed, 9 Apr 2014 12:08:51 +0200
+Subject: [PATCH 2/5] lib/socket: don't fail if no more local ports can be
+ assigned in nl_socket_alloc
+
+By failing inside of nl_socket_alloc(), the user can not even work around
+when running out of local ports. This patch changes that if there are no more
+local ports, we set the port to UINT32_MAX. This is a consistent behavior
+to calling nl_socket_set_local_port(sk, 0).
+
+In general, since nl_socket_set_local_port() does not restict the generated
+ports in any way we cannot assume to have a valid port. So the check in
+the constructor was harmful and users who ever encountered it (because they
+created 1024 libnl3 sockets) could not even work around it.
+
+Acked-by: Thomas Graf <tgraf at suug.ch>
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+(cherry picked from commit 0271578987088210d7d2d68addbd5e8fe27d4383)
+---
+ lib/socket.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/lib/socket.c b/lib/socket.c
+index 959b122..eb4a978 100644
+--- a/lib/socket.c
++++ b/lib/socket.c
+@@ -126,10 +126,6 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
+ 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();
+- if (sk->s_local.nl_pid == UINT32_MAX) {
+- nl_socket_free(sk);
+- return NULL;
+- }
+
+ return sk;
+ }
+--
+1.9.0
+
+
+From e87aa2e70e5a986827f262fb346f1840eba12db7 Mon Sep 17 00:00:00 2001
+From: Thomas Graf <tgraf at suug.ch>
+Date: Mon, 31 Mar 2014 13:21:06 +0200
+Subject: [PATCH 3/5] add BUG_ON macro
+
+(cherry picked from commit 34bfce62150d07cf9894f2d9cbd0c989a203ea52)
+---
+ include/netlink-private/netlink.h | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/include/netlink-private/netlink.h b/include/netlink-private/netlink.h
+index 2e511bf..f87dcb8 100644
+--- a/include/netlink-private/netlink.h
++++ b/include/netlink-private/netlink.h
+@@ -100,6 +100,13 @@ struct trans_list {
+ assert(0); \
+ } while (0)
+
++#define BUG_ON(condition) \
++ do { \
++ if (condition) \
++ BUG(); \
++ } while (0)
++
++
+ #define APPBUG(msg) \
+ do { \
+ fprintf(stderr, "APPLICATION BUG: %s:%d:%s: %s\n", \
+--
+1.9.0
+
+
+From 4f3772b45da9a40ea5fa38ffaa31363ad847fdcf Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Wed, 9 Apr 2014 12:08:52 +0200
+Subject: [PATCH 4/5] 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 at redhat.com>
+(cherry picked from commit 4dd5fdd0af2c0b7ffe1dbc49313f263dbb2e906f)
+
+Conflicts:
+ include/netlink/utils.h
+ lib/utils.c
+ libnl.sym.in
+---
+ include/Makefile.am | 1 +
+ include/netlink-private/socket.h | 31 +++++++++++
+ include/netlink/utils.h | 9 ++++
+ lib/nl.c | 62 +++++++++++++++++++---
+ lib/socket.c | 110 +++++++++++++++++++++++++++++++--------
+ lib/utils.c | 9 ++++
+ libnl.sym.in | 9 ++++
+ 7 files changed, 203 insertions(+), 28 deletions(-)
+ create mode 100644 include/netlink-private/socket.h
+ create mode 100644 libnl.sym.in
+
+diff --git a/include/Makefile.am b/include/Makefile.am
+index 3488c52..f791a35 100644
+--- a/include/Makefile.am
++++ b/include/Makefile.am
+@@ -127,6 +127,7 @@ noinst_HEADERS = \
+ linux/tc_ematch/tc_em_meta.h \
+ netlink-private/genl.h \
+ netlink-private/netlink.h \
++ netlink-private/socket.h \
+ netlink-private/tc.h \
+ netlink-private/types.h \
+ netlink-private/cache-api.h \
+diff --git a/include/netlink-private/socket.h b/include/netlink-private/socket.h
+new file mode 100644
+index 0000000..86a440c
+--- /dev/null
++++ b/include/netlink-private/socket.h
+@@ -0,0 +1,31 @@
++/*
++ * netlink-private/socket.h Private declarations for socket
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation version 2.1
++ * of the License.
++ *
++ * Copyright (c) 2014 Thomas Graf <tgraf at suug.ch>
++ */
++
++#ifndef NETLINK_SOCKET_PRIV_H_
++#define NETLINK_SOCKET_PRIV_H_
++
++#include <netlink-private/netlink.h>
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++int _nl_socket_is_local_port_unspecified (struct nl_sock *sk);
++uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk);
++
++void _nl_socket_used_ports_release_all(const uint32_t *used_ports);
++void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/include/netlink/utils.h b/include/netlink/utils.h
+index da46a55..e929cfd 100644
+--- a/include/netlink/utils.h
++++ b/include/netlink/utils.h
+@@ -82,6 +82,15 @@ extern void nl_dump_line(struct nl_dump_params *, const char *, ...);
+ enum {
+ NL_CAPABILITY_NONE,
+
++ /**
++ * Indicate that the local port is unspecified until the user accesses
++ * it (via nl_socket_get_local_port()) or until nl_connect(). More importantly,
++ * if the port is left unspecified, nl_connect() will retry generating another
++ * port when bind() fails with ADDRINUSE.
++ */
++ NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE = 4,
++#define NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE
++
+ __NL_CAPABILITY_MAX
+ #define NL_CAPABILITY_MAX (__NL_CAPABILITY_MAX - 1)
+ };
+diff --git a/lib/nl.c b/lib/nl.c
+index 565747a..37065d4 100644
+--- a/lib/nl.c
++++ b/lib/nl.c
+@@ -26,6 +26,7 @@
+ */
+
+ #include <netlink-private/netlink.h>
++#include <netlink-private/socket.h>
+ #include <netlink/netlink.h>
+ #include <netlink/utils.h>
+ #include <netlink/handlers.h>
+@@ -75,6 +76,16 @@
+ * be closed automatically if any of the `exec` family functions succeed.
+ * This is essential for multi threaded programs.
+ *
++ * @note The local port (`nl_socket_get_local_port()`) is unspecified after
++ * creating a new socket. It only gets determined when accessing the
++ * port the first time or during `nl_connect()`. When nl_connect()
++ * fails during `bind()` due to `ADDRINUSE`, it will retry with
++ * different ports if the port is unspecified. Unless you want to enforce
++ * the use of a specific local port, don't access the local port (or
++ * reset it to `unspecified` by calling `nl_socket_set_local_port(sk, 0)`).
++ * This capability is indicated by
++ * `%NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE`.
++ *
+ * @see nl_socket_alloc()
+ * @see nl_close()
+ *
+@@ -85,6 +96,7 @@
+ int nl_connect(struct nl_sock *sk, int protocol)
+ {
+ int err, flags = 0;
++ int errsv;
+ socklen_t addrlen;
+
+ #ifdef SOCK_CLOEXEC
+@@ -96,7 +108,9 @@ int nl_connect(struct nl_sock *sk, int protocol)
+
+ sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol);
+ if (sk->s_fd < 0) {
+- err = -nl_syserr2nlerr(errno);
++ errsv = errno;
++ NL_DBG(4, "nl_connect(%p): socket() failed with %d\n", sk, errsv);
++ err = -nl_syserr2nlerr(errsv);
+ goto errout;
+ }
+
+@@ -106,11 +120,45 @@ int nl_connect(struct nl_sock *sk, int protocol)
+ goto errout;
+ }
+
+- err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
+- sizeof(sk->s_local));
+- if (err < 0) {
+- err = -nl_syserr2nlerr(errno);
+- goto errout;
++ if (_nl_socket_is_local_port_unspecified (sk)) {
++ uint32_t port;
++ uint32_t used_ports[32] = { 0 };
++
++ while (1) {
++ port = _nl_socket_generate_local_port_no_release(sk);
++
++ 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)
++ break;
++
++ errsv = errno;
++ if (errsv == EADDRINUSE) {
++ NL_DBG(4, "nl_connect(%p): local port %u already in use. Retry.\n", sk, (unsigned) port);
++ _nl_socket_used_ports_set(used_ports, port);
++ } else {
++ NL_DBG(4, "nl_connect(%p): bind() for port %u failed with %d\n", sk, (unsigned) port, errsv);
++ _nl_socket_used_ports_release_all(used_ports);
++ err = -nl_syserr2nlerr(errsv);
++ goto errout;
++ }
++ }
++ _nl_socket_used_ports_release_all(used_ports);
++ } else {
++ err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
++ sizeof(sk->s_local));
++ if (err != 0) {
++ errsv = errno;
++ NL_DBG(4, "nl_connect(%p): bind() failed with %d\n", sk, errsv);
++ err = -nl_syserr2nlerr(errsv);
++ goto errout;
++ }
+ }
+
+ addrlen = sizeof(sk->s_local);
+@@ -405,7 +453,7 @@ void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg)
+
+ nlh = nlmsg_hdr(msg);
+ if (nlh->nlmsg_pid == NL_AUTO_PORT)
+- nlh->nlmsg_pid = sk->s_local.nl_pid;
++ nlh->nlmsg_pid = nl_socket_get_local_port(sk);
+
+ if (nlh->nlmsg_seq == NL_AUTO_SEQ)
+ nlh->nlmsg_seq = sk->s_seq_next++;
+diff --git a/lib/socket.c b/lib/socket.c
+index eb4a978..c08f053 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;
+ }
+
+diff --git a/lib/utils.c b/lib/utils.c
+index a8bfda4..f94d1d0 100644
+--- a/lib/utils.c
++++ b/lib/utils.c
+@@ -1133,6 +1133,15 @@ int nl_has_capability (int capability)
+ _NL_SETV((i), 1, (v1)) | _NL_SETV((i), 5, (v5)) | \
+ _NL_SETV((i), 2, (v2)) | _NL_SETV((i), 6, (v6)) | \
+ _NL_SETV((i), 3, (v3)) | _NL_SETV((i), 7, (v7)) )
++ _NL_SET(0,
++ 0,
++ 0,
++ 0,
++ NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE,
++ 0,
++ 0,
++ 0,
++ 0),
+ #undef _NL_SET
+ #undef _NL_SETV
+ #undef _NL_ASSERT
+diff --git a/libnl.sym.in b/libnl.sym.in
+new file mode 100644
+index 0000000..df8888c
+--- /dev/null
++++ b/libnl.sym.in
+@@ -0,0 +1,9 @@
++libnl_ at MAJ_VERSION@ {
++global:
++ *;
++local:
++ _nl_socket_generate_local_port_no_release;
++ _nl_socket_is_local_port_unspecified;
++ _nl_socket_used_ports_release_all;
++ _nl_socket_used_ports_set;
++};
+--
+1.9.0
+
+
+From 65705f5c0c64dbded1f3a161b4d34cdd7ee3d37d Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller at redhat.com>
+Date: Wed, 9 Apr 2014 12:08:53 +0200
+Subject: [PATCH 5/5] lib/socket: randomize the generated local port
+
+Instead of always trying the same order of ports when
+looking for an unused port, randomize the order (naively).
+
+As libnl-1 uses the same function, it is likely that two applications
+that are using both libraries generate the same ports. By chosing a
+different order how to select the local port, the chances are smaller
+for this to happen (however, it cannot avoid it entirely. The user
+and/or libnl3 still has to cope with the situation, that somebody
+else might already use the port).
+
+Signed-off-by: Thomas Haller <thaller at redhat.com>
+(cherry picked from commit 1f734a8f892abcd3f81637df4a089155aca1b66a)
+---
+ lib/socket.c | 29 ++++++++++++++++++++++++++---
+ 1 file changed, 26 insertions(+), 3 deletions(-)
+
+diff --git a/lib/socket.c b/lib/socket.c
+index c08f053..5f61b38 100644
+--- a/lib/socket.c
++++ b/lib/socket.c
+@@ -62,16 +62,39 @@ static NL_RW_LOCK(port_map_lock);
+
+ static uint32_t generate_local_port(void)
+ {
+- int i, n;
++ int i, j, n, m;
++ static uint16_t idx_state = 0;
+ uint32_t pid = getpid() & 0x3FFFFF;
+
+ nl_write_lock(&port_map_lock);
+
+- for (i = 0; i < 32; i++) {
++ if (idx_state == 0) {
++ uint32_t t = time(NULL);
++
++ /* from time to time (on average each 2^15 calls), the idx_state will
++ * be zero again. No problem, just "seed" anew with time(). */
++ idx_state = t ^ (t >> 16) ^ 0x3047;
++ } else
++ idx_state = idx_state + 20011; /* add prime number */
++
++ i = idx_state >> 5;
++ n = idx_state;
++ for (j = 0; j < 32; j++) {
++ /* walk the index somewhat randomized, with always leaving the block
++ * #0 as last. The reason is that libnl-1 will start at block #0,
++ * so just leave the first 32 ports preferably for libnl-1 owned sockets
++ * (this is relevant only if the applications ends up using both versions
++ * of the library and doesn't hurt otherwise). */
++ if (j == 31)
++ i = 0;
++ else
++ i = (((i-1) + 7) % 31) + 1;
++
+ if (used_ports_map[i] == 0xFFFFFFFF)
+ continue;
+
+- for (n = 0; n < 32; n++) {
++ for (m = 0; m < 32; m++) {
++ n = (n + 13) % 32;
+ if (1UL & (used_ports_map[i] >> n))
+ continue;
+
+--
+1.9.0
+
diff --git a/libnl3.spec b/libnl3.spec
index d37d067..f5b14c5 100644
--- a/libnl3.spec
+++ b/libnl3.spec
@@ -3,11 +3,14 @@ Group: Development/Libraries
License: LGPLv2
Name: libnl3
Version: 3.2.22
-Release: 2%{?dist}
+Release: 3%{?dist}
URL: http://www.infradead.org/~tgr/libnl/
Source: http://www.infradead.org/~tgr/libnl/files/libnl-%{version}.tar.gz
Source1: http://www.infradead.org/~tgr/libnl/files/libnl-doc-%{version}.tar.gz
Patch1: libnl-3.2.22-963111.patch
+Patch2: 0002-rh1097175-nl-has-capability.patch
+Patch3: 0003-rh1097175-retry-local-port.patch
+
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires: flex bison
BuildRequires: python
@@ -46,7 +49,9 @@ This package contains libnl3 API documentation
%prep
%setup -q -n libnl-%{version}
-%patch1 -p1
+%patch1 -p1
+%patch2 -p1 -b .0002-rh1097175-nl-has-capability.orig
+%patch3 -p1 -b .0003-rh1097175-retry-local-port.orig
tar -xzf %SOURCE1
@@ -99,6 +104,10 @@ find $RPM_BUILD_ROOT -name \*.la -delete
%doc libnl-doc-%{version}/api/*
%changelog
+* Wed May 21 2014 Thomas Haller <thaller at redhat.com> - 3.2.22-3
+- add nl_has_capability() function
+- retry local port on ADDRINUSE (rh #1097175)
+
* Mon Sep 23 2013 Paul Wouters <pwouters at redhat.com> - 3.2.22-2
- Update to 3.2.22 (rhbz#963111)
- Add patch for double tree crasher in rtnl_link_set_address_family()
More information about the scm-commits
mailing list