summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2015-10-07 10:07:17 +0200
committerThomas Haller <thaller@redhat.com>2015-10-08 14:14:19 +0200
commit90fb64024c5086601e6a1501a890890bc9b16499 (patch)
treef76d5c616a94246509de64aad3351a48b3ceb85a
parent68b3790ad36e4a140c43e96335e2be60ac935d76 (diff)
parent8bfe99a1aeaabc552838f6d86770d54f9271ca1b (diff)
downloadNetworkManager-90fb64024c5086601e6a1501a890890bc9b16499.tar.gz
merge: merge branch 'systemd' into master
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am28
-rw-r--r--src/devices/nm-device.c2
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.c18
-rw-r--r--src/systemd/nm-sd-adapt.h11
-rw-r--r--src/systemd/src/basic/fileio.c40
-rw-r--r--src/systemd/src/basic/fileio.h2
-rw-r--r--src/systemd/src/basic/hashmap.c1865
-rw-r--r--src/systemd/src/basic/hashmap.h415
-rw-r--r--src/systemd/src/basic/log.h12
-rw-r--r--src/systemd/src/basic/macro.h39
-rw-r--r--src/systemd/src/basic/mempool.c105
-rw-r--r--src/systemd/src/basic/mempool.h51
-rw-r--r--src/systemd/src/basic/prioq.c320
-rw-r--r--src/systemd/src/basic/prioq.h44
-rw-r--r--src/systemd/src/basic/set.h138
-rw-r--r--src/systemd/src/basic/siphash24.c244
-rw-r--r--src/systemd/src/basic/siphash24.h13
-rw-r--r--src/systemd/src/basic/strv.c72
-rw-r--r--src/systemd/src/basic/strv.h6
-rw-r--r--src/systemd/src/basic/time-util.h4
-rw-r--r--src/systemd/src/basic/util.c375
-rw-r--r--src/systemd/src/basic/util.h75
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.c155
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.h (renamed from src/systemd/src/libsystemd-network/ipv4ll-internal.h)14
-rw-r--r--src/systemd/src/libsystemd-network/ipv4ll-network.c93
-rw-r--r--src/systemd/src/libsystemd-network/ipv4ll-packet.c74
-rw-r--r--src/systemd/src/libsystemd-network/lldp-internal.c362
-rw-r--r--src/systemd/src/libsystemd-network/lldp-internal.h94
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.c86
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.h29
-rw-r--r--src/systemd/src/libsystemd-network/lldp-port.c119
-rw-r--r--src/systemd/src/libsystemd-network/lldp-port.h73
-rw-r--r--src/systemd/src/libsystemd-network/lldp-tlv.c649
-rw-r--r--src/systemd/src/libsystemd-network/lldp-tlv.h101
-rw-r--r--src/systemd/src/libsystemd-network/lldp-util.h28
-rw-r--r--src/systemd/src/libsystemd-network/lldp.h130
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c16
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c20
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c8
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c48
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4acd.c531
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c642
-rw-r--r--src/systemd/src/libsystemd-network/sd-lldp.c736
-rw-r--r--src/systemd/src/libsystemd/sd-event/event-util.h33
-rw-r--r--src/systemd/src/libsystemd/sd-id128/sd-id128.c2
-rw-r--r--src/systemd/src/shared/dns-domain.c11
-rw-r--r--src/systemd/src/shared/dns-domain.h2
-rw-r--r--src/systemd/src/systemd/sd-dhcp-client.h10
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h10
-rw-r--r--src/systemd/src/systemd/sd-icmp6-nd.h14
-rw-r--r--src/systemd/src/systemd/sd-ipv4acd.h57
-rw-r--r--src/systemd/src/systemd/sd-ipv4ll.h8
-rw-r--r--src/systemd/src/systemd/sd-lldp.h76
54 files changed, 7065 insertions, 1046 deletions
diff --git a/configure.ac b/configure.ac
index cebaa437e8..386c1e84b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -53,6 +53,7 @@ dnl
dnl Checks for typedefs, structures, and compiler characteristics.
dnl
AC_TYPE_PID_T
+AC_CHECK_SIZEOF(dev_t)
dnl
dnl translation support
diff --git a/src/Makefile.am b/src/Makefile.am
index 39082de872..d23cbf81a7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -59,6 +59,7 @@ noinst_LTLIBRARIES = \
SYSTEMD_NM_CFLAGS_PATHS = \
-I$(top_srcdir)/src/systemd/src/systemd \
-I$(top_srcdir)/src/systemd/src/libsystemd-network \
+ -I$(top_srcdir)/src/systemd/src/libsystemd/sd-event \
-I$(top_srcdir)/src/systemd/src/basic \
-I$(top_srcdir)/src/systemd
@@ -68,6 +69,8 @@ libsystemd_nm_la_SOURCES = \
systemd/src/basic/async.h \
systemd/src/basic/fileio.c \
systemd/src/basic/fileio.h \
+ systemd/src/basic/hashmap.c \
+ systemd/src/basic/hashmap.h \
systemd/src/basic/hostname-util.c \
systemd/src/basic/hostname-util.h \
systemd/src/basic/in-addr-util.c \
@@ -75,13 +78,18 @@ libsystemd_nm_la_SOURCES = \
systemd/src/basic/list.h \
systemd/src/basic/log.h \
systemd/src/basic/macro.h \
+ systemd/src/basic/mempool.c \
+ systemd/src/basic/mempool.h \
systemd/src/basic/path-util.c \
systemd/src/basic/path-util.h \
+ systemd/src/basic/prioq.c \
+ systemd/src/basic/prioq.h \
systemd/src/basic/random-util.c \
systemd/src/basic/random-util.h \
systemd/src/basic/refcnt.h \
systemd/src/basic/siphash24.c \
systemd/src/basic/siphash24.h \
+ systemd/src/basic/set.h \
systemd/src/basic/socket-util.h \
systemd/src/basic/sparse-endian.h \
systemd/src/basic/strv.c \
@@ -93,6 +101,8 @@ libsystemd_nm_la_SOURCES = \
systemd/src/basic/utf8.h \
systemd/src/basic/util.c \
systemd/src/basic/util.h \
+ systemd/src/libsystemd-network/arp-util.c \
+ systemd/src/libsystemd-network/arp-util.h \
systemd/src/libsystemd-network/dhcp-identifier.c \
systemd/src/libsystemd-network/dhcp-identifier.h \
systemd/src/libsystemd-network/dhcp-internal.h \
@@ -106,17 +116,27 @@ libsystemd_nm_la_SOURCES = \
systemd/src/libsystemd-network/dhcp6-network.c \
systemd/src/libsystemd-network/dhcp6-option.c \
systemd/src/libsystemd-network/dhcp6-protocol.h \
- systemd/src/libsystemd-network/ipv4ll-internal.h \
- systemd/src/libsystemd-network/ipv4ll-network.c \
- systemd/src/libsystemd-network/ipv4ll-packet.c \
+ systemd/src/libsystemd-network/lldp.h \
+ systemd/src/libsystemd-network/lldp-network.h \
+ systemd/src/libsystemd-network/lldp-network.c \
+ systemd/src/libsystemd-network/lldp-tlv.c \
+ systemd/src/libsystemd-network/lldp-tlv.h \
+ systemd/src/libsystemd-network/lldp-port.c \
+ systemd/src/libsystemd-network/lldp-port.h \
+ systemd/src/libsystemd-network/lldp-util.h \
+ systemd/src/libsystemd-network/lldp-internal.h \
+ systemd/src/libsystemd-network/lldp-internal.c \
systemd/src/libsystemd-network/network-internal.c \
systemd/src/libsystemd-network/network-internal.h \
systemd/src/libsystemd-network/sd-dhcp-client.c \
systemd/src/libsystemd-network/sd-dhcp-lease.c \
systemd/src/libsystemd-network/sd-dhcp6-client.c \
systemd/src/libsystemd-network/sd-dhcp6-lease.c \
+ systemd/src/libsystemd-network/sd-ipv4acd.c \
systemd/src/libsystemd-network/sd-ipv4ll.c \
+ systemd/src/libsystemd-network/sd-lldp.c \
systemd/src/libsystemd/sd-id128/sd-id128.c \
+ systemd/src/libsystemd/sd-event/event-util.h \
systemd/src/shared/dns-domain.c \
systemd/src/shared/dns-domain.h \
systemd/src/systemd/_sd-common.h \
@@ -124,9 +144,11 @@ libsystemd_nm_la_SOURCES = \
systemd/src/systemd/sd-dhcp-lease.h \
systemd/src/systemd/sd-dhcp6-client.h \
systemd/src/systemd/sd-dhcp6-lease.h \
+ systemd/src/systemd/sd-lldp.h \
systemd/src/systemd/sd-event.h \
systemd/src/systemd/sd-icmp6-nd.h \
systemd/src/systemd/sd-id128.h \
+ systemd/src/systemd/sd-ipv4acd.h \
systemd/src/systemd/sd-ipv4ll.h
libsystemd_nm_la_CPPFLAGS = \
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 4c7d917418..1f8eaada8d 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -3245,7 +3245,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
return;
switch (event) {
- case IPV4LL_EVENT_BIND:
+ case SD_IPV4LL_EVENT_BIND:
r = sd_ipv4ll_get_address (ll, &address);
if (r < 0) {
_LOGE (LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r);
diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c
index 534b54f25b..4459fd583f 100644
--- a/src/dhcp-manager/nm-dhcp-systemd.c
+++ b/src/dhcp-manager/nm-dhcp-systemd.c
@@ -491,15 +491,15 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data)
nm_log_dbg (LOGD_DHCP4, "(%s): DHCPv4 client event %d", iface, event);
switch (event) {
- case DHCP_EVENT_EXPIRED:
+ case SD_DHCP_CLIENT_EVENT_EXPIRED:
nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_EXPIRE, NULL, NULL);
break;
- case DHCP_EVENT_STOP:
+ case SD_DHCP_CLIENT_EVENT_STOP:
nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL);
break;
- case DHCP_EVENT_RENEW:
- case DHCP_EVENT_IP_CHANGE:
- case DHCP_EVENT_IP_ACQUIRE:
+ case SD_DHCP_CLIENT_EVENT_RENEW:
+ case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
+ case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
bound4_handle (self);
break;
default:
@@ -671,14 +671,14 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
nm_log_dbg (LOGD_DHCP6, "(%s): DHCPv6 client event %d", iface, event);
switch (event) {
- case DHCP6_EVENT_RETRANS_MAX:
+ case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_TIMEOUT, NULL, NULL);
break;
- case DHCP6_EVENT_RESEND_EXPIRE:
- case DHCP6_EVENT_STOP:
+ case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
+ case SD_DHCP6_CLIENT_EVENT_STOP:
nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL);
break;
- case DHCP6_EVENT_IP_ACQUIRE:
+ case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
bound6_handle (self);
break;
default:
diff --git a/src/systemd/nm-sd-adapt.h b/src/systemd/nm-sd-adapt.h
index 03fda64136..a6123d1fe6 100644
--- a/src/systemd/nm-sd-adapt.h
+++ b/src/systemd/nm-sd-adapt.h
@@ -69,6 +69,12 @@ G_STMT_START { \
g_assert_not_reached (); \
} G_STMT_END
+#define log_assert_failed_unreachable(text, file, line, func) \
+G_STMT_START { \
+ log_internal (LOG_CRIT, 0, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", text, file, line, func); \
+ g_assert_not_reached (); \
+} G_STMT_END
+
#define log_assert_failed_return(text, file, line, func) \
({ \
log_internal (LOG_DEBUG, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", text, file, line, func); \
@@ -76,7 +82,6 @@ G_STMT_START { \
(void) 0; \
})
-
/*****************************************************************************
* The remainder of the header is only enabled when building the systemd code
* itself.
@@ -132,6 +137,10 @@ static inline pid_t gettid(void) {
return (pid_t) syscall(SYS_gettid);
}
+static inline bool is_main_thread(void) {
+ return TRUE;
+}
+
#endif /* (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD */
#endif /* NM_SD_ADAPT_H */
diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c
index 10c78d06e4..6246bc9598 100644
--- a/src/systemd/src/basic/fileio.c
+++ b/src/systemd/src/basic/fileio.c
@@ -777,15 +777,19 @@ int executable_is_script(const char *path, char **interpreter) {
/**
* Retrieve one field from a file like /proc/self/status. pattern
- * should start with '\n' and end with a ':'. Whitespace and zeros
- * after the ':' will be skipped. field must be freed afterwards.
+ * should not include whitespace or the delimiter (':'). pattern matches only
+ * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
+ * zeros after the ':' will be skipped. field must be freed afterwards.
+ * terminator specifies the terminating characters of the field value (not
+ * included in the value).
*/
-int get_status_field(const char *filename, const char *pattern, char **field) {
+int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
_cleanup_free_ char *status = NULL;
char *t, *f;
size_t len;
int r;
+ assert(terminator);
assert(filename);
assert(pattern);
assert(field);
@@ -794,11 +798,31 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
if (r < 0)
return r;
- t = strstr(status, pattern);
- if (!t)
- return -ENOENT;
+ t = status;
+
+ do {
+ bool pattern_ok;
+
+ do {
+ t = strstr(t, pattern);
+ if (!t)
+ return -ENOENT;
+
+ /* Check that pattern occurs in beginning of line. */
+ pattern_ok = (t == status || t[-1] == '\n');
+
+ t += strlen(pattern);
+
+ } while (!pattern_ok);
+
+ t += strspn(t, " \t");
+ if (!*t)
+ return -ENOENT;
+
+ } while (*t != ':');
+
+ t++;
- t += strlen(pattern);
if (*t) {
t += strspn(t, " \t");
@@ -814,7 +838,7 @@ int get_status_field(const char *filename, const char *pattern, char **field) {
t --;
}
- len = strcspn(t, WHITESPACE);
+ len = strcspn(t, terminator);
f = strndup(t, len);
if (!f)
diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h
index 977a7f7d6e..f27bb04b47 100644
--- a/src/systemd/src/basic/fileio.h
+++ b/src/systemd/src/basic/fileio.h
@@ -51,4 +51,4 @@ int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
-int get_status_field(const char *filename, const char *pattern, char **field);
+int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c
new file mode 100644
index 0000000000..97d20f79ea
--- /dev/null
+++ b/src/systemd/src/basic/hashmap.c
@@ -0,0 +1,1865 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "set.h"
+#include "macro.h"
+#include "siphash24.h"
+#include "strv.h"
+#include "mempool.h"
+#include "random-util.h"
+
+#ifdef ENABLE_DEBUG_HASHMAP
+#include "list.h"
+#endif
+
+/*
+ * Implementation of hashmaps.
+ * Addressing: open
+ * - uses less RAM compared to closed addressing (chaining), because
+ * our entries are small (especially in Sets, which tend to contain
+ * the majority of entries in systemd).
+ * Collision resolution: Robin Hood
+ * - tends to equalize displacement of entries from their optimal buckets.
+ * Probe sequence: linear
+ * - though theoretically worse than random probing/uniform hashing/double
+ * hashing, it is good for cache locality.
+ *
+ * References:
+ * Celis, P. 1986. Robin Hood Hashing.
+ * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada.
+ * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf
+ * - The results are derived for random probing. Suggests deletion with
+ * tombstones and two mean-centered search methods. None of that works
+ * well for linear probing.
+ *
+ * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies.
+ * ACM Trans. Algorithms 1, 2 (October 2005), 177-213.
+ * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964
+ * http://www.math.uu.se/~svante/papers/sj157.pdf
+ * - Applies to Robin Hood with linear probing. Contains remarks on
+ * the unsuitability of mean-centered search with linear probing.
+ *
+ * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing.
+ * ACM Trans. Algorithms 1, 2 (October 2005), 214-242.
+ * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965
+ * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes
+ * in a successful search), and Janson writes about displacement. C = d + 1.
+ *
+ * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion.
+ * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
+ * - Explanation of backward shift deletion with pictures.
+ *
+ * Khuong, P. 2013. The Other Robin Hood Hashing.
+ * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/
+ * - Short summary of random vs. linear probing, and tombstones vs. backward shift.
+ */
+
+/*
+ * XXX Ideas for improvement:
+ * For unordered hashmaps, randomize iteration order, similarly to Perl:
+ * http://blog.booking.com/hardening-perls-hash-function.html
+ */
+
+/* INV_KEEP_FREE = 1 / (1 - max_load_factor)
+ * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */
+#define INV_KEEP_FREE 5U
+
+/* Fields common to entries of all hashmap/set types */
+struct hashmap_base_entry {
+ const void *key;
+};
+
+/* Entry types for specific hashmap/set types
+ * hashmap_base_entry must be at the beginning of each entry struct. */
+
+struct plain_hashmap_entry {
+ struct hashmap_base_entry b;
+ void *value;
+};
+
+struct ordered_hashmap_entry {
+ struct plain_hashmap_entry p;
+ unsigned iterate_next, iterate_previous;
+};
+
+struct set_entry {
+ struct hashmap_base_entry b;
+};
+
+/* In several functions it is advantageous to have the hash table extended
+ * virtually by a couple of additional buckets. We reserve special index values
+ * for these "swap" buckets. */
+#define _IDX_SWAP_BEGIN (UINT_MAX - 3)
+#define IDX_PUT (_IDX_SWAP_BEGIN + 0)
+#define IDX_TMP (_IDX_SWAP_BEGIN + 1)
+#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2)
+
+#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */
+#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */
+
+assert_cc(IDX_FIRST == _IDX_SWAP_END);
+assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
+
+/* Storage space for the "swap" buckets.
+ * All entry types can fit into a ordered_hashmap_entry. */
+struct swap_entries {
+ struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
+};
+
+/* Distance from Initial Bucket */
+typedef uint8_t dib_raw_t;
+#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */
+#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */
+#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */
+#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */
+
+#define DIB_FREE UINT_MAX
+
+#ifdef ENABLE_DEBUG_HASHMAP
+struct hashmap_debug_info {
+ LIST_FIELDS(struct hashmap_debug_info, debug_list);
+ unsigned max_entries; /* high watermark of n_entries */
+
+ /* who allocated this hashmap */
+ int line;
+ const char *file;
+ const char *func;
+
+ /* fields to detect modification while iterating */
+ unsigned put_count; /* counts puts into the hashmap */
+ unsigned rem_count; /* counts removals from hashmap */
+ unsigned last_rem_idx; /* remembers last removal index */
+};
+
+/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
+static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
+static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
+
+#else /* !ENABLE_DEBUG_HASHMAP */
+#define HASHMAP_DEBUG_FIELDS
+#endif /* ENABLE_DEBUG_HASHMAP */
+
+enum HashmapType {
+ HASHMAP_TYPE_PLAIN,
+ HASHMAP_TYPE_ORDERED,
+ HASHMAP_TYPE_SET,
+ _HASHMAP_TYPE_MAX
+};
+
+struct _packed_ indirect_storage {
+ char *storage; /* where buckets and DIBs are stored */
+ uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */
+
+ unsigned n_entries; /* number of stored entries */
+ unsigned n_buckets; /* number of buckets */
+
+ unsigned idx_lowest_entry; /* Index below which all buckets are free.
+ Makes "while(hashmap_steal_first())" loops
+ O(n) instead of O(n^2) for unordered hashmaps. */
+ uint8_t _pad[3]; /* padding for the whole HashmapBase */
+ /* The bitfields in HashmapBase complete the alignment of the whole thing. */
+};
+
+struct direct_storage {
+ /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
+ * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
+ * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
+ char storage[sizeof(struct indirect_storage)];
+};
+
+#define DIRECT_BUCKETS(entry_t) \
+ (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t)))
+
+/* We should be able to store at least one entry directly. */
+assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1);
+
+/* We have 3 bits for n_direct_entries. */
+assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
+
+/* Hashmaps with directly stored entries all use this shared hash key.
+ * It's no big deal if the key is guessed, because there can be only
+ * a handful of directly stored entries in a hashmap. When a hashmap
+ * outgrows direct storage, it gets its own key for indirect storage. */
+static uint8_t shared_hash_key[HASH_KEY_SIZE];
+static bool shared_hash_key_initialized;
+
+/* Fields that all hashmap/set types must have */
+struct HashmapBase {
+ const struct hash_ops *hash_ops; /* hash and compare ops to use */
+
+ union _packed_ {
+ struct indirect_storage indirect; /* if has_indirect */
+ struct direct_storage direct; /* if !has_indirect */
+ };
+
+ enum HashmapType type:2; /* HASHMAP_TYPE_* */
+ bool has_indirect:1; /* whether indirect storage is used */
+ unsigned n_direct_entries:3; /* Number of entries in direct storage.
+ * Only valid if !has_indirect. */
+ bool from_pool:1; /* whether was allocated from mempool */
+ HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */
+};
+
+/* Specific hash types
+ * HashmapBase must be at the beginning of each hashmap struct. */
+
+struct Hashmap {
+ struct HashmapBase b;
+};
+
+struct OrderedHashmap {
+ struct HashmapBase b;
+ unsigned iterate_list_head, iterate_list_tail;
+};
+
+struct Set {
+ struct HashmapBase b;
+};
+
+DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8);
+DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8);
+/* No need for a separate Set pool */
+assert_cc(sizeof(Hashmap) == sizeof(Set));
+
+struct hashmap_type_info {
+ size_t head_size;
+ size_t entry_size;
+ struct mempool *mempool;
+ unsigned n_direct_buckets;
+};
+
+static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
+ [HASHMAP_TYPE_PLAIN] = {
+ .head_size = sizeof(Hashmap),
+ .entry_size = sizeof(struct plain_hashmap_entry),
+ .mempool = &hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry),
+ },
+ [HASHMAP_TYPE_ORDERED] = {
+ .head_size = sizeof(OrderedHashmap),
+ .entry_size = sizeof(struct ordered_hashmap_entry),
+ .mempool = &ordered_hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry),
+ },
+ [HASHMAP_TYPE_SET] = {
+ .head_size = sizeof(Set),
+ .entry_size = sizeof(struct set_entry),
+ .mempool = &hashmap_pool,
+ .n_direct_buckets = DIRECT_BUCKETS(struct set_entry),
+ },
+};
+
+void string_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(p, strlen(p) + 1, state);
+}
+
+int string_compare_func(const void *a, const void *b) {
+ return strcmp(a, b);
+}
+
+const struct hash_ops string_hash_ops = {
+ .hash = string_hash_func,
+ .compare = string_compare_func
+};
+
+void trivial_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(&p, sizeof(p), state);
+}
+
+int trivial_compare_func(const void *a, const void *b) {
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops trivial_hash_ops = {
+ .hash = trivial_hash_func,
+ .compare = trivial_compare_func
+};
+
+void uint64_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(p, sizeof(uint64_t), state);
+}
+
+int uint64_compare_func(const void *_a, const void *_b) {
+ uint64_t a, b;
+ a = *(const uint64_t*) _a;
+ b = *(const uint64_t*) _b;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops uint64_hash_ops = {
+ .hash = uint64_hash_func,
+ .compare = uint64_compare_func
+};
+
+#if SIZEOF_DEV_T != 8
+void devt_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(p, sizeof(dev_t), state);
+}
+
+int devt_compare_func(const void *_a, const void *_b) {
+ dev_t a, b;
+ a = *(const dev_t*) _a;
+ b = *(const dev_t*) _b;
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+const struct hash_ops devt_hash_ops = {
+ .hash = devt_hash_func,
+ .compare = devt_compare_func
+};
+#endif
+
+static unsigned n_buckets(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.n_buckets
+ : hashmap_type_info[h->type].n_direct_buckets;
+}
+
+static unsigned n_entries(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.n_entries
+ : h->n_direct_entries;
+}
+
+static void n_entries_inc(HashmapBase *h) {
+ if (h->has_indirect)
+ h->indirect.n_entries++;
+ else
+ h->n_direct_entries++;
+}
+
+static void n_entries_dec(HashmapBase *h) {
+ if (h->has_indirect)
+ h->indirect.n_entries--;
+ else
+ h->n_direct_entries--;
+}
+
+static char *storage_ptr(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.storage
+ : h->direct.storage;
+}
+
+static uint8_t *hash_key(HashmapBase *h) {
+ return h->has_indirect ? h->indirect.hash_key
+ : shared_hash_key;
+}
+
+static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
+ struct siphash state;
+ uint64_t hash;
+
+ siphash24_init(&state, hash_key(h));
+
+ h->hash_ops->hash(p, &state);
+
+ siphash24_finalize((uint8_t*)&hash, &state);
+
+ return (unsigned) (hash % n_buckets(h));
+}
+#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
+
+static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
+ static uint8_t current[HASH_KEY_SIZE];
+ static bool current_initialized = false;
+
+ /* Returns a hash function key to use. In order to keep things
+ * fast we will not generate a new key each time we allocate a
+ * new hash table. Instead, we'll just reuse the most recently
+ * generated one, except if we never generated one or when we
+ * are rehashing an entire hash table because we reached a
+ * fill level */
+
+ if (!current_initialized || !reuse_is_ok) {
+ random_bytes(current, sizeof(current));
+ current_initialized = true;
+ }
+
+ memcpy(hash_key, current, sizeof(current));
+}
+
+static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
+ return (struct hashmap_base_entry*)
+ (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
+}
+
+static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
+ return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
+ return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct set_entry *set_bucket_at(Set *h, unsigned idx) {
+ return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
+}
+
+static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) {
+ return &swap->e[idx - _IDX_SWAP_BEGIN];
+}
+
+/* Returns a pointer to the bucket at index idx.
+ * Understands real indexes and swap indexes, hence "_virtual". */
+static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
+ unsigned idx) {
+ if (idx < _IDX_SWAP_BEGIN)
+ return bucket_at(h, idx);
+
+ if (idx < _IDX_SWAP_END)
+ return &bucket_at_swap(swap, idx)->p.b;
+
+ assert_not_reached("Invalid index");
+}
+
+static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
+ return (dib_raw_t*)
+ (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
+}
+
+static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) {
+ return idx >= from ? idx - from
+ : n_buckets(h) + idx - from;
+}
+
+static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) {
+ unsigned initial_bucket;
+
+ if (raw_dib == DIB_RAW_FREE)
+ return DIB_FREE;
+
+ if (_likely_(raw_dib < DIB_RAW_OVERFLOW))
+ return raw_dib;
+
+ /*
+ * Having an overflow DIB value is very unlikely. The hash function
+ * would have to be bad. For example, in a table of size 2^24 filled
+ * to load factor 0.9 the maximum observed DIB is only about 60.
+ * In theory (assuming I used Maxima correctly), for an infinite size
+ * hash table with load factor 0.8 the probability of a given entry
+ * having DIB > 40 is 1.9e-8.
+ * This returns the correct DIB value by recomputing the hash value in
+ * the unlikely case. XXX Hitting this case could be a hint to rehash.
+ */
+ initial_bucket = bucket_hash(h, bucket_at(h, idx)->key);
+ return bucket_distance(h, idx, initial_bucket);
+}
+
+static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) {
+ dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE;
+}
+
+static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) {
+ dib_raw_t *dibs;
+
+ dibs = dib_raw_ptr(h);
+
+ for ( ; idx < n_buckets(h); idx++)
+ if (dibs[idx] != DIB_RAW_FREE)
+ return idx;
+
+ return IDX_NIL;
+}
+
+static void bucket_mark_free(HashmapBase *h, unsigned idx) {
+ memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size);
+ bucket_set_dib(h, idx, DIB_FREE);
+}
+
+static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap,
+ unsigned from, unsigned to) {
+ struct hashmap_base_entry *e_from, *e_to;
+
+ assert(from != to);
+
+ e_from = bucket_at_virtual(h, swap, from);
+ e_to = bucket_at_virtual(h, swap, to);
+
+ memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size);
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ struct ordered_hashmap_entry *le, *le_to;
+
+ le_to = (struct ordered_hashmap_entry*) e_to;
+
+ if (le_to->iterate_next != IDX_NIL) {
+ le = (struct ordered_hashmap_entry*)
+ bucket_at_virtual(h, swap, le_to->iterate_next);
+ le->iterate_previous = to;
+ }
+
+ if (le_to->iterate_previous != IDX_NIL) {
+ le = (struct ordered_hashmap_entry*)
+ bucket_at_virtual(h, swap, le_to->iterate_previous);
+ le->iterate_next = to;
+ }
+
+ if (lh->iterate_list_head == from)
+ lh->iterate_list_head = to;
+ if (lh->iterate_list_tail == from)
+ lh->iterate_list_tail = to;
+ }
+}
+
+static unsigned next_idx(HashmapBase *h, unsigned idx) {
+ return (idx + 1U) % n_buckets(h);
+}
+
+static unsigned prev_idx(HashmapBase *h, unsigned idx) {
+ return (n_buckets(h) + idx - 1U) % n_buckets(h);
+}
+
+static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
+ switch (h->type) {
+
+ case HASHMAP_TYPE_PLAIN:
+ case HASHMAP_TYPE_ORDERED:
+ return ((struct plain_hashmap_entry*)e)->value;
+
+ case HASHMAP_TYPE_SET:
+ return (void*) e->key;
+
+ default:
+ assert_not_reached("Unknown hashmap type");
+ }
+}
+
+static void base_remove_entry(HashmapBase *h, unsigned idx) {
+ unsigned left, right, prev, dib;
+ dib_raw_t raw_dib, *dibs;
+
+ dibs = dib_raw_ptr(h);
+ assert(dibs[idx] != DIB_RAW_FREE);
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.rem_count++;
+ h->debug.last_rem_idx = idx;
+#endif
+
+ left = idx;
+ /* Find the stop bucket ("right"). It is either free or has DIB == 0. */
+ for (right = next_idx(h, left); ; right = next_idx(h, right)) {
+ raw_dib = dibs[right];
+ if (raw_dib == 0 || raw_dib == DIB_RAW_FREE)
+ break;
+
+ /* The buckets are not supposed to be all occupied and with DIB > 0.
+ * That would mean we could make everyone better off by shifting them
+ * backward. This scenario is impossible. */
+ assert(left != right);
+ }
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx);
+
+ if (le->iterate_next != IDX_NIL)
+ ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous;
+ else
+ lh->iterate_list_tail = le->iterate_previous;
+
+ if (le->iterate_previous != IDX_NIL)
+ ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next;
+ else
+ lh->iterate_list_head = le->iterate_next;
+ }
+
+ /* Now shift all buckets in the interval (left, right) one step backwards */
+ for (prev = left, left = next_idx(h, left); left != right;
+ prev = left, left = next_idx(h, left)) {
+ dib = bucket_calculate_dib(h, left, dibs[left]);
+ assert(dib != 0);
+ bucket_move_entry(h, NULL, left, prev);
+ bucket_set_dib(h, prev, dib - 1);
+ }
+
+ bucket_mark_free(h, prev);
+ n_entries_dec(h);
+}
+#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx)
+
+static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) {
+ struct ordered_hashmap_entry *e;
+ unsigned idx;
+
+ assert(h);
+ assert(i);
+
+ if (i->idx == IDX_NIL)
+ goto at_end;
+
+ if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL)
+ goto at_end;
+
+ if (i->idx == IDX_FIRST) {
+ idx = h->iterate_list_head;
+ e = ordered_bucket_at(h, idx);
+ } else {
+ idx = i->idx;
+ e = ordered_bucket_at(h, idx);
+ /*
+ * We allow removing the current entry while iterating, but removal may cause
+ * a backward shift. The next entry may thus move one bucket to the left.
+ * To detect when it happens, we remember the key pointer of the entry we were
+ * going to iterate next. If it does not match, there was a backward shift.
+ */
+ if (e->p.b.key != i->next_key) {
+ idx = prev_idx(HASHMAP_BASE(h), idx);
+ e = ordered_bucket_at(h, idx);
+ }
+ assert(e->p.b.key == i->next_key);
+ }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ i->prev_idx = idx;
+#endif
+
+ if (e->iterate_next != IDX_NIL) {
+ struct ordered_hashmap_entry *n;
+ i->idx = e->iterate_next;
+ n = ordered_bucket_at(h, i->idx);
+ i->next_key = n->p.b.key;
+ } else
+ i->idx = IDX_NIL;
+
+ return idx;
+
+at_end:
+ i->idx = IDX_NIL;
+ return IDX_NIL;
+}
+
+static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) {
+ unsigned idx;
+
+ assert(h);
+ assert(i);
+
+ if (i->idx == IDX_NIL)
+ goto at_end;
+
+ if (i->idx == IDX_FIRST) {
+ /* fast forward to the first occupied bucket */
+ if (h->has_indirect) {
+ i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry);
+ h->indirect.idx_lowest_entry = i->idx;
+ } else
+ i->idx = skip_free_buckets(h, 0);
+
+ if (i->idx == IDX_NIL)
+ goto at_end;
+ } else {
+ struct hashmap_base_entry *e;
+
+ assert(i->idx > 0);
+
+ e = bucket_at(h, i->idx);
+ /*
+ * We allow removing the current entry while iterating, but removal may cause
+ * a backward shift. The next entry may thus move one bucket to the left.
+ * To detect when it happens, we remember the key pointer of the entry we were
+ * going to iterate next. If it does not match, there was a backward shift.
+ */
+ if (e->key != i->next_key)
+ e = bucket_at(h, --i->idx);
+
+ assert(e->key == i->next_key);
+ }
+
+ idx = i->idx;
+#ifdef ENABLE_DEBUG_HASHMAP
+ i->prev_idx = idx;
+#endif
+
+ i->idx = skip_free_buckets(h, i->idx + 1);
+ if (i->idx != IDX_NIL)
+ i->next_key = bucket_at(h, i->idx)->key;
+ else
+ i->idx = IDX_NIL;
+
+ return idx;
+
+at_end:
+ i->idx = IDX_NIL;
+ return IDX_NIL;
+}
+
+static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
+ if (!h) {
+ i->idx = IDX_NIL;
+ return IDX_NIL;
+ }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ if (i->idx == IDX_FIRST) {
+ i->put_count = h->debug.put_count;
+ i->rem_count = h->debug.rem_count;
+ } else {
+ /* While iterating, must not add any new entries */
+ assert(i->put_count == h->debug.put_count);
+ /* ... or remove entries other than the current one */
+ assert(i->rem_count == h->debug.rem_count ||
+ (i->rem_count == h->debug.rem_count - 1 &&
+ i->prev_idx == h->debug.last_rem_idx));
+ /* Reset our removals counter */
+ i->rem_count = h->debug.rem_count;
+ }
+#endif
+
+ return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i)
+ : hashmap_iterate_in_internal_order(h, i);
+}
+
+bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) {
+ struct hashmap_base_entry *e;
+ void *data;
+ unsigned idx;
+
+ idx = hashmap_iterate_entry(h, i);
+ if (idx == IDX_NIL) {
+ if (value)
+ *value = NULL;
+ if (key)
+ *key = NULL;
+
+ return false;
+ }
+
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ if (value)
+ *value = data;
+ if (key)
+ *key = e->key;
+
+ return true;
+}
+
+bool set_iterate(Set *s, Iterator *i, void **value) {
+ return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL);
+}
+
+#define HASHMAP_FOREACH_IDX(idx, h, i) \
+ for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
+ (idx != IDX_NIL); \
+ (idx) = hashmap_iterate_entry((h), &(i)))
+
+static void reset_direct_storage(HashmapBase *h) {
+ const struct hashmap_type_info *hi = &hashmap_type_info[h->type];
+ void *p;
+
+ assert(!h->has_indirect);
+
+ p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets);
+ memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
+}
+
+static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+ HashmapBase *h;
+ const struct hashmap_type_info *hi = &hashmap_type_info[type];
+ bool use_pool;
+
+ use_pool = is_main_thread();
+
+ h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
+
+ if (!h)
+ return NULL;
+
+ h->type = type;
+ h->from_pool = use_pool;
+ h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
+
+ if (type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*)h;
+ lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
+ }
+
+ reset_direct_storage(h);
+
+ if (!shared_hash_key_initialized) {
+ random_bytes(shared_hash_key, sizeof(shared_hash_key));
+ shared_hash_key_initialized= true;
+ }
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.func = func;
+ h->debug.file = file;
+ h->debug.line = line;
+ assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
+ LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug);
+ assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
+#endif
+
+ return h;
+}
+
+Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+}
+
+OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+}
+
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+}
+
+static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
+ enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+ HashmapBase *q;
+
+ assert(h);
+
+ if (*h)
+ return 0;
+
+ q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
+ if (!q)
+ return -ENOMEM;
+
+ *h = q;
+ return 0;
+}
+
+int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) {
+ return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+}
+
+static void hashmap_free_no_clear(HashmapBase *h) {
+ assert(!h->has_indirect);
+ assert(!h->n_direct_entries);
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
+ LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug);
+ assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0);
+#endif
+
+ if (h->from_pool)
+ mempool_free_tile(hashmap_type_info[h->type].mempool, h);
+ else
+ free(h);
+}
+
+HashmapBase *internal_hashmap_free(HashmapBase *h) {
+
+ /* Free the hashmap, but nothing in it */
+
+ if (h) {
+ internal_hashmap_clear(h);
+ hashmap_free_no_clear(h);
+ }
+
+ return NULL;
+}
+
+HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
+
+ /* Free the hashmap and all data objects in it, but not the
+ * keys */
+
+ if (h) {
+ internal_hashmap_clear_free(h);
+ hashmap_free_no_clear(h);
+ }
+
+ return NULL;
+}
+
+Hashmap *hashmap_free_free_free(Hashmap *h) {
+
+ /* Free the hashmap and all data and key objects in it */
+
+ if (h) {
+ hashmap_clear_free_free(h);
+ hashmap_free_no_clear(HASHMAP_BASE(h));
+ }
+
+ return NULL;
+}
+
+void internal_hashmap_clear(HashmapBase *h) {
+ if (!h)
+ return;
+
+ if (h->has_indirect) {
+ free(h->indirect.storage);
+ h->has_indirect = false;
+ }
+
+ h->n_direct_entries = 0;
+ reset_direct_storage(h);
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+ lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL;
+ }
+}
+
+void internal_hashmap_clear_free(HashmapBase *h) {
+ unsigned idx;
+
+ if (!h)
+ return;
+
+ for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
+ idx = skip_free_buckets(h, idx + 1))
+ free(entry_value(h, bucket_at(h, idx)));
+
+ internal_hashmap_clear(h);
+}
+
+void hashmap_clear_free_free(Hashmap *h) {
+ unsigned idx;
+
+ if (!h)
+ return;
+
+ for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
+ idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
+ struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
+ free((void*)e->b.key);
+ free(e->value);
+ }
+
+ internal_hashmap_clear(HASHMAP_BASE(h));
+}
+
+static int resize_buckets(HashmapBase *h, unsigned entries_add);
+
+/*
+ * Finds an empty bucket to put an entry into, starting the scan at 'idx'.
+ * Performs Robin Hood swaps as it goes. The entry to put must be placed
+ * by the caller into swap slot IDX_PUT.
+ * If used for in-place resizing, may leave a displaced entry in swap slot
+ * IDX_PUT. Caller must rehash it next.
+ * Returns: true if it left a displaced entry to rehash next in IDX_PUT,
+ * false otherwise.
+ */
+static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx,
+ struct swap_entries *swap) {
+ dib_raw_t raw_dib, *dibs;
+ unsigned dib, distance;
+
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.put_count++;
+#endif
+
+ dibs = dib_raw_ptr(h);
+
+ for (distance = 0; ; distance++) {
+ raw_dib = dibs[idx];
+ if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) {
+ if (raw_dib == DIB_RAW_REHASH)
+ bucket_move_entry(h, swap, idx, IDX_TMP);
+
+ if (h->has_indirect && h->indirect.idx_lowest_entry > idx)
+ h->indirect.idx_lowest_entry = idx;
+
+ bucket_set_dib(h, idx, distance);
+ bucket_move_entry(h, swap, IDX_PUT, idx);
+ if (raw_dib == DIB_RAW_REHASH) {
+ bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
+ return true;
+ }
+
+ return false;
+ }
+
+ dib = bucket_calculate_dib(h, idx, raw_dib);
+
+ if (dib < distance) {
+ /* Found a wealthier entry. Go Robin Hood! */
+ bucket_set_dib(h, idx, distance);
+
+ /* swap the entries */
+ bucket_move_entry(h, swap, idx, IDX_TMP);
+ bucket_move_entry(h, swap, IDX_PUT, idx);
+ bucket_move_entry(h, swap, IDX_TMP, IDX_PUT);
+
+ distance = dib;
+ }
+
+ idx = next_idx(h, idx);
+ }
+}
+
+/*
+ * Puts an entry into a hashmap, boldly - no check whether key already exists.
+ * The caller must place the entry (only its key and value, not link indexes)
+ * in swap slot IDX_PUT.
+ * Caller must ensure: the key does not exist yet in the hashmap.
+ * that resize is not needed if !may_resize.
+ * Returns: 1 if entry was put successfully.
+ * -ENOMEM if may_resize==true and resize failed with -ENOMEM.
+ * Cannot return -ENOMEM if !may_resize.
+ */
+static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx,
+ struct swap_entries *swap, bool may_resize) {
+ struct ordered_hashmap_entry *new_entry;
+ int r;
+
+ assert(idx < n_buckets(h));
+
+ new_entry = bucket_at_swap(swap, IDX_PUT);
+
+ if (may_resize) {
+ r = resize_buckets(h, 1);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ idx = bucket_hash(h, new_entry->p.b.key);
+ }
+ assert(n_entries(h) < n_buckets(h));
+
+ if (h->type == HASHMAP_TYPE_ORDERED) {
+ OrderedHashmap *lh = (OrderedHashmap*) h;
+
+ new_entry->iterate_next = IDX_NIL;
+ new_entry->iterate_previous = lh->iterate_list_tail;
+
+ if (lh->iterate_list_tail != IDX_NIL) {
+ struct ordered_hashmap_entry *old_tail;
+
+ old_tail = ordered_bucket_at(lh, lh->iterate_list_tail);
+ assert(old_tail->iterate_next == IDX_NIL);
+ old_tail->iterate_next = IDX_PUT;
+ }
+
+ lh->iterate_list_tail = IDX_PUT;
+ if (lh->iterate_list_head == IDX_NIL)
+ lh->iterate_list_head = IDX_PUT;
+ }
+
+ assert_se(hashmap_put_robin_hood(h, idx, swap) == false);
+
+ n_entries_inc(h);
+#ifdef ENABLE_DEBUG_HASHMAP
+ h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h));
+#endif
+
+ return 1;
+}
+#define hashmap_put_boldly(h, idx, swap, may_resize) \
+ hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize)
+
+/*
+ * Returns 0 if resize is not needed.
+ * 1 if successfully resized.
+ * -ENOMEM on allocation failure.
+ */
+static int resize_buckets(HashmapBase *h, unsigned entries_add) {
+ struct swap_entries swap;
+ char *new_storage;
+ dib_raw_t *old_dibs, *new_dibs;
+ const struct hashmap_type_info *hi;
+ unsigned idx, optimal_idx;
+ unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries;
+ uint8_t new_shift;
+ bool rehash_next;
+
+ assert(h);
+
+ hi = &hashmap_type_info[h->type];
+ new_n_entries = n_entries(h) + entries_add;
+
+ /* overflow? */
+ if (_unlikely_(new_n_entries < entries_add))
+ return -ENOMEM;
+
+ /* For direct storage we allow 100% load, because it's tiny. */
+ if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets)
+ return 0;
+
+ /*
+ * Load factor = n/m = 1 - (1/INV_KEEP_FREE).
+ * From it follows: m = n + n/(INV_KEEP_FREE - 1)
+ */
+ new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1);
+ /* overflow? */
+ if (_unlikely_(new_n_buckets < new_n_entries))
+ return -ENOMEM;
+
+ if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t))))
+ return -ENOMEM;
+
+ old_n_buckets = n_buckets(h);
+
+ if (_likely_(new_n_buckets <= old_n_buckets))
+ return 0;
+
+ new_shift = log2u_round_up(MAX(
+ new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)),
+ 2 * sizeof(struct direct_storage)));
+
+ /* Realloc storage (buckets and DIB array). */
+ new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL,
+ 1U << new_shift);
+ if (!new_storage)
+ return -ENOMEM;
+
+ /* Must upgrade direct to indirect storage. */
+ if (!h->has_indirect) {
+ memcpy(new_storage, h->direct.storage,
+ old_n_buckets * (hi->entry_size + sizeof(dib_raw_t)));
+ h->indirect.n_entries = h->n_direct_entries;
+ h->indirect.idx_lowest_entry = 0;
+ h->n_direct_entries = 0;
+ }
+
+ /* Get a new hash key. If we've just upgraded to indirect storage,
+ * allow reusing a previously generated key. It's still a different key
+ * from the shared one that we used for direct storage. */
+ get_hash_key(h->indirect.hash_key, !h->has_indirect);
+
+ h->has_indirect = true;
+ h->indirect.storage = new_storage;
+ h->indirect.n_buckets = (1U << new_shift) /
+ (hi->entry_size + sizeof(dib_raw_t));
+
+ old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets);
+ new_dibs = dib_raw_ptr(h);
+
+ /*
+ * Move the DIB array to the new place, replacing valid DIB values with
+ * DIB_RAW_REHASH to indicate all of the used buckets need rehashing.
+ * Note: Overlap is not possible, because we have at least doubled the
+ * number of buckets and dib_raw_t is smaller than any entry type.
+ */
+ for (idx = 0; idx < old_n_buckets; idx++) {
+ assert(old_dibs[idx] != DIB_RAW_REHASH);
+ new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE
+ : DIB_RAW_REHASH;
+ }
+
+ /* Zero the area of newly added entries (including the old DIB area) */
+ memzero(bucket_at(h, old_n_buckets),
+ (n_buckets(h) - old_n_buckets) * hi->entry_size);
+
+ /* The upper half of the new DIB array needs initialization */
+ memset(&new_dibs[old_n_buckets], DIB_RAW_INIT,
+ (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t));
+
+ /* Rehash entries that need it */
+ n_rehashed = 0;
+ for (idx = 0; idx < old_n_buckets; idx++) {
+ if (new_dibs[idx] != DIB_RAW_REHASH)
+ continue;
+
+ optimal_idx = bucket_hash(h, bucket_at(h, idx)->key);
+
+ /*
+ * Not much to do if by luck the entry hashes to its current
+ * location. Just set its DIB.
+ */
+ if (optimal_idx == idx) {
+ new_dibs[idx] = 0;
+ n_rehashed++;
+ continue;
+ }
+
+ new_dibs[idx] = DIB_RAW_FREE;
+ bucket_move_entry(h, &swap, idx, IDX_PUT);
+ /* bucket_move_entry does not clear the source */
+ memzero(bucket_at(h, idx), hi->entry_size);
+
+ do {
+ /*
+ * Find the new bucket for the current entry. This may make
+ * another entry homeless and load it into IDX_PUT.
+ */
+ rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap);
+ n_rehashed++;
+
+ /* Did the current entry displace another one? */
+ if (rehash_next)
+ optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key);
+ } while (rehash_next);
+ }
+
+ assert(n_rehashed == n_entries(h));
+
+ return 1;
+}
+
+/*
+ * Finds an entry with a matching key
+ * Returns: index of the found entry, or IDX_NIL if not found.
+ */
+static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned dib, distance;
+ dib_raw_t *dibs = dib_raw_ptr(h);
+
+ assert(idx < n_buckets(h));
+
+ for (distance = 0; ; distance++) {
+ if (dibs[idx] == DIB_RAW_FREE)
+ return IDX_NIL;
+
+ dib = bucket_calculate_dib(h, idx, dibs[idx]);
+
+ if (dib < distance)
+ return IDX_NIL;
+ if (dib == distance) {
+ e = bucket_at(h, idx);
+ if (h->hash_ops->compare(e->key, key) == 0)
+ return idx;
+ }
+
+ idx = next_idx(h, idx);
+ }
+}
+#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key)
+
+int hashmap_put(Hashmap *h, const void *key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(h);
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx != IDX_NIL) {
+ e = plain_bucket_at(h, idx);
+ if (e->value == value)
+ return 0;
+ return -EEXIST;
+ }
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = key;
+ e->value = value;
+ return hashmap_put_boldly(h, hash, &swap, true);
+}
+
+int set_put(Set *s, const void *key) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
+
+ assert(s);
+
+ hash = bucket_hash(s, key);
+ idx = bucket_scan(s, hash, key);
+ if (idx != IDX_NIL)
+ return 0;
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ e->key = key;
+ return hashmap_put_boldly(s, hash, &swap, true);
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(h);
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx != IDX_NIL) {
+ e = plain_bucket_at(h, idx);
+#ifdef ENABLE_DEBUG_HASHMAP
+ /* Although the key is equal, the key pointer may have changed,
+ * and this would break our assumption for iterating. So count
+ * this operation as incompatible with iteration. */
+ if (e->b.key != key) {
+ h->b.debug.put_count++;
+ h->b.debug.rem_count++;
+ h->b.debug.last_rem_idx = idx;
+ }
+#endif
+ e->b.key = key;
+ e->value = value;
+ return 0;
+ }
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = key;
+ e->value = value;
+ return hashmap_put_boldly(h, hash, &swap, true);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ assert(h);
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ e = plain_bucket_at(h, idx);
+ e->value = value;
+ return 0;
+}
+
+void *internal_hashmap_get(HashmapBase *h, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ return entry_value(h, e);
+}
+
+void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = plain_bucket_at(h, idx);
+ if (key2)
+ *key2 = (void*) e->b.key;
+
+ return e->value;
+}
+
+bool internal_hashmap_contains(HashmapBase *h, const void *key) {
+ unsigned hash;
+
+ if (!h)
+ return false;
+
+ hash = bucket_hash(h, key);
+ return bucket_scan(h, hash, key) != IDX_NIL;
+}
+
+void *internal_hashmap_remove(HashmapBase *h, const void *key) {
+ struct hashmap_base_entry *e;
+ unsigned hash, idx;
+ void *data;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ remove_entry(h, idx);
+
+ return data;
+}
+
+void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+ void *data;
+
+ if (!h) {
+ if (rkey)
+ *rkey = NULL;
+ return NULL;
+ }
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL) {
+ if (rkey)
+ *rkey = NULL;
+ return NULL;
+ }
+
+ e = plain_bucket_at(h, idx);
+ data = e->value;
+ if (rkey)
+ *rkey = (void*) e->b.key;
+
+ remove_entry(h, idx);
+
+ return data;
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned old_hash, new_hash, idx;
+
+ if (!h)
+ return -ENOENT;
+
+ old_hash = bucket_hash(h, old_key);
+ idx = bucket_scan(h, old_hash, old_key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ new_hash = bucket_hash(h, new_key);
+ if (bucket_scan(h, new_hash, new_key) != IDX_NIL)
+ return -EEXIST;
+
+ remove_entry(h, idx);
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = new_key;
+ e->value = value;
+ assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
+
+ return 0;
+}
+
+int set_remove_and_put(Set *s, const void *old_key, const void *new_key) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e;
+ unsigned old_hash, new_hash, idx;
+
+ if (!s)
+ return -ENOENT;
+
+ old_hash = bucket_hash(s, old_key);
+ idx = bucket_scan(s, old_hash, old_key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ new_hash = bucket_hash(s, new_key);
+ if (bucket_scan(s, new_hash, new_key) != IDX_NIL)
+ return -EEXIST;
+
+ remove_entry(s, idx);
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ e->key = new_key;
+ assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1);
+
+ return 0;
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
+ struct swap_entries swap;
+ struct plain_hashmap_entry *e;
+ unsigned old_hash, new_hash, idx_old, idx_new;
+
+ if (!h)
+ return -ENOENT;
+
+ old_hash = bucket_hash(h, old_key);
+ idx_old = bucket_scan(h, old_hash, old_key);
+ if (idx_old == IDX_NIL)
+ return -ENOENT;
+
+ old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key;
+
+ new_hash = bucket_hash(h, new_key);
+ idx_new = bucket_scan(h, new_hash, new_key);
+ if (idx_new != IDX_NIL)
+ if (idx_old != idx_new) {
+ remove_entry(h, idx_new);
+ /* Compensate for a possible backward shift. */
+ if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key)
+ idx_old = prev_idx(HASHMAP_BASE(h), idx_old);
+ assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key);
+ }
+
+ remove_entry(h, idx_old);
+
+ e = &bucket_at_swap(&swap, IDX_PUT)->p;
+ e->b.key = new_key;
+ e->value = value;
+ assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1);
+
+ return 0;
+}
+
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
+ struct plain_hashmap_entry *e;
+ unsigned hash, idx;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = plain_bucket_at(h, idx);
+ if (e->value != value)
+ return NULL;
+
+ remove_entry(h, idx);
+
+ return value;
+}
+
+static unsigned find_first_entry(HashmapBase *h) {
+ Iterator i = ITERATOR_FIRST;
+
+ if (!h || !n_entries(h))
+ return IDX_NIL;
+
+ return hashmap_iterate_entry(h, &i);
+}
+
+void *internal_hashmap_first(HashmapBase *h) {
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ return entry_value(h, bucket_at(h, idx));
+}
+
+void *internal_hashmap_first_key(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ return (void*) e->key;
+}
+
+void *internal_hashmap_steal_first(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ void *data;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ data = entry_value(h, e);
+ remove_entry(h, idx);
+
+ return data;
+}
+
+void *internal_hashmap_steal_first_key(HashmapBase *h) {
+ struct hashmap_base_entry *e;
+ void *key;
+ unsigned idx;
+
+ idx = find_first_entry(h);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = bucket_at(h, idx);
+ key = (void*) e->key;
+ remove_entry(h, idx);
+
+ return key;
+}
+
+unsigned internal_hashmap_size(HashmapBase *h) {
+
+ if (!h)
+ return 0;
+
+ return n_entries(h);
+}
+
+unsigned internal_hashmap_buckets(HashmapBase *h) {
+
+ if (!h)
+ return 0;
+
+ return n_buckets(h);
+}
+
+int internal_hashmap_merge(Hashmap *h, Hashmap *other) {
+ Iterator i;
+ unsigned idx;
+
+ assert(h);
+
+ HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
+ struct plain_hashmap_entry *pe = plain_bucket_at(other, idx);
+ int r;
+
+ r = hashmap_put(h, pe->b.key, pe->value);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ return 0;
+}
+
+int set_merge(Set *s, Set *other) {
+ Iterator i;
+ unsigned idx;
+
+ assert(s);
+
+ HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) {
+ struct set_entry *se = set_bucket_at(other, idx);
+ int r;
+
+ r = set_put(s, se->b.key);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
+ int r;
+
+ assert(h);
+
+ r = resize_buckets(h, entries_add);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+/*
+ * The same as hashmap_merge(), but every new item from other is moved to h.
+ * Keys already in h are skipped and stay in other.
+ * Returns: 0 on success.
+ * -ENOMEM on alloc failure, in which case no move has been done.
+ */
+int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
+ struct swap_entries swap;
+ struct hashmap_base_entry *e, *n;
+ Iterator i;
+ unsigned idx;
+ int r;
+
+ assert(h);
+
+ if (!other)
+ return 0;
+
+ assert(other->type == h->type);
+
+ /*
+ * This reserves buckets for the worst case, where none of other's
+ * entries are yet present in h. This is preferable to risking
+ * an allocation failure in the middle of the moving and having to
+ * rollback or return a partial result.
+ */
+ r = resize_buckets(h, n_entries(other));
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH_IDX(idx, other, i) {
+ unsigned h_hash;
+
+ e = bucket_at(other, idx);
+ h_hash = bucket_hash(h, e->key);
+ if (bucket_scan(h, h_hash, e->key) != IDX_NIL)
+ continue;
+
+ n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ n->key = e->key;
+ if (h->type != HASHMAP_TYPE_SET)
+ ((struct plain_hashmap_entry*) n)->value =
+ ((struct plain_hashmap_entry*) e)->value;
+ assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1);
+
+ remove_entry(other, idx);
+ }
+
+ return 0;
+}
+
+int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
+ struct swap_entries swap;
+ unsigned h_hash, other_hash, idx;
+ struct hashmap_base_entry *e, *n;
+ int r;
+
+ assert(h);
+
+ h_hash = bucket_hash(h, key);
+ if (bucket_scan(h, h_hash, key) != IDX_NIL)
+ return -EEXIST;
+
+ if (!other)
+ return -ENOENT;
+
+ assert(other->type == h->type);
+
+ other_hash = bucket_hash(other, key);
+ idx = bucket_scan(other, other_hash, key);
+ if (idx == IDX_NIL)
+ return -ENOENT;
+
+ e = bucket_at(other, idx);
+
+ n = &bucket_at_swap(&swap, IDX_PUT)->p.b;
+ n->key = e->key;
+ if (h->type != HASHMAP_TYPE_SET)
+ ((struct plain_hashmap_entry*) n)->value =
+ ((struct plain_hashmap_entry*) e)->value;
+ r = hashmap_put_boldly(h, h_hash, &swap, true);
+ if (r < 0)
+ return r;
+
+ remove_entry(other, idx);
+ return 0;
+}
+
+HashmapBase *internal_hashmap_copy(HashmapBase *h) {
+ HashmapBase *copy;
+ int r;
+
+ assert(h);
+
+ copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS);
+ if (!copy)
+ return NULL;
+
+ switch (h->type) {
+ case HASHMAP_TYPE_PLAIN:
+ case HASHMAP_TYPE_ORDERED:
+ r = hashmap_merge((Hashmap*)copy, (Hashmap*)h);
+ break;
+ case HASHMAP_TYPE_SET:
+ r = set_merge((Set*)copy, (Set*)h);
+ break;
+ default:
+ assert_not_reached("Unknown hashmap type");
+ }
+
+ if (r < 0) {
+ internal_hashmap_free(copy);
+ return NULL;
+ }
+
+ return copy;
+}
+
+char **internal_hashmap_get_strv(HashmapBase *h) {
+ char **sv;
+ Iterator i;
+ unsigned idx, n;
+
+ sv = new(char*, n_entries(h)+1);
+ if (!sv)
+ return NULL;
+
+ n = 0;
+ HASHMAP_FOREACH_IDX(idx, h, i)
+ sv[n++] = entry_value(h, bucket_at(h, idx));
+ sv[n] = NULL;
+
+ return sv;
+}
+
+void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
+ struct ordered_hashmap_entry *e;
+ unsigned hash, idx;
+
+ if (!h)
+ return NULL;
+
+ hash = bucket_hash(h, key);
+ idx = bucket_scan(h, hash, key);
+ if (idx == IDX_NIL)
+ return NULL;
+
+ e = ordered_bucket_at(h, idx);
+ if (e->iterate_next == IDX_NIL)
+ return NULL;
+ return ordered_bucket_at(h, e->iterate_next)->p.value;
+}
+
+int set_consume(Set *s, void *value) {
+ int r;
+
+ r = set_put(s, value);
+ if (r <= 0)
+ free(value);
+
+ return r;
+}
+
+int set_put_strdup(Set *s, const char *p) {
+ char *c;
+ int r;
+
+ assert(s);
+ assert(p);
+
+ c = strdup(p);
+ if (!c)
+ return -ENOMEM;
+
+ r = set_consume(s, c);
+ if (r == -EEXIST)
+ return 0;
+
+ return r;
+}
+
+int set_put_strdupv(Set *s, char **l) {
+ int n = 0, r;
+ char **i;
+
+ STRV_FOREACH(i, l) {
+ r = set_put_strdup(s, *i);
+ if (r < 0)
+ return r;
+
+ n += r;
+ }
+
+ return n;
+}
diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h
new file mode 100644
index 0000000000..c2c9948bff
--- /dev/null
+++ b/src/systemd/src/basic/hashmap.h
@@ -0,0 +1,415 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <stdbool.h>
+
+#include "macro.h"
+#include "siphash24.h"
+#include "util.h"
+
+/*
+ * A hash table implementation. As a minor optimization a NULL hashmap object
+ * will be treated as empty hashmap for all read operations. That way it is not
+ * necessary to instantiate an object for each Hashmap use.
+ *
+ * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
+ * the implemention will:
+ * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
+ * - perform extra checks for invalid use of iterators
+ */
+
+#define HASH_KEY_SIZE 16
+
+/* The base type for all hashmap and set types. Many functions in the
+ * implementation take (HashmapBase*) parameters and are run-time polymorphic,
+ * though the API is not meant to be polymorphic (do not call functions
+ * internal_*() directly). */
+typedef struct HashmapBase HashmapBase;
+
+/* Specific hashmap/set types */
+typedef struct Hashmap Hashmap; /* Maps keys to values */
+typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */
+typedef struct Set Set; /* Stores just keys */
+
+/* Ideally the Iterator would be an opaque struct, but it is instantiated
+ * by hashmap users, so the definition has to be here. Do not use its fields
+ * directly. */
+typedef struct {
+ unsigned idx; /* index of an entry to be iterated next */
+ const void *next_key; /* expected value of that entry's key pointer */
+#ifdef ENABLE_DEBUG_HASHMAP
+ unsigned put_count; /* hashmap's put_count recorded at start of iteration */
+ unsigned rem_count; /* hashmap's rem_count in previous iteration */
+ unsigned prev_idx; /* idx in previous iteration */
+#endif
+} Iterator;
+
+#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
+#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
+
+typedef void (*hash_func_t)(const void *p, struct siphash *state);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+struct hash_ops {
+ hash_func_t hash;
+ compare_func_t compare;
+};
+
+void string_hash_func(const void *p, struct siphash *state);
+int string_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops string_hash_ops;
+
+/* This will compare the passed pointers directly, and will not
+ * dereference them. This is hence not useful for strings or
+ * suchlike. */
+void trivial_hash_func(const void *p, struct siphash *state);
+int trivial_compare_func(const void *a, const void *b) _const_;
+extern const struct hash_ops trivial_hash_ops;
+
+/* 32bit values we can always just embedd in the pointer itself, but
+ * in order to support 32bit archs we need store 64bit values
+ * indirectly, since they don't fit in a pointer. */
+void uint64_hash_func(const void *p, struct siphash *state);
+int uint64_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops uint64_hash_ops;
+
+/* On some archs dev_t is 32bit, and on others 64bit. And sometimes
+ * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */
+#if SIZEOF_DEV_T != 8
+void devt_hash_func(const void *p, struct siphash *state) _pure_;
+int devt_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops devt_hash_ops = {
+ .hash = devt_hash_func,
+ .compare = devt_compare_func
+};
+#else
+#define devt_hash_func uint64_hash_func
+#define devt_compare_func uint64_compare_func
+#define devt_hash_ops uint64_hash_ops
+#endif
+
+/* Macros for type checking */
+#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
+ (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \
+ __builtin_types_compatible_p(typeof(h), Hashmap*) || \
+ __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \
+ __builtin_types_compatible_p(typeof(h), Set*))
+
+#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \
+ (__builtin_types_compatible_p(typeof(h), Hashmap*) || \
+ __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \
+
+#define HASHMAP_BASE(h) \
+ __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \
+ (HashmapBase*)(h), \
+ (void)0)
+
+#define PLAIN_HASHMAP(h) \
+ __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \
+ (Hashmap*)(h), \
+ (void)0)
+
+#ifdef ENABLE_DEBUG_HASHMAP
+# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
+# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__
+# define HASHMAP_DEBUG_PASS_ARGS , func, file, line
+#else
+# define HASHMAP_DEBUG_PARAMS
+# define HASHMAP_DEBUG_SRC_ARGS
+# define HASHMAP_DEBUG_PASS_ARGS
+#endif
+
+Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
+
+HashmapBase *internal_hashmap_free(HashmapBase *h);
+static inline Hashmap *hashmap_free(Hashmap *h) {
+ return (void*)internal_hashmap_free(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
+ return (void*)internal_hashmap_free(HASHMAP_BASE(h));
+}
+
+HashmapBase *internal_hashmap_free_free(HashmapBase *h);
+static inline Hashmap *hashmap_free_free(Hashmap *h) {
+ return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
+ return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
+}
+
+Hashmap *hashmap_free_free_free(Hashmap *h);
+static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
+ return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
+}
+
+HashmapBase *internal_hashmap_copy(HashmapBase *h);
+static inline Hashmap *hashmap_copy(Hashmap *h) {
+ return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+}
+static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
+ return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
+}
+
+int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+
+int hashmap_put(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_put(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_update(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_update(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value);
+static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_replace(PLAIN_HASHMAP(h), key, value);
+}
+
+void *internal_hashmap_get(HashmapBase *h, const void *key);
+static inline void *hashmap_get(Hashmap *h, const void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(h), key);
+}
+static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(h), key);
+}
+
+void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
+static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
+ return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
+}
+
+bool internal_hashmap_contains(HashmapBase *h, const void *key);
+static inline bool hashmap_contains(Hashmap *h, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(h), key);
+}
+static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(h), key);
+}
+
+void *internal_hashmap_remove(HashmapBase *h, const void *key);
+static inline void *hashmap_remove(Hashmap *h, const void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(h), key);
+}
+static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(h), key);
+}
+
+void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
+static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
+ return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
+}
+
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
+static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
+ return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
+static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
+ return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value);
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
+static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) {
+ return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
+}
+
+/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
+ * should just work, allow this by having looser type-checking here. */
+int internal_hashmap_merge(Hashmap *h, Hashmap *other);
+#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
+#define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
+
+int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
+static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+
+int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
+/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
+static inline int hashmap_move(Hashmap *h, Hashmap *other) {
+ return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+}
+static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
+ return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+}
+
+int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
+static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+}
+static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+}
+
+unsigned internal_hashmap_size(HashmapBase *h) _pure_;
+static inline unsigned hashmap_size(Hashmap *h) {
+ return internal_hashmap_size(HASHMAP_BASE(h));
+}
+static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
+ return internal_hashmap_size(HASHMAP_BASE(h));
+}
+
+static inline bool hashmap_isempty(Hashmap *h) {
+ return hashmap_size(h) == 0;
+}
+static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
+ return ordered_hashmap_size(h) == 0;
+}
+
+unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
+static inline unsigned hashmap_buckets(Hashmap *h) {
+ return internal_hashmap_buckets(HASHMAP_BASE(h));
+}
+static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
+ return internal_hashmap_buckets(HASHMAP_BASE(h));
+}
+
+bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key);
+static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) {
+ return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
+}
+static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) {
+ return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
+}
+
+void internal_hashmap_clear(HashmapBase *h);
+static inline void hashmap_clear(Hashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_clear(OrderedHashmap *h) {
+ internal_hashmap_clear(HASHMAP_BASE(h));
+}
+
+void internal_hashmap_clear_free(HashmapBase *h);
+static inline void hashmap_clear_free(Hashmap *h) {
+ internal_hashmap_clear_free(HASHMAP_BASE(h));
+}
+static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
+ internal_hashmap_clear_free(HASHMAP_BASE(h));
+}
+
+void hashmap_clear_free_free(Hashmap *h);
+static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
+ hashmap_clear_free_free(PLAIN_HASHMAP(h));
+}
+
+/*
+ * Note about all *_first*() functions
+ *
+ * For plain Hashmaps and Sets the order of entries is undefined.
+ * The functions find whatever entry is first in the implementation
+ * internal order.
+ *
+ * Only for OrderedHashmaps the order is well defined and finding
+ * the first entry is O(1).
+ */
+
+void *internal_hashmap_steal_first(HashmapBase *h);
+static inline void *hashmap_steal_first(Hashmap *h) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_steal_first_key(HashmapBase *h);
+static inline void *hashmap_steal_first_key(Hashmap *h) {
+ return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
+ return internal_hashmap_steal_first_key(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_first_key(HashmapBase *h) _pure_;
+static inline void *hashmap_first_key(Hashmap *h) {
+ return internal_hashmap_first_key(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
+ return internal_hashmap_first_key(HASHMAP_BASE(h));
+}
+
+void *internal_hashmap_first(HashmapBase *h) _pure_;
+static inline void *hashmap_first(Hashmap *h) {
+ return internal_hashmap_first(HASHMAP_BASE(h));
+}
+static inline void *ordered_hashmap_first(OrderedHashmap *h) {
+ return internal_hashmap_first(HASHMAP_BASE(h));
+}
+
+/* no hashmap_next */
+void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
+
+char **internal_hashmap_get_strv(HashmapBase *h);
+static inline char **hashmap_get_strv(Hashmap *h) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(h));
+}
+static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(h));
+}
+
+/*
+ * Hashmaps are iterated in unpredictable order.
+ * OrderedHashmaps are an exception to this. They are iterated in the order
+ * the entries were inserted.
+ * It is safe to remove the current entry.
+ */
+#define HASHMAP_FOREACH(e, h, i) \
+ for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); )
+
+#define ORDERED_HASHMAP_FOREACH(e, h, i) \
+ for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); )
+
+#define HASHMAP_FOREACH_KEY(e, k, h, i) \
+ for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
+
+#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
+ for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
+
+#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
+#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
+#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
+#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
+#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
+#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h
index 0514234426..b23b325265 100644
--- a/src/systemd/src/basic/log.h
+++ b/src/systemd/src/basic/log.h
@@ -231,3 +231,15 @@ int log_syntax_internal(
? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
+
+#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
+ ({ \
+ int _level = (level); \
+ if (log_get_max_level() >= LOG_PRI(_level)) { \
+ _cleanup_free_ char *_p = NULL; \
+ _p = utf8_escape_invalid(rvalue); \
+ log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
+ "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
+ } \
+ -EINVAL; \
+ })
diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h
index 80c64904e2..e95ccc0279 100644
--- a/src/systemd/src/basic/macro.h
+++ b/src/systemd/src/basic/macro.h
@@ -125,8 +125,11 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
}
-#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
-
+#define ELEMENTSOF(x) \
+ __extension__ (__builtin_choose_expr( \
+ !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
+ sizeof(x)/sizeof((x)[0]), \
+ (void)0))
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
@@ -215,18 +218,20 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
(__x / __y + !!(__x % __y)); \
})
-#define assert_se(expr) \
+#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
- log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
- } while (false) \
+ log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } while (false)
+
+#define assert_se(expr) assert_message_se(expr, #expr)
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) do {} while(false)
#else
-#define assert(expr) assert_se(expr)
+#define assert(expr) assert_message_se(expr, #expr)
#endif
#define assert_not_reached(t) \
@@ -251,19 +256,19 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
REENABLE_WARNING
#endif
-#define assert_log(expr) ((_likely_(expr)) \
- ? (true) \
- : (log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
+#define assert_log(expr, message) ((_likely_(expr)) \
+ ? (true) \
+ : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
#define assert_return(expr, r) \
do { \
- if (!assert_log(expr)) \
+ if (!assert_log(expr, #expr)) \
return (r); \
} while (false)
#define assert_return_errno(expr, r, err) \
do { \
- if (!assert_log(expr)) { \
+ if (!assert_log(expr, #expr)) { \
errno = err; \
return (r); \
} \
@@ -469,18 +474,6 @@ do { \
#define MODE_INVALID ((mode_t) -1)
#endif /* NM_IGNORED */
-static inline bool UID_IS_INVALID(uid_t uid) {
- /* We consider both the old 16bit -1 user and the newer 32bit
- * -1 user invalid, since they are or used to be incompatible
- * with syscalls such as setresuid() or chown(). */
-
- return uid == (uid_t) ((uint32_t) -1) || uid == (uid_t) ((uint16_t) -1);
-}
-
-static inline bool GID_IS_INVALID(gid_t gid) {
- return gid == (gid_t) ((uint32_t) -1) || gid == (gid_t) ((uint16_t) -1);
-}
-
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \
diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c
new file mode 100644
index 0000000000..a88ca454b0
--- /dev/null
+++ b/src/systemd/src/basic/mempool.c
@@ -0,0 +1,105 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2014 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include "mempool.h"
+#include "macro.h"
+#include "util.h"
+
+struct pool {
+ struct pool *next;
+ unsigned n_tiles;
+ unsigned n_used;
+};
+
+void* mempool_alloc_tile(struct mempool *mp) {
+ unsigned i;
+
+ /* When a tile is released we add it to the list and simply
+ * place the next pointer at its offset 0. */
+
+ assert(mp->tile_size >= sizeof(void*));
+ assert(mp->at_least > 0);
+
+ if (mp->freelist) {
+ void *r;
+
+ r = mp->freelist;
+ mp->freelist = * (void**) mp->freelist;
+ return r;
+ }
+
+ if (_unlikely_(!mp->first_pool) ||
+ _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) {
+ unsigned n;
+ size_t size;
+ struct pool *p;
+
+ n = mp->first_pool ? mp->first_pool->n_tiles : 0;
+ n = MAX(mp->at_least, n * 2);
+ size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size);
+ n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size;
+
+ p = malloc(size);
+ if (!p)
+ return NULL;
+
+ p->next = mp->first_pool;
+ p->n_tiles = n;
+ p->n_used = 0;
+
+ mp->first_pool = p;
+ }
+
+ i = mp->first_pool->n_used++;
+
+ return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size;
+}
+
+void* mempool_alloc0_tile(struct mempool *mp) {
+ void *p;
+
+ p = mempool_alloc_tile(mp);
+ if (p)
+ memzero(p, mp->tile_size);
+ return p;
+}
+
+void mempool_free_tile(struct mempool *mp, void *p) {
+ * (void**) p = mp->freelist;
+ mp->freelist = p;
+}
+
+#ifdef VALGRIND
+
+void mempool_drop(struct mempool *mp) {
+ struct pool *p = mp->first_pool;
+ while (p) {
+ struct pool *n;
+ n = p->next;
+ free(p);
+ p = n;
+ }
+}
+
+#endif
diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h
new file mode 100644
index 0000000000..e9753adb7b
--- /dev/null
+++ b/src/systemd/src/basic/mempool.h
@@ -0,0 +1,51 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011-2014 Lennart Poettering
+ Copyright 2014 Michal Schmidt
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <stddef.h>
+
+struct pool;
+
+struct mempool {
+ struct pool *first_pool;
+ void *freelist;
+ size_t tile_size;
+ unsigned at_least;
+};
+
+void* mempool_alloc_tile(struct mempool *mp);
+void* mempool_alloc0_tile(struct mempool *mp);
+void mempool_free_tile(struct mempool *mp, void *p);
+
+#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \
+struct mempool pool_name = { \
+ .tile_size = sizeof(tile_type), \
+ .at_least = alloc_at_least, \
+}
+
+
+#ifdef VALGRIND
+void mempool_drop(struct mempool *mp);
+#endif
diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c
new file mode 100644
index 0000000000..50728f1a1c
--- /dev/null
+++ b/src/systemd/src/basic/prioq.c
@@ -0,0 +1,320 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/*
+ * Priority Queue
+ * The prioq object implements a priority queue. That is, it orders objects by
+ * their priority and allows O(1) access to the object with the highest
+ * priority. Insertion and removal are Θ(log n). Optionally, the caller can
+ * provide a pointer to an index which will be kept up-to-date by the prioq.
+ *
+ * The underlying algorithm used in this implementation is a Heap.
+ */
+
+#include "nm-sd-adapt.h"
+
+#include "util.h"
+#include "prioq.h"
+
+struct prioq_item {
+ void *data;
+ unsigned *idx;
+};
+
+struct Prioq {
+ compare_func_t compare_func;
+ unsigned n_items, n_allocated;
+
+ struct prioq_item *items;
+};
+
+Prioq *prioq_new(compare_func_t compare_func) {
+ Prioq *q;
+
+ q = new0(Prioq, 1);
+ if (!q)
+ return q;
+
+ q->compare_func = compare_func;
+ return q;
+}
+
+Prioq* prioq_free(Prioq *q) {
+ if (!q)
+ return NULL;
+
+ free(q->items);
+ free(q);
+
+ return NULL;
+}
+
+int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
+ assert(q);
+
+ if (*q)
+ return 0;
+
+ *q = prioq_new(compare_func);
+ if (!*q)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void swap(Prioq *q, unsigned j, unsigned k) {
+ void *saved_data;
+ unsigned *saved_idx;
+
+ assert(q);
+ assert(j < q->n_items);
+ assert(k < q->n_items);
+
+ assert(!q->items[j].idx || *(q->items[j].idx) == j);
+ assert(!q->items[k].idx || *(q->items[k].idx) == k);
+
+ saved_data = q->items[j].data;
+ saved_idx = q->items[j].idx;
+ q->items[j].data = q->items[k].data;
+ q->items[j].idx = q->items[k].idx;
+ q->items[k].data = saved_data;
+ q->items[k].idx = saved_idx;
+
+ if (q->items[j].idx)
+ *q->items[j].idx = j;
+
+ if (q->items[k].idx)
+ *q->items[k].idx = k;
+}
+
+static unsigned shuffle_up(Prioq *q, unsigned idx) {
+ assert(q);
+
+ while (idx > 0) {
+ unsigned k;
+
+ k = (idx-1)/2;
+
+ if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
+ break;
+
+ swap(q, idx, k);
+ idx = k;
+ }
+
+ return idx;
+}
+
+static unsigned shuffle_down(Prioq *q, unsigned idx) {
+ assert(q);
+
+ for (;;) {
+ unsigned j, k, s;
+
+ k = (idx+1)*2; /* right child */
+ j = k-1; /* left child */
+
+ if (j >= q->n_items)
+ break;
+
+ if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
+
+ /* So our left child is smaller than we are, let's
+ * remember this fact */
+ s = j;
+ else
+ s = idx;
+
+ if (k < q->n_items &&
+ q->compare_func(q->items[k].data, q->items[s].data) < 0)
+
+ /* So our right child is smaller than we are, let's
+ * remember this fact */
+ s = k;
+
+ /* s now points to the smallest of the three items */
+
+ if (s == idx)
+ /* No swap necessary, we're done */
+ break;
+
+ swap(q, idx, s);
+ idx = s;
+ }
+
+ return idx;
+}
+
+int prioq_put(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+ unsigned k;
+
+ assert(q);
+
+ if (q->n_items >= q->n_allocated) {
+ unsigned n;
+ struct prioq_item *j;
+
+ n = MAX((q->n_items+1) * 2, 16u);
+ j = realloc(q->items, sizeof(struct prioq_item) * n);
+ if (!j)
+ return -ENOMEM;
+
+ q->items = j;
+ q->n_allocated = n;
+ }
+
+ k = q->n_items++;
+ i = q->items + k;
+ i->data = data;
+ i->idx = idx;
+
+ if (idx)
+ *idx = k;
+
+ shuffle_up(q, k);
+
+ return 0;
+}
+
+static void remove_item(Prioq *q, struct prioq_item *i) {
+ struct prioq_item *l;
+
+ assert(q);
+ assert(i);
+
+ l = q->items + q->n_items - 1;
+
+ if (i == l)
+ /* Last entry, let's just remove it */
+ q->n_items--;
+ else {
+ unsigned k;
+
+ /* Not last entry, let's replace the last entry with
+ * this one, and reshuffle */
+
+ k = i - q->items;
+
+ i->data = l->data;
+ i->idx = l->idx;
+ if (i->idx)
+ *i->idx = k;
+ q->n_items--;
+
+ k = shuffle_down(q, k);
+ shuffle_up(q, k);
+ }
+}
+
+_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+
+ assert(q);
+
+ if (idx) {
+ if (*idx == PRIOQ_IDX_NULL ||
+ *idx > q->n_items)
+ return NULL;
+
+ i = q->items + *idx;
+ if (i->data != data)
+ return NULL;
+
+ return i;
+ } else {
+ for (i = q->items; i < q->items + q->n_items; i++)
+ if (i->data == data)
+ return i;
+ return NULL;
+ }
+}
+
+int prioq_remove(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+
+ if (!q)
+ return 0;
+
+ i = find_item(q, data, idx);
+ if (!i)
+ return 0;
+
+ remove_item(q, i);
+ return 1;
+}
+
+int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
+ struct prioq_item *i;
+ unsigned k;
+
+ assert(q);
+
+ i = find_item(q, data, idx);
+ if (!i)
+ return 0;
+
+ k = i - q->items;
+ k = shuffle_down(q, k);
+ shuffle_up(q, k);
+ return 1;
+}
+
+void *prioq_peek(Prioq *q) {
+
+ if (!q)
+ return NULL;
+
+ if (q->n_items <= 0)
+ return NULL;
+
+ return q->items[0].data;
+}
+
+void *prioq_pop(Prioq *q) {
+ void *data;
+
+ if (!q)
+ return NULL;
+
+ if (q->n_items <= 0)
+ return NULL;
+
+ data = q->items[0].data;
+ remove_item(q, q->items);
+ return data;
+}
+
+unsigned prioq_size(Prioq *q) {
+
+ if (!q)
+ return 0;
+
+ return q->n_items;
+}
+
+bool prioq_isempty(Prioq *q) {
+
+ if (!q)
+ return true;
+
+ return q->n_items <= 0;
+}
diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h
new file mode 100644
index 0000000000..820ac21677
--- /dev/null
+++ b/src/systemd/src/basic/prioq.h
@@ -0,0 +1,44 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include "hashmap.h"
+
+typedef struct Prioq Prioq;
+
+#define PRIOQ_IDX_NULL ((unsigned) -1)
+
+Prioq *prioq_new(compare_func_t compare);
+Prioq *prioq_free(Prioq *q);
+int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
+
+int prioq_put(Prioq *q, void *data, unsigned *idx);
+int prioq_remove(Prioq *q, void *data, unsigned *idx);
+int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
+
+void *prioq_peek(Prioq *q) _pure_;
+void *prioq_pop(Prioq *q);
+
+unsigned prioq_size(Prioq *q) _pure_;
+bool prioq_isempty(Prioq *q) _pure_;
diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h
new file mode 100644
index 0000000000..109d00347e
--- /dev/null
+++ b/src/systemd/src/basic/set.h
@@ -0,0 +1,138 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include "hashmap.h"
+#include "macro.h"
+
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
+
+
+static inline Set *set_free(Set *s) {
+ internal_hashmap_free(HASHMAP_BASE(s));
+ return NULL;
+}
+
+static inline Set *set_free_free(Set *s) {
+ internal_hashmap_free_free(HASHMAP_BASE(s));
+ return NULL;
+}
+
+/* no set_free_free_free */
+
+static inline Set *set_copy(Set *s) {
+ return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
+}
+
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+
+int set_put(Set *s, const void *key);
+/* no set_update */
+/* no set_replace */
+static inline void *set_get(Set *s, void *key) {
+ return internal_hashmap_get(HASHMAP_BASE(s), key);
+}
+/* no set_get2 */
+
+static inline bool set_contains(Set *s, const void *key) {
+ return internal_hashmap_contains(HASHMAP_BASE(s), key);
+}
+
+static inline void *set_remove(Set *s, const void *key) {
+ return internal_hashmap_remove(HASHMAP_BASE(s), key);
+}
+
+/* no set_remove2 */
+/* no set_remove_value */
+int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
+/* no set_remove_and_replace */
+int set_merge(Set *s, Set *other);
+
+static inline int set_reserve(Set *h, unsigned entries_add) {
+ return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+}
+
+static inline int set_move(Set *s, Set *other) {
+ return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
+}
+
+static inline int set_move_one(Set *s, Set *other, const void *key) {
+ return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
+}
+
+static inline unsigned set_size(Set *s) {
+ return internal_hashmap_size(HASHMAP_BASE(s));
+}
+
+static inline bool set_isempty(Set *s) {
+ return set_size(s) == 0;
+}
+
+static inline unsigned set_buckets(Set *s) {
+ return internal_hashmap_buckets(HASHMAP_BASE(s));
+}
+
+bool set_iterate(Set *s, Iterator *i, void **value);
+
+static inline void set_clear(Set *s) {
+ internal_hashmap_clear(HASHMAP_BASE(s));
+}
+
+static inline void set_clear_free(Set *s) {
+ internal_hashmap_clear_free(HASHMAP_BASE(s));
+}
+
+/* no set_clear_free_free */
+
+static inline void *set_steal_first(Set *s) {
+ return internal_hashmap_steal_first(HASHMAP_BASE(s));
+}
+
+/* no set_steal_first_key */
+/* no set_first_key */
+
+static inline void *set_first(Set *s) {
+ return internal_hashmap_first(HASHMAP_BASE(s));
+}
+
+/* no set_next */
+
+static inline char **set_get_strv(Set *s) {
+ return internal_hashmap_get_strv(HASHMAP_BASE(s));
+}
+
+int set_consume(Set *s, void *value);
+int set_put_strdup(Set *s, const char *p);
+int set_put_strdupv(Set *s, char **l);
+
+#define SET_FOREACH(e, s, i) \
+ for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
+
+#define _cleanup_set_free_ _cleanup_(set_freep)
+#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
diff --git a/src/systemd/src/basic/siphash24.c b/src/systemd/src/basic/siphash24.c
index a26f8e28ca..7a8e8f1388 100644
--- a/src/systemd/src/basic/siphash24.c
+++ b/src/systemd/src/basic/siphash24.c
@@ -13,126 +13,172 @@
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
(Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd)
+ (Refactored by Tom Gundersen to split up in several functions and follow systemd
+ coding style)
*/
#include "nm-sd-adapt.h"
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
+#include "sparse-endian.h"
#include "siphash24.h"
+#include "util.h"
-typedef uint64_t u64;
-typedef uint32_t u32;
-typedef uint8_t u8;
-
-#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
-
-#define U32TO8_LE(p, v) \
- (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
- (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
-
-#define U64TO8_LE(p, v) \
- U32TO8_LE((p), (u32)((v) )); \
- U32TO8_LE((p) + 4, (u32)((v) >> 32));
-
-#define U8TO64_LE(p) \
- (((u64)((p)[0]) ) | \
- ((u64)((p)[1]) << 8) | \
- ((u64)((p)[2]) << 16) | \
- ((u64)((p)[3]) << 24) | \
- ((u64)((p)[4]) << 32) | \
- ((u64)((p)[5]) << 40) | \
- ((u64)((p)[6]) << 48) | \
- ((u64)((p)[7]) << 56))
-
-#define SIPROUND \
- do { \
- v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
- v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
- v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
- v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
- } while(0)
+static inline uint64_t rotate_left(uint64_t x, uint8_t b) {
+ assert(b < 64);
+
+ return (x << b) | (x >> (64 - b));
+}
+
+static inline void sipround(struct siphash *state) {
+ assert(state);
+
+ state->v0 += state->v1;
+ state->v1 = rotate_left(state->v1, 13);
+ state->v1 ^= state->v0;
+ state->v0 = rotate_left(state->v0, 32);
+ state->v2 += state->v3;
+ state->v3 = rotate_left(state->v3, 16);
+ state->v3 ^= state->v2;
+ state->v0 += state->v3;
+ state->v3 = rotate_left(state->v3, 21);
+ state->v3 ^= state->v0;
+ state->v2 += state->v1;
+ state->v1 = rotate_left(state->v1, 17);
+ state->v1 ^= state->v2;
+ state->v2 = rotate_left(state->v2, 32);
+}
+
+void siphash24_init(struct siphash *state, const uint8_t k[16]) {
+ uint64_t k0, k1;
+
+ assert(state);
+ assert(k);
+
+ k0 = le64toh(*(le64_t*) k);
+ k1 = le64toh(*(le64_t*) (k + 8));
+
+ /* "somepseudorandomlygeneratedbytes" */
+ state->v0 = 0x736f6d6570736575ULL ^ k0;
+ state->v1 = 0x646f72616e646f6dULL ^ k1;
+ state->v2 = 0x6c7967656e657261ULL ^ k0;
+ state->v3 = 0x7465646279746573ULL ^ k1;
+ state->padding = 0;
+ state->inlen = 0;
+}
+
+void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
+ uint64_t m;
+ const uint8_t *in = _in;
+ const uint8_t *end = in + inlen;
+ unsigned left = state->inlen & 7;
+
+ assert(in);
+ assert(state);
+
+ /* update total length */
+ state->inlen += inlen;
+
+ /* if padding exists, fill it out */
+ if (left > 0) {
+ for ( ; in < end && left < 8; in ++, left ++ )
+ state->padding |= ( ( uint64_t )*in ) << (left * 8);
+
+ if (in == end && left < 8)
+ /* we did not have enough input to fill out the padding completely */
+ return;
-/* SipHash-2-4 */
-void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16])
-{
- /* "somepseudorandomlygeneratedbytes" */
- u64 v0 = 0x736f6d6570736575ULL;
- u64 v1 = 0x646f72616e646f6dULL;
- u64 v2 = 0x6c7967656e657261ULL;
- u64 v3 = 0x7465646279746573ULL;
- u64 b;
- u64 k0 = U8TO64_LE( k );
- u64 k1 = U8TO64_LE( k + 8 );
- u64 m;
- const u8 *in = _in;
- const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
- const int left = inlen & 7;
- b = ( ( u64 )inlen ) << 56;
- v3 ^= k1;
- v2 ^= k0;
- v1 ^= k1;
- v0 ^= k0;
-
- for ( ; in != end; in += 8 )
- {
- m = U8TO64_LE( in );
#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
- printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
+ printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+ printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+ printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+ printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+ printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding);
#endif
- v3 ^= m;
- SIPROUND;
- SIPROUND;
- v0 ^= m;
- }
+ state->v3 ^= state->padding;
+ sipround(state);
+ sipround(state);
+ state->v0 ^= state->padding;
- switch( left )
- {
- case 7: b |= ( ( u64 )in[ 6] ) << 48;
+ state->padding = 0;
+ }
- case 6: b |= ( ( u64 )in[ 5] ) << 40;
+ end -= ( state->inlen % sizeof (uint64_t) );
- case 5: b |= ( ( u64 )in[ 4] ) << 32;
+ for ( ; in < end; in += 8 ) {
+ m = le64toh(*(le64_t*) in);
+#ifdef DEBUG
+ printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+ printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+ printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+ printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
+ printf("(%3zu) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m);
+#endif
+ state->v3 ^= m;
+ sipround(state);
+ sipround(state);
+ state->v0 ^= m;
+ }
+
+ left = state->inlen & 7;
+
+ switch(left)
+ {
+ case 7: state->padding |= ((uint64_t) in[6]) << 48;
- case 4: b |= ( ( u64 )in[ 3] ) << 24;
+ case 6: state->padding |= ((uint64_t) in[5]) << 40;
- case 3: b |= ( ( u64 )in[ 2] ) << 16;
+ case 5: state->padding |= ((uint64_t) in[4]) << 32;
- case 2: b |= ( ( u64 )in[ 1] ) << 8;
+ case 4: state->padding |= ((uint64_t) in[3]) << 24;
- case 1: b |= ( ( u64 )in[ 0] ); break;
+ case 3: state->padding |= ((uint64_t) in[2]) << 16;
+
+ case 2: state->padding |= ((uint64_t) in[1]) << 8;
+
+ case 1: state->padding |= ((uint64_t) in[0]); break;
+
+ case 0: break;
+ }
+}
- case 0: break;
- }
+void siphash24_finalize(uint8_t out[8], struct siphash *state) {
+ uint64_t b;
+ b = state->padding | (( ( uint64_t )state->inlen ) << 56);
#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
- printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
+ printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t)state->v0);
+ printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t)state->v1);
+ printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t)state->v2);
+ printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t)state->v3);
+ printf("(%3zu) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding);
#endif
- v3 ^= b;
- SIPROUND;
- SIPROUND;
- v0 ^= b;
+ state->v3 ^= b;
+ sipround(state);
+ sipround(state);
+ state->v0 ^= b;
+
#ifdef DEBUG
- printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
- printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
- printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
- printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+ printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0);
+ printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1);
+ printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2);
+ printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3);
#endif
- v2 ^= 0xff;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- b = v0 ^ v1 ^ v2 ^ v3;
- U64TO8_LE( out, b );
+ state->v2 ^= 0xff;
+
+ sipround(state);
+ sipround(state);
+ sipround(state);
+ sipround(state);
+
+ *(le64_t*)out = htole64(state->v0 ^ state->v1 ^ state->v2 ^ state->v3);
+}
+
+/* SipHash-2-4 */
+void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) {
+ struct siphash state;
+
+ siphash24_init(&state, k);
+ siphash24_compress(_in, inlen, &state);
+ siphash24_finalize(out, &state);
}
diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h
index 3450d754fe..6515c820fd 100644
--- a/src/systemd/src/basic/siphash24.h
+++ b/src/systemd/src/basic/siphash24.h
@@ -5,4 +5,17 @@
#include <inttypes.h>
#include <sys/types.h>
+struct siphash {
+ uint64_t v0;
+ uint64_t v1;
+ uint64_t v2;
+ uint64_t v3;
+ uint64_t padding;
+ size_t inlen;
+};
+
+void siphash24_init(struct siphash *state, const uint8_t k[16]);
+void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
+void siphash24_finalize(uint8_t out[8], struct siphash *state);
+
void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]);
diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c
index de0f371613..bd4cac7ce1 100644
--- a/src/systemd/src/basic/strv.c
+++ b/src/systemd/src/basic/strv.c
@@ -280,8 +280,8 @@ char **strv_split_newlines(const char *s) {
#if 0 /* NM_IGNORED */
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
- size_t n = 0, allocated = 0;
_cleanup_strv_free_ char **l = NULL;
+ size_t n = 0, allocated = 0;
int r;
assert(t);
@@ -305,13 +305,16 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
l[n] = NULL;
}
- if (!l)
+ if (!l) {
l = new0(char*, 1);
+ if (!l)
+ return -ENOMEM;
+ }
*t = l;
l = NULL;
- return 0;
+ return (int) n;
}
#endif /* NM_IGNORED */
@@ -724,3 +727,66 @@ bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
return false;
}
+
+char ***strv_free_free(char ***l) {
+ char ***i;
+
+ if (!l)
+ return NULL;
+
+ for (i = l; *i; i++)
+ strv_free(*i);
+
+ free(l);
+ return NULL;
+}
+
+char **strv_skip(char **l, size_t n) {
+
+ while (n > 0) {
+ if (strv_isempty(l))
+ return l;
+
+ l++, n--;
+ }
+
+ return l;
+}
+
+int strv_extend_n(char ***l, const char *value, size_t n) {
+ size_t i, j, k;
+ char **nl;
+
+ assert(l);
+
+ if (!value)
+ return 0;
+ if (n == 0)
+ return 0;
+
+ /* Adds the value value n times to l */
+
+ k = strv_length(*l);
+
+ nl = realloc(*l, sizeof(char*) * (k + n + 1));
+ if (!nl)
+ return -ENOMEM;
+
+ *l = nl;
+
+ for (i = k; i < k + n; i++) {
+ nl[i] = strdup(value);
+ if (!nl[i])
+ goto rollback;
+ }
+
+ nl[i] = NULL;
+ return 0;
+
+rollback:
+ for (j = k; j < i; i++)
+ free(nl[j]);
+
+ nl[k] = NULL;
+ return -ENOMEM;
+}
diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h
index df3855d9fa..3917377bc8 100644
--- a/src/systemd/src/basic/strv.h
+++ b/src/systemd/src/basic/strv.h
@@ -156,3 +156,9 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i
return strv_isempty(patterns) ||
strv_fnmatch(patterns, s, flags);
}
+
+char ***strv_free_free(char ***l);
+
+char **strv_skip(char **l, size_t n);
+
+int strv_extend_n(char ***l, const char *value, size_t n);
diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h
index 463d509b4a..0a20d55a60 100644
--- a/src/systemd/src/basic/time-util.h
+++ b/src/systemd/src/basic/time-util.h
@@ -114,6 +114,8 @@ bool timezone_is_valid(const char *name);
clockid_t clock_boottime_or_monotonic(void);
-#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0)
+#define xstrftime(buf, fmt, tm) \
+ assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \
+ "xstrftime: " #buf "[] must be big enough")
int get_timezone(char **timezone);
diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c
index 3b41adad8d..31361e5252 100644
--- a/src/systemd/src/basic/util.c
+++ b/src/systemd/src/basic/util.c
@@ -21,49 +21,48 @@
#include "nm-sd-adapt.h"
-#include <string.h>
-#include <unistd.h>
+#include <ctype.h>
+#include <dirent.h>
#include <errno.h>
-#include <stdlib.h>
-#include <signal.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <grp.h>
+#include <langinfo.h>
#include <libintl.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <sched.h>
-#include <sys/resource.h>
+#include <limits.h>
+#include <linux/magic.h>
#include <linux/sched.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <stdarg.h>
+#include <locale.h>
+#include <netinet/ip.h>
#include <poll.h>
-#include <ctype.h>
-#include <sys/prctl.h>
-#include <sys/utsname.h>
#include <pwd.h>
-#include <netinet/ip.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <glob.h>
-#include <grp.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
#include <sys/mman.h>
-#include <sys/vfs.h>
#include <sys/mount.h>
-#include <linux/magic.h>
-#include <limits.h>
-#include <langinfo.h>
-#include <locale.h>
#include <sys/personality.h>
-#include <sys/xattr.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
#include <sys/statvfs.h>
-#include <sys/file.h>
-#include <linux/fs.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/vfs.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <syslog.h>
+#include <unistd.h>
/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the POSIX
- * version which is really broken. We prefer GNU basename(). */
+ * undefine basename() since libgen.h defines it as a macro to the
+ * POSIX version which is really broken. We prefer GNU basename(). */
#include <libgen.h>
#undef basename
@@ -71,36 +70,42 @@
#include <sys/auxv.h>
#endif
-#include "config.h"
-#include "macro.h"
-#include "util.h"
+/* We include linux/fs.h as last of the system headers, as it
+ * otherwise conflicts with sys/mount.h. Yay, Linux is great! */
+#include <linux/fs.h>
+
#if 0 /* NM_IGNORED */
+#include "build.h"
+#include "def.h"
+#include "device-nodes.h"
+#include "env-util.h"
+#include "exit-status.h"
+#include "fileio.h"
+#include "formats-util.h"
+#include "gunicode.h"
+#include "hashmap.h"
+#include "hostname-util.h"
#include "ioprio.h"
-#include "missing.h"
#include "log.h"
-#include "strv.h"
+#endif /* NM_IGNORED */
+#include "macro.h"
+#if 0 /* NM_IGNORED */
+#include "missing.h"
#include "mkdir.h"
#endif /* NM_IGNORED */
#include "path-util.h"
#if 0 /* NM_IGNORED */
-#include "exit-status.h"
-#include "hashmap.h"
-#include "env-util.h"
-#include "fileio.h"
-#include "device-nodes.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "signal-util.h"
+#include "sparse-endian.h"
+#include "strv.h"
+#include "terminal-util.h"
#endif /* NM_IGNORED */
#include "utf8.h"
+#include "util.h"
#if 0 /* NM_IGNORED */
-#include "gunicode.h"
#include "virt.h"
-#include "def.h"
-#include "sparse-endian.h"
-#include "formats-util.h"
-#include "process-util.h"
-#include "random-util.h"
-#include "terminal-util.h"
-#include "hostname-util.h"
-#include "signal-util.h"
#endif /* NM_IGNORED */
/* Put this test here for a lack of better place */
@@ -109,6 +114,7 @@ assert_cc(EAGAIN == EWOULDBLOCK);
#if 0 /* NM_IGNORED */
int saved_argc = 0;
char **saved_argv = NULL;
+#endif /* NM_IGNORED */
size_t page_size(void) {
static thread_local size_t pgsz = 0;
@@ -123,7 +129,6 @@ size_t page_size(void) {
pgsz = (size_t) r;
return pgsz;
}
-#endif /* NM_IGNORED */
int strcmp_ptr(const char *a, const char *b) {
@@ -366,6 +371,17 @@ FILE* safe_fclose(FILE *f) {
return NULL;
}
+DIR* safe_closedir(DIR *d) {
+
+ if (d) {
+ PROTECT_ERRNO;
+
+ assert_se(closedir(d) >= 0 || errno != EBADF);
+ }
+
+ return NULL;
+}
+
int unlink_noerrno(const char *path) {
PROTECT_ERRNO;
int r;
@@ -2159,7 +2175,13 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
assert(fd >= 0);
assert(buf);
- while (nbytes > 0) {
+ /* If called with nbytes == 0, let's call read() at least
+ * once, to validate the operation */
+
+ if (nbytes > (size_t) SSIZE_MAX)
+ return -EINVAL;
+
+ do {
ssize_t k;
k = read(fd, p, nbytes);
@@ -2173,7 +2195,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
* and expect that any error/EOF is reported
* via read() */
- fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
+ (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY);
continue;
}
@@ -2183,10 +2205,12 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
if (k == 0)
return n;
+ assert((size_t) k <= nbytes);
+
p += k;
nbytes -= k;
n += k;
- }
+ } while (nbytes > 0);
return n;
}
@@ -2196,9 +2220,10 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
n = loop_read(fd, buf, nbytes, do_poll);
if (n < 0)
- return n;
+ return (int) n;
if ((size_t) n != nbytes)
return -EIO;
+
return 0;
}
@@ -2209,7 +2234,8 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
assert(fd >= 0);
assert(buf);
- errno = 0;
+ if (nbytes > (size_t) SSIZE_MAX)
+ return -EINVAL;
do {
ssize_t k;
@@ -2224,16 +2250,18 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
* and expect that any error/EOF is reported
* via write() */
- fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
+ (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
continue;
}
return -errno;
}
- if (nbytes > 0 && k == 0) /* Can't really happen */
+ if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
return -EIO;
+ assert((size_t) k <= nbytes);
+
p += k;
nbytes -= k;
} while (nbytes > 0);
@@ -2552,34 +2580,6 @@ int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
return 0;
}
-cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
- cpu_set_t *r;
- unsigned n = 1024;
-
- /* Allocates the cpuset in the right size */
-
- for (;;) {
- if (!(r = CPU_ALLOC(n)))
- return NULL;
-
- if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
- CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
-
- if (ncpus)
- *ncpus = n;
-
- return r;
- }
-
- CPU_FREE(r);
-
- if (errno != EINVAL)
- return NULL;
-
- n *= 2;
- }
-}
-
int files_same(const char *filea, const char *fileb) {
struct stat a, b;
@@ -5279,6 +5279,19 @@ unsigned long personality_from_string(const char *p) {
if (streq(p, "x86"))
return PER_LINUX;
+
+#elif defined(__s390x__)
+
+ if (streq(p, "s390"))
+ return PER_LINUX32;
+
+ if (streq(p, "s390x"))
+ return PER_LINUX;
+
+#elif defined(__s390__)
+
+ if (streq(p, "s390"))
+ return PER_LINUX;
#endif
return PERSONALITY_INVALID;
@@ -5298,6 +5311,20 @@ const char* personality_to_string(unsigned long p) {
if (p == PER_LINUX)
return "x86";
+
+#elif defined(__s390x__)
+
+ if (p == PER_LINUX)
+ return "s390x";
+
+ if (p == PER_LINUX32)
+ return "s390";
+
+#elif defined(__s390__)
+
+ if (p == PER_LINUX)
+ return "s390";
+
#endif
return NULL;
@@ -5362,15 +5389,13 @@ int update_reboot_param_file(const char *param) {
int r = 0;
if (param) {
-
r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
if (r < 0)
- log_error("Failed to write reboot param to "
- REBOOT_PARAM_FILE": %s", strerror(-r));
+ return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
} else
- unlink(REBOOT_PARAM_FILE);
+ (void) unlink(REBOOT_PARAM_FILE);
- return r;
+ return 0;
}
int umount_recursive(const char *prefix, int flags) {
@@ -6006,6 +6031,7 @@ int extract_first_word_and_warn(
const char *filename,
unsigned line,
const char *rvalue) {
+
/* Try to unquote it, if it fails, warn about it and try again but this
* time using EXTRACT_CUNESCAPE_RELAX to keep the backslashes verbatim
* in invalid escape sequences. */
@@ -6014,17 +6040,17 @@ int extract_first_word_and_warn(
save = *p;
r = extract_first_word(p, ret, separators, flags);
- if (r < 0 && !(flags&EXTRACT_CUNESCAPE_RELAX)) {
+ if (r < 0 && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
+
/* Retry it with EXTRACT_CUNESCAPE_RELAX. */
*p = save;
r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
else
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
- "Invalid escape sequences in command line: \"%s\"", rvalue);
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid escape sequences in command line: \"%s\"", rvalue);
}
+
return r;
}
@@ -6138,15 +6164,6 @@ int ptsname_malloc(int fd, char **ret) {
int openpt_in_namespace(pid_t pid, int flags) {
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
siginfo_t si;
pid_t child;
int r;
@@ -6180,15 +6197,7 @@ int openpt_in_namespace(pid_t pid, int flags) {
if (unlockpt(master) < 0)
_exit(EXIT_FAILURE);
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
-
- mh.msg_controllen = cmsg->cmsg_len;
-
- if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
+ if (send_one_fd(pair[1], master, 0) < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
@@ -6202,39 +6211,23 @@ int openpt_in_namespace(pid_t pid, int flags) {
if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
return -EIO;
- if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
- return -errno;
-
- CMSG_FOREACH(cmsg, &mh)
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- int *fds;
- unsigned n_fds;
-
- fds = (int*) CMSG_DATA(cmsg);
- n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
-
- if (n_fds != 1) {
- close_many(fds, n_fds);
- return -EIO;
- }
-
- return fds[0];
- }
-
- return -EIO;
+ return receive_one_fd(pair[0], 0);
}
ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) {
+ char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -1;
ssize_t l;
/* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
- fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
+ fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
if (fd < 0)
return -errno;
- l = fgetxattr(fd, attribute, value, size);
+ xsprintf(fn, "/proc/self/fd/%i", fd);
+
+ l = getxattr(fn, attribute, value, size);
if (l < 0)
return -errno;
@@ -6584,7 +6577,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
for (i = 0; i < len; ++i)
if (streq_ptr(table[i], key))
- return (ssize_t)i;
+ return (ssize_t) i;
return -1;
}
@@ -6824,4 +6817,110 @@ int fgetxattr_malloc(int fd, const char *name, char **value) {
return -errno;
}
}
+
+int send_one_fd(int transport_fd, int fd, int flags) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+
+ assert(transport_fd >= 0);
+ assert(fd >= 0);
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+ mh.msg_controllen = CMSG_SPACE(sizeof(int));
+ if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int receive_one_fd(int transport_fd, int flags) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg, *found = NULL;
+
+ assert(transport_fd >= 0);
+
+ /*
+ * Receive a single FD via @transport_fd. We don't care for
+ * the transport-type. We retrieve a single FD at most, so for
+ * packet-based transports, the caller must ensure to send
+ * only a single FD per packet. This is best used in
+ * combination with send_one_fd().
+ */
+
+ if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0)
+ return -errno;
+
+ CMSG_FOREACH(cmsg, &mh) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+ assert(!found);
+ found = cmsg;
+ break;
+ }
+ }
+
+ if (!found) {
+ cmsg_close_all(&mh);
+ return -EIO;
+ }
+
+ return *(int*) CMSG_DATA(found);
+}
+
+void nop_signal_handler(int sig) {
+ /* nothing here */
+}
+
+int version(void) {
+ puts(PACKAGE_STRING "\n"
+ SYSTEMD_FEATURES);
+ return 0;
+}
+
+bool fdname_is_valid(const char *s) {
+ const char *p;
+
+ /* Validates a name for $LISTEN_FDNAMES. We basically allow
+ * everything ASCII that's not a control character. Also, as
+ * special exception the ":" character is not allowed, as we
+ * use that as field separator in $LISTEN_FDNAMES.
+ *
+ * Note that the empty string is explicitly allowed
+ * here. However, we limit the length of the names to 255
+ * characters. */
+
+ if (!s)
+ return false;
+
+ for (p = s; *p; p++) {
+ if (*p < ' ')
+ return false;
+ if (*p >= 127)
+ return false;
+ if (*p == ':')
+ return false;
+ }
+
+ return p - s < 256;
+}
#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h
index 154aabadee..b6285daaeb 100644
--- a/src/systemd/src/basic/util.h
+++ b/src/systemd/src/basic/util.h
@@ -24,34 +24,33 @@
#include "nm-sd-adapt.h"
#include <alloca.h>
+#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <time.h>
+#include <limits.h>
+#include <locale.h>
+#include <mntent.h>
#include <stdarg.h>
#include <stdbool.h>
-#include <stdlib.h>
+#include <stddef.h>
#include <stdio.h>
-#include <sched.h>
-#include <limits.h>
-#include <sys/types.h>
+#include <stdlib.h>
+#include <sys/inotify.h>
#include <sys/socket.h>
#include <sys/stat.h>
-#include <dirent.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <locale.h>
-#include <mntent.h>
-#include <sys/inotify.h>
#include <sys/statfs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#if 0 /* NM_IGNORED */
+#include "formats-util.h"
+#endif /* NM_IGNORED */
#include "macro.h"
#if 0 /* NM_IGNORED */
#include "missing.h"
#endif /* NM_IGNORED */
#include "time-util.h"
-#if 0 /* NM_IGNORED */
-#include "formats-util.h"
-#endif /* NM_IGNORED */
/* What is interpreted as whitespace? */
#define WHITESPACE " \t\n\r"
@@ -157,6 +156,7 @@ void close_many(const int fds[], unsigned n_fd);
int fclose_nointr(FILE *f);
FILE* safe_fclose(FILE *f);
+DIR* safe_closedir(DIR *f);
int parse_size(const char *t, uint64_t base, uint64_t *size);
@@ -166,7 +166,10 @@ int parse_uid(const char *s, uid_t* ret_uid);
#define parse_gid(s, ret_gid) parse_uid(s, ret_gid)
bool uid_is_valid(uid_t uid);
-#define gid_is_valid(gid) uid_is_valid(gid)
+
+static inline bool gid_is_valid(gid_t gid) {
+ return uid_is_valid((uid_t) gid);
+}
int safe_atou(const char *s, unsigned *ret_u);
int safe_atoi(const char *s, int *ret_i);
@@ -295,9 +298,9 @@ bool chars_intersect(const char *a, const char *b) _pure_;
ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
-#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
- scope inline type name##_from_string(const char *s) { \
- return (type)string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
+#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
+ scope type name##_from_string(const char *s) { \
+ return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
}
#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
@@ -314,17 +317,15 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
int name##_to_string_alloc(type i, char **str) { \
char *s; \
- int r; \
if (i < 0 || i > max) \
return -ERANGE; \
if (i < (type) ELEMENTSOF(name##_table)) { \
s = strdup(name##_table[i]); \
if (!s) \
- return log_oom(); \
+ return -ENOMEM; \
} else { \
- r = asprintf(&s, "%i", i); \
- if (r < 0) \
- return log_oom(); \
+ if (asprintf(&s, "%i", i) < 0) \
+ return -ENOMEM; \
} \
*str = s; \
return 0; \
@@ -332,10 +333,10 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
type name##_from_string(const char *s) { \
type i; \
unsigned u = 0; \
- assert(s); \
- for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \
- if (name##_table[i] && \
- streq(name##_table[i], s)) \
+ if (!s) \
+ return (type) -1; \
+ for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \
+ if (streq_ptr(name##_table[i], s)) \
return i; \
if (safe_atou(s, &u) >= 0 && u <= max) \
return (type) u; \
@@ -375,12 +376,9 @@ int fd_is_temporary_fs(int fd);
int pipe_eof(int fd);
-DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
-#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp)
-
-cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-
-#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
+#define xsprintf(buf, fmt, ...) \
+ assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), \
+ "xsprintf: " #buf "[] must be big enough")
int files_same(const char *filea, const char *fileb);
@@ -440,7 +438,9 @@ int get_files_in_directory(const char *path, char ***list);
char *strjoin(const char *x, ...) _sentinel_;
+#if 0 /* NM_IGNORED */
bool is_main_thread(void);
+#endif /* NM_IGNORED */
static inline bool _pure_ in_charset(const char *s, const char* charset) {
assert(s);
@@ -948,3 +948,12 @@ int reset_uid_gid(void);
int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink);
int fgetxattr_malloc(int fd, const char *name, char **value);
+
+int send_one_fd(int transport_fd, int fd, int flags);
+int receive_one_fd(int transport_fd, int flags);
+
+void nop_signal_handler(int sig);
+
+int version(void);
+
+bool fdname_is_valid(const char *s);
diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c
new file mode 100644
index 0000000000..39a159314f
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/arp-util.c
@@ -0,0 +1,155 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <linux/filter.h>
+#include <arpa/inet.h>
+
+#include "util.h"
+#include "arp-util.h"
+
+int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
+ BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ /* Sender Hardware Address must be different from our own */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
+ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
+ BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ };
+ struct sock_fprog fprog = {
+ .len = ELEMENTSOF(filter),
+ .filter = (struct sock_filter*) filter
+ };
+ union sockaddr_union link = {
+ .ll.sll_family = AF_PACKET,
+ .ll.sll_protocol = htons(ETH_P_ARP),
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = ETH_ALEN,
+ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ };
+ _cleanup_close_ int s = -1;
+ int r;
+
+ assert(ifindex > 0);
+
+ s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (s < 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+ if (r < 0)
+ return -errno;
+
+ r = bind(s, &link.sa, sizeof(link.ll));
+ if (r < 0)
+ return -errno;
+
+ r = s;
+ s = -1;
+
+ return r;
+}
+
+static int arp_send_packet(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha,
+ bool announce) {
+ union sockaddr_union link = {
+ .ll.sll_family = AF_PACKET,
+ .ll.sll_protocol = htons(ETH_P_ARP),
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = ETH_ALEN,
+ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ };
+ struct ether_arp arp = {
+ .ea_hdr.ar_hrd = htons(ARPHRD_ETHER), /* HTYPE */
+ .ea_hdr.ar_pro = htons(ETHERTYPE_IP), /* PTYPE */
+ .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
+ .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
+ .ea_hdr.ar_op = htons(ARPOP_REQUEST), /* REQUEST */
+ };
+ int r;
+
+ assert(fd >= 0);
+ assert(pa != 0);
+ assert(ha);
+
+ memcpy(&arp.arp_sha, ha, ETH_ALEN);
+ memcpy(&arp.arp_tpa, &pa, sizeof(pa));
+
+ if (announce)
+ memcpy(&arp.arp_spa, &pa, sizeof(pa));
+
+ r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+int arp_send_probe(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ return arp_send_packet(fd, ifindex, pa, ha, false);
+}
+
+int arp_send_announcement(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha) {
+ return arp_send_packet(fd, ifindex, pa, ha, true);
+}
diff --git a/src/systemd/src/libsystemd-network/ipv4ll-internal.h b/src/systemd/src/libsystemd-network/arp-util.h
index 3a0668fab4..60abe88be6 100644
--- a/src/systemd/src/libsystemd-network/ipv4ll-internal.h
+++ b/src/systemd/src/libsystemd-network/arp-util.h
@@ -28,13 +28,9 @@
#include "sparse-endian.h"
#include "socket-util.h"
-int arp_network_bind_raw_socket(int index, union sockaddr_union *link);
-int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
- const struct ether_arp *arp);
+int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
-void arp_packet_init(struct ether_arp *arp);
-void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
-void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
-int arp_packet_verify_headers(struct ether_arp *arp);
-
-#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__)
+int arp_send_probe(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha);
+int arp_send_announcement(int fd, int ifindex,
+ be32_t pa, const struct ether_addr *ha);
diff --git a/src/systemd/src/libsystemd-network/ipv4ll-network.c b/src/systemd/src/libsystemd-network/ipv4ll-network.c
deleted file mode 100644
index de1e9ddfd4..0000000000
--- a/src/systemd/src/libsystemd-network/ipv4ll-network.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Axis Communications AB. All rights reserved.
-
- systemd 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; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "nm-sd-adapt.h"
-
-#include <linux/filter.h>
-
-#include "util.h"
-#include "ipv4ll-internal.h"
-
-int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
- const struct ether_arp *arp) {
- int r;
-
- assert(arp);
- assert(link);
- assert(fd >= 0);
-
- r = sendto(fd, arp, sizeof(struct ether_arp), 0, &link->sa, sizeof(link->ll));
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int arp_network_bind_raw_socket(int ifindex, union sockaddr_union *link) {
-
- static const struct sock_filter filter[] = {
- BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
- BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 0, 1), /* protocol == request ? */
- BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1), /* protocol == reply ? */
- BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- };
- struct sock_fprog fprog = {
- .len = ELEMENTSOF(filter),
- .filter = (struct sock_filter*) filter
- };
- _cleanup_close_ int s = -1;
- int r;
-
- assert(ifindex > 0);
- assert(link);
-
- s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (s < 0)
- return -errno;
-
- r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
- if (r < 0)
- return -errno;
-
- link->ll.sll_family = AF_PACKET;
- link->ll.sll_protocol = htons(ETH_P_ARP);
- link->ll.sll_ifindex = ifindex;
- link->ll.sll_halen = ETH_ALEN;
- memset(link->ll.sll_addr, 0xff, ETH_ALEN);
-
- r = bind(s, &link->sa, sizeof(link->ll));
- if (r < 0)
- return -errno;
-
- r = s;
- s = -1;
-
- return r;
-}
diff --git a/src/systemd/src/libsystemd-network/ipv4ll-packet.c b/src/systemd/src/libsystemd-network/ipv4ll-packet.c
deleted file mode 100644
index 5e89a119b0..0000000000
--- a/src/systemd/src/libsystemd-network/ipv4ll-packet.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Axis Communications AB. All rights reserved.
-
- systemd 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; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "nm-sd-adapt.h"
-
-#include <arpa/inet.h>
-
-#include "util.h"
-#include "ipv4ll-internal.h"
-
-void arp_packet_init(struct ether_arp *arp) {
- assert(arp);
-
- memzero(arp, sizeof(struct ether_arp));
- /* Header */
- arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); /* HTYPE */
- arp->ea_hdr.ar_pro = htons(ETHERTYPE_IP); /* PTYPE */
- arp->ea_hdr.ar_hln = ETH_ALEN; /* HLEN */
- arp->ea_hdr.ar_pln = sizeof arp->arp_spa; /* PLEN */
- arp->ea_hdr.ar_op = htons(ARPOP_REQUEST); /* REQUEST */
-}
-
-void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
- assert(ha);
-
- arp_packet_init(arp);
- memcpy(arp->arp_sha, ha, ETH_ALEN);
- memcpy(arp->arp_tpa, &pa, sizeof(pa));
-}
-
-void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
- assert(ha);
-
- arp_packet_init(arp);
- memcpy(arp->arp_sha, ha, ETH_ALEN);
- memcpy(arp->arp_tpa, &pa, sizeof(pa));
- memcpy(arp->arp_spa, &pa, sizeof(pa));
-}
-
-int arp_packet_verify_headers(struct ether_arp *arp) {
- assert(arp);
-
- if (arp->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) {
- log_ipv4ll(NULL, "ignoring packet: header is not ARPHRD_ETHER");
- return -EINVAL;
- }
- if (arp->ea_hdr.ar_pro != htons(ETHERTYPE_IP)) {
- log_ipv4ll(NULL, "ignoring packet: protocol is not ETHERTYPE_IP");
- return -EINVAL;
- }
- if (arp->ea_hdr.ar_op != htons(ARPOP_REQUEST) &&
- arp->ea_hdr.ar_op != htons(ARPOP_REPLY)) {
- log_ipv4ll(NULL, "ignoring packet: operation is not ARPOP_REQUEST or ARPOP_REPLY");
- return -EINVAL;
- }
-
- return 0;
-}
diff --git a/src/systemd/src/libsystemd-network/lldp-internal.c b/src/systemd/src/libsystemd-network/lldp-internal.c
new file mode 100644
index 0000000000..b8238fe365
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-internal.c
@@ -0,0 +1,362 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include "lldp-internal.h"
+#include "sd-lldp.h"
+
+/* We store maximum 1K chassis entries */
+#define LLDP_MIB_MAX_CHASSIS 1024
+
+/* Maximum Ports can be attached to any chassis */
+#define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
+
+/* 10.5.5.2.2 mibUpdateObjects ()
+ * The mibUpdateObjects () procedure updates the MIB objects corresponding to
+ * the TLVs contained in the received LLDPDU for the LLDP remote system
+ * indicated by the LLDP remote systems update process defined in 10.3.5 */
+
+int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
+ lldp_neighbour_port *p;
+ uint16_t length, ttl;
+ uint8_t *data;
+ uint8_t type;
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(tlv, -EINVAL);
+
+ r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
+ if (r < 0)
+ return r;
+
+ /* Update the packet if we already have */
+ LIST_FOREACH(port, p, c->ports) {
+
+ if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
+
+ r = sd_lldp_packet_read_ttl(tlv, &ttl);
+ if (r < 0)
+ return r;
+
+ p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
+
+ sd_lldp_packet_unref(p->packet);
+ p->packet = tlv;
+
+ prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
+ lldp_neighbour_port *p, *q;
+ uint8_t *data;
+ uint16_t length;
+ uint8_t type;
+ int r;
+
+ assert_return(c, -EINVAL);
+ assert_return(tlv, -EINVAL);
+
+ r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH_SAFE(port, p, q, c->ports) {
+
+ /* Find the port */
+ if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
+ lldp_neighbour_port_remove_and_free(p);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int lldp_mib_add_objects(Prioq *by_expiry,
+ Hashmap *neighbour_mib,
+ tlv_packet *tlv) {
+ _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
+ _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
+ lldp_chassis_id chassis_id;
+ bool new_chassis = false;
+ uint8_t subtype, *data;
+ uint16_t ttl, length;
+ int r;
+
+ assert_return(by_expiry, -EINVAL);
+ assert_return(neighbour_mib, -EINVAL);
+ assert_return(tlv, -EINVAL);
+
+ r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
+ if (r < 0)
+ goto drop;
+
+ r = sd_lldp_packet_read_ttl(tlv, &ttl);
+ if (r < 0)
+ goto drop;
+
+ /* Make hash key */
+ chassis_id.type = subtype;
+ chassis_id.length = length;
+ chassis_id.data = data;
+
+ /* Try to find the Chassis */
+ c = hashmap_get(neighbour_mib, &chassis_id);
+ if (!c) {
+
+ /* Don't create chassis if ttl 0 is received . Silently drop it */
+ if (ttl == 0) {
+ log_lldp("TTL value 0 received. Skiping Chassis creation.");
+ goto drop;
+ }
+
+ /* Admission Control: Can we store this packet ? */
+ if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
+
+ log_lldp("Exceeding number of chassie: %d. Dropping ...",
+ hashmap_size(neighbour_mib));
+ goto drop;
+ }
+
+ r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
+ if (r < 0)
+ goto drop;
+
+ new_chassis = true;
+
+ r = hashmap_put(neighbour_mib, &c->chassis_id, c);
+ if (r < 0)
+ goto drop;
+
+ } else {
+
+ /* When the TTL field is set to zero, the receiving LLDP agent is notified all
+ * system information associated with the LLDP agent/port is to be deleted */
+ if (ttl == 0) {
+ log_lldp("TTL value 0 received . Deleting associated Port ...");
+
+ lldp_mib_remove_objects(c, tlv);
+
+ c = NULL;
+ goto drop;
+ }
+
+ /* if we already have this port just update it */
+ r = lldp_mib_update_objects(c, tlv);
+ if (r >= 0) {
+ c = NULL;
+ return r;
+ }
+
+ /* Admission Control: Can this port attached to the existing chassis ? */
+ if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
+ log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
+
+ c = NULL;
+ goto drop;
+ }
+ }
+
+ /* This is a new port */
+ r = lldp_neighbour_port_new(c, tlv, &p);
+ if (r < 0)
+ goto drop;
+
+ r = prioq_put(c->by_expiry, p, &p->prioq_idx);
+ if (r < 0)
+ goto drop;
+
+ /* Attach new port to chassis */
+ LIST_PREPEND(port, c->ports, p);
+ c->n_ref ++;
+
+ p = NULL;
+ c = NULL;
+
+ return 0;
+
+ drop:
+ sd_lldp_packet_unref(tlv);
+
+ if (new_chassis)
+ hashmap_remove(neighbour_mib, &c->chassis_id);
+
+ return r;
+}
+
+void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
+ lldp_chassis *c;
+
+ assert(p);
+ assert(p->c);
+
+ c = p->c;
+
+ prioq_remove(c->by_expiry, p, &p->prioq_idx);
+
+ LIST_REMOVE(port, c->ports, p);
+ lldp_neighbour_port_free(p);
+
+ /* Drop the Chassis if no port is attached */
+ c->n_ref --;
+ if (c->n_ref <= 1) {
+ hashmap_remove(c->neighbour_mib, &c->chassis_id);
+ lldp_chassis_free(c);
+ }
+}
+
+void lldp_neighbour_port_free(lldp_neighbour_port *p) {
+
+ if(!p)
+ return;
+
+ sd_lldp_packet_unref(p->packet);
+
+ free(p->data);
+ free(p);
+}
+
+int lldp_neighbour_port_new(lldp_chassis *c,
+ tlv_packet *tlv,
+ lldp_neighbour_port **ret) {
+ _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
+ uint16_t length, ttl;
+ uint8_t *data;
+ uint8_t type;
+ int r;
+
+ assert(tlv);
+
+ r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
+ if (r < 0)
+ return r;
+
+ r = sd_lldp_packet_read_ttl(tlv, &ttl);
+ if (r < 0)
+ return r;
+
+ p = new0(lldp_neighbour_port, 1);
+ if (!p)
+ return -ENOMEM;
+
+ p->c = c;
+ p->type = type;
+ p->length = length;
+ p->packet = tlv;
+ p->prioq_idx = PRIOQ_IDX_NULL;
+ p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
+
+ p->data = memdup(data, length);
+ if (!p->data)
+ return -ENOMEM;
+
+ *ret = p;
+ p = NULL;
+
+ return 0;
+}
+
+void lldp_chassis_free(lldp_chassis *c) {
+
+ if (!c)
+ return;
+
+ if (c->n_ref > 1)
+ return;
+
+ free(c->chassis_id.data);
+ free(c);
+}
+
+int lldp_chassis_new(tlv_packet *tlv,
+ Prioq *by_expiry,
+ Hashmap *neighbour_mib,
+ lldp_chassis **ret) {
+ _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
+ uint16_t length;
+ uint8_t *data;
+ uint8_t type;
+ int r;
+
+ assert(tlv);
+
+ r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
+ if (r < 0)
+ return r;
+
+ c = new0(lldp_chassis, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->n_ref = 1;
+ c->chassis_id.type = type;
+ c->chassis_id.length = length;
+
+ c->chassis_id.data = memdup(data, length);
+ if (!c->chassis_id.data)
+ return -ENOMEM;
+
+ LIST_HEAD_INIT(c->ports);
+
+ c->by_expiry = by_expiry;
+ c->neighbour_mib = neighbour_mib;
+
+ *ret = c;
+ c = NULL;
+
+ return 0;
+}
+
+int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_lldp_packet_unref_ tlv_packet *packet = NULL;
+ tlv_packet *p;
+ uint16_t length;
+ int r;
+
+ assert(fd);
+ assert(userdata);
+
+ r = tlv_packet_new(&packet);
+ if (r < 0)
+ return r;
+
+ length = read(fd, &packet->pdu, sizeof(packet->pdu));
+
+ /* Silently drop the packet */
+ if ((size_t) length > ETHER_MAX_LEN)
+ return 0;
+
+ packet->userdata = userdata;
+
+ p = packet;
+ packet = NULL;
+
+ return lldp_handle_packet(p, (uint16_t) length);
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h
new file mode 100644
index 0000000000..885082ef3a
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-internal.h
@@ -0,0 +1,94 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include "log.h"
+#include "list.h"
+#include "lldp-tlv.h"
+#include "prioq.h"
+#include "sd-event.h"
+
+typedef struct lldp_neighbour_port lldp_neighbour_port;
+typedef struct lldp_chassis lldp_chassis;
+typedef struct lldp_chassis_id lldp_chassis_id;
+typedef struct lldp_agent_statistics lldp_agent_statistics;
+
+struct lldp_neighbour_port {
+ uint8_t type;
+ uint8_t *data;
+
+ uint16_t length;
+ usec_t until;
+
+ unsigned prioq_idx;
+
+ lldp_chassis *c;
+ tlv_packet *packet;
+
+ LIST_FIELDS(lldp_neighbour_port, port);
+};
+
+int lldp_neighbour_port_new(lldp_chassis *c, tlv_packet *tlv, lldp_neighbour_port **ret);
+void lldp_neighbour_port_free(lldp_neighbour_port *p);
+void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_neighbour_port *, lldp_neighbour_port_free);
+#define _cleanup_lldp_neighbour_port_free_ _cleanup_(lldp_neighbour_port_freep)
+
+struct lldp_chassis_id {
+ uint8_t type;
+ uint16_t length;
+
+ uint8_t *data;
+};
+
+struct lldp_chassis {
+ unsigned n_ref;
+
+ lldp_chassis_id chassis_id;
+
+ Prioq *by_expiry;
+ Hashmap *neighbour_mib;
+
+ LIST_HEAD(lldp_neighbour_port, ports);
+};
+
+int lldp_chassis_new(tlv_packet *tlv,
+ Prioq *by_expiry,
+ Hashmap *neighbour_mib,
+ lldp_chassis **ret);
+
+void lldp_chassis_free(lldp_chassis *c);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_chassis *, lldp_chassis_free);
+#define _cleanup_lldp_chassis_free_ _cleanup_(lldp_chassis_freep)
+
+int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv);
+int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv);
+int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv);
+
+int lldp_handle_packet(tlv_packet *m, uint16_t length);
+int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
+#define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c
new file mode 100644
index 0000000000..b68dce19ad
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-network.c
@@ -0,0 +1,86 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <linux/filter.h>
+#include <linux/if_ether.h>
+
+#include "socket-util.h"
+#include "lldp-tlv.h"
+#include "lldp-network.h"
+#include "lldp-internal.h"
+
+int lldp_network_bind_raw_socket(int ifindex) {
+ typedef struct LLDPFrame {
+ struct ethhdr hdr;
+ uint8_t tlvs[0];
+ } LLDPFrame;
+
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest)), /* A <- 4 bytes of destination MAC */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_proto)), /* A <- protocol */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
+ BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */
+ };
+
+ struct sock_fprog fprog = {
+ .len = ELEMENTSOF(filter),
+ .filter = filter
+ };
+
+ _cleanup_close_ int s = -1;
+
+ union sockaddr_union saddrll = {
+ .ll.sll_family = AF_PACKET,
+ .ll.sll_ifindex = ifindex,
+ };
+
+ int r;
+
+ assert(ifindex > 0);
+
+ s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (s < 0)
+ return -errno;
+
+ r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+ if (r < 0)
+ return -errno;
+
+ r = bind(s, &saddrll.sa, sizeof(saddrll.ll));
+ if (r < 0)
+ return -errno;
+
+ r = s;
+ s = -1;
+
+ return r;
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-network.h b/src/systemd/src/libsystemd-network/lldp-network.h
new file mode 100644
index 0000000000..b1e428e7fb
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-network.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include "sd-event.h"
+
+int lldp_network_bind_raw_socket(int ifindex);
diff --git a/src/systemd/src/libsystemd-network/lldp-port.c b/src/systemd/src/libsystemd-network/lldp-port.c
new file mode 100644
index 0000000000..9b0cf0477e
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-port.c
@@ -0,0 +1,119 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include "async.h"
+#include "lldp-port.h"
+#include "lldp-network.h"
+#include "lldp-internal.h"
+
+int lldp_port_start(lldp_port *p) {
+ int r;
+
+ assert_return(p, -EINVAL);
+
+ r = lldp_network_bind_raw_socket(p->ifindex);
+ if (r < 0)
+ return r;
+
+ p->rawfd = r;
+
+ r = sd_event_add_io(p->event, &p->lldp_port_rx,
+ p->rawfd, EPOLLIN, lldp_receive_packet, p);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to allocate event source: %m");
+ goto fail;
+ }
+
+ r = sd_event_source_set_priority(p->lldp_port_rx, p->event_priority);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to set event priority: %m");
+ goto fail;
+ }
+
+ r = sd_event_source_set_description(p->lldp_port_rx, "lldp-port-rx");
+ if (r < 0) {
+ log_debug_errno(r, "Failed to set event name: %m");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ lldp_port_stop(p);
+
+ return r;
+}
+
+int lldp_port_stop(lldp_port *p) {
+
+ assert_return(p, -EINVAL);
+
+ p->rawfd = asynchronous_close(p->rawfd);
+ p->lldp_port_rx = sd_event_source_unref(p->lldp_port_rx);
+
+ return 0;
+}
+
+void lldp_port_free(lldp_port *p) {
+ if (!p)
+ return;
+
+ lldp_port_stop(p);
+
+ free(p->ifname);
+ free(p);
+}
+
+int lldp_port_new(int ifindex,
+ const char *ifname,
+ const struct ether_addr *addr,
+ void *userdata,
+ lldp_port **ret) {
+ _cleanup_free_ lldp_port *p = NULL;
+
+ assert_return(ifindex, -EINVAL);
+ assert_return(ifname, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ p = new0(lldp_port, 1);
+ if (!p)
+ return -ENOMEM;
+
+ p->rawfd = -1;
+ p->ifindex = ifindex;
+
+ p->ifname = strdup(ifname);
+ if (!p->ifname)
+ return -ENOMEM;
+
+ memcpy(&p->mac, addr, ETH_ALEN);
+
+ p->userdata = userdata;
+
+ *ret = p;
+
+ p = NULL;
+
+ return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-port.h b/src/systemd/src/libsystemd-network/lldp-port.h
new file mode 100644
index 0000000000..063eaa275e
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-port.h
@@ -0,0 +1,73 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+#include "sd-lldp.h"
+
+#include "util.h"
+
+typedef struct lldp_port lldp_port;
+
+typedef enum LLDPPortStatus {
+ LLDP_PORT_STATUS_NONE,
+ LLDP_PORT_STATUS_ENABLED,
+ LLDP_PORT_STATUS_DISABLED,
+ _LLDP_PORT_STATUS_MAX,
+ _LLDP_PORT_STATUS_INVALID = -1,
+} LLDPPortStatus;
+
+struct lldp_port {
+ LLDPPortStatus status;
+
+ int ifindex;
+ char *ifname;
+
+ struct ether_addr mac;
+
+ int rawfd;
+
+ sd_event *event;
+ sd_event_source *lldp_port_rx;
+
+ int event_priority;
+
+ void *userdata;
+};
+
+int lldp_port_new(int ifindex,
+ const char *ifname,
+ const struct ether_addr *addr,
+ void *userdata,
+ lldp_port **ret);
+void lldp_port_free(lldp_port *p);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_port*, lldp_port_free);
+#define _cleanup_lldp_port_free_ _cleanup_(lldp_port_freep)
+
+int lldp_port_start(lldp_port *p);
+int lldp_port_stop(lldp_port *p);
diff --git a/src/systemd/src/libsystemd-network/lldp-tlv.c b/src/systemd/src/libsystemd-network/lldp-tlv.c
new file mode 100644
index 0000000000..de9160037e
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-tlv.c
@@ -0,0 +1,649 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include "macro.h"
+#include "lldp-tlv.h"
+
+int tlv_section_new(tlv_section **ret) {
+ tlv_section *s;
+
+ s = new0(tlv_section, 1);
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+
+ return 0;
+}
+
+void tlv_section_free(tlv_section *m) {
+
+ if (!m)
+ return;
+
+ free(m);
+}
+
+int tlv_packet_new(tlv_packet **ret) {
+ tlv_packet *m;
+
+ m = new0(tlv_packet, 1);
+ if (!m)
+ return -ENOMEM;
+
+ LIST_HEAD_INIT(m->sections);
+ m->n_ref = 1;
+
+ *ret = m;
+
+ return 0;
+}
+
+tlv_packet *sd_lldp_packet_ref(tlv_packet *m) {
+
+ if (!m)
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref++;
+
+ return m;
+}
+
+tlv_packet *sd_lldp_packet_unref(tlv_packet *m) {
+ tlv_section *s, *n;
+
+ if (!m)
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref--;
+
+ if (m->n_ref > 0)
+ return m;
+
+ LIST_FOREACH_SAFE(section, s, n, m->sections)
+ tlv_section_free(s);
+
+ free(m);
+ return NULL;
+}
+
+int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
+ uint8_t *p;
+
+ assert_return(m, -EINVAL);
+ assert_return(data, -EINVAL);
+ assert_return(data_length, -EINVAL);
+
+ if (m->length + data_length > ETHER_MAX_LEN)
+ return -ENOMEM;
+
+ p = m->pdu + m->length;
+ memcpy(p, data, data_length);
+ m->length += data_length;
+
+ return 0;
+}
+
+int tlv_packet_append_u8(tlv_packet *m, uint8_t data) {
+
+ assert_return(m, -EINVAL);
+
+ return tlv_packet_append_bytes(m, &data, sizeof(uint8_t));
+}
+
+int tlv_packet_append_u16(tlv_packet *m, uint16_t data) {
+ uint16_t type;
+
+ assert_return(m, -EINVAL);
+
+ type = htons(data);
+
+ return tlv_packet_append_bytes(m, &type, sizeof(uint16_t));
+}
+
+int tlv_packet_append_u32(tlv_packet *m, uint32_t data) {
+ uint32_t type;
+
+ assert_return(m, -EINVAL);
+
+ type = htonl(data);
+
+ return tlv_packet_append_bytes(m, &type, sizeof(uint32_t));
+}
+
+int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size) {
+
+ assert_return(m, -EINVAL);
+
+ return tlv_packet_append_bytes(m, data, size);
+}
+
+int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type) {
+
+ assert_return(m, -EINVAL);
+
+ m->container_pos = m->pdu + m->length;
+
+ return tlv_packet_append_u16(m, type << 9);
+}
+
+int lldp_tlv_packet_close_container(tlv_packet *m) {
+ uint16_t type;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->container_pos, -EINVAL);
+
+ memcpy(&type, m->container_pos, sizeof(uint16_t));
+
+ type |= htons(((m->pdu + m->length) - (m->container_pos + 2)) & 0x01ff);
+ memcpy(m->container_pos, &type, sizeof(uint16_t));
+
+ return 0;
+}
+
+static inline int tlv_packet_read_internal(tlv_section *m, void **data) {
+
+ assert_return(m->read_pos, -EINVAL);
+
+ *data = m->read_pos;
+
+ return 0;
+}
+
+int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
+ void *val = NULL;
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = tlv_packet_read_internal(m->container, &val);
+ if (r < 0)
+ return r;
+
+ memcpy(data, val, sizeof(uint8_t));
+
+ m->container->read_pos ++;
+
+ return 0;
+}
+
+int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) {
+ uint16_t t;
+ void *val = NULL;
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = tlv_packet_read_internal(m->container, &val);
+ if (r < 0)
+ return r;
+
+ memcpy(&t, val, sizeof(uint16_t));
+ *data = ntohs(t);
+
+ m->container->read_pos += 2;
+
+ return 0;
+}
+
+int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) {
+ uint32_t t;
+ void *val;
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = tlv_packet_read_internal(m->container, &val);
+ if (r < 0)
+ return r;
+
+ memcpy(&t, val, sizeof(uint32_t));
+ *data = ntohl(t);
+
+ m->container->read_pos += 4;
+
+ return r;
+}
+
+int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
+ void *val = NULL;
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = tlv_packet_read_internal(m->container, &val);
+ if (r < 0)
+ return r;
+
+ *data = (char *) val;
+ *data_length = m->container->data + m->container->length - m->container->read_pos;
+
+ m->container->read_pos += *data_length;
+
+ return 0;
+}
+
+int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) {
+ void *val = NULL;
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = tlv_packet_read_internal(m->container, &val);
+ if (r < 0)
+ return r;
+
+ *data = (uint8_t *) val;
+ *data_length = m->container->data + m->container->length - m->container->read_pos;
+
+ m->container->read_pos += *data_length;
+
+ return 0;
+}
+
+/* parse raw TLV packet */
+int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
+ tlv_section *section, *tail;
+ uint16_t t, l;
+ uint8_t *p;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(size, -EINVAL);
+
+ p = m->pdu;
+
+ /* extract ethernet header */
+ memcpy(&m->mac, p, ETH_ALEN);
+ p += sizeof(struct ether_header);
+
+ for (l = 0; l <= size; ) {
+ r = tlv_section_new(&section);
+ if (r < 0)
+ return r;
+
+ memcpy(&t, p, sizeof(uint16_t));
+
+ section->type = ntohs(t) >> 9;
+ section->length = ntohs(t) & 0x01ff;
+
+ if (section->type == LLDP_TYPE_END || section->type >=_LLDP_TYPE_MAX) {
+ tlv_section_free(section);
+ break;
+ }
+
+ p += 2;
+
+ if (section->type == LLDP_TYPE_PRIVATE &&
+ section->length >= LLDP_OUI_LEN + 1) {
+ section->oui = p;
+ p += LLDP_OUI_LEN;
+ section->subtype = *p++;
+
+ section->length -= LLDP_OUI_LEN + 1;
+ l += LLDP_OUI_LEN + 1;
+ }
+
+ section->data = p;
+
+ LIST_FIND_TAIL(section, m->sections, tail);
+ LIST_INSERT_AFTER(section, m->sections, tail, section);
+
+ p += section->length;
+ l += (section->length + 2);
+ }
+
+ return 0;
+}
+
+int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
+ tlv_section *s;
+
+ assert_return(m, -EINVAL);
+ assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
+
+ LIST_FOREACH(section, s, m->sections)
+ if (s->type == type)
+ break;
+ if (!s)
+ return -1;
+
+ m->container = s;
+
+ m->container->read_pos = s->data;
+ if (!m->container->read_pos) {
+ m->container = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) {
+ tlv_section *s;
+
+ assert_return(m, -EINVAL);
+ assert_return(oui, -EINVAL);
+
+ LIST_FOREACH(section, s, m->sections) {
+ if (s->type == LLDP_TYPE_PRIVATE &&
+ s->oui &&
+ s->subtype == subtype &&
+ !memcmp(s->oui, oui, LLDP_OUI_LEN))
+ break;
+ }
+
+ if (!s)
+ return -1;
+
+ m->container = s;
+
+ m->container->read_pos = s->data;
+ if (!m->container->read_pos) {
+ m->container = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int lldp_tlv_packet_exit_container(tlv_packet *m) {
+ assert_return(m, -EINVAL);
+
+ m->container = 0;
+
+ return 0;
+}
+
+static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container(tlv, type);
+ if (r < 0)
+ goto out;
+
+ r = tlv_packet_read_u16(tlv, value);
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+ return r < 0 ? r : r2;
+}
+
+static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) {
+ char *s;
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container(tlv, type);
+ if (r < 0)
+ return r;
+
+ r = tlv_packet_read_string(tlv, &s, length);
+ if (r < 0)
+ goto out;
+
+ *data = (char *) s;
+
+ out:
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_chassis_id(tlv_packet *tlv,
+ uint8_t *type,
+ uint8_t **data,
+ uint16_t *length) {
+ uint8_t subtype;
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
+ if (r < 0)
+ goto out2;
+
+ r = tlv_packet_read_u8(tlv, &subtype);
+ if (r < 0)
+ goto out1;
+
+ switch (subtype) {
+ case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+
+ r = tlv_packet_read_bytes(tlv, data, length);
+ if (r < 0)
+ goto out1;
+
+ break;
+ default:
+ r = -EOPNOTSUPP;
+ break;
+ }
+
+ *type = subtype;
+
+ out1:
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out2:
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_port_id(tlv_packet *tlv,
+ uint8_t *type,
+ uint8_t **data,
+ uint16_t *length) {
+ uint8_t subtype;
+ char *s;
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
+ if (r < 0)
+ goto out2;
+
+ r = tlv_packet_read_u8(tlv, &subtype);
+ if (r < 0)
+ goto out1;
+
+ switch (subtype) {
+ case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+ case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+ case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+ case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+
+ r = tlv_packet_read_string(tlv, &s, length);
+ if (r < 0)
+ goto out1;
+
+ *data = (uint8_t *) s;
+
+ break;
+ case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+
+ r = tlv_packet_read_bytes(tlv, data, length);
+ if (r < 0)
+ goto out1;
+
+ break;
+ default:
+ r = -EOPNOTSUPP;
+ break;
+ }
+
+ *type = subtype;
+
+ out1:
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out2:
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
+ return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl);
+}
+
+int sd_lldp_packet_read_system_name(tlv_packet *tlv,
+ char **data,
+ uint16_t *length) {
+ return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length);
+}
+
+int sd_lldp_packet_read_system_description(tlv_packet *tlv,
+ char **data,
+ uint16_t *length) {
+ return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length);
+}
+
+int sd_lldp_packet_read_port_description(tlv_packet *tlv,
+ char **data,
+ uint16_t *length) {
+ return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length);
+}
+
+int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) {
+ return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data);
+}
+
+int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID);
+ if (r < 0)
+ goto out;
+
+ r = tlv_packet_read_u16(tlv, id);
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID);
+ if (r < 0)
+ goto out;
+
+ r = tlv_packet_read_u8(tlv, flags);
+ if (r >= 0)
+ r = tlv_packet_read_u16(tlv, id);
+
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) {
+ int r, r2;
+ uint8_t len = 0;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME);
+ if (r < 0)
+ goto out;
+
+ r = tlv_packet_read_u16(tlv, vlan_id);
+ if (r >= 0)
+ r = tlv_packet_read_u8(tlv, &len);
+ if (r >= 0)
+ r = tlv_packet_read_string(tlv, name, length);
+
+ if (r >= 0 && len < *length)
+ *length = len;
+
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID);
+ if (r < 0)
+ goto out;
+
+ r = tlv_packet_read_u16(tlv, id);
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) {
+ int r, r2;
+
+ assert_return(tlv, -EINVAL);
+
+ r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION);
+ if (r < 0)
+ goto out;
+
+ r = tlv_packet_read_u8(tlv, status);
+ if (r >= 0)
+ r = tlv_packet_read_u32(tlv, id);
+
+ r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+ return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) {
+ assert_return(tlv, -EINVAL);
+ assert_return(dest, -EINVAL);
+
+ /* 802.1AB-2009, Table 7-1 */
+ if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN))
+ *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE;
+ else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN))
+ *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE;
+ else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN))
+ *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE;
+ else
+ return -EINVAL;
+
+ return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/lldp-tlv.h b/src/systemd/src/libsystemd-network/lldp-tlv.h
new file mode 100644
index 0000000000..56d17cfeb0
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-tlv.h
@@ -0,0 +1,101 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include <net/ethernet.h>
+
+#include "util.h"
+#include "lldp.h"
+#include "list.h"
+
+#include "sd-lldp.h"
+
+typedef struct tlv_packet tlv_packet;
+typedef struct tlv_section tlv_section;
+
+#define LLDP_OUI_LEN 3
+
+struct tlv_section {
+ uint16_t type;
+ uint16_t length;
+ uint8_t *oui;
+ uint8_t subtype;
+
+ uint8_t *read_pos;
+ uint8_t *data;
+
+ LIST_FIELDS(tlv_section, section);
+};
+
+#define LLDP_MAC_NEAREST_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
+#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }
+#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }
+
+int tlv_section_new(tlv_section **ret);
+void tlv_section_free(tlv_section *ret);
+
+struct tlv_packet {
+ unsigned n_ref;
+
+ uint16_t type;
+ uint16_t length;
+ usec_t ts;
+
+ uint8_t *container_pos;
+ uint8_t pdu[ETHER_MAX_LEN];
+
+ void *userdata;
+
+ struct ether_addr mac;
+ tlv_section *container;
+
+ LIST_HEAD(tlv_section, sections);
+};
+
+int tlv_packet_new(tlv_packet **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_lldp_packet*, sd_lldp_packet_unref);
+#define _cleanup_lldp_packet_unref_ _cleanup_(sd_lldp_packet_unrefp)
+
+int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type);
+int lldp_tlv_packet_close_container(tlv_packet *m);
+
+int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length);
+int tlv_packet_append_u8(tlv_packet *m, uint8_t data);
+int tlv_packet_append_u16(tlv_packet *m, uint16_t data);
+int tlv_packet_append_u32(tlv_packet *m, uint32_t data);
+int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size);
+
+int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type);
+int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype);
+int lldp_tlv_packet_exit_container(tlv_packet *m);
+
+int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length);
+int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length);
+int tlv_packet_read_u8(tlv_packet *m, uint8_t *data);
+int tlv_packet_read_u16(tlv_packet *m, uint16_t *data);
+int tlv_packet_read_u32(tlv_packet *m, uint32_t *data);
+
+int tlv_packet_parse_pdu(tlv_packet *t, uint16_t size);
diff --git a/src/systemd/src/libsystemd-network/lldp-util.h b/src/systemd/src/libsystemd-network/lldp-util.h
new file mode 100644
index 0000000000..e6c22d592e
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp-util.h
@@ -0,0 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_lldp *, sd_lldp_free);
+#define _cleanup_lldp_free_ _cleanup_(sd_lldp_freep)
diff --git a/src/systemd/src/libsystemd-network/lldp.h b/src/systemd/src/libsystemd-network/lldp.h
new file mode 100644
index 0000000000..974fd8b0e8
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/lldp.h
@@ -0,0 +1,130 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
+
+#define ETHERTYPE_LLDP 0x88cc
+
+/* IEEE 802.3AB Clause 9: TLV Types */
+typedef enum LLDPTypes {
+ LLDP_TYPE_END = 0,
+ LLDP_TYPE_CHASSIS_ID = 1,
+ LLDP_TYPE_PORT_ID = 2,
+ LLDP_TYPE_TTL = 3,
+ LLDP_TYPE_PORT_DESCRIPTION = 4,
+ LLDP_TYPE_SYSTEM_NAME = 5,
+ LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
+ LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
+ LLDP_TYPE_MGMT_ADDRESS = 8,
+ LLDP_TYPE_PRIVATE = 127,
+ _LLDP_TYPE_MAX,
+ _LLDP_TYPE_INVALID = -1,
+} LLDPTypes;
+
+/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
+typedef enum LLDPChassisSubtypes {
+ LLDP_CHASSIS_SUBTYPE_RESERVED = 0,
+ LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1,
+ LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2,
+ LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3,
+ LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4,
+ LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5,
+ LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6,
+ LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7,
+ _LLDP_CHASSIS_SUBTYPE_MAX,
+ _LLDP_CHASSIS_SUBTYPE_INVALID = -1,
+} LLDPChassisSubtypes;
+
+/* IEEE 802.3AB Clause 9.5.3: Port subtype */
+typedef enum LLDPPortSubtypes {
+ LLDP_PORT_SUBTYPE_RESERVED = 0,
+ LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
+ LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
+ LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
+ LLDP_PORT_SUBTYPE_NETWORK = 4,
+ LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
+ LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
+ LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
+ _LLDP_PORT_SUBTYPE_MAX,
+ _LLDP_PORT_SUBTYPE_INVALID = -1
+} LLDPPortSubtypes;
+
+typedef enum LLDPSystemCapabilities {
+ LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
+ LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
+ LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
+ LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
+ LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
+ LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
+ LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
+ LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
+ LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
+ LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
+ LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
+ _LLDP_SYSTEM_CAPABILITIES_MAX,
+ _LLDP_SYSTEM_CAPABILITIES_INVALID = -1,
+} LLDPSystemCapabilities;
+
+typedef enum LLDPMedSubtype {
+ LLDP_MED_SUBTYPE_RESERVED = 0,
+ LLDP_MED_SUBTYPE_CAPABILITIES = 1,
+ LLDP_MED_SUBTYPE_NETWORK_POLICY = 2,
+ LLDP_MED_SUBTYPE_LOCATION_ID = 3,
+ LLDP_MED_SUBTYPE_EXTENDED_PVMDI = 4,
+ LLDP_MED_SUBTYPE_INV_HWREV = 5,
+ LLDP_MED_SUBTYPE_INV_FWREV = 6,
+ LLDP_MED_SUBTYPE_INV_SWREV = 7,
+ LLDP_MED_SUBTYPE_INV_SERIAL = 8,
+ LLDP_MED_SUBTYPE_INV_MANUFACTURER = 9,
+ LLDP_MED_SUBTYPE_INV_MODELNAME = 10,
+ LLDP_MED_SUBTYPE_INV_ASSETID = 11,
+ _LLDP_MED_SUBTYPE_MAX,
+ _LLDP_MED_SUBTYPE_INVALID = -1,
+} LLDPMedSubtype;
+
+typedef enum LLDPMedCapability {
+ LLDP_MED_CAPABILITY_CAPAPILITIES = 1 << 0,
+ LLDP_MED_CAPABILITY_NETWORK_POLICY = 1 << 1,
+ LLDP_MED_CAPABILITY_LOCATION_ID = 1 << 2,
+ LLDP_MED_CAPABILITY_EXTENDED_PSE = 1 << 3,
+ LLDP_MED_CAPABILITY_EXTENDED_PD = 1 << 4,
+ LLDP_MED_CAPABILITY_INVENTORY = 1 << 5,
+ LLDP_MED_CAPABILITY_MAX,
+ LLDP_MED_CAPABILITY_INVALID = -1,
+} LLDPMedCapability;
+
+#define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
+#define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
+
+enum {
+ LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID = 1,
+ LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID = 2,
+ LLDP_OUI_SUBTYPE_802_1_VLAN_NAME = 3,
+ LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY = 4,
+ LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST = 5,
+ LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID = 6,
+ LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION = 7,
+};
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index 99b59aea75..8f1c551622 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -205,8 +205,7 @@ int config_parse_ifname(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
return 0;
}
@@ -249,8 +248,7 @@ int config_parse_ifnames(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
@@ -287,8 +285,7 @@ int config_parse_ifalias(const char *unit,
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
return 0;
}
@@ -333,8 +330,7 @@ int config_parse_hwaddr(const char *unit,
&n->ether_addr_octet[4],
&n->ether_addr_octet[5]);
if (r != 6) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Not a valid MAC address, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
@@ -404,8 +400,8 @@ void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
assert(size);
for (i = 0; i < size; i++)
- fprintf(f, SD_ICMP6_ADDRESS_FORMAT_STR"%s",
- SD_ICMP6_ADDRESS_FORMAT_VAL(addresses[i]),
+ fprintf(f, SD_ICMP6_ND_ADDRESS_FORMAT_STR"%s",
+ SD_ICMP6_ND_ADDRESS_FORMAT_VAL(addresses[i]),
(i < (size - 1)) ? " ": "");
}
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index ca8e2f24c0..2a33ae924e 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -215,7 +215,7 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
log_dhcp_client(client, "Changing MAC address on running DHCP "
"client, restarting");
need_restart = true;
- client_stop(client, DHCP_EVENT_STOP);
+ client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
}
memcpy(&client->mac_addr, addr, addr_len);
@@ -279,7 +279,7 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
log_dhcp_client(client, "Changing client ID on running DHCP "
"client, restarting");
need_restart = true;
- client_stop(client, DHCP_EVENT_STOP);
+ client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
}
client->client_id.type = type;
@@ -387,7 +387,7 @@ static void client_stop(sd_dhcp_client *client, int error) {
if (error < 0)
log_dhcp_client(client, "STOPPED: %s", strerror(-error));
- else if (error == DHCP_EVENT_STOP)
+ else if (error == SD_DHCP_CLIENT_EVENT_STOP)
log_dhcp_client(client, "STOPPED");
else
log_dhcp_client(client, "STOPPED: Unknown event");
@@ -985,7 +985,7 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec,
log_dhcp_client(client, "EXPIRED");
- client_notify(client, DHCP_EVENT_EXPIRED);
+ client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
/* lease was lost, start over if not freed or stopped in callback */
if (client->state != DHCP_STATE_STOPPED) {
@@ -1145,14 +1145,14 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
}
}
- r = DHCP_EVENT_IP_ACQUIRE;
+ r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
if (client->lease) {
if (client->lease->address != lease->address ||
client->lease->subnet_mask != lease->subnet_mask ||
client->lease->router != lease->router) {
- r = DHCP_EVENT_IP_CHANGE;
+ r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
} else
- r = DHCP_EVENT_RENEW;
+ r = SD_DHCP_CLIENT_EVENT_RENEW;
client->lease = sd_dhcp_lease_unref(client->lease);
}
@@ -1384,8 +1384,8 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
if (IN_SET(client->state, DHCP_STATE_REQUESTING,
DHCP_STATE_REBOOTING))
- notify_event = DHCP_EVENT_IP_ACQUIRE;
- else if (r != DHCP_EVENT_IP_ACQUIRE)
+ notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
+ else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
notify_event = r;
client->state = DHCP_STATE_BOUND;
@@ -1635,7 +1635,7 @@ int sd_dhcp_client_stop(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
- client_stop(client, DHCP_EVENT_STOP);
+ client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
client->state = DHCP_STATE_STOPPED;
return 0;
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
index a36a7f2ed0..7ee3d5e8ea 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
@@ -316,10 +316,14 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
else {
char *string;
- if (memchr(option, 0, len))
+ /*
+ * One trailing NUL byte is OK, we don't mind. See:
+ * https://github.com/systemd/systemd/issues/1337
+ */
+ if (memchr(option, 0, len - 1))
return -EINVAL;
- string = strndup((const char *)option, len);
+ string = strndup((const char *) option, len);
if (!string)
return -ENOMEM;
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index ee61b4f114..4942f71350 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -129,6 +129,8 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) {
assert_return(client, -EINVAL);
assert_return(interface_index >= -1, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
client->index = interface_index;
return 0;
@@ -144,6 +146,8 @@ int sd_dhcp6_client_set_mac(
assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
assert_return(arp_type > 0, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
if (arp_type == ARPHRD_ETHER)
assert_return(addr_len == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
@@ -177,6 +181,8 @@ int sd_dhcp6_client_set_duid(
assert_return(duid, -EINVAL);
assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
switch (type) {
case DHCP6_DUID_LLT:
if (duid_len <= sizeof(client->duid.llt))
@@ -209,6 +215,8 @@ int sd_dhcp6_client_set_duid(
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enabled) {
assert_return(client, -EINVAL);
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
client->information_request = enabled;
return 0;
@@ -270,13 +278,18 @@ static void client_notify(sd_dhcp6_client *client, int event) {
client->cb(client, event, client->userdata);
}
-static int client_reset(sd_dhcp6_client *client) {
- assert_return(client, -EINVAL);
-
+static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) {
if (client->lease) {
dhcp6_lease_clear_timers(&client->lease->ia);
- client->lease = sd_dhcp6_lease_unref(client->lease);
+ sd_dhcp6_lease_unref(client->lease);
}
+ client->lease = lease;
+}
+
+static int client_reset(sd_dhcp6_client *client) {
+ assert_return(client, -EINVAL);
+
+ client_set_lease(client, NULL);
client->receive_message =
sd_event_source_unref(client->receive_message);
@@ -468,7 +481,7 @@ static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
state = client->state;
- client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
+ client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
/* RFC 3315, section 18.1.4., says that "...the client may choose to
use a Solicit message to locate a new DHCP server..." */
@@ -558,7 +571,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
if (max_retransmit_count &&
client->retransmit_count >= max_retransmit_count) {
- client_stop(client, DHCP6_EVENT_RETRANS_MAX);
+ client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
return 0;
}
@@ -830,12 +843,7 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
return 0;
}
- if (client->lease) {
- dhcp6_lease_clear_timers(&client->lease->ia);
- client->lease = sd_dhcp6_lease_unref(client->lease);
- }
-
- client->lease = lease;
+ client_set_lease(client, lease);
lease = NULL;
return DHCP6_STATE_BOUND;
@@ -864,11 +872,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver
r = dhcp6_lease_get_preference(client->lease, &pref_lease);
if (r < 0 || pref_advertise > pref_lease) {
- if (client->lease) {
- dhcp6_lease_clear_timers(&client->lease->ia);
- sd_dhcp6_lease_unref(client->lease);
- }
- client->lease = lease;
+ client_set_lease(client, lease);
lease = NULL;
r = 0;
}
@@ -937,7 +941,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
if (r < 0)
return 0;
- client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
+ client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
client_start(client, DHCP6_STATE_STOPPED);
@@ -969,7 +973,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
return 0;
}
- client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
+ client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
}
break;
@@ -1120,7 +1124,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
}
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
- client_stop(client, DHCP6_EVENT_STOP);
+ client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
return 0;
}
@@ -1133,6 +1137,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
assert_return(client->event, -EINVAL);
assert_return(client->index > 0, -EINVAL);
+ if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
+ return -EALREADY;
+
r = client_reset(client);
if (r < 0)
return r;
@@ -1240,7 +1247,6 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
client_reset(client);
sd_dhcp6_client_detach_event(client);
- sd_dhcp6_lease_unref(client->lease);
free(client->req_opts);
free(client);
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
new file mode 100644
index 0000000000..8a21ae5097
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
@@ -0,0 +1,531 @@
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "event-util.h"
+#include "in-addr-util.h"
+#include "list.h"
+#include "refcnt.h"
+#include "random-util.h"
+#include "siphash24.h"
+#include "util.h"
+
+#include "arp-util.h"
+#include "sd-ipv4acd.h"
+
+/* Constants from the RFC */
+#define PROBE_WAIT 1
+#define PROBE_NUM 3
+#define PROBE_MIN 1
+#define PROBE_MAX 2
+#define ANNOUNCE_WAIT 2
+#define ANNOUNCE_NUM 2
+#define ANNOUNCE_INTERVAL 2
+#define MAX_CONFLICTS 10
+#define RATE_LIMIT_INTERVAL 60
+#define DEFEND_INTERVAL 10
+
+#define IPV4ACD_NETWORK 0xA9FE0000L
+#define IPV4ACD_NETMASK 0xFFFF0000L
+
+#define log_ipv4acd_full(ll, level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "ACD: " fmt, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug(ll, ...) log_ipv4acd_full(ll, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_ipv4acd_info(ll, ...) log_ipv4acd_full(ll, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_ipv4acd_notice(ll, ...) log_ipv4acd_full(ll, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_ipv4acd_warning(ll, ...) log_ipv4acd_full(ll, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_ipv4acd_error(ll, ...) log_ipv4acd_full(ll, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_ipv4acd_debug_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_ipv4acd_info_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_INFO, error, ##__VA_ARGS__)
+#define log_ipv4acd_notice_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_ipv4acd_warning_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_ipv4acd_error_errno(ll, error, ...) log_ipv4acd_full(ll, LOG_ERR, error, ##__VA_ARGS__)
+
+typedef enum IPv4ACDState {
+ IPV4ACD_STATE_INIT,
+ IPV4ACD_STATE_WAITING_PROBE,
+ IPV4ACD_STATE_PROBING,
+ IPV4ACD_STATE_WAITING_ANNOUNCE,
+ IPV4ACD_STATE_ANNOUNCING,
+ IPV4ACD_STATE_RUNNING,
+ _IPV4ACD_STATE_MAX,
+ _IPV4ACD_STATE_INVALID = -1
+} IPv4ACDState;
+
+struct sd_ipv4acd {
+ RefCount n_ref;
+
+ IPv4ACDState state;
+ int index;
+ int fd;
+ int iteration;
+ int conflict;
+ sd_event_source *receive_message;
+ sd_event_source *timer;
+ usec_t defend_window;
+ be32_t address;
+ /* External */
+ struct ether_addr mac_addr;
+ sd_event *event;
+ int event_priority;
+ sd_ipv4acd_cb_t cb;
+ void* userdata;
+};
+
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll) {
+ if (ll)
+ assert_se(REFCNT_INC(ll->n_ref) >= 2);
+
+ return ll;
+}
+
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll) {
+ if (!ll || REFCNT_DEC(ll->n_ref) > 0)
+ return NULL;
+
+ ll->receive_message = sd_event_source_unref(ll->receive_message);
+ ll->fd = safe_close(ll->fd);
+
+ ll->timer = sd_event_source_unref(ll->timer);
+
+ sd_ipv4acd_detach_event(ll);
+
+ free(ll);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4acd*, sd_ipv4acd_unref);
+#define _cleanup_ipv4acd_unref_ _cleanup_(sd_ipv4acd_unrefp)
+
+int sd_ipv4acd_new(sd_ipv4acd **ret) {
+ _cleanup_ipv4acd_unref_ sd_ipv4acd *ll = NULL;
+
+ assert_return(ret, -EINVAL);
+
+ ll = new0(sd_ipv4acd, 1);
+ if (!ll)
+ return -ENOMEM;
+
+ ll->n_ref = REFCNT_INIT;
+ ll->state = IPV4ACD_STATE_INIT;
+ ll->index = -1;
+ ll->fd = -1;
+
+ *ret = ll;
+ ll = NULL;
+
+ return 0;
+}
+
+static void ipv4acd_set_state(sd_ipv4acd *ll, IPv4ACDState st, bool reset_counter) {
+
+ assert(ll);
+ assert(st < _IPV4ACD_STATE_MAX);
+
+ if (st == ll->state && !reset_counter)
+ ll->iteration++;
+ else {
+ ll->state = st;
+ ll->iteration = 0;
+ }
+}
+
+static void ipv4acd_client_notify(sd_ipv4acd *ll, int event) {
+ assert(ll);
+
+ if (ll->cb)
+ ll->cb(ll, event, ll->userdata);
+}
+
+static void ipv4acd_stop(sd_ipv4acd *ll) {
+ assert(ll);
+
+ ll->receive_message = sd_event_source_unref(ll->receive_message);
+ ll->fd = safe_close(ll->fd);
+
+ ll->timer = sd_event_source_unref(ll->timer);
+
+ log_ipv4acd_debug(ll, "STOPPED");
+
+ ipv4acd_set_state (ll, IPV4ACD_STATE_INIT, true);
+}
+
+int sd_ipv4acd_stop(sd_ipv4acd *ll) {
+ assert_return(ll, -EINVAL);
+
+ ipv4acd_stop(ll);
+
+ ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_STOP);
+
+ return 0;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
+
+static int ipv4acd_set_next_wakeup(sd_ipv4acd *ll, int sec, int random_sec) {
+ _cleanup_event_source_unref_ sd_event_source *timer = NULL;
+ usec_t next_timeout;
+ usec_t time_now;
+ int r;
+
+ assert(sec >= 0);
+ assert(random_sec >= 0);
+ assert(ll);
+
+ next_timeout = sec * USEC_PER_SEC;
+
+ if (random_sec)
+ next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
+
+ assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+ r = sd_event_add_time(ll->event, &timer, clock_boottime_or_monotonic(),
+ time_now + next_timeout, 0, ipv4acd_on_timeout, ll);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(timer, ll->event_priority);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_description(timer, "ipv4acd-timer");
+ if (r < 0)
+ return r;
+
+ ll->timer = sd_event_source_unref(ll->timer);
+ ll->timer = timer;
+ timer = NULL;
+
+ return 0;
+}
+
+static bool ipv4acd_arp_conflict(sd_ipv4acd *ll, struct ether_arp *arp) {
+ assert(ll);
+ assert(arp);
+
+ /* see the BPF */
+ if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0)
+ return true;
+
+ /* the TPA matched instead of the SPA, this is not a conflict */
+ return false;
+}
+
+static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_ipv4acd *ll = userdata;
+ int r = 0;
+
+ assert(ll);
+
+ switch (ll->state) {
+ case IPV4ACD_STATE_INIT:
+
+ ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_PROBE, true);
+
+ if (ll->conflict >= MAX_CONFLICTS) {
+ log_ipv4acd_notice(ll, "Max conflicts reached, delaying by %us", RATE_LIMIT_INTERVAL);
+ r = ipv4acd_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
+ if (r < 0)
+ goto out;
+
+ ll->conflict = 0;
+ } else {
+ r = ipv4acd_set_next_wakeup(ll, 0, PROBE_WAIT);
+ if (r < 0)
+ goto out;
+ }
+
+ break;
+ case IPV4ACD_STATE_WAITING_PROBE:
+ case IPV4ACD_STATE_PROBING:
+ /* Send a probe */
+ r = arp_send_probe(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP probe: %m");
+ goto out;
+ } else {
+ _cleanup_free_ char *address = NULL;
+ union in_addr_union addr = { .in.s_addr = ll->address };
+
+ r = in_addr_to_string(AF_INET, &addr, &address);
+ if (r >= 0)
+ log_ipv4acd_debug(ll, "Probing %s", address);
+ }
+
+ if (ll->iteration < PROBE_NUM - 2) {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_PROBING, false);
+
+ r = ipv4acd_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
+ if (r < 0)
+ goto out;
+ } else {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
+
+ r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
+ if (r < 0)
+ goto out;
+ }
+
+ break;
+
+ case IPV4ACD_STATE_ANNOUNCING:
+ if (ll->iteration >= ANNOUNCE_NUM - 1) {
+ ipv4acd_set_state(ll, IPV4ACD_STATE_RUNNING, false);
+
+ break;
+ }
+ case IPV4ACD_STATE_WAITING_ANNOUNCE:
+ /* Send announcement packet */
+ r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+ goto out;
+ } else
+ log_ipv4acd_debug(ll, "ANNOUNCE");
+
+ ipv4acd_set_state(ll, IPV4ACD_STATE_ANNOUNCING, false);
+
+ r = ipv4acd_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
+ if (r < 0)
+ goto out;
+
+ if (ll->iteration == 0) {
+ ll->conflict = 0;
+ ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_BIND);
+ }
+
+ break;
+ default:
+ assert_not_reached("Invalid state.");
+ }
+
+out:
+ if (r < 0)
+ sd_ipv4acd_stop(ll);
+
+ return 1;
+}
+
+static void ipv4acd_on_conflict(sd_ipv4acd *ll) {
+ _cleanup_free_ char *address = NULL;
+ union in_addr_union addr = { .in.s_addr = ll->address };
+ int r;
+
+ assert(ll);
+
+ ll->conflict++;
+
+ r = in_addr_to_string(AF_INET, &addr, &address);
+ if (r >= 0)
+ log_ipv4acd_debug(ll, "Conflict on %s (%u)", address, ll->conflict);
+
+ ipv4acd_stop(ll);
+
+ ipv4acd_client_notify(ll, SD_IPV4ACD_EVENT_CONFLICT);
+}
+
+static int ipv4acd_on_packet(sd_event_source *s, int fd,
+ uint32_t revents, void *userdata) {
+ sd_ipv4acd *ll = userdata;
+ struct ether_arp packet;
+ int r;
+
+ assert(ll);
+ assert(fd >= 0);
+
+ r = read(fd, &packet, sizeof(struct ether_arp));
+ if (r < (int) sizeof(struct ether_arp))
+ goto out;
+
+ switch (ll->state) {
+ case IPV4ACD_STATE_ANNOUNCING:
+ case IPV4ACD_STATE_RUNNING:
+ if (ipv4acd_arp_conflict(ll, &packet)) {
+ usec_t ts;
+
+ assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ /* Defend address */
+ if (ts > ll->defend_window) {
+ ll->defend_window = ts + DEFEND_INTERVAL * USEC_PER_SEC;
+ r = arp_send_announcement(ll->fd, ll->index, ll->address, &ll->mac_addr);
+ if (r < 0) {
+ log_ipv4acd_error_errno(ll, r, "Failed to send ARP announcement: %m");
+ goto out;
+ } else
+ log_ipv4acd_debug(ll, "DEFEND");
+
+ } else
+ ipv4acd_on_conflict(ll);
+ }
+
+ break;
+ case IPV4ACD_STATE_WAITING_PROBE:
+ case IPV4ACD_STATE_PROBING:
+ case IPV4ACD_STATE_WAITING_ANNOUNCE:
+ /* BPF ensures this packet indicates a conflict */
+ ipv4acd_on_conflict(ll);
+
+ break;
+ default:
+ assert_not_reached("Invalid state.");
+ }
+
+out:
+ if (r < 0)
+ sd_ipv4acd_stop(ll);
+
+ return 1;
+}
+
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index) {
+ assert_return(ll, -EINVAL);
+ assert_return(interface_index > 0, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->index = interface_index;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr) {
+ assert_return(ll, -EINVAL);
+ assert_return(addr, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ memcpy(&ll->mac_addr, addr, ETH_ALEN);
+
+ return 0;
+}
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll) {
+ assert_return(ll, -EINVAL);
+
+ ll->event = sd_event_unref(ll->event);
+
+ return 0;
+}
+
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) {
+ int r;
+
+ assert_return(ll, -EINVAL);
+ assert_return(!ll->event, -EBUSY);
+
+ if (event)
+ ll->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&ll->event);
+ if (r < 0)
+ return r;
+ }
+
+ ll->event_priority = priority;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) {
+ assert_return(ll, -EINVAL);
+
+ ll->cb = cb;
+ ll->userdata = userdata;
+
+ return 0;
+}
+
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){
+ assert_return(ll, -EINVAL);
+ assert_return(address, -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->address = address->s_addr;
+
+ return 0;
+}
+
+bool sd_ipv4acd_is_running(sd_ipv4acd *ll) {
+ assert_return(ll, false);
+
+ return ll->state != IPV4ACD_STATE_INIT;
+}
+
+static bool ether_addr_is_nul(const struct ether_addr *addr) {
+ const struct ether_addr nul_addr = {};
+
+ assert(addr);
+
+ return memcmp(addr, &nul_addr, sizeof(struct ether_addr)) == 0;
+}
+
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
+int sd_ipv4acd_start(sd_ipv4acd *ll) {
+ int r;
+
+ assert_return(ll, -EINVAL);
+ assert_return(ll->event, -EINVAL);
+ assert_return(ll->index > 0, -EINVAL);
+ assert_return(ll->address != 0, -EINVAL);
+ assert_return(!ether_addr_is_nul(&ll->mac_addr), -EINVAL);
+ assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY);
+
+ ll->defend_window = 0;
+
+ r = arp_network_bind_raw_socket(ll->index, ll->address, &ll->mac_addr);
+ if (r < 0)
+ goto out;
+
+ ll->fd = safe_close(ll->fd);
+ ll->fd = r;
+
+ r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
+ EPOLLIN, ipv4acd_on_packet, ll);
+ if (r < 0)
+ goto out;
+
+ r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
+ if (r < 0)
+ goto out;
+
+ r = sd_event_source_set_description(ll->receive_message, "ipv4acd-receive-message");
+ if (r < 0)
+ goto out;
+
+ r = ipv4acd_set_next_wakeup(ll, 0, 0);
+ if (r < 0)
+ goto out;
+out:
+ if (r < 0) {
+ ipv4acd_stop(ll);
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index d652ea9a2f..167669a588 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -2,6 +2,7 @@
This file is part of systemd.
Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -25,429 +26,153 @@
#include <stdio.h>
#include <arpa/inet.h>
-#include "util.h"
-#include "siphash24.h"
+#include "event-util.h"
#include "list.h"
#include "random-util.h"
+#include "refcnt.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "util.h"
-#include "ipv4ll-internal.h"
+#include "sd-ipv4acd.h"
#include "sd-ipv4ll.h"
-/* Constants from the RFC */
-#define PROBE_WAIT 1
-#define PROBE_NUM 3
-#define PROBE_MIN 1
-#define PROBE_MAX 2
-#define ANNOUNCE_WAIT 2
-#define ANNOUNCE_NUM 2
-#define ANNOUNCE_INTERVAL 2
-#define MAX_CONFLICTS 10
-#define RATE_LIMIT_INTERVAL 60
-#define DEFEND_INTERVAL 10
-
#define IPV4LL_NETWORK 0xA9FE0000L
#define IPV4LL_NETMASK 0xFFFF0000L
-typedef enum IPv4LLTrigger{
- IPV4LL_TRIGGER_NULL,
- IPV4LL_TRIGGER_PACKET,
- IPV4LL_TRIGGER_TIMEOUT,
- _IPV4LL_TRIGGER_MAX,
- _IPV4LL_TRIGGER_INVALID = -1
-} IPv4LLTrigger;
-
-typedef enum IPv4LLState {
- IPV4LL_STATE_INIT,
- IPV4LL_STATE_WAITING_PROBE,
- IPV4LL_STATE_PROBING,
- IPV4LL_STATE_WAITING_ANNOUNCE,
- IPV4LL_STATE_ANNOUNCING,
- IPV4LL_STATE_RUNNING,
- IPV4LL_STATE_STOPPED,
- _IPV4LL_STATE_MAX,
- _IPV4LL_STATE_INVALID = -1
-} IPv4LLState;
+#define IPV4LL_DONT_DESTROY(ll) \
+ _cleanup_ipv4ll_unref_ _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
struct sd_ipv4ll {
unsigned n_ref;
- IPv4LLState state;
- int index;
- int fd;
- union sockaddr_union link;
- int iteration;
- int conflict;
- sd_event_source *receive_message;
- sd_event_source *timer;
- usec_t next_wakeup;
- usec_t defend_window;
- int next_wakeup_valid;
- be32_t address;
+ sd_ipv4acd *acd;
+ be32_t address; /* the address pushed to ACD */
struct random_data *random_data;
char *random_data_state;
+
/* External */
be32_t claimed_address;
- struct ether_addr mac_addr;
- sd_event *event;
- int event_priority;
sd_ipv4ll_cb_t cb;
void* userdata;
};
-static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data);
-
-static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) {
-
- assert(ll);
- assert(st < _IPV4LL_STATE_MAX);
-
- if (st == ll->state && !reset_counter) {
- ll->iteration++;
- } else {
- ll->state = st;
- ll->iteration = 0;
- }
-}
-
-static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
- assert(ll);
-
- if (ll->cb) {
- ll = sd_ipv4ll_ref(ll);
- ll->cb(ll, event, ll->userdata);
- ll = sd_ipv4ll_unref(ll);
- }
-
- return ll;
-}
-
-static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) {
- assert(ll);
-
- ll->receive_message = sd_event_source_unref(ll->receive_message);
- ll->fd = safe_close(ll->fd);
-
- ll->timer = sd_event_source_unref(ll->timer);
-
- log_ipv4ll(ll, "STOPPED");
-
- ll = ipv4ll_client_notify(ll, event);
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
+ if (!ll)
+ return NULL;
- if (ll) {
- ll->claimed_address = 0;
- ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
- }
+ assert(ll->n_ref >= 1);
+ ll->n_ref++;
return ll;
}
-static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
- be32_t addr;
- int r;
- int32_t random;
-
- assert(ll);
- assert(address);
- assert(ll->random_data);
-
- do {
- r = random_r(ll->random_data, &random);
- if (r < 0)
- return r;
- addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
- } while (addr == ll->address ||
- (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
- (ntohl(addr) & 0x0000FF00) == 0x0000 ||
- (ntohl(addr) & 0x0000FF00) == 0xFF00);
-
- *address = addr;
- return 0;
-}
-
-static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
-
- assert(ll);
-
- ll->next_wakeup_valid = 0;
- ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL);
-
- return 0;
-}
-
-static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
- usec_t next_timeout = 0;
- usec_t time_now = 0;
-
- assert(sec >= 0);
- assert(random_sec >= 0);
- assert(ll);
-
- next_timeout = sec * USEC_PER_SEC;
-
- if (random_sec)
- next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
-
- assert_se(sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) >= 0);
-
- ll->next_wakeup = time_now + next_timeout;
- ll->next_wakeup_valid = 1;
-}
-
-static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
- assert(ll);
- assert(arp);
-
- if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 &&
- memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0)
- return true;
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
+ if (!ll)
+ return NULL;
- return false;
-}
+ assert(ll->n_ref >= 1);
+ ll->n_ref--;
-static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
- assert(ll);
- assert(arp);
+ if (ll->n_ref > 0)
+ return NULL;
- if (ipv4ll_arp_conflict(ll, arp))
- return true;
+ sd_ipv4acd_unref(ll->acd);
- if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 &&
- memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN))
- return true;
+ free(ll->random_data);
+ free(ll->random_data_state);
+ free(ll);
- return false;
+ return NULL;
}
-static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) {
- struct ether_arp out_packet;
- int out_packet_ready = 0;
- int r = 0;
-
- assert(ll);
- assert(trigger < _IPV4LL_TRIGGER_MAX);
-
- if (ll->state == IPV4LL_STATE_INIT) {
-
- log_ipv4ll(ll, "PROBE");
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
- ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
-
- } else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
- (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) {
-
- /* Send a probe */
- arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0);
-
- ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
-
- } else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) {
-
- /* Send the last probe */
- arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1);
-
- ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
-
- } else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
- (ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) {
-
- /* Send announcement packet */
- arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0);
-
- ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
-
- if (ll->iteration == 0) {
- log_ipv4ll(ll, "ANNOUNCE");
- ll->claimed_address = ll->address;
- ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
- if (!ll || ll->state == IPV4LL_STATE_STOPPED)
- goto out;
-
- ll->conflict = 0;
- }
-
- } else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT &&
- ll->iteration >= ANNOUNCE_NUM-1)) {
-
- ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0);
- ll->next_wakeup_valid = 0;
-
- } else if (trigger == IPV4LL_TRIGGER_PACKET) {
-
- int conflicted = 0;
- usec_t time_now;
- struct ether_arp* in_packet = (struct ether_arp*)trigger_data;
-
- assert(in_packet);
-
- if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) {
-
- if (ipv4ll_arp_conflict(ll, in_packet)) {
-
- r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now);
- if (r < 0)
- goto out;
-
- /* Defend address */
- if (time_now > ll->defend_window) {
- ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC;
- arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
- out_packet_ready = 1;
- } else
- conflicted = 1;
- }
-
- } else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE,
- IPV4LL_STATE_PROBING,
- IPV4LL_STATE_WAITING_ANNOUNCE)) {
-
- conflicted = ipv4ll_arp_probe_conflict(ll, in_packet);
- }
-
- if (conflicted) {
- log_ipv4ll(ll, "CONFLICT");
- ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
- if (!ll || ll->state == IPV4LL_STATE_STOPPED)
- goto out;
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
+#define _cleanup_ipv4ll_unref_ _cleanup_(sd_ipv4ll_unrefp)
- ll->claimed_address = 0;
+static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
- /* Pick a new address */
- r = ipv4ll_pick_address(ll, &ll->address);
- if (r < 0)
- goto out;
- ll->conflict++;
- ll->defend_window = 0;
- ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
+int sd_ipv4ll_new(sd_ipv4ll **ret) {
+ _cleanup_ipv4ll_unref_ sd_ipv4ll *ll = NULL;
+ int r;
- if (ll->conflict >= MAX_CONFLICTS) {
- log_ipv4ll(ll, "MAX_CONFLICTS");
- ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
- } else
- ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
+ assert_return(ret, -EINVAL);
- }
- }
+ ll = new0(sd_ipv4ll, 1);
+ if (!ll)
+ return -ENOMEM;
- if (out_packet_ready) {
- r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet);
- if (r < 0) {
- log_ipv4ll(ll, "failed to send arp packet out");
- goto out;
- }
- }
+ r = sd_ipv4acd_new(&ll->acd);
+ if (r < 0)
+ return r;
- if (ll->next_wakeup_valid) {
- ll->timer = sd_event_source_unref(ll->timer);
- r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(),
- ll->next_wakeup, 0, ipv4ll_timer, ll);
- if (r < 0)
- goto out;
+ r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
+ if (r < 0)
+ return r;
- r = sd_event_source_set_priority(ll->timer, ll->event_priority);
- if (r < 0)
- goto out;
+ ll->n_ref = 1;
- r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
- if (r < 0)
- goto out;
- }
+ *ret = ll;
+ ll = NULL;
-out:
- if (r < 0 && ll)
- ipv4ll_stop(ll, r);
+ return 0;
}
-static int ipv4ll_receive_message(sd_event_source *s, int fd,
- uint32_t revents, void *userdata) {
+int sd_ipv4ll_stop(sd_ipv4ll *ll) {
int r;
- struct ether_arp arp;
- sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
- assert(ll);
-
- r = read(fd, &arp, sizeof(struct ether_arp));
- if (r < (int) sizeof(struct ether_arp))
- return 0;
+ assert_return(ll, -EINVAL);
- r = arp_packet_verify_headers(&arp);
+ r = sd_ipv4acd_stop(ll->acd);
if (r < 0)
- return 0;
-
- ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp);
+ return r;
return 0;
}
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
assert_return(ll, -EINVAL);
- assert_return(interface_index > 0, -EINVAL);
- assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
- IPV4LL_STATE_STOPPED), -EBUSY);
- ll->index = interface_index;
-
- return 0;
+ return sd_ipv4acd_set_index(ll->acd, interface_index);
}
+#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
+
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
- bool need_restart = false;
+ int r;
assert_return(ll, -EINVAL);
- assert_return(addr, -EINVAL);
-
- if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
- return 0;
- if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) {
- log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
- "client, restarting");
- ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- need_restart = true;
- }
+ if (!ll->random_data) {
+ uint8_t seed[8];
- if (!ll)
- return 0;
+ /* If no random data is set, generate some from the MAC */
+ siphash24(seed, &addr->ether_addr_octet,
+ ETH_ALEN, HASH_KEY.bytes);
- memcpy(&ll->mac_addr, addr, ETH_ALEN);
+ assert_cc(sizeof(unsigned) <= 8);
- if (need_restart)
- sd_ipv4ll_start(ll);
+ r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed);
+ if (r < 0)
+ return r;
+ }
- return 0;
+ return sd_ipv4acd_set_mac(ll->acd, addr);
}
int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
assert_return(ll, -EINVAL);
- ll->event = sd_event_unref(ll->event);
-
- return 0;
+ return sd_ipv4acd_detach_event(ll->acd);
}
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
int r;
assert_return(ll, -EINVAL);
- assert_return(!ll->event, -EBUSY);
-
- if (event)
- ll->event = sd_event_ref(event);
- else {
- r = sd_event_default(&ll->event);
- if (r < 0) {
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- return r;
- }
- }
- ll->event_priority = priority;
+ r = sd_ipv4acd_attach_event(ll->acd, event, priority);
+ if (r < 0)
+ return r;
return 0;
}
@@ -469,189 +194,146 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
return -ENOENT;
address->s_addr = ll->claimed_address;
+
return 0;
}
-int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) {
- unsigned int entropy;
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed) {
+ _cleanup_free_ struct random_data *random_data = NULL;
+ _cleanup_free_ char *random_data_state = NULL;
int r;
assert_return(ll, -EINVAL);
- assert_return(seed, -EINVAL);
- entropy = *seed;
+ random_data = new0(struct random_data, 1);
+ if (!random_data)
+ return -ENOMEM;
- free(ll->random_data);
- free(ll->random_data_state);
+ random_data_state = new0(char, 128);
+ if (!random_data_state)
+ return -ENOMEM;
- ll->random_data = new0(struct random_data, 1);
- ll->random_data_state = new0(char, 128);
+ r = initstate_r(seed, random_data_state, 128, random_data);
+ if (r < 0)
+ return r;
- if (!ll->random_data || !ll->random_data_state) {
- r = -ENOMEM;
- goto error;
- }
+ free(ll->random_data);
+ ll->random_data = random_data;
+ random_data = NULL;
- r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
- if (r < 0)
- goto error;
+ free(ll->random_data_state);
+ ll->random_data_state = random_data_state;
+ random_data_state = NULL;
-error:
- if (r < 0){
- free(ll->random_data);
- free(ll->random_data_state);
- ll->random_data = NULL;
- ll->random_data_state = NULL;
- }
- return r;
+ return 0;
}
bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
assert_return(ll, false);
- return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
+ return sd_ipv4acd_is_running(ll->acd);
}
-#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
-
-int sd_ipv4ll_start (sd_ipv4ll *ll) {
+static int ipv4ll_pick_address(sd_ipv4ll *ll) {
+ struct in_addr in_addr;
+ be32_t addr;
int r;
+ int32_t random;
- assert_return(ll, -EINVAL);
- assert_return(ll->event, -EINVAL);
- assert_return(ll->index > 0, -EINVAL);
- assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
- IPV4LL_STATE_STOPPED), -EBUSY);
+ assert(ll);
+ assert(ll->random_data);
- ll->state = IPV4LL_STATE_INIT;
+ do {
+ r = random_r(ll->random_data, &random);
+ if (r < 0)
+ return r;
+ addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
+ } while (addr == ll->address ||
+ (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
+ (ntohl(addr) & 0x0000FF00) == 0x0000 ||
+ (ntohl(addr) & 0x0000FF00) == 0xFF00);
- r = arp_network_bind_raw_socket(ll->index, &ll->link);
+ in_addr.s_addr = addr;
+ r = sd_ipv4acd_set_address(ll->acd, &in_addr);
if (r < 0)
- goto out;
+ return r;
- ll->fd = r;
- ll->conflict = 0;
- ll->defend_window = 0;
- ll->claimed_address = 0;
+ ll->address = addr;
- if (!ll->random_data) {
- uint8_t seed[8];
+ return 0;
+}
- /* Fallback to mac */
- siphash24(seed, &ll->mac_addr.ether_addr_octet,
- ETH_ALEN, HASH_KEY.bytes);
+int sd_ipv4ll_start(sd_ipv4ll *ll) {
+ int r;
- r = sd_ipv4ll_set_address_seed(ll, seed);
- if (r < 0)
- goto out;
- }
+ assert_return(ll, -EINVAL);
+ assert_return(ll->random_data, -EINVAL);
if (ll->address == 0) {
- r = ipv4ll_pick_address(ll, &ll->address);
+ r = ipv4ll_pick_address(ll);
if (r < 0)
- goto out;
+ return r;
}
- ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
-
- r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
- EPOLLIN, ipv4ll_receive_message, ll);
+ r = sd_ipv4acd_start(ll->acd);
if (r < 0)
- goto out;
-
- r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message");
- if (r < 0)
- goto out;
-
- r = sd_event_add_time(ll->event,
- &ll->timer,
- clock_boottime_or_monotonic(),
- now(clock_boottime_or_monotonic()), 0,
- ipv4ll_timer, ll);
-
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_priority(ll->timer, ll->event_priority);
- if (r < 0)
- goto out;
-
- r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
-out:
- if (r < 0)
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
+ return r;
return 0;
}
-int sd_ipv4ll_stop(sd_ipv4ll *ll) {
- ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
- if (ll)
- ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1);
-
- return 0;
-}
-
-sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
-
- if (!ll)
- return NULL;
-
- assert(ll->n_ref >= 1);
- ll->n_ref++;
+static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
+ assert(ll);
- return ll;
+ if (ll->cb)
+ ll->cb(ll, event, ll->userdata);
}
-sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
-
- if (!ll)
- return NULL;
-
- assert(ll->n_ref >= 1);
- ll->n_ref--;
-
- if (ll->n_ref > 0)
- return ll;
-
- ll->receive_message = sd_event_source_unref(ll->receive_message);
- ll->fd = safe_close(ll->fd);
-
- ll->timer = sd_event_source_unref(ll->timer);
+void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
+ sd_ipv4ll *ll = userdata;
+ IPV4LL_DONT_DESTROY(ll);
+ int r;
- sd_ipv4ll_detach_event(ll);
+ assert(acd);
+ assert(ll);
- free(ll->random_data);
- free(ll->random_data_state);
- free(ll);
+ switch (event) {
+ case SD_IPV4ACD_EVENT_STOP:
+ ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
- return NULL;
-}
+ ll->claimed_address = 0;
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
-#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
+ break;
+ case SD_IPV4ACD_EVENT_BIND:
+ ll->claimed_address = ll->address;
+ ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
-int sd_ipv4ll_new(sd_ipv4ll **ret) {
- _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
+ break;
+ case SD_IPV4ACD_EVENT_CONFLICT:
+ /* if an address was already bound we must call up to the
+ user to handle this, otherwise we just try again */
+ if (ll->claimed_address != 0) {
+ ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
- assert_return(ret, -EINVAL);
+ ll->claimed_address = 0;
+ } else {
+ r = ipv4ll_pick_address(ll);
+ if (r < 0)
+ goto error;
- ll = new0(sd_ipv4ll, 1);
- if (!ll)
- return -ENOMEM;
+ r = sd_ipv4acd_start(ll->acd);
+ if (r < 0)
+ goto error;
+ }
- ll->n_ref = 1;
- ll->state = IPV4LL_STATE_INIT;
- ll->index = -1;
- ll->fd = -1;
+ break;
+ default:
+ assert_not_reached("Invalid IPv4ACD event.");
+ }
- *ret = ll;
- ll = NULL;
+ return;
- return 0;
+error:
+ ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
}
diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c
new file mode 100644
index 0000000000..7f765e98cd
--- /dev/null
+++ b/src/systemd/src/libsystemd-network/sd-lldp.c
@@ -0,0 +1,736 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <arpa/inet.h>
+
+#include "siphash24.h"
+#include "hashmap.h"
+
+#include "lldp-tlv.h"
+#include "lldp-port.h"
+#include "sd-lldp.h"
+#include "prioq.h"
+#include "lldp-internal.h"
+#include "lldp-util.h"
+
+typedef enum LLDPAgentRXState {
+ LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4,
+ LLDP_AGENT_RX_DELETE_AGED_INFO,
+ LLDP_AGENT_RX_LLDP_INITIALIZE,
+ LLDP_AGENT_RX_WAIT_FOR_FRAME,
+ LLDP_AGENT_RX_RX_FRAME,
+ LLDP_AGENT_RX_DELETE_INFO,
+ LLDP_AGENT_RX_UPDATE_INFO,
+ _LLDP_AGENT_RX_STATE_MAX,
+ _LLDP_AGENT_RX_INVALID = -1,
+} LLDPAgentRXState;
+
+/* Section 10.5.2.2 Reception counters */
+struct lldp_agent_statistics {
+ uint64_t stats_ageouts_total;
+ uint64_t stats_frames_discarded_total;
+ uint64_t stats_frames_in_errors_total;
+ uint64_t stats_frames_in_total;
+ uint64_t stats_tlvs_discarded_total;
+ uint64_t stats_tlvs_unrecognized_total;
+};
+
+struct sd_lldp {
+ lldp_port *port;
+
+ Prioq *by_expiry;
+ Hashmap *neighbour_mib;
+
+ sd_lldp_cb_t cb;
+
+ void *userdata;
+
+ LLDPAgentRXState rx_state;
+ lldp_agent_statistics statistics;
+};
+
+static void chassis_id_hash_func(const void *p, struct siphash *state) {
+ const lldp_chassis_id *id = p;
+
+ assert(id);
+ assert(id->data);
+
+ siphash24_compress(&id->length, sizeof(id->length), state);
+ siphash24_compress(id->data, id->length, state);
+}
+
+static int chassis_id_compare_func(const void *_a, const void *_b) {
+ const lldp_chassis_id *a, *b;
+
+ a = _a;
+ b = _b;
+
+ assert(!a->length || a->data);
+ assert(!b->length || b->data);
+
+ if (a->type != b->type)
+ return -1;
+
+ if (a->length != b->length)
+ return a->length < b->length ? -1 : 1;
+
+ return memcmp(a->data, b->data, a->length);
+}
+
+static const struct hash_ops chassis_id_hash_ops = {
+ .hash = chassis_id_hash_func,
+ .compare = chassis_id_compare_func
+};
+
+static void lldp_mib_delete_objects(sd_lldp *lldp);
+static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state);
+static void lldp_run_state_machine(sd_lldp *ll);
+
+static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
+ int r;
+
+ assert(lldp);
+ assert(tlv);
+
+ /* Remove expired packets */
+ if (prioq_size(lldp->by_expiry) > 0) {
+
+ lldp_set_state(lldp, LLDP_AGENT_RX_DELETE_INFO);
+
+ lldp_mib_delete_objects(lldp);
+ }
+
+ r = lldp_mib_add_objects(lldp->by_expiry, lldp->neighbour_mib, tlv);
+ if (r < 0)
+ goto out;
+
+ lldp_set_state(lldp, LLDP_AGENT_RX_UPDATE_INFO);
+
+ log_lldp("Packet added. MIB size: %d , PQ size: %d",
+ hashmap_size(lldp->neighbour_mib),
+ prioq_size(lldp->by_expiry));
+
+ lldp->statistics.stats_frames_in_total ++;
+
+ out:
+ if (r < 0)
+ log_lldp("Receive frame failed: %s", strerror(-r));
+
+ lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+
+ return 0;
+}
+
+/* 10.3.2 LLDPDU validation: rxProcessFrame() */
+int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
+ uint16_t type, len, i, l, t;
+ bool chassis_id = false;
+ bool malformed = false;
+ bool port_id = false;
+ bool ttl = false;
+ bool end = false;
+ lldp_port *port;
+ uint8_t *p, *q;
+ sd_lldp *lldp;
+ int r;
+
+ assert(tlv);
+ assert(length > 0);
+
+ port = (lldp_port *) tlv->userdata;
+ lldp = (sd_lldp *) port->userdata;
+
+ if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
+ log_lldp("Port is disabled : %s . Dropping ...",
+ lldp->port->ifname);
+ goto out;
+ }
+
+ lldp_set_state(lldp, LLDP_AGENT_RX_RX_FRAME);
+
+ p = tlv->pdu;
+ p += sizeof(struct ether_header);
+
+ for (i = 1, l = 0; l <= length; i++) {
+
+ memcpy(&t, p, sizeof(uint16_t));
+
+ type = ntohs(t) >> 9;
+ len = ntohs(t) & 0x01ff;
+
+ if (type == LLDP_TYPE_END) {
+ if (len != 0) {
+ log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
+ len);
+
+ malformed = true;
+ goto out;
+ }
+
+ end = true;
+
+ break;
+ } else if (type >=_LLDP_TYPE_MAX) {
+ log_lldp("TLV type not recognized %d . Dropping ...",
+ type);
+
+ malformed = true;
+ goto out;
+ }
+
+ /* skip type and length encoding */
+ p += 2;
+ q = p;
+
+ p += len;
+ l += (len + 2);
+
+ if (i <= 3) {
+ if (i != type) {
+ log_lldp("TLV missing or out of order. Dropping ...");
+
+ malformed = true;
+ goto out;
+ }
+ }
+
+ switch(type) {
+ case LLDP_TYPE_CHASSIS_ID:
+
+ if (len < 2) {
+ log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
+ len);
+
+ malformed = true;
+ goto out;
+ }
+
+ if (chassis_id) {
+ log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
+
+ malformed = true;
+ goto out;
+ }
+
+ /* Look what subtype it has */
+ if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED ||
+ *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
+ log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
+ *q);
+
+ malformed = true;
+ goto out;
+
+ }
+
+ chassis_id = true;
+
+ break;
+ case LLDP_TYPE_PORT_ID:
+
+ if (len < 2) {
+ log_lldp("Received malformed Port ID TLV len = %d. Dropping",
+ len);
+
+ malformed = true;
+ goto out;
+ }
+
+ if (port_id) {
+ log_lldp("Duplicate Port ID TLV found. Dropping ...");
+
+ malformed = true;
+ goto out;
+ }
+
+ /* Look what subtype it has */
+ if (*q == LLDP_PORT_SUBTYPE_RESERVED ||
+ *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
+ log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
+ *q);
+
+ malformed = true;
+ goto out;
+
+ }
+
+ port_id = true;
+
+ break;
+ case LLDP_TYPE_TTL:
+
+ if(len != 2) {
+ log_lldp(
+ "Received invalid lenth: %d TTL TLV. Dropping ...",
+ len);
+
+ malformed = true;
+ goto out;
+ }
+
+ if (ttl) {
+ log_lldp("Duplicate TTL TLV found. Dropping ...");
+
+ malformed = true;
+ goto out;
+ }
+
+ ttl = true;
+
+ break;
+ default:
+
+ if (len == 0) {
+ log_lldp("TLV type = %d's, length 0 received . Dropping ...",
+ type);
+
+ malformed = true;
+ goto out;
+ }
+ break;
+ }
+ }
+
+ if(!chassis_id || !port_id || !ttl || !end) {
+ log_lldp( "One or more mandotory TLV missing . Dropping ...");
+
+ malformed = true;
+ goto out;
+
+ }
+
+ r = tlv_packet_parse_pdu(tlv, length);
+ if (r < 0) {
+ log_lldp( "Failed to parse the TLV. Dropping ...");
+
+ malformed = true;
+ goto out;
+ }
+
+ return lldp_receive_frame(lldp, tlv);
+
+ out:
+ lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+
+ if (malformed) {
+ lldp->statistics.stats_frames_discarded_total ++;
+ lldp->statistics.stats_frames_in_errors_total ++;
+ }
+
+ sd_lldp_packet_unref(tlv);
+
+ return 0;
+}
+
+static int ttl_expiry_item_prioq_compare_func(const void *a, const void *b) {
+ const lldp_neighbour_port *p = a, *q = b;
+
+ if (p->until < q->until)
+ return -1;
+
+ if (p->until > q->until)
+ return 1;
+
+ return 0;
+}
+
+static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state) {
+
+ assert(lldp);
+ assert(state < _LLDP_AGENT_RX_STATE_MAX);
+
+ lldp->rx_state = state;
+
+ lldp_run_state_machine(lldp);
+}
+
+static void lldp_run_state_machine(sd_lldp *lldp) {
+ if (!lldp->cb)
+ return;
+
+ switch (lldp->rx_state) {
+ case LLDP_AGENT_RX_UPDATE_INFO:
+ lldp->cb(lldp, SD_LLDP_EVENT_UPDATE_INFO, lldp->userdata);
+ break;
+ default:
+ break;
+ }
+}
+
+/* 10.5.5.2.1 mibDeleteObjects ()
+ * The mibDeleteObjects () procedure deletes all information in the LLDP remote
+ * systems MIB associated with the MSAP identifier if an LLDPDU is received with
+ * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
+
+static void lldp_mib_delete_objects(sd_lldp *lldp) {
+ lldp_neighbour_port *p;
+ usec_t t = 0;
+
+ /* Remove all entries that are past their TTL */
+ for (;;) {
+
+ if (prioq_size(lldp->by_expiry) <= 0)
+ break;
+
+ p = prioq_peek(lldp->by_expiry);
+ if (!p)
+ break;
+
+ if (t <= 0)
+ t = now(clock_boottime_or_monotonic());
+
+ if (p->until > t)
+ break;
+
+ lldp_neighbour_port_remove_and_free(p);
+
+ lldp->statistics.stats_ageouts_total ++;
+ }
+}
+
+static void lldp_mib_objects_flush(sd_lldp *lldp) {
+ lldp_neighbour_port *p, *q;
+ lldp_chassis *c;
+
+ assert(lldp);
+ assert(lldp->neighbour_mib);
+ assert(lldp->by_expiry);
+
+ /* Drop all packets */
+ while ((c = hashmap_steal_first(lldp->neighbour_mib))) {
+
+ LIST_FOREACH_SAFE(port, p, q, c->ports) {
+ lldp_neighbour_port_remove_and_free(p);
+ }
+ }
+
+ assert(hashmap_size(lldp->neighbour_mib) == 0);
+ assert(prioq_size(lldp->by_expiry) == 0);
+}
+
+int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ uint8_t *mac, *port_id, type;
+ lldp_neighbour_port *p;
+ uint16_t data = 0, length = 0;
+ char buf[LINE_MAX];
+ lldp_chassis *c;
+ usec_t time;
+ Iterator i;
+ int r;
+
+ assert(lldp);
+ assert(lldp_file);
+
+ r = fopen_temporary(lldp_file, &f, &temp_path);
+ if (r < 0)
+ goto fail;
+
+ fchmod(fileno(f), 0644);
+
+ HASHMAP_FOREACH(c, lldp->neighbour_mib, i) {
+ LIST_FOREACH(port, p, c->ports) {
+ _cleanup_free_ char *s = NULL;
+ char *k, *t;
+
+ r = sd_lldp_packet_read_chassis_id(p->packet, &type, &mac, &length);
+ if (r < 0)
+ continue;
+
+ sprintf(buf, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
+
+ s = strdup(buf);
+ if (!s) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = sd_lldp_packet_read_port_id(p->packet, &type, &port_id, &length);
+ if (r < 0)
+ continue;
+
+ if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) {
+ k = strndup((char *) port_id, length -1);
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type);
+ free(k);
+ } else {
+ mac = port_id;
+ sprintf(buf, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
+ }
+
+ k = strappend(s, buf);
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ free(s);
+ s = k;
+
+ time = now(clock_boottime_or_monotonic());
+
+ /* Don't write expired packets */
+ if (time - p->until <= 0)
+ continue;
+
+ sprintf(buf, "'_TTL="USEC_FMT"' ", p->until);
+
+ k = strappend(s, buf);
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ free(s);
+ s = k;
+
+ r = sd_lldp_packet_read_system_name(p->packet, &k, &length);
+ if (r < 0)
+ k = strappend(s, "'_NAME=N/A' ");
+ else {
+ t = strndup(k, length);
+ if (!t) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ k = strjoin(s, "'_NAME=", t, "' ", NULL);
+ free(t);
+ }
+
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ free(s);
+ s = k;
+
+ (void) sd_lldp_packet_read_system_capability(p->packet, &data);
+
+ sprintf(buf, "'_CAP=%x'", data);
+
+ k = strappend(s, buf);
+ if (!k) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ free(s);
+ s = k;
+
+ fprintf(f, "%s\n", s);
+ }
+ }
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ goto fail;
+
+ if (rename(temp_path, lldp_file) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ return 0;
+
+ fail:
+ if (temp_path)
+ (void) unlink(temp_path);
+
+ return log_error_errno(r, "Failed to save lldp data %s: %m", lldp_file);
+}
+
+int sd_lldp_start(sd_lldp *lldp) {
+ int r;
+
+ assert_return(lldp, -EINVAL);
+ assert_return(lldp->port, -EINVAL);
+
+ lldp->port->status = LLDP_PORT_STATUS_ENABLED;
+
+ lldp_set_state(lldp, LLDP_AGENT_RX_LLDP_INITIALIZE);
+
+ r = lldp_port_start(lldp->port);
+ if (r < 0) {
+ log_lldp("Failed to start Port : %s , %s",
+ lldp->port->ifname,
+ strerror(-r));
+
+ lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL);
+
+ return r;
+ }
+
+ lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
+
+ return 0;
+}
+
+int sd_lldp_stop(sd_lldp *lldp) {
+ int r;
+
+ assert_return(lldp, -EINVAL);
+ assert_return(lldp->port, -EINVAL);
+
+ lldp->port->status = LLDP_PORT_STATUS_DISABLED;
+
+ r = lldp_port_stop(lldp->port);
+ if (r < 0)
+ return r;
+
+ lldp_mib_objects_flush(lldp);
+
+ return 0;
+}
+
+int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) {
+ int r;
+
+ assert_return(lldp, -EINVAL);
+ assert_return(!lldp->port->event, -EBUSY);
+
+ if (event)
+ lldp->port->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&lldp->port->event);
+ if (r < 0)
+ return r;
+ }
+
+ lldp->port->event_priority = priority;
+
+ return 0;
+}
+
+int sd_lldp_detach_event(sd_lldp *lldp) {
+
+ assert_return(lldp, -EINVAL);
+
+ lldp->port->event = sd_event_unref(lldp->port->event);
+
+ return 0;
+}
+
+int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) {
+ assert_return(lldp, -EINVAL);
+
+ lldp->cb = cb;
+ lldp->userdata = userdata;
+
+ return 0;
+}
+
+void sd_lldp_free(sd_lldp *lldp) {
+
+ if (!lldp)
+ return;
+
+ /* Drop all packets */
+ lldp_mib_objects_flush(lldp);
+
+ lldp_port_free(lldp->port);
+
+ hashmap_free(lldp->neighbour_mib);
+ prioq_free(lldp->by_expiry);
+
+ free(lldp);
+}
+
+int sd_lldp_new(int ifindex,
+ const char *ifname,
+ const struct ether_addr *mac,
+ sd_lldp **ret) {
+ _cleanup_lldp_free_ sd_lldp *lldp = NULL;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(ifindex > 0, -EINVAL);
+ assert_return(ifname, -EINVAL);
+ assert_return(mac, -EINVAL);
+
+ lldp = new0(sd_lldp, 1);
+ if (!lldp)
+ return -ENOMEM;
+
+ r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port);
+ if (r < 0)
+ return r;
+
+ lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops);
+ if (!lldp->neighbour_mib)
+ return -ENOMEM;
+
+ r = prioq_ensure_allocated(&lldp->by_expiry,
+ ttl_expiry_item_prioq_compare_func);
+ if (r < 0)
+ return r;
+
+ lldp->rx_state = LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL;
+
+ *ret = lldp;
+ lldp = NULL;
+
+ return 0;
+}
+
+int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs) {
+ lldp_neighbour_port *p;
+ lldp_chassis *c;
+ Iterator iter;
+ unsigned count = 0, i;
+
+ assert_return(lldp, -EINVAL);
+ assert_return(tlvs, -EINVAL);
+
+ HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
+ LIST_FOREACH(port, p, c->ports)
+ count++;
+ }
+
+ if (!count) {
+ *tlvs = NULL;
+ return 0;
+ }
+
+ *tlvs = new(sd_lldp_packet *, count);
+ if (!*tlvs)
+ return -ENOMEM;
+
+ i = 0;
+ HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
+ LIST_FOREACH(port, p, c->ports)
+ (*tlvs)[i++] = sd_lldp_packet_ref(p->packet);
+ }
+
+ return count;
+}
diff --git a/src/systemd/src/libsystemd/sd-event/event-util.h b/src/systemd/src/libsystemd/sd-event/event-util.h
new file mode 100644
index 0000000000..a478277bc4
--- /dev/null
+++ b/src/systemd/src/libsystemd/sd-event/event-util.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include "util.h"
+#include "sd-event.h"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event*, sd_event_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, sd_event_source_unref);
+
+#define _cleanup_event_unref_ _cleanup_(sd_event_unrefp)
+#define _cleanup_event_source_unref_ _cleanup_(sd_event_source_unrefp)
diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
index 263b1a893e..4ef93c8eb5 100644
--- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c
@@ -31,7 +31,7 @@
#include "random-util.h"
#if 0 /* NM_IGNORED */
-_public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) {
+_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
unsigned n;
assert_return(s, NULL);
diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c
index c95654f299..4267e51800 100644
--- a/src/systemd/src/shared/dns-domain.c
+++ b/src/systemd/src/shared/dns-domain.c
@@ -382,9 +382,8 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
}
#if 0 /* NM_IGNORED */
-unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
+void dns_name_hash_func(const void *s, struct siphash *state) {
const char *p = s;
- unsigned long ul = hash_key[0];
int r;
assert(p);
@@ -403,13 +402,17 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_
if (k > 0)
r = k;
+ if (r == 0)
+ break;
+
label[r] = 0;
ascii_strlower(label);
- ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
+ string_hash_func(label, state);
}
- return ul;
+ /* enforce that all names are terminated by the empty label */
+ string_hash_func("", state);
}
int dns_name_compare_func(const void *a, const void *b) {
diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h
index 89e0901ad4..46d8207dae 100644
--- a/src/systemd/src/shared/dns-domain.h
+++ b/src/systemd/src/shared/dns-domain.h
@@ -58,7 +58,7 @@ static inline int dns_name_is_valid(const char *s) {
}
#if 0 /* NM_IGNORED */
-unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]);
+void dns_name_hash_func(const void *s, struct siphash *state);
int dns_name_compare_func(const void *a, const void *b);
extern const struct hash_ops dns_name_hash_ops;
diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h
index 527c4239d1..c453a288bd 100644
--- a/src/systemd/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/src/systemd/sd-dhcp-client.h
@@ -31,11 +31,11 @@
#include "sd-dhcp-lease.h"
enum {
- DHCP_EVENT_STOP = 0,
- DHCP_EVENT_IP_ACQUIRE = 1,
- DHCP_EVENT_IP_CHANGE = 2,
- DHCP_EVENT_EXPIRED = 3,
- DHCP_EVENT_RENEW = 4,
+ SD_DHCP_CLIENT_EVENT_STOP = 0,
+ SD_DHCP_CLIENT_EVENT_IP_ACQUIRE = 1,
+ SD_DHCP_CLIENT_EVENT_IP_CHANGE = 2,
+ SD_DHCP_CLIENT_EVENT_EXPIRED = 3,
+ SD_DHCP_CLIENT_EVENT_RENEW = 4,
};
typedef struct sd_dhcp_client sd_dhcp_client;
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
index e2646cbbc4..9367061495 100644
--- a/src/systemd/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -31,11 +31,11 @@
#include "sd-dhcp6-lease.h"
enum {
- DHCP6_EVENT_STOP = 0,
- DHCP6_EVENT_RESEND_EXPIRE = 10,
- DHCP6_EVENT_RETRANS_MAX = 11,
- DHCP6_EVENT_IP_ACQUIRE = 12,
- DHCP6_EVENT_INFORMATION_REQUEST = 13,
+ SD_DHCP6_CLIENT_EVENT_STOP = 0,
+ SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE = 10,
+ SD_DHCP6_CLIENT_EVENT_RETRANS_MAX = 11,
+ SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE = 12,
+ SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13,
};
typedef struct sd_dhcp6_client sd_dhcp6_client;
diff --git a/src/systemd/src/systemd/sd-icmp6-nd.h b/src/systemd/src/systemd/sd-icmp6-nd.h
index 5f5a9217f1..19cb9593d7 100644
--- a/src/systemd/src/systemd/sd-icmp6-nd.h
+++ b/src/systemd/src/systemd/sd-icmp6-nd.h
@@ -29,11 +29,11 @@
#include "sd-event.h"
enum {
- ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE = 0,
- ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1,
- ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER = 2,
- ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3,
- ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4,
+ SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE = 0,
+ SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1,
+ SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER = 2,
+ SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3,
+ SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4,
};
typedef struct sd_icmp6_nd sd_icmp6_nd;
@@ -66,9 +66,9 @@ int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr,
int sd_icmp6_nd_stop(sd_icmp6_nd *nd);
int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd);
-#define SD_ICMP6_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+#define SD_ICMP6_ND_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
-#define SD_ICMP6_ADDRESS_FORMAT_VAL(address) \
+#define SD_ICMP6_ND_ADDRESS_FORMAT_VAL(address) \
be16toh((address).s6_addr16[0]), \
be16toh((address).s6_addr16[1]), \
be16toh((address).s6_addr16[2]), \
diff --git a/src/systemd/src/systemd/sd-ipv4acd.h b/src/systemd/src/systemd/sd-ipv4acd.h
new file mode 100644
index 0000000000..6ec863cb7b
--- /dev/null
+++ b/src/systemd/src/systemd/sd-ipv4acd.h
@@ -0,0 +1,57 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdipv4acdfoo
+#define foosdipv4acdfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Axis Communications AB. All rights reserved.
+ Copyright (C) 2015 Tom Gundersen
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "nm-sd-adapt.h"
+
+#include <stdbool.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+#include "sd-event.h"
+
+enum {
+ SD_IPV4ACD_EVENT_STOP = 0,
+ SD_IPV4ACD_EVENT_BIND = 1,
+ SD_IPV4ACD_EVENT_CONFLICT = 2,
+};
+
+typedef struct sd_ipv4acd sd_ipv4acd;
+typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata);
+
+int sd_ipv4acd_detach_event(sd_ipv4acd *ll);
+int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority);
+int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address);
+int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata);
+int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr);
+int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index);
+int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address);
+bool sd_ipv4acd_is_running(sd_ipv4acd *ll);
+int sd_ipv4acd_start(sd_ipv4acd *ll);
+int sd_ipv4acd_stop(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *ll);
+sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *ll);
+int sd_ipv4acd_new (sd_ipv4acd **ret);
+
+#endif
diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h
index 10725a1184..35b745c14b 100644
--- a/src/systemd/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/src/systemd/sd-ipv4ll.h
@@ -31,9 +31,9 @@
#include "sd-event.h"
enum {
- IPV4LL_EVENT_STOP = 0,
- IPV4LL_EVENT_BIND = 1,
- IPV4LL_EVENT_CONFLICT = 2,
+ SD_IPV4LL_EVENT_STOP = 0,
+ SD_IPV4LL_EVENT_BIND = 1,
+ SD_IPV4LL_EVENT_CONFLICT = 2,
};
typedef struct sd_ipv4ll sd_ipv4ll;
@@ -45,7 +45,7 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint8_t seed[8]);
+int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed);
bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
int sd_ipv4ll_start(sd_ipv4ll *ll);
int sd_ipv4ll_stop(sd_ipv4ll *ll);
diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h
new file mode 100644
index 0000000000..b6a7132cf4
--- /dev/null
+++ b/src/systemd/src/systemd/sd-lldp.h
@@ -0,0 +1,76 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2014 Tom Gundersen
+ Copyright (C) 2014 Susant Sahani
+
+ systemd 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; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include "nm-sd-adapt.h"
+
+#include "sd-event.h"
+
+enum {
+ SD_LLDP_EVENT_UPDATE_INFO = 0,
+};
+
+enum {
+ SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE,
+ SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE,
+ SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE,
+};
+
+typedef struct sd_lldp sd_lldp;
+typedef struct tlv_packet sd_lldp_packet;
+
+typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata);
+
+int sd_lldp_new(int ifindex, const char *ifname, const struct ether_addr *mac, sd_lldp **ret);
+void sd_lldp_free(sd_lldp *lldp);
+
+int sd_lldp_start(sd_lldp *lldp);
+int sd_lldp_stop(sd_lldp *lldp);
+
+int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority);
+int sd_lldp_detach_event(sd_lldp *lldp);
+
+int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata);
+int sd_lldp_save(sd_lldp *lldp, const char *file);
+
+int sd_lldp_packet_read_chassis_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
+int sd_lldp_packet_read_port_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
+int sd_lldp_packet_read_ttl(sd_lldp_packet *tlv, uint16_t *ttl);
+int sd_lldp_packet_read_system_name(sd_lldp_packet *tlv, char **data, uint16_t *length);
+int sd_lldp_packet_read_system_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
+int sd_lldp_packet_read_system_capability(sd_lldp_packet *tlv, uint16_t *data);
+int sd_lldp_packet_read_port_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
+
+/* IEEE 802.1 organizationally specific TLVs */
+int sd_lldp_packet_read_port_vlan_id(sd_lldp_packet *tlv, uint16_t *id);
+int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id);
+int sd_lldp_packet_read_vlan_name(sd_lldp_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length);
+int sd_lldp_packet_read_management_vid(sd_lldp_packet *tlv, uint16_t *id);
+int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id);
+
+sd_lldp_packet *sd_lldp_packet_ref(sd_lldp_packet *tlv);
+sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv);
+
+int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest);
+
+int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs);