backport retry on local port in use (rh #1097176) (3.2.24-3)

This commit is contained in:
Thomas Haller 2014-05-21 17:19:48 +02:00
parent 92602dbecc
commit 5e7332abc4
5 changed files with 1006 additions and 1 deletions

View File

@ -0,0 +1,89 @@
From 6de4c9ea6c2ae4fa02f7f7b46f03e9df2ced157b Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@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@suug.ch>
Signed-off-by: Thomas Haller <thaller@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 c04d83f..18bd950 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -1111,6 +1111,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

View File

@ -0,0 +1,644 @@
From b87aba5b8ddf0475854265fdbf93a02501ab0ffa Mon Sep 17 00:00:00 2001
From: Thomas Graf <tgraf@suug.ch>
Date: Mon, 31 Mar 2014 13:21:06 +0200
Subject: [PATCH 1/5] link: Catch missing io_free() implementations
Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: Thomas Haller <thaller@redhat.com>
(cherry picked from commit 34bfce62150d07cf9894f2d9cbd0c989a203ea52)
---
include/netlink-private/netlink.h | 7 +++++++
lib/route/link.c | 4 ++++
2 files changed, 11 insertions(+)
diff --git a/include/netlink-private/netlink.h b/include/netlink-private/netlink.h
index 2b1a68e..43ca804 100644
--- a/include/netlink-private/netlink.h
+++ b/include/netlink-private/netlink.h
@@ -102,6 +102,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", \
diff --git a/lib/route/link.c b/lib/route/link.c
index 9979b5b..85790e4 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -194,6 +194,10 @@ static void release_link_info(struct rtnl_link *link)
if (io != NULL) {
if (io->io_free)
io->io_free(link);
+ else {
+ /* Catch missing io_free() implementations */
+ BUG_ON(link->l_info);
+ }
rtnl_link_info_ops_put(io);
link->l_info_ops = NULL;
}
--
1.9.0
From 88d7ec560f358b23e071acfc41700101de4f673a Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 9 Apr 2014 12:08:50 +0200
Subject: [PATCH 2/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@suug.ch>
Signed-off-by: Thomas Haller <thaller@redhat.com>
(cherry picked from commit 0fd510b3673f479637a6376db3d66dcb9f8911d0)
---
lib/socket.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lib/socket.c b/lib/socket.c
index 00d8d6a..959b122 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -82,7 +82,7 @@ static uint32_t generate_local_port(void)
nl_write_unlock(&port_map_lock);
- return pid + (n << 22);
+ return pid + (((uint32_t)n) << 22);
}
}
@@ -90,14 +90,14 @@ static uint32_t generate_local_port(void)
/* 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 UINT_MAX;
+ 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;
@@ -126,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 e029e9d1632d7c76338ee366fba097ea2cb6dc8b Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 9 Apr 2014 12:08:51 +0200
Subject: [PATCH 3/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@suug.ch>
Signed-off-by: Thomas Haller <thaller@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 02c22298f6a5b20ad67a90050fd91ca5b8f358fb Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@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@redhat.com>
(cherry picked from commit 4dd5fdd0af2c0b7ffe1dbc49313f263dbb2e906f)
Conflicts:
include/netlink/utils.h
lib/utils.c
---
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 | 5 ++
7 files changed, 199 insertions(+), 28 deletions(-)
create mode 100644 include/netlink-private/socket.h
diff --git a/include/Makefile.am b/include/Makefile.am
index 32f872f..ac5ce65 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -138,6 +138,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@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 4692490..25fd59c 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 18bd950..275138d 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -1143,6 +1143,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
index e8f6c53..df8888c 100644
--- a/libnl.sym.in
+++ b/libnl.sym.in
@@ -1,4 +1,9 @@
libnl_@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 04f64c10aea96c97dea0327c6a2ac80b1da63c7c Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@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@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

View File

@ -0,0 +1,157 @@
From b982d2da8f3511f0d21984d70aa18cc6c43338d2 Mon Sep 17 00:00:00 2001
From: Hiroaki KAWAI <kawai@stratosphere.co.jp>
Date: Wed, 9 Apr 2014 10:09:16 +0900
Subject: [PATCH 1/3] python: fix wrongly passing argument to function in
ObjIterator.next()
self.__next__() bound method does not take an extra argument.
https://github.com/thom311/libnl/pull/57
Signed-off-by: Hiroaki KAWAI <kawai@stratosphere.co.jp>
Signed-off-by: Thomas Haller <thaller@redhat.com>
(cherry picked from commit cb319e22f5680b49fad62dc7f0eb35b7d737cb3b)
---
python/netlink/core.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/netlink/core.py b/python/netlink/core.py
index fbd1c9e..e5864cf 100644
--- a/python/netlink/core.py
+++ b/python/netlink/core.py
@@ -449,7 +449,7 @@ class ObjIterator(object):
return capi.nl_cache_get_next(self._nl_object)
def next(self):
- return self.__next__(self)
+ return self.__next__()
def __next__(self):
if self._end:
--
1.9.0
From a6aab53a1ab360f50947db1b026286647ff94049 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Fri, 31 Jan 2014 14:15:13 +0100
Subject: [PATCH 2/3] route: fix return value of nl_rtgen_request()
According to documentation, nl_rtgen_request() returns 0 on success,
but before it returned the number of bytes sent.
Signed-off-by: Thomas Haller <thaller@redhat.com>
(cherry picked from commit b70174668b9867de573cf51471bc98bfe7fd2bc3)
---
lib/route/rtnl.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/lib/route/rtnl.c b/lib/route/rtnl.c
index 82397e9..6a55ca1 100644
--- a/lib/route/rtnl.c
+++ b/lib/route/rtnl.c
@@ -34,15 +34,20 @@
* Fills out a routing netlink request message and sends it out
* using nl_send_simple().
*
- * @return 0 on success or a negative error code.
+ * @return 0 on success or a negative error code. Due to a bug in
+ * older versions, this returned the number of bytes sent. So for
+ * compatibility, treat positive return values as success too.
*/
int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags)
{
+ int err;
struct rtgenmsg gmsg = {
.rtgen_family = family,
};
- return nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg));
+ err = nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg));
+
+ return err >= 0 ? 0 : err;
}
/** @} */
--
1.9.0
From 0e0e12bfc306b7f4ca945af6c8c31f1dc5549191 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Wed, 19 Feb 2014 19:22:13 +0100
Subject: [PATCH 3/3] utils: fix nl_msec2str() which always returned '0msec'
for whole second durations
If the duration was without subsecond part, the function always returned
'0msec', instead of giving the time in days, hours, minutes or seconds.
Regression introduced by commit b3fb89f445108677d405c62865b25aeea209d10a.
Signed-off-by: Thomas Haller <thaller@redhat.com>
Acked-by: Thomas Graf <tgraf@suug.ch>
(cherry picked from commit 3fb0aae0bc37eafe868d28f0f12ee8803d7ad266)
---
lib/utils.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/utils.c b/lib/utils.c
index 275138d..267368e 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -551,6 +551,11 @@ char * nl_msec2str(uint64_t msec, char *buf, size_t len)
static const char *units[5] = {"d", "h", "m", "s", "msec"};
char * const buf_orig = buf;
+ if (msec == 0) {
+ snprintf(buf, len, "0msec");
+ return buf_orig;
+ }
+
#define _SPLIT(idx, unit) if ((split[idx] = msec / unit)) msec %= unit
_SPLIT(0, 86400000); /* days */
_SPLIT(1, 3600000); /* hours */
@@ -559,11 +564,6 @@ char * nl_msec2str(uint64_t msec, char *buf, size_t len)
#undef _SPLIT
split[4] = msec;
- if (msec == 0) {
- snprintf(buf, len, "0msec");
- return buf_orig;
- }
-
for (i = 0; i < ARRAY_SIZE(split) && len; i++) {
int l;
if (split[i] == 0)
--
1.9.0
From a0688f9a6ef3f37cb6908251826d352a8f50d8e5 Mon Sep 17 00:00:00 2001
From: Cong Wang <xiyou.wangcong@gmail.com>
Date: Sun, 23 Mar 2014 12:02:10 -0700
Subject: [PATCH 1/1] act: fix a pointer in rtnl_act_msg_parse()
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: Thomas Haller <thaller@redhat.com>
(cherry picked from commit b8d90d9bb19b5d9e4e5f71c4f6bb0eaadd78d7a0)
---
lib/route/act.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/route/act.c b/lib/route/act.c
index 2bb222b..d3391ec 100644
--- a/lib/route/act.c
+++ b/lib/route/act.c
@@ -452,7 +452,7 @@ err_free:
static int rtnl_act_msg_parse(struct nlmsghdr *n, struct rtnl_act **act)
{
- struct rtnl_tc *tc = TC_CAST(act);
+ struct rtnl_tc *tc = TC_CAST(*act);
struct nl_cache *link_cache;
struct nlattr *tb[TCAA_MAX + 1];
struct tcamsg *tm;
--
1.9.0

View File

@ -0,0 +1,98 @@
From b95a9f0a7253f1d6d772bf0489bb0d55897411f9 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Thu, 13 Feb 2014 21:31:37 +0100
Subject: [PATCH 1/2] route: rtnl_route_build_msg() should not overwrite the
route scope
rtnl_route_build_msg() should allow the user to set the route scope
explicitly to RT_SCOPE_NOWHERE.
This is useful for IPv4 routes, because when deleting a route,
the kernel requires the scope to match, unless the scope is set to
RT_SCOPE_NOWHERE. Thus by setting the scope to RT_SCOPE_NOWHERE,
the user can delete a route, even without knowing its scope.
rtnl_route_build_msg() should only try to guess the scope, if it was
not explicitly specified.
Signed-off-by: Thomas Haller <thaller@redhat.com>
Acked-by: Thomas Graf <tgraf@suug.ch>
(cherry picked from commit 85ec9c7ad80c60f4f619472f2bb9d9595da93b26)
---
lib/route/route_obj.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c
index f2de523..dd4bf49 100644
--- a/lib/route/route_obj.c
+++ b/lib/route/route_obj.c
@@ -1198,7 +1198,7 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
if (route->rt_src)
rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src);
- if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE)
+ if (!(route->ce_mask & ROUTE_ATTR_SCOPE))
rtmsg.rtm_scope = rtnl_route_guess_scope(route);
if (rtnl_route_get_nnexthops(route) == 1) {
--
1.9.0
From 06633875c6ebbfae598edbc20a305f6c8fb55fc6 Mon Sep 17 00:00:00 2001
From: Thomas Haller <thaller@redhat.com>
Date: Thu, 13 Mar 2014 13:16:51 +0100
Subject: [PATCH 2/2] utils: indicate capability
NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE
This capability indicates that libnl does no longer overwrites
the route scope in rtnl_route_build_msg(), as fixed by commit
85ec9c7ad80c60f4f619472f2bb9d9595da93b26.
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: Thomas Haller <thaller@redhat.com>
(cherry picked from commit 015c4ee59b786fec35118c2a963532b3e05ba5a2)
Conflicts:
include/netlink/utils.h
lib/utils.c
---
include/netlink/utils.h | 8 ++++++++
lib/utils.c | 2 +-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/include/netlink/utils.h b/include/netlink/utils.h
index e929cfd..5b0d275 100644
--- a/include/netlink/utils.h
+++ b/include/netlink/utils.h
@@ -83,6 +83,14 @@ enum {
NL_CAPABILITY_NONE,
/**
+ * rtnl_route_build_msg() no longer guesses the route scope
+ * if explicitly set to RT_SCOPE_NOWHERE.
+ * @ingroup utils
+ */
+ NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE = 1,
+#define NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE
+
+ /**
* 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
diff --git a/lib/utils.c b/lib/utils.c
index 267368e..77d1ff0 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -1144,7 +1144,7 @@ int nl_has_capability (int capability)
_NL_SETV((i), 2, (v2)) | _NL_SETV((i), 6, (v6)) | \
_NL_SETV((i), 3, (v3)) | _NL_SETV((i), 7, (v7)) )
_NL_SET(0,
- 0,
+ NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE,
0,
0,
NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE,
--
1.9.0

View File

@ -3,11 +3,15 @@ Group: Development/Libraries
License: LGPLv2
Name: libnl3
Version: 3.2.24
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: 0001.ifa_flags-workaround.patch
Patch2: 0002-rh1097176-nl-has-capability.patch
Patch3: 0003-rh1097176-retry-local-port.patch
Patch4: 0004-backport-diverse-fixes.patch
Patch5: 0005-route-scope-in-route-build-msg.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildRequires: flex bison
BuildRequires: python
@ -47,6 +51,10 @@ This package contains libnl3 API documentation
%prep
%setup -q -n libnl-%{version}
%patch1 -p1 -b .0001.ifa_flags-workaround.orig
%patch2 -p1 -b .0002-rh1097175-nl-has-capability.orig
%patch3 -p1 -b .0003-rh1097175-retry-local-port.orig
%patch4 -p1 -b .0004-backport-diverse-fixes.orig
%patch5 -p1 -b .0005-route-scope-in-route-build-msg.orig
tar -xzf %SOURCE1
@ -99,6 +107,15 @@ find $RPM_BUILD_ROOT -name \*.la -delete
%doc libnl-doc-%{version}/api/*
%changelog
* Thu May 22 2014 Thomas Haller <thaller@redhat.com> - 3.2.24-3
- add nl_has_capability() function
- retry local port on ADDRINUSE (rh #1097175)
- python: fix passing wrong argument in netlink/core.py
- fix return value of nl_rtgen_request()
- fix nl_msec2str()
- fix crash in rtnl_act_msg_parse()
- fix rtnl_route_build_msg() not to guess the route scope if RT_SCOPE_NOWHERE
* Fri Apr 4 2014 Thomas Haller <thaller@redhat.com> - 3.2.24-2
- fix breaking on older kernels due to IFA_FLAGS attribute (rh #1063885)