|
|
50bdc6 |
From 2b7b4ae45f5171231b562ba51be256d1e2c40401 Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Graf <tgraf@suug.ch>
|
|
|
50bdc6 |
Date: Mon, 31 Mar 2014 13:21:06 +0200
|
|
|
50bdc6 |
Subject: [PATCH 01/11] link: Catch missing io_free() implementations
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Signed-off-by: Thomas Graf <tgraf@suug.ch>
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
(cherry picked from commit 34bfce62150d07cf9894f2d9cbd0c989a203ea52)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
include/netlink-private/netlink.h | 7 +++++++
|
|
|
50bdc6 |
lib/route/link.c | 4 ++++
|
|
|
50bdc6 |
2 files changed, 11 insertions(+)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/include/netlink-private/netlink.h b/include/netlink-private/netlink.h
|
|
|
50bdc6 |
index 6ae6d17..83eefad 100644
|
|
|
50bdc6 |
--- a/include/netlink-private/netlink.h
|
|
|
50bdc6 |
+++ b/include/netlink-private/netlink.h
|
|
|
50bdc6 |
@@ -95,6 +95,13 @@ struct trans_list {
|
|
|
50bdc6 |
assert(0); \
|
|
|
50bdc6 |
} while (0)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
+#define BUG_ON(condition) \
|
|
|
50bdc6 |
+ do { \
|
|
|
50bdc6 |
+ if (condition) \
|
|
|
50bdc6 |
+ BUG(); \
|
|
|
50bdc6 |
+ } while (0)
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
#define APPBUG(msg) \
|
|
|
50bdc6 |
do { \
|
|
|
50bdc6 |
NL_DBG(1, "APPLICATION BUG: %s:%d:%s: %s\n", \
|
|
|
50bdc6 |
diff --git a/lib/route/link.c b/lib/route/link.c
|
|
|
50bdc6 |
index 8fe3376..1f27247 100644
|
|
|
50bdc6 |
--- a/lib/route/link.c
|
|
|
50bdc6 |
+++ b/lib/route/link.c
|
|
|
50bdc6 |
@@ -184,6 +184,10 @@ static void release_link_info(struct rtnl_link *link)
|
|
|
50bdc6 |
if (io != NULL) {
|
|
|
50bdc6 |
if (io->io_free)
|
|
|
50bdc6 |
io->io_free(link);
|
|
|
50bdc6 |
+ else {
|
|
|
50bdc6 |
+ /* Catch missing io_free() implementations */
|
|
|
50bdc6 |
+ BUG_ON(link->l_info);
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
rtnl_link_info_ops_put(io);
|
|
|
50bdc6 |
link->l_info_ops = NULL;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From 369857fcf8d6c2783be8c1d225ad192363245098 Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Graf <tgraf@suug.ch>
|
|
|
50bdc6 |
Date: Fri, 28 Jun 2013 18:53:16 +0200
|
|
|
50bdc6 |
Subject: [PATCH 02/11] socket: Warn via debug message if local port namespace
|
|
|
50bdc6 |
is exhausted
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Signed-off-by: Thomas Graf <tgraf@suug.ch>
|
|
|
50bdc6 |
(cherry picked from commit 2d0810eb93704defb2375d9861892d6041da4623)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
lib/socket.c | 1 +
|
|
|
50bdc6 |
1 file changed, 1 insertion(+)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index d3e636e..f3171f5 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -89,6 +89,7 @@ static uint32_t generate_local_port(void)
|
|
|
50bdc6 |
nl_write_unlock(&port_map_lock);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
/* Out of sockets in our own PID namespace, what to do? FIXME */
|
|
|
50bdc6 |
+ NL_DBG(1, "Warning: Ran out of unique local port namespace\n");
|
|
|
50bdc6 |
return UINT_MAX;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From bba9ce1426728f00eccc5aa97d6d6c954d383481 Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Wed, 9 Apr 2014 12:08:50 +0200
|
|
|
50bdc6 |
Subject: [PATCH 03/11] lib/socket: use proper typed constant UINT32_MAX for
|
|
|
50bdc6 |
uint32_t typed port
|
|
|
50bdc6 |
|
|
|
50bdc6 |
This was a bug on architectures with native int type less then 32 bit.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Acked-by: Thomas Graf <tgraf@suug.ch>
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
(cherry picked from commit 0fd510b3673f479637a6376db3d66dcb9f8911d0)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
lib/socket.c | 8 ++++----
|
|
|
50bdc6 |
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index f3171f5..fa12e25 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -82,7 +82,7 @@ static uint32_t generate_local_port(void)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
nl_write_unlock(&port_map_lock);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- return pid + (n << 22);
|
|
|
50bdc6 |
+ return pid + (((uint32_t)n) << 22);
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
@@ -90,14 +90,14 @@ static uint32_t generate_local_port(void)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
/* Out of sockets in our own PID namespace, what to do? FIXME */
|
|
|
50bdc6 |
NL_DBG(1, "Warning: Ran out of unique local port namespace\n");
|
|
|
50bdc6 |
- return UINT_MAX;
|
|
|
50bdc6 |
+ return UINT32_MAX;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
static void release_local_port(uint32_t port)
|
|
|
50bdc6 |
{
|
|
|
50bdc6 |
int nr;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- if (port == UINT_MAX)
|
|
|
50bdc6 |
+ if (port == UINT32_MAX)
|
|
|
50bdc6 |
return;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
nr = port >> 22;
|
|
|
50bdc6 |
@@ -126,7 +126,7 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
|
|
|
50bdc6 |
sk->s_peer.nl_family = AF_NETLINK;
|
|
|
50bdc6 |
sk->s_seq_expect = sk->s_seq_next = time(0);
|
|
|
50bdc6 |
sk->s_local.nl_pid = generate_local_port();
|
|
|
50bdc6 |
- if (sk->s_local.nl_pid == UINT_MAX) {
|
|
|
50bdc6 |
+ if (sk->s_local.nl_pid == UINT32_MAX) {
|
|
|
50bdc6 |
nl_socket_free(sk);
|
|
|
50bdc6 |
return NULL;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From b4c09ead0b289a74d86dd47fbf1081daafcd4826 Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Wed, 9 Apr 2014 12:08:51 +0200
|
|
|
50bdc6 |
Subject: [PATCH 04/11] lib/socket: don't fail if no more local ports can be
|
|
|
50bdc6 |
assigned in nl_socket_alloc
|
|
|
50bdc6 |
|
|
|
50bdc6 |
By failing inside of nl_socket_alloc(), the user can not even work around
|
|
|
50bdc6 |
when running out of local ports. This patch changes that if there are no more
|
|
|
50bdc6 |
local ports, we set the port to UINT32_MAX. This is a consistent behavior
|
|
|
50bdc6 |
to calling nl_socket_set_local_port(sk, 0).
|
|
|
50bdc6 |
|
|
|
50bdc6 |
In general, since nl_socket_set_local_port() does not restict the generated
|
|
|
50bdc6 |
ports in any way we cannot assume to have a valid port. So the check in
|
|
|
50bdc6 |
the constructor was harmful and users who ever encountered it (because they
|
|
|
50bdc6 |
created 1024 libnl3 sockets) could not even work around it.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Acked-by: Thomas Graf <tgraf@suug.ch>
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
(cherry picked from commit 0271578987088210d7d2d68addbd5e8fe27d4383)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
lib/socket.c | 4 ----
|
|
|
50bdc6 |
1 file changed, 4 deletions(-)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index fa12e25..f9e68ff 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -126,10 +126,6 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
|
|
|
50bdc6 |
sk->s_peer.nl_family = AF_NETLINK;
|
|
|
50bdc6 |
sk->s_seq_expect = sk->s_seq_next = time(0);
|
|
|
50bdc6 |
sk->s_local.nl_pid = generate_local_port();
|
|
|
50bdc6 |
- if (sk->s_local.nl_pid == UINT32_MAX) {
|
|
|
50bdc6 |
- nl_socket_free(sk);
|
|
|
50bdc6 |
- return NULL;
|
|
|
50bdc6 |
- }
|
|
|
50bdc6 |
|
|
|
50bdc6 |
return sk;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From 5de5c84c094fd178216accd2c29f32bfe5f7f67b Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Wed, 9 Apr 2014 12:08:52 +0200
|
|
|
50bdc6 |
Subject: [PATCH 05/11] lib/socket: retry generate local port in nl_connect on
|
|
|
50bdc6 |
ADDRINUSE
|
|
|
50bdc6 |
|
|
|
50bdc6 |
It can easily happen that the generated local netlink port is alrady in
|
|
|
50bdc6 |
use. In that case bind will fail with ADDRINUSE.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Users of libnl3 could workaround this, by managing the local ports
|
|
|
50bdc6 |
themselves, but sometimes these users are libraries too and they also
|
|
|
50bdc6 |
don't know which ports might be used by other components.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
This patch changes that nl_socket_alloc() no longer initilizes the local
|
|
|
50bdc6 |
port id immediately. Instead it will be initialized when the user calls
|
|
|
50bdc6 |
nl_socket_get_local_port() the first time and thereby shows interest in
|
|
|
50bdc6 |
the value.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
If bind() fails with ADDRINUSE, check if the user ever cared about the
|
|
|
50bdc6 |
local port, i.e. whether the local port is still unset. If it is still
|
|
|
50bdc6 |
unset, assume that libnl should choose a suitable port and retry until
|
|
|
50bdc6 |
an unused port can be found.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
|
|
|
50bdc6 |
[thaller@redhat.com: modify original patch to use explicit gcc attribute
|
|
|
50bdc6 |
to hide private symbols instead of "libnl.sym"]
|
|
|
50bdc6 |
|
|
|
50bdc6 |
(cherry picked from commit 4dd5fdd0af2c0b7ffe1dbc49313f263dbb2e906f)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
include/Makefile.am | 1 +
|
|
|
50bdc6 |
include/netlink-private/socket.h | 31 +++++++++++
|
|
|
50bdc6 |
include/netlink/utils.h | 9 +++
|
|
|
50bdc6 |
lib/nl.c | 62 ++++++++++++++++++---
|
|
|
50bdc6 |
lib/socket.c | 116 ++++++++++++++++++++++++++++++++-------
|
|
|
50bdc6 |
lib/utils.c | 2 +-
|
|
|
50bdc6 |
6 files changed, 192 insertions(+), 29 deletions(-)
|
|
|
50bdc6 |
create mode 100644 include/netlink-private/socket.h
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/include/Makefile.am b/include/Makefile.am
|
|
|
50bdc6 |
index b9487e0..d0ed008 100644
|
|
|
50bdc6 |
--- a/include/Makefile.am
|
|
|
50bdc6 |
+++ b/include/Makefile.am
|
|
|
50bdc6 |
@@ -125,6 +125,7 @@ noinst_HEADERS = \
|
|
|
50bdc6 |
linux/tc_ematch/tc_em_meta.h \
|
|
|
50bdc6 |
netlink-private/genl.h \
|
|
|
50bdc6 |
netlink-private/netlink.h \
|
|
|
50bdc6 |
+ netlink-private/socket.h \
|
|
|
50bdc6 |
netlink-private/tc.h \
|
|
|
50bdc6 |
netlink-private/types.h \
|
|
|
50bdc6 |
netlink-private/cache-api.h \
|
|
|
50bdc6 |
diff --git a/include/netlink-private/socket.h b/include/netlink-private/socket.h
|
|
|
50bdc6 |
new file mode 100644
|
|
|
50bdc6 |
index 0000000..86a440c
|
|
|
50bdc6 |
--- /dev/null
|
|
|
50bdc6 |
+++ b/include/netlink-private/socket.h
|
|
|
50bdc6 |
@@ -0,0 +1,31 @@
|
|
|
50bdc6 |
+/*
|
|
|
50bdc6 |
+ * netlink-private/socket.h Private declarations for socket
|
|
|
50bdc6 |
+ *
|
|
|
50bdc6 |
+ * This library is free software; you can redistribute it and/or
|
|
|
50bdc6 |
+ * modify it under the terms of the GNU Lesser General Public
|
|
|
50bdc6 |
+ * License as published by the Free Software Foundation version 2.1
|
|
|
50bdc6 |
+ * of the License.
|
|
|
50bdc6 |
+ *
|
|
|
50bdc6 |
+ * Copyright (c) 2014 Thomas Graf <tgraf@suug.ch>
|
|
|
50bdc6 |
+ */
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+#ifndef NETLINK_SOCKET_PRIV_H_
|
|
|
50bdc6 |
+#define NETLINK_SOCKET_PRIV_H_
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+#include <netlink-private/netlink.h>
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+#ifdef __cplusplus
|
|
|
50bdc6 |
+extern "C" {
|
|
|
50bdc6 |
+#endif
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+int _nl_socket_is_local_port_unspecified (struct nl_sock *sk);
|
|
|
50bdc6 |
+uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk);
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+void _nl_socket_used_ports_release_all(const uint32_t *used_ports);
|
|
|
50bdc6 |
+void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port);
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+#ifdef __cplusplus
|
|
|
50bdc6 |
+}
|
|
|
50bdc6 |
+#endif
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+#endif
|
|
|
50bdc6 |
diff --git a/include/netlink/utils.h b/include/netlink/utils.h
|
|
|
50bdc6 |
index 2094bb4..5b0d275 100644
|
|
|
50bdc6 |
--- a/include/netlink/utils.h
|
|
|
50bdc6 |
+++ b/include/netlink/utils.h
|
|
|
50bdc6 |
@@ -90,6 +90,15 @@ enum {
|
|
|
50bdc6 |
NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE = 1,
|
|
|
50bdc6 |
#define NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE
|
|
|
50bdc6 |
|
|
|
50bdc6 |
+ /**
|
|
|
50bdc6 |
+ * Indicate that the local port is unspecified until the user accesses
|
|
|
50bdc6 |
+ * it (via nl_socket_get_local_port()) or until nl_connect(). More importantly,
|
|
|
50bdc6 |
+ * if the port is left unspecified, nl_connect() will retry generating another
|
|
|
50bdc6 |
+ * port when bind() fails with ADDRINUSE.
|
|
|
50bdc6 |
+ */
|
|
|
50bdc6 |
+ NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE = 4,
|
|
|
50bdc6 |
+#define NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
__NL_CAPABILITY_MAX
|
|
|
50bdc6 |
#define NL_CAPABILITY_MAX (__NL_CAPABILITY_MAX - 1)
|
|
|
50bdc6 |
};
|
|
|
50bdc6 |
diff --git a/lib/nl.c b/lib/nl.c
|
|
|
50bdc6 |
index 565747a..37065d4 100644
|
|
|
50bdc6 |
--- a/lib/nl.c
|
|
|
50bdc6 |
+++ b/lib/nl.c
|
|
|
50bdc6 |
@@ -26,6 +26,7 @@
|
|
|
50bdc6 |
*/
|
|
|
50bdc6 |
|
|
|
50bdc6 |
#include <netlink-private/netlink.h>
|
|
|
50bdc6 |
+#include <netlink-private/socket.h>
|
|
|
50bdc6 |
#include <netlink/netlink.h>
|
|
|
50bdc6 |
#include <netlink/utils.h>
|
|
|
50bdc6 |
#include <netlink/handlers.h>
|
|
|
50bdc6 |
@@ -75,6 +76,16 @@
|
|
|
50bdc6 |
* be closed automatically if any of the `exec` family functions succeed.
|
|
|
50bdc6 |
* This is essential for multi threaded programs.
|
|
|
50bdc6 |
*
|
|
|
50bdc6 |
+ * @note The local port (`nl_socket_get_local_port()`) is unspecified after
|
|
|
50bdc6 |
+ * creating a new socket. It only gets determined when accessing the
|
|
|
50bdc6 |
+ * port the first time or during `nl_connect()`. When nl_connect()
|
|
|
50bdc6 |
+ * fails during `bind()` due to `ADDRINUSE`, it will retry with
|
|
|
50bdc6 |
+ * different ports if the port is unspecified. Unless you want to enforce
|
|
|
50bdc6 |
+ * the use of a specific local port, don't access the local port (or
|
|
|
50bdc6 |
+ * reset it to `unspecified` by calling `nl_socket_set_local_port(sk, 0)`).
|
|
|
50bdc6 |
+ * This capability is indicated by
|
|
|
50bdc6 |
+ * `%NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE`.
|
|
|
50bdc6 |
+ *
|
|
|
50bdc6 |
* @see nl_socket_alloc()
|
|
|
50bdc6 |
* @see nl_close()
|
|
|
50bdc6 |
*
|
|
|
50bdc6 |
@@ -85,6 +96,7 @@
|
|
|
50bdc6 |
int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
{
|
|
|
50bdc6 |
int err, flags = 0;
|
|
|
50bdc6 |
+ int errsv;
|
|
|
50bdc6 |
socklen_t addrlen;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
#ifdef SOCK_CLOEXEC
|
|
|
50bdc6 |
@@ -96,7 +108,9 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol);
|
|
|
50bdc6 |
if (sk->s_fd < 0) {
|
|
|
50bdc6 |
- err = -nl_syserr2nlerr(errno);
|
|
|
50bdc6 |
+ errsv = errno;
|
|
|
50bdc6 |
+ NL_DBG(4, "nl_connect(%p): socket() failed with %d\n", sk, errsv);
|
|
|
50bdc6 |
+ err = -nl_syserr2nlerr(errsv);
|
|
|
50bdc6 |
goto errout;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
@@ -106,11 +120,45 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
goto errout;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
|
|
|
50bdc6 |
- sizeof(sk->s_local));
|
|
|
50bdc6 |
- if (err < 0) {
|
|
|
50bdc6 |
- err = -nl_syserr2nlerr(errno);
|
|
|
50bdc6 |
- goto errout;
|
|
|
50bdc6 |
+ if (_nl_socket_is_local_port_unspecified (sk)) {
|
|
|
50bdc6 |
+ uint32_t port;
|
|
|
50bdc6 |
+ uint32_t used_ports[32] = { 0 };
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ while (1) {
|
|
|
50bdc6 |
+ port = _nl_socket_generate_local_port_no_release(sk);
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ if (port == UINT32_MAX) {
|
|
|
50bdc6 |
+ NL_DBG(4, "nl_connect(%p): no more unused local ports.\n", sk);
|
|
|
50bdc6 |
+ _nl_socket_used_ports_release_all(used_ports);
|
|
|
50bdc6 |
+ err = -NLE_EXIST;
|
|
|
50bdc6 |
+ goto errout;
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+ err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
|
|
|
50bdc6 |
+ sizeof(sk->s_local));
|
|
|
50bdc6 |
+ if (err == 0)
|
|
|
50bdc6 |
+ break;
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ errsv = errno;
|
|
|
50bdc6 |
+ if (errsv == EADDRINUSE) {
|
|
|
50bdc6 |
+ NL_DBG(4, "nl_connect(%p): local port %u already in use. Retry.\n", sk, (unsigned) port);
|
|
|
50bdc6 |
+ _nl_socket_used_ports_set(used_ports, port);
|
|
|
50bdc6 |
+ } else {
|
|
|
50bdc6 |
+ NL_DBG(4, "nl_connect(%p): bind() for port %u failed with %d\n", sk, (unsigned) port, errsv);
|
|
|
50bdc6 |
+ _nl_socket_used_ports_release_all(used_ports);
|
|
|
50bdc6 |
+ err = -nl_syserr2nlerr(errsv);
|
|
|
50bdc6 |
+ goto errout;
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+ _nl_socket_used_ports_release_all(used_ports);
|
|
|
50bdc6 |
+ } else {
|
|
|
50bdc6 |
+ err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
|
|
|
50bdc6 |
+ sizeof(sk->s_local));
|
|
|
50bdc6 |
+ if (err != 0) {
|
|
|
50bdc6 |
+ errsv = errno;
|
|
|
50bdc6 |
+ NL_DBG(4, "nl_connect(%p): bind() failed with %d\n", sk, errsv);
|
|
|
50bdc6 |
+ err = -nl_syserr2nlerr(errsv);
|
|
|
50bdc6 |
+ goto errout;
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
addrlen = sizeof(sk->s_local);
|
|
|
50bdc6 |
@@ -405,7 +453,7 @@ void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
nlh = nlmsg_hdr(msg);
|
|
|
50bdc6 |
if (nlh->nlmsg_pid == NL_AUTO_PORT)
|
|
|
50bdc6 |
- nlh->nlmsg_pid = sk->s_local.nl_pid;
|
|
|
50bdc6 |
+ nlh->nlmsg_pid = nl_socket_get_local_port(sk);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
if (nlh->nlmsg_seq == NL_AUTO_SEQ)
|
|
|
50bdc6 |
nlh->nlmsg_seq = sk->s_seq_next++;
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index f9e68ff..2d6a2d3 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -30,12 +30,15 @@
|
|
|
50bdc6 |
#include "defs.h"
|
|
|
50bdc6 |
|
|
|
50bdc6 |
#include <netlink-private/netlink.h>
|
|
|
50bdc6 |
+#include <netlink-private/socket.h>
|
|
|
50bdc6 |
#include <netlink/netlink.h>
|
|
|
50bdc6 |
#include <netlink/utils.h>
|
|
|
50bdc6 |
#include <netlink/handlers.h>
|
|
|
50bdc6 |
#include <netlink/msg.h>
|
|
|
50bdc6 |
#include <netlink/attr.h>
|
|
|
50bdc6 |
|
|
|
50bdc6 |
+#define NOEXPORT __attribute__ ((visibility ("hidden")))
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
static int default_cb = NL_CB_DEFAULT;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
static void __init init_default_cb(void)
|
|
|
50bdc6 |
@@ -96,17 +99,61 @@ static uint32_t generate_local_port(void)
|
|
|
50bdc6 |
static void release_local_port(uint32_t port)
|
|
|
50bdc6 |
{
|
|
|
50bdc6 |
int nr;
|
|
|
50bdc6 |
+ uint32_t mask;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
if (port == UINT32_MAX)
|
|
|
50bdc6 |
return;
|
|
|
50bdc6 |
-
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ BUG_ON(port == 0);
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
nr = port >> 22;
|
|
|
50bdc6 |
+ mask = 1UL << (nr % 32);
|
|
|
50bdc6 |
+ nr /= 32;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
nl_write_lock(&port_map_lock);
|
|
|
50bdc6 |
- used_ports_map[nr / 32] &= ~(1 << (nr % 32));
|
|
|
50bdc6 |
+ BUG_ON((used_ports_map[nr] & mask) != mask);
|
|
|
50bdc6 |
+ used_ports_map[nr] &= ~mask;
|
|
|
50bdc6 |
nl_write_unlock(&port_map_lock);
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
+/** \cond skip */
|
|
|
50bdc6 |
+NOEXPORT
|
|
|
50bdc6 |
+void _nl_socket_used_ports_release_all(const uint32_t *used_ports)
|
|
|
50bdc6 |
+{
|
|
|
50bdc6 |
+ int i;
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ for (i = 0; i < 32; i++) {
|
|
|
50bdc6 |
+ if (used_ports[i] != 0) {
|
|
|
50bdc6 |
+ nl_write_lock(&port_map_lock);
|
|
|
50bdc6 |
+ for (; i < 32; i++) {
|
|
|
50bdc6 |
+ BUG_ON((used_ports_map[i] & used_ports[i]) != used_ports[i]);
|
|
|
50bdc6 |
+ used_ports_map[i] &= ~(used_ports[i]);
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+ nl_write_unlock(&port_map_lock);
|
|
|
50bdc6 |
+ return;
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+}
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+NOEXPORT
|
|
|
50bdc6 |
+void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port)
|
|
|
50bdc6 |
+{
|
|
|
50bdc6 |
+ int nr;
|
|
|
50bdc6 |
+ int32_t mask;
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ nr = port >> 22;
|
|
|
50bdc6 |
+ mask = 1UL << (nr % 32);
|
|
|
50bdc6 |
+ nr /= 32;
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ /*
|
|
|
50bdc6 |
+ BUG_ON(port == UINT32_MAX || port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
|
|
|
50bdc6 |
+ BUG_ON(used_ports[nr] & mask);
|
|
|
50bdc6 |
+ */
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ used_ports[nr] |= mask;
|
|
|
50bdc6 |
+}
|
|
|
50bdc6 |
+/** \endcond */
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
/**
|
|
|
50bdc6 |
* @name Allocation
|
|
|
50bdc6 |
* @{
|
|
|
50bdc6 |
@@ -125,7 +172,9 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
|
|
|
50bdc6 |
sk->s_local.nl_family = AF_NETLINK;
|
|
|
50bdc6 |
sk->s_peer.nl_family = AF_NETLINK;
|
|
|
50bdc6 |
sk->s_seq_expect = sk->s_seq_next = time(0);
|
|
|
50bdc6 |
- sk->s_local.nl_pid = generate_local_port();
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ /* the port is 0 (unspecified), meaning NL_OWN_PORT */
|
|
|
50bdc6 |
+ sk->s_flags = NL_OWN_PORT;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
return sk;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
@@ -255,6 +304,28 @@ void nl_socket_enable_auto_ack(struct nl_sock *sk)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
/** @} */
|
|
|
50bdc6 |
|
|
|
50bdc6 |
+/** \cond skip */
|
|
|
50bdc6 |
+NOEXPORT
|
|
|
50bdc6 |
+int _nl_socket_is_local_port_unspecified(struct nl_sock *sk)
|
|
|
50bdc6 |
+{
|
|
|
50bdc6 |
+ return (sk->s_local.nl_pid == 0);
|
|
|
50bdc6 |
+}
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+NOEXPORT
|
|
|
50bdc6 |
+uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
|
|
|
50bdc6 |
+{
|
|
|
50bdc6 |
+ uint32_t port;
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ /* reset the port to generate_local_port(), but do not release
|
|
|
50bdc6 |
+ * the previously generated port. */
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ port = generate_local_port();
|
|
|
50bdc6 |
+ sk->s_flags &= ~NL_OWN_PORT;
|
|
|
50bdc6 |
+ sk->s_local.nl_pid = port;
|
|
|
50bdc6 |
+ return port;
|
|
|
50bdc6 |
+}
|
|
|
50bdc6 |
+/** \endcond */
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
/**
|
|
|
50bdc6 |
* @name Source Idenficiation
|
|
|
50bdc6 |
* @{
|
|
|
50bdc6 |
@@ -262,6 +333,18 @@ void nl_socket_enable_auto_ack(struct nl_sock *sk)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
|
|
|
50bdc6 |
{
|
|
|
50bdc6 |
+ if (sk->s_local.nl_pid == 0) {
|
|
|
50bdc6 |
+ /* modify the const argument sk. This is justified, because
|
|
|
50bdc6 |
+ * nobody ever saw the local_port from externally. So, we
|
|
|
50bdc6 |
+ * initilize it on first use.
|
|
|
50bdc6 |
+ *
|
|
|
50bdc6 |
+ * Note that this also means that you cannot call this function
|
|
|
50bdc6 |
+ * from multiple threads without synchronization. But nl_sock
|
|
|
50bdc6 |
+ * is not automatically threadsafe anyway, so the user is not
|
|
|
50bdc6 |
+ * allowed to do that.
|
|
|
50bdc6 |
+ */
|
|
|
50bdc6 |
+ return _nl_socket_generate_local_port_no_release((struct nl_sock *) sk);
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
return sk->s_local.nl_pid;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
@@ -270,27 +353,18 @@ uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
|
|
|
50bdc6 |
* @arg sk Netlink socket.
|
|
|
50bdc6 |
* @arg port Local port identifier
|
|
|
50bdc6 |
*
|
|
|
50bdc6 |
- * Assigns a local port identifier to the socket. If port is 0
|
|
|
50bdc6 |
- * a unique port identifier will be generated automatically.
|
|
|
50bdc6 |
+ * Assigns a local port identifier to the socket.
|
|
|
50bdc6 |
+ *
|
|
|
50bdc6 |
+ * If port is 0, the port is reset to 'unspecified' as it is after newly
|
|
|
50bdc6 |
+ * calling nl_socket_alloc().
|
|
|
50bdc6 |
+ * Unspecified means, that the port will be generated automatically later
|
|
|
50bdc6 |
+ * on first use (either on nl_socket_get_local_port() or nl_connect()).
|
|
|
50bdc6 |
*/
|
|
|
50bdc6 |
void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port)
|
|
|
50bdc6 |
{
|
|
|
50bdc6 |
- if (port == 0) {
|
|
|
50bdc6 |
- port = generate_local_port();
|
|
|
50bdc6 |
- /*
|
|
|
50bdc6 |
- * Release local port after generation of a new one to be
|
|
|
50bdc6 |
- * able to change local port using nl_socket_set_local_port(, 0)
|
|
|
50bdc6 |
- */
|
|
|
50bdc6 |
- if (!(sk->s_flags & NL_OWN_PORT))
|
|
|
50bdc6 |
- release_local_port(sk->s_local.nl_pid);
|
|
|
50bdc6 |
- else
|
|
|
50bdc6 |
- sk->s_flags &= ~NL_OWN_PORT;
|
|
|
50bdc6 |
- } else {
|
|
|
50bdc6 |
- if (!(sk->s_flags & NL_OWN_PORT))
|
|
|
50bdc6 |
- release_local_port(sk->s_local.nl_pid);
|
|
|
50bdc6 |
- sk->s_flags |= NL_OWN_PORT;
|
|
|
50bdc6 |
- }
|
|
|
50bdc6 |
-
|
|
|
50bdc6 |
+ if (!(sk->s_flags & NL_OWN_PORT))
|
|
|
50bdc6 |
+ release_local_port(sk->s_local.nl_pid);
|
|
|
50bdc6 |
+ sk->s_flags |= NL_OWN_PORT;
|
|
|
50bdc6 |
sk->s_local.nl_pid = port;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/utils.c b/lib/utils.c
|
|
|
50bdc6 |
index 44350c3..5149e07 100644
|
|
|
50bdc6 |
--- a/lib/utils.c
|
|
|
50bdc6 |
+++ b/lib/utils.c
|
|
|
50bdc6 |
@@ -1138,7 +1138,7 @@ int nl_has_capability (int capability)
|
|
|
50bdc6 |
NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE,
|
|
|
50bdc6 |
0,
|
|
|
50bdc6 |
0,
|
|
|
50bdc6 |
- 0,
|
|
|
50bdc6 |
+ NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE,
|
|
|
50bdc6 |
0,
|
|
|
50bdc6 |
0,
|
|
|
50bdc6 |
0,
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From 4177e716532b3263abb90c5a2afa6c60f37937e3 Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Wed, 9 Apr 2014 12:08:53 +0200
|
|
|
50bdc6 |
Subject: [PATCH 06/11] lib/socket: randomize the generated local port
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Instead of always trying the same order of ports when
|
|
|
50bdc6 |
looking for an unused port, randomize the order (naively).
|
|
|
50bdc6 |
|
|
|
50bdc6 |
As libnl-1 uses the same function, it is likely that two applications
|
|
|
50bdc6 |
that are using both libraries generate the same ports. By chosing a
|
|
|
50bdc6 |
different order how to select the local port, the chances are smaller
|
|
|
50bdc6 |
for this to happen (however, it cannot avoid it entirely. The user
|
|
|
50bdc6 |
and/or libnl3 still has to cope with the situation, that somebody
|
|
|
50bdc6 |
else might already use the port).
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
(cherry picked from commit 1f734a8f892abcd3f81637df4a089155aca1b66a)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
lib/socket.c | 29 ++++++++++++++++++++++++++---
|
|
|
50bdc6 |
1 file changed, 26 insertions(+), 3 deletions(-)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index 2d6a2d3..01cf2a2 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -64,16 +64,39 @@ static NL_RW_LOCK(port_map_lock);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
static uint32_t generate_local_port(void)
|
|
|
50bdc6 |
{
|
|
|
50bdc6 |
- int i, n;
|
|
|
50bdc6 |
+ int i, j, n, m;
|
|
|
50bdc6 |
+ static uint16_t idx_state = 0;
|
|
|
50bdc6 |
uint32_t pid = getpid() & 0x3FFFFF;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
nl_write_lock(&port_map_lock);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- for (i = 0; i < 32; i++) {
|
|
|
50bdc6 |
+ if (idx_state == 0) {
|
|
|
50bdc6 |
+ uint32_t t = time(NULL);
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ /* from time to time (on average each 2^15 calls), the idx_state will
|
|
|
50bdc6 |
+ * be zero again. No problem, just "seed" anew with time(). */
|
|
|
50bdc6 |
+ idx_state = t ^ (t >> 16) ^ 0x3047;
|
|
|
50bdc6 |
+ } else
|
|
|
50bdc6 |
+ idx_state = idx_state + 20011; /* add prime number */
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
+ i = idx_state >> 5;
|
|
|
50bdc6 |
+ n = idx_state;
|
|
|
50bdc6 |
+ for (j = 0; j < 32; j++) {
|
|
|
50bdc6 |
+ /* walk the index somewhat randomized, with always leaving the block
|
|
|
50bdc6 |
+ * #0 as last. The reason is that libnl-1 will start at block #0,
|
|
|
50bdc6 |
+ * so just leave the first 32 ports preferably for libnl-1 owned sockets
|
|
|
50bdc6 |
+ * (this is relevant only if the applications ends up using both versions
|
|
|
50bdc6 |
+ * of the library and doesn't hurt otherwise). */
|
|
|
50bdc6 |
+ if (j == 31)
|
|
|
50bdc6 |
+ i = 0;
|
|
|
50bdc6 |
+ else
|
|
|
50bdc6 |
+ i = (((i-1) + 7) % 31) + 1;
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
if (used_ports_map[i] == 0xFFFFFFFF)
|
|
|
50bdc6 |
continue;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- for (n = 0; n < 32; n++) {
|
|
|
50bdc6 |
+ for (m = 0; m < 32; m++) {
|
|
|
50bdc6 |
+ n = (n + 13) % 32;
|
|
|
50bdc6 |
if (1UL & (used_ports_map[i] >> n))
|
|
|
50bdc6 |
continue;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From 1e7b7e5472fd3ee2ba69d2029bac02e4cf4148a1 Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Thu, 5 Mar 2015 08:46:31 +0100
|
|
|
50bdc6 |
Subject: [PATCH 07/11] lib/socket: remove NL_SOCK_BUFSIZE_SET socket flag
|
|
|
50bdc6 |
|
|
|
50bdc6 |
The flag was not actually used.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
NL_SOCK_BUFSIZE_SET was only set by nl_socket_set_buffer_size().
|
|
|
50bdc6 |
Note that you can only call nl_socket_set_buffer_size() on a socket that
|
|
|
50bdc6 |
is already connected via nl_connect().
|
|
|
50bdc6 |
|
|
|
50bdc6 |
On first call, nl_connect() would always see NL_SOCK_BUFSIZE_SET unset, and
|
|
|
50bdc6 |
call nl_socket_set_buffer_size().
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Since the flag was never unset, when trying to connect a socket a second
|
|
|
50bdc6 |
time, we would not set the buffer size again. Which was a bug.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
(cherry picked from commit 15824e42730980132a9e52d0c9d6929808e5ae78)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
include/netlink-private/types.h | 1 -
|
|
|
50bdc6 |
lib/nl.c | 8 +++-----
|
|
|
50bdc6 |
lib/socket.c | 2 --
|
|
|
50bdc6 |
3 files changed, 3 insertions(+), 8 deletions(-)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
|
|
|
50bdc6 |
index 72570c4..70b95c1 100644
|
|
|
50bdc6 |
--- a/include/netlink-private/types.h
|
|
|
50bdc6 |
+++ b/include/netlink-private/types.h
|
|
|
50bdc6 |
@@ -19,7 +19,6 @@
|
|
|
50bdc6 |
#include <netlink/route/route.h>
|
|
|
50bdc6 |
#include <netlink-private/route/tc-api.h>
|
|
|
50bdc6 |
|
|
|
50bdc6 |
-#define NL_SOCK_BUFSIZE_SET (1<<0)
|
|
|
50bdc6 |
#define NL_SOCK_PASSCRED (1<<1)
|
|
|
50bdc6 |
#define NL_OWN_PORT (1<<2)
|
|
|
50bdc6 |
#define NL_MSG_PEEK (1<<3)
|
|
|
50bdc6 |
diff --git a/lib/nl.c b/lib/nl.c
|
|
|
50bdc6 |
index 37065d4..69d5f8f 100644
|
|
|
50bdc6 |
--- a/lib/nl.c
|
|
|
50bdc6 |
+++ b/lib/nl.c
|
|
|
50bdc6 |
@@ -114,11 +114,9 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
goto errout;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- if (!(sk->s_flags & NL_SOCK_BUFSIZE_SET)) {
|
|
|
50bdc6 |
- err = nl_socket_set_buffer_size(sk, 0, 0);
|
|
|
50bdc6 |
- if (err < 0)
|
|
|
50bdc6 |
- goto errout;
|
|
|
50bdc6 |
- }
|
|
|
50bdc6 |
+ err = nl_socket_set_buffer_size(sk, 0, 0);
|
|
|
50bdc6 |
+ if (err < 0)
|
|
|
50bdc6 |
+ goto errout;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
if (_nl_socket_is_local_port_unspecified (sk)) {
|
|
|
50bdc6 |
uint32_t port;
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index 01cf2a2..4741719 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -692,8 +692,6 @@ int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf)
|
|
|
50bdc6 |
if (err < 0)
|
|
|
50bdc6 |
return -nl_syserr2nlerr(errno);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- sk->s_flags |= NL_SOCK_BUFSIZE_SET;
|
|
|
50bdc6 |
-
|
|
|
50bdc6 |
return 0;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From 38a6d5589e7d0cfe44331687ad76271f35ab7e12 Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Thu, 5 Mar 2015 10:50:04 +0100
|
|
|
50bdc6 |
Subject: [PATCH 08/11] lib/nl: preserve s_local if nl_connect() fails
|
|
|
50bdc6 |
|
|
|
50bdc6 |
s_local.nl_pid is used to track the generated port unless NL_OWN_PORT is set.
|
|
|
50bdc6 |
Ensure that getsockname() doesn't overwrite the value and possibly reset
|
|
|
50bdc6 |
the local port manually to release the generated port.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
(cherry picked from commit 9614acf4c4354892b5b734cace4778acfc019999)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
lib/nl.c | 24 ++++++++++++++++--------
|
|
|
50bdc6 |
1 file changed, 16 insertions(+), 8 deletions(-)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/nl.c b/lib/nl.c
|
|
|
50bdc6 |
index 69d5f8f..b7a9ab4 100644
|
|
|
50bdc6 |
--- a/lib/nl.c
|
|
|
50bdc6 |
+++ b/lib/nl.c
|
|
|
50bdc6 |
@@ -98,6 +98,7 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
int err, flags = 0;
|
|
|
50bdc6 |
int errsv;
|
|
|
50bdc6 |
socklen_t addrlen;
|
|
|
50bdc6 |
+ struct sockaddr_nl local = { 0 };
|
|
|
50bdc6 |
|
|
|
50bdc6 |
#ifdef SOCK_CLOEXEC
|
|
|
50bdc6 |
flags |= SOCK_CLOEXEC;
|
|
|
50bdc6 |
@@ -159,32 +160,39 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- addrlen = sizeof(sk->s_local);
|
|
|
50bdc6 |
- err = getsockname(sk->s_fd, (struct sockaddr *) &sk->s_local,
|
|
|
50bdc6 |
+ addrlen = sizeof(local);
|
|
|
50bdc6 |
+ err = getsockname(sk->s_fd, (struct sockaddr *) &local,
|
|
|
50bdc6 |
&addrlen);
|
|
|
50bdc6 |
if (err < 0) {
|
|
|
50bdc6 |
err = -nl_syserr2nlerr(errno);
|
|
|
50bdc6 |
goto errout;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- if (addrlen != sizeof(sk->s_local)) {
|
|
|
50bdc6 |
+ if (addrlen != sizeof(local)) {
|
|
|
50bdc6 |
err = -NLE_NOADDR;
|
|
|
50bdc6 |
goto errout;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- if (sk->s_local.nl_family != AF_NETLINK) {
|
|
|
50bdc6 |
+ if (local.nl_family != AF_NETLINK) {
|
|
|
50bdc6 |
err = -NLE_AF_NOSUPPORT;
|
|
|
50bdc6 |
goto errout;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
+ if (sk->s_local.nl_pid != local.nl_pid) {
|
|
|
50bdc6 |
+ /* strange, the port id is not as expected. Set the local
|
|
|
50bdc6 |
+ * port id to release a possibly generated port and un-own
|
|
|
50bdc6 |
+ * it. */
|
|
|
50bdc6 |
+ nl_socket_set_local_port (sk, local.nl_pid);
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+ sk->s_local = local;
|
|
|
50bdc6 |
sk->s_proto = protocol;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
return 0;
|
|
|
50bdc6 |
errout:
|
|
|
50bdc6 |
- if (sk->s_fd != -1) {
|
|
|
50bdc6 |
- close(sk->s_fd);
|
|
|
50bdc6 |
- sk->s_fd = -1;
|
|
|
50bdc6 |
- }
|
|
|
50bdc6 |
+ if (sk->s_fd != -1) {
|
|
|
50bdc6 |
+ close(sk->s_fd);
|
|
|
50bdc6 |
+ sk->s_fd = -1;
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
|
|
|
50bdc6 |
return err;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From 3c00a3b7d518cee0ac2092487f38ff2e75b8728e Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Fri, 10 Jul 2015 14:58:50 +0200
|
|
|
50bdc6 |
Subject: [PATCH 09/11] socket: clear port when unable to generate local port
|
|
|
50bdc6 |
|
|
|
50bdc6 |
When running out of local ports, _nl_socket_generate_local_port_no_release()
|
|
|
50bdc6 |
would leave the socket with port UINT32_MAX. That means if nl_connect()
|
|
|
50bdc6 |
fails due to out-of-ports, it would leave the port id assigned to an
|
|
|
50bdc6 |
invalid port and the socket instance was not re-usable until the user
|
|
|
50bdc6 |
called nl_socket_set_local_port(). Fix that by resetting the local port
|
|
|
50bdc6 |
to zero.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Thereby, also change generate_local_port() to return zero when
|
|
|
50bdc6 |
running out of ports. zero is a more natural value for ~no port found~.
|
|
|
50bdc6 |
It also matches the port that _nl_socket_generate_local_port_no_release()
|
|
|
50bdc6 |
uses when failing to generate a port.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Also ensure that zero cannot be returned as valid port by generate_local_port().
|
|
|
50bdc6 |
Arguably, that would only be possible if (getpid() & 0x3FFFFF)
|
|
|
50bdc6 |
returns zero. Just be extra cautious.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
(cherry picked from commit f78c3e82398a505ccf7e297b4021f23559ad8977)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
lib/nl.c | 2 +-
|
|
|
50bdc6 |
lib/socket.c | 30 ++++++++++++++++++++++--------
|
|
|
50bdc6 |
2 files changed, 23 insertions(+), 9 deletions(-)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/nl.c b/lib/nl.c
|
|
|
50bdc6 |
index b7a9ab4..20de342 100644
|
|
|
50bdc6 |
--- a/lib/nl.c
|
|
|
50bdc6 |
+++ b/lib/nl.c
|
|
|
50bdc6 |
@@ -126,7 +126,7 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
while (1) {
|
|
|
50bdc6 |
port = _nl_socket_generate_local_port_no_release(sk);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- if (port == UINT32_MAX) {
|
|
|
50bdc6 |
+ if (port == 0) {
|
|
|
50bdc6 |
NL_DBG(4, "nl_connect(%p): no more unused local ports.\n", sk);
|
|
|
50bdc6 |
_nl_socket_used_ports_release_all(used_ports);
|
|
|
50bdc6 |
err = -NLE_EXIST;
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index 4741719..4f05d09 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -108,7 +108,9 @@ static uint32_t generate_local_port(void)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
nl_write_unlock(&port_map_lock);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- return pid + (((uint32_t)n) << 22);
|
|
|
50bdc6 |
+ /* ensure we don't return zero. */
|
|
|
50bdc6 |
+ pid = pid + (((uint32_t)n) << 22);
|
|
|
50bdc6 |
+ return pid ? pid : 1024;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
@@ -116,7 +118,7 @@ static uint32_t generate_local_port(void)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
/* Out of sockets in our own PID namespace, what to do? FIXME */
|
|
|
50bdc6 |
NL_DBG(1, "Warning: Ran out of unique local port namespace\n");
|
|
|
50bdc6 |
- return UINT32_MAX;
|
|
|
50bdc6 |
+ return 0;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
static void release_local_port(uint32_t port)
|
|
|
50bdc6 |
@@ -124,9 +126,6 @@ static void release_local_port(uint32_t port)
|
|
|
50bdc6 |
int nr;
|
|
|
50bdc6 |
uint32_t mask;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- if (port == UINT32_MAX)
|
|
|
50bdc6 |
- return;
|
|
|
50bdc6 |
-
|
|
|
50bdc6 |
BUG_ON(port == 0);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
nr = port >> 22;
|
|
|
50bdc6 |
@@ -169,7 +168,7 @@ void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port)
|
|
|
50bdc6 |
nr /= 32;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
/*
|
|
|
50bdc6 |
- BUG_ON(port == UINT32_MAX || port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
|
|
|
50bdc6 |
+ BUG_ON(port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
|
|
|
50bdc6 |
BUG_ON(used_ports[nr] & mask);
|
|
|
50bdc6 |
*/
|
|
|
50bdc6 |
|
|
|
50bdc6 |
@@ -343,8 +342,13 @@ uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
|
|
|
50bdc6 |
* the previously generated port. */
|
|
|
50bdc6 |
|
|
|
50bdc6 |
port = generate_local_port();
|
|
|
50bdc6 |
- sk->s_flags &= ~NL_OWN_PORT;
|
|
|
50bdc6 |
sk->s_local.nl_pid = port;
|
|
|
50bdc6 |
+ if (port == 0) {
|
|
|
50bdc6 |
+ /* failed to find an unsed port. Restore the socket to have an
|
|
|
50bdc6 |
+ * unspecified port. */
|
|
|
50bdc6 |
+ sk->s_flags |= NL_OWN_PORT;
|
|
|
50bdc6 |
+ } else
|
|
|
50bdc6 |
+ sk->s_flags &= ~NL_OWN_PORT;
|
|
|
50bdc6 |
return port;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
/** \endcond */
|
|
|
50bdc6 |
@@ -357,6 +361,8 @@ uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
|
|
|
50bdc6 |
uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
|
|
|
50bdc6 |
{
|
|
|
50bdc6 |
if (sk->s_local.nl_pid == 0) {
|
|
|
50bdc6 |
+ struct nl_sock *sk_mutable = (struct nl_sock *) sk;
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
/* modify the const argument sk. This is justified, because
|
|
|
50bdc6 |
* nobody ever saw the local_port from externally. So, we
|
|
|
50bdc6 |
* initilize it on first use.
|
|
|
50bdc6 |
@@ -366,7 +372,15 @@ uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
|
|
|
50bdc6 |
* is not automatically threadsafe anyway, so the user is not
|
|
|
50bdc6 |
* allowed to do that.
|
|
|
50bdc6 |
*/
|
|
|
50bdc6 |
- return _nl_socket_generate_local_port_no_release((struct nl_sock *) sk);
|
|
|
50bdc6 |
+ sk_mutable->s_local.nl_pid = generate_local_port();
|
|
|
50bdc6 |
+ if (sk_mutable->s_local.nl_pid == 0) {
|
|
|
50bdc6 |
+ /* could not generate a local port. Assign UINT32_MAX to preserve
|
|
|
50bdc6 |
+ * backward compatibility. A user who cares can clear that anyway
|
|
|
50bdc6 |
+ * with nl_socket_set_local_port(). */
|
|
|
50bdc6 |
+ sk_mutable->s_local.nl_pid = UINT32_MAX;
|
|
|
50bdc6 |
+ sk_mutable->s_flags |= NL_OWN_PORT;
|
|
|
50bdc6 |
+ } else
|
|
|
50bdc6 |
+ sk_mutable->s_flags &= ~NL_OWN_PORT;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
return sk->s_local.nl_pid;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From eb5e052347de18e1133d3cbf507e31d05cd5db6e Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Fri, 10 Jul 2015 14:58:51 +0200
|
|
|
50bdc6 |
Subject: [PATCH 10/11] socket: add fallback for nl_connect() by trying to bind
|
|
|
50bdc6 |
to unspecified local port
|
|
|
50bdc6 |
|
|
|
50bdc6 |
libnl allows the user to explicitly set the local port before connecting
|
|
|
50bdc6 |
the socket. A more convenient way is to leave the local port unspecified
|
|
|
50bdc6 |
and let libnl generate a port id.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
As it is, generate_local_port() would try at most 1024 ports, that
|
|
|
50bdc6 |
means if a user tries to connect more sockets, the automatism will
|
|
|
50bdc6 |
fail.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Kernel also supports choosing the local port itself (via netlink_autobind()).
|
|
|
50bdc6 |
So, this could be fixed by always leaving the port unspecified and let
|
|
|
50bdc6 |
kernel decide on the port. For that we could entirely drop generate_local_port().
|
|
|
50bdc6 |
|
|
|
50bdc6 |
There are however problems with that:
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- it is unclear why generate_local_port() was even introduced in the
|
|
|
50bdc6 |
first place instead of always relying kernel. This code already
|
|
|
50bdc6 |
appeared in libnl-1, so maybe there was a good reason for it or
|
|
|
50bdc6 |
it is necessary on some kernel versions.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- The deprecated libnl-1 library also uses a form of generate_local_port().
|
|
|
50bdc6 |
Its first guess would always be getpid(), but the problem is that
|
|
|
50bdc6 |
it would not retry on EADDRINUSE. Currently libnl-3 generates ports in
|
|
|
50bdc6 |
a different sequence and will not generate a conflicting port (until it
|
|
|
50bdc6 |
already exhausted 1016 other ports).
|
|
|
50bdc6 |
Hence, currently if your application uses libnl1 and libnl3
|
|
|
50bdc6 |
together, the automatism might just work without conflicts
|
|
|
50bdc6 |
(commit 1f734a8f892abcd3f81637df4a089155aca1b66a).
|
|
|
50bdc6 |
Accidently, kernel/netlink_autobind() also first tries the process
|
|
|
50bdc6 |
id as port. That means, if we change libnl-3 to leave the decision
|
|
|
50bdc6 |
to kernel, and
|
|
|
50bdc6 |
- the application connects sockets both via libnl-1 and libnl-3
|
|
|
50bdc6 |
- and the libnl-3 socket happens to connect first
|
|
|
50bdc6 |
then the libnl-1 socket would fail to connect without retrying
|
|
|
50bdc6 |
another port.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- Removing generate_local_port() entirely changes behavior in the
|
|
|
50bdc6 |
following case:
|
|
|
50bdc6 |
|
|
|
50bdc6 |
sk = nl_socket_alloc();
|
|
|
50bdc6 |
/* accessing local port before connecting the socket used to
|
|
|
50bdc6 |
* freeze the local port to the generated value. */
|
|
|
50bdc6 |
port = nl_socket_get_local_port(sk);
|
|
|
50bdc6 |
nl_connect(sk, NETLINK_...);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Maybe the issues are minor and it would simplify the code just to get
|
|
|
50bdc6 |
rid of the cruft. But instead fix the issue without changing behavior.
|
|
|
50bdc6 |
Just keep trying with generate_local_port() first, before fallback to
|
|
|
50bdc6 |
kernel.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Reported-by: Julien Courtat <julien.courtat@6wind.com>
|
|
|
50bdc6 |
Signed-off-by: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
|
|
|
50bdc6 |
http://lists.infradead.org/pipermail/libnl/2015-June/001889.html
|
|
|
50bdc6 |
(cherry picked from commit 96e1e5bdc2e803700055395cc3c428fa2525d1ca)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
lib/nl.c | 29 ++++++++++++++++++-----------
|
|
|
50bdc6 |
lib/socket.c | 3 ---
|
|
|
50bdc6 |
2 files changed, 18 insertions(+), 14 deletions(-)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/nl.c b/lib/nl.c
|
|
|
50bdc6 |
index 20de342..bbd1c14 100644
|
|
|
50bdc6 |
--- a/lib/nl.c
|
|
|
50bdc6 |
+++ b/lib/nl.c
|
|
|
50bdc6 |
@@ -99,6 +99,7 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
int errsv;
|
|
|
50bdc6 |
socklen_t addrlen;
|
|
|
50bdc6 |
struct sockaddr_nl local = { 0 };
|
|
|
50bdc6 |
+ int try_bind = 1;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
#ifdef SOCK_CLOEXEC
|
|
|
50bdc6 |
flags |= SOCK_CLOEXEC;
|
|
|
50bdc6 |
@@ -122,20 +123,26 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
if (_nl_socket_is_local_port_unspecified (sk)) {
|
|
|
50bdc6 |
uint32_t port;
|
|
|
50bdc6 |
uint32_t used_ports[32] = { 0 };
|
|
|
50bdc6 |
+ int ntries = 0;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
while (1) {
|
|
|
50bdc6 |
+ if (ntries++ > 5) {
|
|
|
50bdc6 |
+ /* try only a few times. We hit this only if many ports are already in
|
|
|
50bdc6 |
+ * use but allocated *outside* libnl/generate_local_port(). */
|
|
|
50bdc6 |
+ nl_socket_set_local_port (sk, 0);
|
|
|
50bdc6 |
+ break;
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+
|
|
|
50bdc6 |
port = _nl_socket_generate_local_port_no_release(sk);
|
|
|
50bdc6 |
+ if (port == 0)
|
|
|
50bdc6 |
+ break;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- if (port == 0) {
|
|
|
50bdc6 |
- NL_DBG(4, "nl_connect(%p): no more unused local ports.\n", sk);
|
|
|
50bdc6 |
- _nl_socket_used_ports_release_all(used_ports);
|
|
|
50bdc6 |
- err = -NLE_EXIST;
|
|
|
50bdc6 |
- goto errout;
|
|
|
50bdc6 |
- }
|
|
|
50bdc6 |
err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
|
|
|
50bdc6 |
sizeof(sk->s_local));
|
|
|
50bdc6 |
- if (err == 0)
|
|
|
50bdc6 |
+ if (err == 0) {
|
|
|
50bdc6 |
+ try_bind = 0;
|
|
|
50bdc6 |
break;
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
|
|
|
50bdc6 |
errsv = errno;
|
|
|
50bdc6 |
if (errsv == EADDRINUSE) {
|
|
|
50bdc6 |
@@ -149,7 +156,8 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
_nl_socket_used_ports_release_all(used_ports);
|
|
|
50bdc6 |
- } else {
|
|
|
50bdc6 |
+ }
|
|
|
50bdc6 |
+ if (try_bind) {
|
|
|
50bdc6 |
err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
|
|
|
50bdc6 |
sizeof(sk->s_local));
|
|
|
50bdc6 |
if (err != 0) {
|
|
|
50bdc6 |
@@ -179,9 +187,8 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
if (sk->s_local.nl_pid != local.nl_pid) {
|
|
|
50bdc6 |
- /* strange, the port id is not as expected. Set the local
|
|
|
50bdc6 |
- * port id to release a possibly generated port and un-own
|
|
|
50bdc6 |
- * it. */
|
|
|
50bdc6 |
+ /* The port id is different. That can happen if the port id was zero
|
|
|
50bdc6 |
+ * and kernel assigned a local port. */
|
|
|
50bdc6 |
nl_socket_set_local_port (sk, local.nl_pid);
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
sk->s_local = local;
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index 4f05d09..6e2aaf5 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -115,9 +115,6 @@ static uint32_t generate_local_port(void)
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
nl_write_unlock(&port_map_lock);
|
|
|
50bdc6 |
-
|
|
|
50bdc6 |
- /* Out of sockets in our own PID namespace, what to do? FIXME */
|
|
|
50bdc6 |
- NL_DBG(1, "Warning: Ran out of unique local port namespace\n");
|
|
|
50bdc6 |
return 0;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|
|
|
50bdc6 |
|
|
|
50bdc6 |
From 9d7d3895fe979ccffab350d2f71793982b060c8b Mon Sep 17 00:00:00 2001
|
|
|
50bdc6 |
From: Thomas Haller <thaller@redhat.com>
|
|
|
50bdc6 |
Date: Mon, 24 Aug 2015 17:57:16 +0200
|
|
|
50bdc6 |
Subject: [PATCH 11/11] socket: fix assertion in nl_connect() when all ports
|
|
|
50bdc6 |
are already in use
|
|
|
50bdc6 |
|
|
|
50bdc6 |
When generating a port fails a few times (because they are already in used
|
|
|
50bdc6 |
outside of libnl's knowledge), we would back off generating a local
|
|
|
50bdc6 |
port and instead let kernel decide.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
There was however a bug in nl_connect() that caused an assertion:
|
|
|
50bdc6 |
|
|
|
50bdc6 |
BUG at file position socket.c:147:_nl_socket_used_ports_release_all
|
|
|
50bdc6 |
app: socket.c:147: _nl_socket_used_ports_release_all: Assertion `0' failed.
|
|
|
50bdc6 |
|
|
|
50bdc6 |
Fixes: 96e1e5bdc2e803700055395cc3c428fa2525d1ca
|
|
|
50bdc6 |
(cherry picked from commit eaa75b7c7d3e6a4df1a2e7591ae295acfae3f73e)
|
|
|
50bdc6 |
---
|
|
|
50bdc6 |
include/netlink-private/socket.h | 2 +-
|
|
|
50bdc6 |
lib/nl.c | 4 ++--
|
|
|
50bdc6 |
lib/socket.c | 7 +++++--
|
|
|
50bdc6 |
3 files changed, 8 insertions(+), 5 deletions(-)
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/include/netlink-private/socket.h b/include/netlink-private/socket.h
|
|
|
50bdc6 |
index 86a440c..9ceecfd 100644
|
|
|
50bdc6 |
--- a/include/netlink-private/socket.h
|
|
|
50bdc6 |
+++ b/include/netlink-private/socket.h
|
|
|
50bdc6 |
@@ -19,7 +19,7 @@ extern "C" {
|
|
|
50bdc6 |
#endif
|
|
|
50bdc6 |
|
|
|
50bdc6 |
int _nl_socket_is_local_port_unspecified (struct nl_sock *sk);
|
|
|
50bdc6 |
-uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk);
|
|
|
50bdc6 |
+uint32_t _nl_socket_set_local_port_no_release(struct nl_sock *sk, int generate_other);
|
|
|
50bdc6 |
|
|
|
50bdc6 |
void _nl_socket_used_ports_release_all(const uint32_t *used_ports);
|
|
|
50bdc6 |
void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port);
|
|
|
50bdc6 |
diff --git a/lib/nl.c b/lib/nl.c
|
|
|
50bdc6 |
index bbd1c14..db000a3 100644
|
|
|
50bdc6 |
--- a/lib/nl.c
|
|
|
50bdc6 |
+++ b/lib/nl.c
|
|
|
50bdc6 |
@@ -129,11 +129,11 @@ int nl_connect(struct nl_sock *sk, int protocol)
|
|
|
50bdc6 |
if (ntries++ > 5) {
|
|
|
50bdc6 |
/* try only a few times. We hit this only if many ports are already in
|
|
|
50bdc6 |
* use but allocated *outside* libnl/generate_local_port(). */
|
|
|
50bdc6 |
- nl_socket_set_local_port (sk, 0);
|
|
|
50bdc6 |
+ _nl_socket_set_local_port_no_release (sk, 0);
|
|
|
50bdc6 |
break;
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- port = _nl_socket_generate_local_port_no_release(sk);
|
|
|
50bdc6 |
+ port = _nl_socket_set_local_port_no_release(sk, 1);
|
|
|
50bdc6 |
if (port == 0)
|
|
|
50bdc6 |
break;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
diff --git a/lib/socket.c b/lib/socket.c
|
|
|
50bdc6 |
index 6e2aaf5..d3eab60 100644
|
|
|
50bdc6 |
--- a/lib/socket.c
|
|
|
50bdc6 |
+++ b/lib/socket.c
|
|
|
50bdc6 |
@@ -331,14 +331,17 @@ int _nl_socket_is_local_port_unspecified(struct nl_sock *sk)
|
|
|
50bdc6 |
}
|
|
|
50bdc6 |
|
|
|
50bdc6 |
NOEXPORT
|
|
|
50bdc6 |
-uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
|
|
|
50bdc6 |
+uint32_t _nl_socket_set_local_port_no_release(struct nl_sock *sk, int generate_other)
|
|
|
50bdc6 |
{
|
|
|
50bdc6 |
uint32_t port;
|
|
|
50bdc6 |
|
|
|
50bdc6 |
/* reset the port to generate_local_port(), but do not release
|
|
|
50bdc6 |
* the previously generated port. */
|
|
|
50bdc6 |
|
|
|
50bdc6 |
- port = generate_local_port();
|
|
|
50bdc6 |
+ if (generate_other)
|
|
|
50bdc6 |
+ port = generate_local_port();
|
|
|
50bdc6 |
+ else
|
|
|
50bdc6 |
+ port = 0;
|
|
|
50bdc6 |
sk->s_local.nl_pid = port;
|
|
|
50bdc6 |
if (port == 0) {
|
|
|
50bdc6 |
/* failed to find an unsed port. Restore the socket to have an
|
|
|
50bdc6 |
--
|
|
|
50bdc6 |
2.4.3
|
|
|
50bdc6 |
|