diff --git a/0002-rh1097176-nl-has-capability.patch b/0002-rh1097176-nl-has-capability.patch new file mode 100644 index 0000000..35a6383 --- /dev/null +++ b/0002-rh1097176-nl-has-capability.patch @@ -0,0 +1,89 @@ +From 6de4c9ea6c2ae4fa02f7f7b46f03e9df2ced157b Mon Sep 17 00:00:00 2001 +From: Thomas Haller +Date: Thu, 13 Mar 2014 13:16:05 +0100 +Subject: [PATCH 1/1] utils: add nl_has_capability() function + +Acked-by: Thomas Graf +Signed-off-by: Thomas Haller +(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 + diff --git a/0003-rh1097176-retry-local-port.patch b/0003-rh1097176-retry-local-port.patch new file mode 100644 index 0000000..20cef64 --- /dev/null +++ b/0003-rh1097176-retry-local-port.patch @@ -0,0 +1,644 @@ +From b87aba5b8ddf0475854265fdbf93a02501ab0ffa Mon Sep 17 00:00:00 2001 +From: Thomas Graf +Date: Mon, 31 Mar 2014 13:21:06 +0200 +Subject: [PATCH 1/5] link: Catch missing io_free() implementations + +Signed-off-by: Thomas Graf +Signed-off-by: Thomas Haller +(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 +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 +Signed-off-by: Thomas Haller +(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 +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 +Signed-off-by: Thomas Haller +(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 +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 +(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 ++ */ ++ ++#ifndef NETLINK_SOCKET_PRIV_H_ ++#define NETLINK_SOCKET_PRIV_H_ ++ ++#include ++ ++#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 ++#include + #include + #include + #include +@@ -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 ++#include + #include + #include + #include +@@ -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 +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 +(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/0004-backport-diverse-fixes.patch b/0004-backport-diverse-fixes.patch new file mode 100644 index 0000000..8a1f7da --- /dev/null +++ b/0004-backport-diverse-fixes.patch @@ -0,0 +1,157 @@ +From b982d2da8f3511f0d21984d70aa18cc6c43338d2 Mon Sep 17 00:00:00 2001 +From: Hiroaki KAWAI +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 +Signed-off-by: Thomas Haller +(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 +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 +(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 +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 +Acked-by: Thomas Graf +(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 +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 +Acked-by: Thomas Graf +Signed-off-by: Thomas Haller +(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 + diff --git a/0005-route-scope-in-route-build-msg.patch b/0005-route-scope-in-route-build-msg.patch new file mode 100644 index 0000000..3ecc084 --- /dev/null +++ b/0005-route-scope-in-route-build-msg.patch @@ -0,0 +1,98 @@ +From b95a9f0a7253f1d6d772bf0489bb0d55897411f9 Mon Sep 17 00:00:00 2001 +From: Thomas Haller +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 +Acked-by: Thomas Graf +(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 +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 +Signed-off-by: Thomas Haller +(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 + diff --git a/libnl3.spec b/libnl3.spec index 5c5acf5..cd0825d 100644 --- a/libnl3.spec +++ b/libnl3.spec @@ -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 - 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 - 3.2.24-2 - fix breaking on older kernels due to IFA_FLAGS attribute (rh #1063885)