[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