merge: merge branch 'systemd' into master
authorBeniamino Galvani <bgalvani@redhat.com>
Wed, 7 Oct 2015 08:07:17 +0000 (10:07 +0200)
committerThomas Haller <thaller@redhat.com>
Thu, 8 Oct 2015 12:14:19 +0000 (14:14 +0200)
52 files changed:
1  2 
configure.ac
src/Makefile.am
src/devices/nm-device.c
src/dhcp-manager/nm-dhcp-systemd.c
src/systemd/nm-sd-adapt.h
src/systemd/src/basic/fileio.c
src/systemd/src/basic/fileio.h
src/systemd/src/basic/hashmap.c
src/systemd/src/basic/hashmap.h
src/systemd/src/basic/log.h
src/systemd/src/basic/macro.h
src/systemd/src/basic/mempool.c
src/systemd/src/basic/mempool.h
src/systemd/src/basic/prioq.c
src/systemd/src/basic/prioq.h
src/systemd/src/basic/set.h
src/systemd/src/basic/siphash24.c
src/systemd/src/basic/siphash24.h
src/systemd/src/basic/strv.c
src/systemd/src/basic/strv.h
src/systemd/src/basic/time-util.h
src/systemd/src/basic/util.c
src/systemd/src/basic/util.h
src/systemd/src/libsystemd-network/arp-util.c
src/systemd/src/libsystemd-network/arp-util.h
src/systemd/src/libsystemd-network/lldp-internal.c
src/systemd/src/libsystemd-network/lldp-internal.h
src/systemd/src/libsystemd-network/lldp-network.c
src/systemd/src/libsystemd-network/lldp-network.h
src/systemd/src/libsystemd-network/lldp-port.c
src/systemd/src/libsystemd-network/lldp-port.h
src/systemd/src/libsystemd-network/lldp-tlv.c
src/systemd/src/libsystemd-network/lldp-tlv.h
src/systemd/src/libsystemd-network/lldp-util.h
src/systemd/src/libsystemd-network/lldp.h
src/systemd/src/libsystemd-network/network-internal.c
src/systemd/src/libsystemd-network/sd-dhcp-client.c
src/systemd/src/libsystemd-network/sd-dhcp-lease.c
src/systemd/src/libsystemd-network/sd-dhcp6-client.c
src/systemd/src/libsystemd-network/sd-ipv4acd.c
src/systemd/src/libsystemd-network/sd-ipv4ll.c
src/systemd/src/libsystemd-network/sd-lldp.c
src/systemd/src/libsystemd/sd-event/event-util.h
src/systemd/src/libsystemd/sd-id128/sd-id128.c
src/systemd/src/shared/dns-domain.c
src/systemd/src/shared/dns-domain.h
src/systemd/src/systemd/sd-dhcp-client.h
src/systemd/src/systemd/sd-dhcp6-client.h
src/systemd/src/systemd/sd-icmp6-nd.h
src/systemd/src/systemd/sd-ipv4acd.h
src/systemd/src/systemd/sd-ipv4ll.h
src/systemd/src/systemd/sd-lldp.h

diff --cc configure.ac
@@@ -53,6 -65,6 +53,7 @@@ dn
  dnl Checks for typedefs, structures, and compiler characteristics.
  dnl
  AC_TYPE_PID_T
++AC_CHECK_SIZEOF(dev_t)
  
  dnl
  dnl translation support
diff --cc src/Makefile.am
@@@ -47,101 -48,6 +47,123 @@@ AM_CPPFLAGS =                             
  # primarily for its side effect of removing duplicates.
  AM_CPPFLAGS += $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d)
  
-       systemd/src/libsystemd-network/ipv4ll-internal.h \
-       systemd/src/libsystemd-network/ipv4ll-network.c \
-       systemd/src/libsystemd-network/ipv4ll-packet.c \
 +noinst_LTLIBRARIES = \
 +      libNetworkManager.la \
 +      libnm-iface-helper.la \
 +      libsystemd-nm.la
 +
 +######################
 +# libsystemd-nm
 +######################
 +
 +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
 +
 +libsystemd_nm_la_SOURCES = \
 +      systemd/nm-sd-adapt.c \
 +      systemd/nm-sd-adapt.h \
 +      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 \
 +      systemd/src/basic/in-addr-util.h \
 +      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 \
 +      systemd/src/basic/strv.h \
 +      systemd/src/basic/time-util.c \
 +      systemd/src/basic/time-util.h \
 +      systemd/src/basic/unaligned.h \
 +      systemd/src/basic/utf8.c \
 +      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 \
 +      systemd/src/libsystemd-network/dhcp-lease-internal.h \
 +      systemd/src/libsystemd-network/dhcp-network.c \
 +      systemd/src/libsystemd-network/dhcp-option.c \
 +      systemd/src/libsystemd-network/dhcp-packet.c \
 +      systemd/src/libsystemd-network/dhcp-protocol.h \
 +      systemd/src/libsystemd-network/dhcp6-internal.h \
 +      systemd/src/libsystemd-network/dhcp6-lease-internal.h \
 +      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/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 \
 +      systemd/src/systemd/sd-dhcp-client.h \
 +      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 = \
 +      -I$(top_srcdir)/include \
 +      -I$(top_builddir)/include \
 +      -I$(top_srcdir)/libnm-core \
 +      -I$(top_builddir)/libnm-core \
 +      $(SYSTEMD_NM_CFLAGS_PATHS) \
 +      -I$(top_srcdir)/src/systemd/src/shared \
 +      -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD \
 +      $(GLIB_CFLAGS)
 +
 +libsystemd_nm_la_LIBADD = \
 +      $(GLIB_LIBS)
 +
  ###########################################
  # NetworkManager
  ###########################################
@@@ -3244,13 -2426,13 +3244,13 @@@ nm_device_handle_ipv4ll_event (sd_ipv4l
        if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0)
                return;
  
 -      if (strcmp (event, "BIND") == 0) {
 -              guint32 lla;
 -              NMIP4Config *config;
 -
 -              if (inet_pton (AF_INET, address, &lla) <= 0) {
 -                      _LOGE (LOGD_AUTOIP4, "invalid address %s received from avahi-autoipd.", address);
 -                      nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR);
 +      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);
 +                      priv->ip4_state = IP_FAIL;
 +                      nm_device_check_ip_failed (self, FALSE);
                        return;
                }
  
index 534b54f,0000000..4459fd5
mode 100644,000000..100644
--- /dev/null
@@@ -1,850 -1,0 +1,850 @@@
-       case DHCP_EVENT_EXPIRED:
 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 +/* This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2, or (at your option)
 + * any later version.
 + *
 + * This program 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 General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + * Copyright (C) 2014 Red Hat, Inc.
 + */
 +
 +#include "config.h"
 +
 +#include <string.h>
 +#include <stdlib.h>
 +#include <errno.h>
 +#include <unistd.h>
 +#include <stdio.h>
 +#include <netinet/in.h>
 +#include <arpa/inet.h>
 +#include <ctype.h>
 +#include <net/if_arp.h>
 +
 +#include "nm-default.h"
 +#include "nm-dhcp-systemd.h"
 +#include "nm-utils.h"
 +#include "nm-dhcp-utils.h"
 +#include "NetworkManagerUtils.h"
 +#include "nm-platform.h"
 +
 +#include "sd-dhcp-client.h"
 +#include "sd-dhcp6-client.h"
 +#include "dhcp-protocol.h"
 +#include "dhcp-lease-internal.h"
 +#include "dhcp6-protocol.h"
 +#include "dhcp6-lease-internal.h"
 +
 +G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
 +
 +#define NM_DHCP_SYSTEMD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemdPrivate))
 +
 +typedef struct {
 +      struct sd_dhcp_client *client4;
 +      struct sd_dhcp6_client *client6;
 +      char *lease_file;
 +
 +      guint timeout_id;
 +      guint request_count;
 +
 +      gboolean privacy;
 +} NMDhcpSystemdPrivate;
 +
 +/************************************************************/
 +
 +#define DHCP_OPTION_NIS_DOMAIN         40
 +#define DHCP_OPTION_NIS_SERVERS        41
 +#define DHCP_OPTION_DOMAIN_SEARCH     119
 +#define DHCP_OPTION_RFC3442_ROUTES    121
 +#define DHCP_OPTION_MS_ROUTES         249
 +#define DHCP_OPTION_WPAD              252
 +
 +/* Internal values */
 +#define DHCP_OPTION_IP_ADDRESS       1024
 +#define DHCP_OPTION_EXPIRY           1025
 +#define DHCP6_OPTION_IP_ADDRESS      1026
 +#define DHCP6_OPTION_PREFIXLEN       1027
 +#define DHCP6_OPTION_PREFERRED_LIFE  1028
 +#define DHCP6_OPTION_MAX_LIFE        1029
 +#define DHCP6_OPTION_STARTS          1030
 +#define DHCP6_OPTION_LIFE_STARTS     1031
 +#define DHCP6_OPTION_RENEW           1032
 +#define DHCP6_OPTION_REBIND          1033
 +#define DHCP6_OPTION_IAID            1034
 +
 +typedef struct {
 +      guint num;
 +      const char *name;
 +      gboolean include;
 +} ReqOption;
 +
 +#define REQPREFIX "requested_"
 +
 +static const ReqOption dhcp4_requests[] = {
 +      { DHCP_OPTION_SUBNET_MASK,            REQPREFIX "subnet_mask",                     TRUE },
 +      { DHCP_OPTION_TIME_OFFSET,            REQPREFIX "time_offset",                     TRUE },
 +      { DHCP_OPTION_ROUTER,                 REQPREFIX "routers",                         TRUE },
 +      { DHCP_OPTION_DOMAIN_NAME_SERVER,     REQPREFIX "domain_name_servers",             TRUE },
 +      { DHCP_OPTION_HOST_NAME,              REQPREFIX "host_name",                       TRUE },
 +      { DHCP_OPTION_DOMAIN_NAME,            REQPREFIX "domain_name",                     TRUE },
 +      { DHCP_OPTION_INTERFACE_MTU,          REQPREFIX "interface_mtu",                   TRUE },
 +      { DHCP_OPTION_BROADCAST,              REQPREFIX "broadcast_address",               TRUE },
 +      { DHCP_OPTION_STATIC_ROUTE,           REQPREFIX "static_routes",                   TRUE },
 +      { DHCP_OPTION_NIS_DOMAIN,             REQPREFIX "nis_domain",                      TRUE },
 +      { DHCP_OPTION_NIS_SERVERS,            REQPREFIX "nis_servers",                     TRUE },
 +      { DHCP_OPTION_NTP_SERVER,             REQPREFIX "ntp_servers",                     TRUE },
 +      { DHCP_OPTION_SERVER_IDENTIFIER,      REQPREFIX "dhcp_server_identifier",          TRUE },
 +      { DHCP_OPTION_DOMAIN_SEARCH,          REQPREFIX "domain_search",                   TRUE },
 +      { DHCP_OPTION_CLASSLESS_STATIC_ROUTE, REQPREFIX "rfc3442_classless_static_routes", TRUE },
 +      { DHCP_OPTION_MS_ROUTES,              REQPREFIX "ms_classless_static_routes",      TRUE },
 +      { DHCP_OPTION_WPAD,                   REQPREFIX "wpad",                            TRUE },
 +
 +      /* Internal values */
 +      { DHCP_OPTION_IP_ADDRESS_LEASE_TIME, REQPREFIX "expiry",                          FALSE },
 +      { DHCP_OPTION_CLIENT_IDENTIFIER,     REQPREFIX "dhcp_client_identifier",          FALSE },
 +      { DHCP_OPTION_IP_ADDRESS,            REQPREFIX "ip_address",                      FALSE },
 +      { 0, NULL, FALSE }
 +};
 +
 +static const ReqOption dhcp6_requests[] = {
 +      { DHCP6_OPTION_CLIENTID,       REQPREFIX "dhcp6_client_id",     TRUE },
 +
 +      /* Don't request server ID by default; some servers don't reply to
 +       * Information Requests that request the Server ID.
 +       */
 +      { DHCP6_OPTION_SERVERID,       REQPREFIX "dhcp6_server_id",     FALSE },
 +
 +      { DHCP6_OPTION_DNS_SERVERS,    REQPREFIX "dhcp6_name_servers",  TRUE },
 +      { DHCP6_OPTION_DOMAIN_LIST,    REQPREFIX "dhcp6_domain_search", TRUE },
 +      { DHCP6_OPTION_SNTP_SERVERS,   REQPREFIX "dhcp6_sntp_servers",  TRUE },
 +
 +      /* Internal values */
 +      { DHCP6_OPTION_IP_ADDRESS,     REQPREFIX "ip6_address",         FALSE },
 +      { DHCP6_OPTION_PREFIXLEN,      REQPREFIX "ip6_prefixlen",       FALSE },
 +      { DHCP6_OPTION_PREFERRED_LIFE, REQPREFIX "preferred_life",      FALSE },
 +      { DHCP6_OPTION_MAX_LIFE,       REQPREFIX "max_life",            FALSE },
 +      { DHCP6_OPTION_STARTS,         REQPREFIX "starts",              FALSE },
 +      { DHCP6_OPTION_LIFE_STARTS,    REQPREFIX "life_starts",         FALSE },
 +      { DHCP6_OPTION_RENEW,          REQPREFIX "renew",               FALSE },
 +      { DHCP6_OPTION_REBIND,         REQPREFIX "rebind",              FALSE },
 +      { DHCP6_OPTION_IAID,           REQPREFIX "iaid",                FALSE },
 +      { 0, NULL, FALSE }
 +};
 +
 +static void
 +take_option (GHashTable *options,
 +             const ReqOption *requests,
 +             guint option,
 +             char *value)
 +{
 +      guint i;
 +
 +      g_return_if_fail (value != NULL);
 +
 +      for (i = 0; requests[i].name; i++) {
 +              if (requests[i].num == option) {
 +                      g_hash_table_insert (options,
 +                                           (gpointer) (requests[i].name + STRLEN (REQPREFIX)),
 +                                           value);
 +                      break;
 +              }
 +      }
 +      /* Option should always be found */
 +      g_assert (requests[i].name);
 +}
 +
 +static void
 +add_option (GHashTable *options, const ReqOption *requests, guint option, const char *value)
 +{
 +      if (options)
 +              take_option (options, requests, option, g_strdup (value));
 +}
 +
 +static void
 +add_option_u32 (GHashTable *options, const ReqOption *requests, guint option, guint32 value)
 +{
 +      if (options)
 +              take_option (options, requests, option, g_strdup_printf ("%u", value));
 +}
 +
 +static void
 +add_option_u64 (GHashTable *options, const ReqOption *requests, guint option, guint64 value)
 +{
 +      if (options)
 +              take_option (options, requests, option, g_strdup_printf ("%" G_GUINT64_FORMAT, value));
 +}
 +
 +static void
 +add_requests_to_options (GHashTable *options, const ReqOption *requests)
 +{
 +      guint i;
 +
 +      for (i = 0; options && requests[i].name; i++) {
 +              if (requests[i].include)
 +                      g_hash_table_insert (options, (gpointer) requests[i].name, g_strdup ("1"));
 +      }
 +}
 +
 +#define LOG_LEASE(domain, ...) \
 +G_STMT_START { \
 +      if (log_lease) { \
 +              nm_log (LOGL_INFO, (domain), __VA_ARGS__); \
 +      } \
 +} G_STMT_END
 +
 +static NMIP4Config *
 +lease_to_ip4_config (const char *iface,
 +                     int ifindex,
 +                     sd_dhcp_lease *lease,
 +                     GHashTable *options,
 +                     guint32 default_priority,
 +                     gboolean log_lease,
 +                     GError **error)
 +{
 +      NMIP4Config *ip4_config = NULL;
 +      struct in_addr tmp_addr;
 +      const struct in_addr *addr_list;
 +      char buf[INET_ADDRSTRLEN];
 +      const char *str;
 +      guint32 lifetime = 0, i;
 +      NMPlatformIP4Address address;
 +      GString *l;
 +      struct sd_dhcp_route *routes;
 +      guint16 mtu;
 +      int r, num;
 +      guint64 end_time;
 +      const void *data;
 +      gsize data_len;
 +      gboolean metered = FALSE;
 +
 +      g_return_val_if_fail (lease != NULL, NULL);
 +
 +      ip4_config = nm_ip4_config_new (ifindex);
 +
 +      /* Address */
 +      sd_dhcp_lease_get_address (lease, &tmp_addr);
 +      memset (&address, 0, sizeof (address));
 +      address.address = tmp_addr.s_addr;
 +      str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
 +      LOG_LEASE (LOGD_DHCP4, "  address %s", str);
 +      add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str);
 +
 +      /* Prefix/netmask */
 +      sd_dhcp_lease_get_netmask (lease, &tmp_addr);
 +      address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr);
 +      LOG_LEASE (LOGD_DHCP4, "  plen %d", address.plen);
 +      add_option (options,
 +                  dhcp4_requests,
 +                  DHCP_OPTION_SUBNET_MASK,
 +                  nm_utils_inet4_ntop (tmp_addr.s_addr, NULL));
 +
 +      /* Lease time */
 +      sd_dhcp_lease_get_lifetime (lease, &lifetime);
 +      address.timestamp = nm_utils_get_monotonic_timestamp_s ();
 +      address.lifetime = address.preferred = lifetime;
 +      end_time = (guint64) time (NULL) + lifetime;
 +      LOG_LEASE (LOGD_DHCP4, "  expires in %" G_GUINT32_FORMAT " seconds", lifetime);
 +      add_option_u64 (options,
 +                      dhcp4_requests,
 +                      DHCP_OPTION_IP_ADDRESS_LEASE_TIME,
 +                      end_time);
 +
 +      address.source = NM_IP_CONFIG_SOURCE_DHCP;
 +      nm_ip4_config_add_address (ip4_config, &address);
 +
 +      /* Gateway */
 +      r = sd_dhcp_lease_get_router (lease, &tmp_addr);
 +      if (r == 0) {
 +              nm_ip4_config_set_gateway (ip4_config, tmp_addr.s_addr);
 +              str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL);
 +              LOG_LEASE (LOGD_DHCP4, "  gateway %s", str);
 +              add_option (options, dhcp4_requests, DHCP_OPTION_ROUTER, str);
 +      }
 +
 +      /* DNS Servers */
 +      num = sd_dhcp_lease_get_dns (lease, &addr_list);
 +      if (num > 0) {
 +              l = g_string_sized_new (30);
 +              for (i = 0; i < num; i++) {
 +                      if (addr_list[i].s_addr) {
 +                              nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr);
 +                              str = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL);
 +                              LOG_LEASE (LOGD_DHCP4, "  nameserver '%s'", str);
 +                              g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
 +                      }
 +              }
 +              if (l->len)
 +                      add_option (options, dhcp4_requests, DHCP_OPTION_DOMAIN_NAME_SERVER, l->str);
 +              g_string_free (l, TRUE);
 +      }
 +
 +      /* Domain Name */
 +      r = sd_dhcp_lease_get_domainname (lease, &str);
 +      if (r == 0) {
 +              /* Multiple domains sometimes stuffed into the option */
 +              char **domains = g_strsplit (str, " ", 0);
 +              char **s;
 +
 +              for (s = domains; *s; s++) {
 +                      LOG_LEASE (LOGD_DHCP4, "  domain name '%s'", *s);
 +                      nm_ip4_config_add_domain (ip4_config, *s);
 +              }
 +              g_strfreev (domains);
 +              add_option (options, dhcp4_requests, DHCP_OPTION_DOMAIN_NAME, str);
 +      }
 +
 +      /* Hostname */
 +      r = sd_dhcp_lease_get_hostname (lease, &str);
 +      if (r == 0) {
 +              LOG_LEASE (LOGD_DHCP4, "  hostname '%s'", str);
 +              add_option (options, dhcp4_requests, DHCP_OPTION_HOST_NAME, str);
 +      }
 +
 +      /* Routes */
 +      num = sd_dhcp_lease_get_routes (lease, &routes);
 +      if (num > 0) {
 +              l = g_string_sized_new (30);
 +              for (i = 0; i < num; i++) {
 +                      NMPlatformIP4Route route;
 +                      const char *gw_str;
 +
 +                      memset (&route, 0, sizeof (route));
 +                      route.network = routes[i].dst_addr.s_addr;
 +                      route.plen = routes[i].dst_prefixlen;
 +                      route.gateway = routes[i].gw_addr.s_addr;
 +                      route.source = NM_IP_CONFIG_SOURCE_DHCP;
 +                      route.metric = default_priority;
 +                      nm_ip4_config_add_route (ip4_config, &route);
 +
 +                      str = nm_utils_inet4_ntop (route.network, buf);
 +                      gw_str = nm_utils_inet4_ntop (route.gateway, NULL);
 +                      LOG_LEASE (LOGD_DHCP4, "  static route %s/%d gw %s", str, route.plen, gw_str);
 +
 +                      g_string_append_printf (l, "%s%s/%d %s", l->len ? " " : "", str, route.plen, gw_str);
 +              }
 +              add_option (options, dhcp4_requests, DHCP_OPTION_RFC3442_ROUTES, l->str);
 +              g_string_free (l, TRUE);
 +      }
 +
 +      /* MTU */
 +      r = sd_dhcp_lease_get_mtu (lease, &mtu);
 +      if (r == 0 && mtu) {
 +              nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP);
 +              add_option_u32 (options, dhcp4_requests, DHCP_OPTION_INTERFACE_MTU, mtu);
 +              LOG_LEASE (LOGD_DHCP4, "  mtu %u", mtu);
 +      }
 +
 +      /* NTP servers */
 +      num = sd_dhcp_lease_get_ntp (lease, &addr_list);
 +      if (num > 0) {
 +              l = g_string_sized_new (30);
 +              for (i = 0; i < num; i++) {
 +                      str = nm_utils_inet4_ntop (addr_list[i].s_addr, buf);
 +                      LOG_LEASE (LOGD_DHCP4, "  ntp server '%s'", str);
 +                      g_string_append_printf (l, "%s%s", l->len ? " " : "", str);
 +              }
 +              add_option (options, dhcp4_requests, DHCP_OPTION_NTP_SERVER, l->str);
 +              g_string_free (l, TRUE);
 +      }
 +
 +      r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len);
 +      if (r >= 0)
 +              metered = !!memmem (data, data_len, "ANDROID_METERED", STRLEN ("ANDROID_METERED"));
 +      nm_ip4_config_set_metered (ip4_config, metered);
 +
 +      return ip4_config;
 +}
 +
 +/************************************************************/
 +
 +static char *
 +get_leasefile_path (const char *iface, const char *uuid, gboolean ipv6)
 +{
 +      return g_strdup_printf (NMSTATEDIR "/internal%s-%s-%s.lease",
 +                              ipv6 ? "6" : "",
 +                              uuid,
 +                              iface);
 +}
 +
 +static GSList *
 +nm_dhcp_systemd_get_lease_ip_configs (const char *iface,
 +                                      int ifindex,
 +                                      const char *uuid,
 +                                      gboolean ipv6,
 +                                      guint32 default_route_metric)
 +{
 +      GSList *leases = NULL;
 +      gs_free char *path = NULL;
 +      sd_dhcp_lease *lease = NULL;
 +      NMIP4Config *ip4_config;
 +      int r;
 +
 +      if (ipv6)
 +              return NULL;
 +
 +      path = get_leasefile_path (iface, uuid, FALSE);
 +      r = dhcp_lease_load (&lease, path);
 +      if (r == 0 && lease) {
 +              ip4_config = lease_to_ip4_config (iface, ifindex, lease, NULL, default_route_metric, FALSE, NULL);
 +              if (ip4_config)
 +                      leases = g_slist_append (leases, ip4_config);
 +              sd_dhcp_lease_unref (lease);
 +      }
 +
 +      return leases;
 +}
 +
 +/************************************************************/
 +
 +static void
 +_save_client_id (NMDhcpSystemd *self,
 +                 uint8_t type,
 +                 const uint8_t *client_id,
 +                 size_t len)
 +{
 +      gs_unref_bytes GBytes *b = NULL;
 +      gs_free char *buf = NULL;
 +
 +      g_return_if_fail (self != NULL);
 +      g_return_if_fail (client_id != NULL);
 +      g_return_if_fail (len > 0);
 +
 +      if (!nm_dhcp_client_get_client_id (NM_DHCP_CLIENT (self))) {
 +              buf = g_malloc (len + 1);
 +              buf[0] = type;
 +              memcpy (buf + 1, client_id, len);
 +              b = g_bytes_new (buf, len + 1);
 +              nm_dhcp_client_set_client_id (NM_DHCP_CLIENT (self), b);
 +      }
 +}
 +
 +static void
 +bound4_handle (NMDhcpSystemd *self)
 +{
 +      NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 +      const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 +      sd_dhcp_lease *lease;
 +      NMIP4Config *ip4_config;
 +      GHashTable *options;
 +      GError *error = NULL;
 +      int r;
 +
 +      nm_log_dbg (LOGD_DHCP4, "(%s): lease available", iface);
 +
 +      r = sd_dhcp_client_get_lease (priv->client4, &lease);
 +      if (r < 0 || !lease) {
 +              nm_log_warn (LOGD_DHCP4, "(%s): no lease!", iface);
 +              nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
 +              return;
 +      }
 +
 +      options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
 +      ip4_config = lease_to_ip4_config (iface,
 +                                        nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)),
 +                                        lease,
 +                                        options,
 +                                        nm_dhcp_client_get_priority (NM_DHCP_CLIENT (self)),
 +                                        TRUE,
 +                                        &error);
 +      if (ip4_config) {
 +              const uint8_t *client_id = NULL;
 +              size_t client_id_len = 0;
 +              uint8_t type = 0;
 +
 +              add_requests_to_options (options, dhcp4_requests);
 +              dhcp_lease_save (lease, priv->lease_file);
 +
 +              sd_dhcp_client_get_client_id(priv->client4, &type, &client_id, &client_id_len);
 +              if (client_id)
 +                      _save_client_id (self, type, client_id, client_id_len);
 +
 +              nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
 +                                        NM_DHCP_STATE_BOUND,
 +                                        G_OBJECT (ip4_config),
 +                                        options);
 +      } else {
 +              nm_log_warn (LOGD_DHCP4, "(%s): %s", iface, error->message);
 +              nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
 +              g_clear_error (&error);
 +      }
 +
 +      g_hash_table_destroy (options);
 +      g_clear_object (&ip4_config);
 +}
 +
 +static void
 +dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data)
 +{
 +      NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data);
 +      NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 +      const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 +
 +      g_assert (priv->client4 == client);
 +
 +      nm_log_dbg (LOGD_DHCP4, "(%s): DHCPv4 client event %d", iface, event);
 +
 +      switch (event) {
-       case DHCP_EVENT_STOP:
++      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_RENEW:
-       case DHCP_EVENT_IP_CHANGE:
-       case DHCP_EVENT_IP_ACQUIRE:
++      case SD_DHCP_CLIENT_EVENT_STOP:
 +              nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL);
 +              break;
-       case DHCP6_EVENT_RETRANS_MAX:
++      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:
 +              nm_log_warn (LOGD_DHCP4, "(%s): unhandled DHCP event %d", iface, event);
 +              break;
 +      }
 +}
 +
 +static guint16
 +get_arp_type (const GByteArray *hwaddr)
 +{
 +      if (hwaddr->len == ETH_ALEN)
 +              return ARPHRD_ETHER;
 +      else if (hwaddr->len == INFINIBAND_ALEN)
 +              return ARPHRD_INFINIBAND;
 +      else
 +              g_assert_not_reached ();
 +}
 +
 +static gboolean
 +ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
 +{
 +      NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
 +      const char *iface = nm_dhcp_client_get_iface (client);
 +      const GByteArray *hwaddr;
 +      sd_dhcp_lease *lease = NULL;
 +      GBytes *override_client_id;
 +      const uint8_t *client_id = NULL;
 +      size_t client_id_len = 0;
 +      struct in_addr last_addr = { 0 };
 +      const char *hostname;
 +      int r, i;
 +      gboolean success = FALSE;
 +
 +      g_assert (priv->client4 == NULL);
 +      g_assert (priv->client6 == NULL);
 +
 +      g_free (priv->lease_file);
 +      priv->lease_file = get_leasefile_path (iface, nm_dhcp_client_get_uuid (client), FALSE);
 +
 +      r = sd_dhcp_client_new (&priv->client4);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP4, "(%s): failed to create DHCPv4 client (%d)", iface, r);
 +              return FALSE;
 +      }
 +
 +      r = sd_dhcp_client_attach_event (priv->client4, NULL, 0);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP4, "(%s): failed to attach DHCP event (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      hwaddr = nm_dhcp_client_get_hw_addr (client);
 +      if (hwaddr) {
 +              r = sd_dhcp_client_set_mac (priv->client4,
 +                                          hwaddr->data,
 +                                          hwaddr->len,
 +                                          get_arp_type (hwaddr));
 +              if (r < 0) {
 +                      nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP MAC address (%d)", iface, r);
 +                      goto error;
 +              }
 +      }
 +
 +      r = sd_dhcp_client_set_index (priv->client4, nm_dhcp_client_get_ifindex (client));
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP ifindex (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      r = sd_dhcp_client_set_callback (priv->client4, dhcp_event_cb, client);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP callback (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      r = sd_dhcp_client_set_request_broadcast (priv->client4, true);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP broadcast (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      dhcp_lease_load (&lease, priv->lease_file);
 +
 +      if (last_ip4_address)
 +              inet_pton (AF_INET, last_ip4_address, &last_addr);
 +      else if (lease)
 +              sd_dhcp_lease_get_address (lease, &last_addr);
 +
 +      if (last_addr.s_addr) {
 +              r = sd_dhcp_client_set_request_address (priv->client4, &last_addr);
 +              if (r < 0) {
 +                      nm_log_warn (LOGD_DHCP4, "(%s): failed to set last IPv4 address (%d)", iface, r);
 +                      goto error;
 +              }
 +      }
 +
 +      override_client_id = nm_dhcp_client_get_client_id (client);
 +      if (override_client_id) {
 +              client_id = g_bytes_get_data (override_client_id, &client_id_len);
 +              g_assert (client_id && client_id_len);
 +              sd_dhcp_client_set_client_id (priv->client4,
 +                                            client_id[0],
 +                                            client_id + 1,
 +                                            client_id_len - 1);
 +      } else if (lease) {
 +              r = sd_dhcp_lease_get_client_id (lease, (const void **) &client_id, &client_id_len);
 +              if (r == 0 && client_id_len) {
 +                      sd_dhcp_client_set_client_id (priv->client4,
 +                                                    client_id[0],
 +                                                    client_id + 1,
 +                                                    client_id_len - 1);
 +                      _save_client_id (NM_DHCP_SYSTEMD (client),
 +                                       client_id[0],
 +                                       client_id + 1,
 +                                       client_id_len - 1);
 +              }
 +      }
 +
 +
 +      /* Add requested options */
 +      for (i = 0; dhcp4_requests[i].name; i++) {
 +              if (dhcp4_requests[i].include)
 +                      sd_dhcp_client_set_request_option (priv->client4, dhcp4_requests[i].num);
 +      }
 +
 +      hostname = nm_dhcp_client_get_hostname (client);
 +      if (hostname) {
 +              r = sd_dhcp_client_set_hostname (priv->client4, hostname);
 +              if (r < 0) {
 +                      nm_log_warn (LOGD_DHCP4, "(%s): failed to set DHCP hostname (%d)", iface, r);
 +                      goto error;
 +              }
 +      }
 +
 +      r = sd_dhcp_client_start (priv->client4);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP4, "(%s): failed to start DHCP (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      success = TRUE;
 +
 +error:
 +      sd_dhcp_lease_unref (lease);
 +      if (!success)
 +              priv->client4 = sd_dhcp_client_unref (priv->client4);
 +      return success;
 +}
 +
 +static void
 +bound6_handle (NMDhcpSystemd *self)
 +{
 +      /* not yet supported... */
 +      nm_log_warn (LOGD_DHCP6, "(%s): internal DHCP does not yet support DHCPv6",
 +                   nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)));
 +      nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
 +}
 +
 +static void
 +dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
 +{
 +      NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data);
 +      NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 +      const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 +
 +      g_assert (priv->client6 == client);
 +
 +      nm_log_dbg (LOGD_DHCP6, "(%s): DHCPv6 client event %d", iface, event);
 +
 +      switch (event) {
-       case DHCP6_EVENT_RESEND_EXPIRE:
-       case DHCP6_EVENT_STOP:
++      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_IP_ACQUIRE:
++      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 SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
 +              bound6_handle (self);
 +              break;
 +      default:
 +              nm_log_warn (LOGD_DHCP6, "(%s): unhandled DHCPv6 event %d", iface, event);
 +              break;
 +      }
 +}
 +
 +static gboolean
 +ip6_start (NMDhcpClient *client,
 +           const char *dhcp_anycast_addr,
 +           gboolean info_only,
 +           NMSettingIP6ConfigPrivacy privacy,
 +           const GByteArray *duid)
 +{
 +      NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
 +      const char *iface = nm_dhcp_client_get_iface (client);
 +      const GByteArray *hwaddr;
 +      int r, i;
 +
 +      g_assert (priv->client4 == NULL);
 +      g_assert (priv->client6 == NULL);
 +      g_return_val_if_fail (duid != NULL, FALSE);
 +
 +      g_free (priv->lease_file);
 +      priv->lease_file = get_leasefile_path (iface, nm_dhcp_client_get_uuid (client), TRUE);
 +
 +      r = sd_dhcp6_client_new (&priv->client6);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP6, "(%s): failed to create DHCPv6 client (%d)", iface, r);
 +              return FALSE;
 +      }
 +
 +      /* NM stores the entire DUID which includes the uint16 "type", while systemd
 +       * wants the type passed separately from the following data.
 +       */
 +      r = sd_dhcp6_client_set_duid (priv->client6,
 +                                    ntohs (((const guint16 *) duid->data)[0]),
 +                                    duid->data + 2,
 +                                    duid->len - 2);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP6, "(%s): failed to create DHCPv6 client (%d)", iface, r);
 +              return FALSE;
 +      }
 +
 +      r = sd_dhcp6_client_attach_event (priv->client6, NULL, 0);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP6, "(%s): failed to attach DHCP event (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      hwaddr = nm_dhcp_client_get_hw_addr (client);
 +      if (hwaddr) {
 +              r = sd_dhcp6_client_set_mac (priv->client6,
 +                                           hwaddr->data,
 +                                           hwaddr->len,
 +                                           get_arp_type (hwaddr));
 +              if (r < 0) {
 +                      nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP MAC address (%d)", iface, r);
 +                      goto error;
 +              }
 +      }
 +
 +      r = sd_dhcp6_client_set_index (priv->client6, nm_dhcp_client_get_ifindex (client));
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP ifindex (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      r = sd_dhcp6_client_set_callback (priv->client6, dhcp6_event_cb, client);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP callback (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      /* Add requested options */
 +      for (i = 0; dhcp6_requests[i].name; i++) {
 +              if (dhcp6_requests[i].include)
 +                      sd_dhcp6_client_set_request_option (priv->client6, dhcp6_requests[i].num);
 +      }
 +
 +      r = sd_dhcp6_client_start (priv->client6);
 +      if (r < 0) {
 +              nm_log_warn (LOGD_DHCP6, "(%s): failed to start DHCP (%d)", iface, r);
 +              goto error;
 +      }
 +
 +      return TRUE;
 +
 +error:
 +      sd_dhcp6_client_unref (priv->client6);
 +      priv->client6 = NULL;
 +      return FALSE;
 +}
 +
 +static void
 +stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 +{
 +      NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
 +      int r = 0;
 +
 +      if (priv->client4)
 +              r = sd_dhcp_client_stop (priv->client4);
 +      else if (priv->client6)
 +              r = sd_dhcp6_client_stop (priv->client6);
 +
 +      if (r) {
 +              nm_log_warn (priv->client6 ? LOGD_DHCP6 : LOGD_DHCP4,
 +                               "(%s): failed to stop DHCP client (%d)",
 +                               nm_dhcp_client_get_iface (client),
 +                               r);
 +      }
 +}
 +
 +/***************************************************/
 +
 +static void
 +nm_dhcp_systemd_init (NMDhcpSystemd *self)
 +{
 +}
 +
 +static void
 +dispose (GObject *object)
 +{
 +      NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (object);
 +
 +      g_clear_pointer (&priv->lease_file, g_free);
 +
 +      if (priv->client4) {
 +              sd_dhcp_client_stop (priv->client4);
 +              sd_dhcp_client_unref (priv->client4);
 +              priv->client4 = NULL;
 +      }
 +
 +      if (priv->client6) {
 +              sd_dhcp6_client_stop (priv->client6);
 +              sd_dhcp6_client_unref (priv->client6);
 +              priv->client6 = NULL;
 +      }
 +
 +      G_OBJECT_CLASS (nm_dhcp_systemd_parent_class)->dispose (object);
 +}
 +
 +static void
 +nm_dhcp_systemd_class_init (NMDhcpSystemdClass *sdhcp_class)
 +{
 +      NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS (sdhcp_class);
 +      GObjectClass *object_class = G_OBJECT_CLASS (sdhcp_class);
 +
 +      g_type_class_add_private (sdhcp_class, sizeof (NMDhcpSystemdPrivate));
 +
 +      /* virtual methods */
 +      object_class->dispose = dispose;
 +
 +      client_class->ip4_start = ip4_start;
 +      client_class->ip6_start = ip6_start;
 +      client_class->stop = stop;
 +}
 +
 +static void __attribute__((constructor))
 +register_dhcp_dhclient (void)
 +{
 +      nm_g_type_init ();
 +      _nm_dhcp_client_register (NM_TYPE_DHCP_SYSTEMD,
 +                                "internal",
 +                                NULL,
 +                                nm_dhcp_systemd_get_lease_ip_configs);
 +}
 +
index 03fda64,0000000..a6123d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,138 -1,0 +1,147 @@@
 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 +/* This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2, or (at your option)
 + * any later version.
 + *
 + * This program 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 General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License along
 + * with this program; if not, write to the Free Software Foundation, Inc.,
 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + * Copyright (C) 2014 - 2015 Red Hat, Inc.
 + */
 +
 +#ifndef NM_SD_ADAPT_H
 +#define NM_SD_ADAPT_H
 +
 +#include "config.h"
 +
 +#include <stdbool.h>
 +#include <syslog.h>
 +#include <sys/resource.h>
 +
 +#include "nm-default.h"
 +
 +#define noreturn G_GNUC_NORETURN
 +
 +/*****************************************************************************/
 +
 +static inline NMLogLevel
 +_slog_level_to_nm (int slevel)
 +{
 +    switch (slevel) {
 +    case LOG_DEBUG:   return LOGL_DEBUG;
 +      case LOG_WARNING: return LOGL_WARN;
 +      case LOG_CRIT:
 +      case LOG_ERR:     return LOGL_ERR;
 +      case LOG_INFO:
 +      case LOG_NOTICE:
 +      default:          return LOGL_INFO;
 +      }
 +}
 +
 +#define log_internal(level, error, file, line, func, format, ...) \
 +({ \
 +      const int _nm_e = (error); \
 +      const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \
 +      \
 +      if (nm_logging_enabled (_nm_l, LOGD_DHCP)) { \
 +              const char *_nm_location = strrchr ((""file), '/'); \
 +              \
 +              _nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \
 +      } \
 +      (_nm_e > 0 ? -_nm_e : _nm_e); \
 +})
 +
 +#define log_full_errno(level, error, ...) \
 +({ \
 +      log_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__); \
 +})
 +
 +#define log_assert_failed(text, file, line, func) \
 +G_STMT_START { \
 +      log_internal (LOG_CRIT, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", text, file, line, func); \
 +      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); \
 +      g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, text); \
 +      (void) 0; \
 +})
 +
 +/*****************************************************************************
 + * The remainder of the header is only enabled when building the systemd code
 + * itself.
 + *****************************************************************************/
 +
 +#if (NETWORKMANAGER_COMPILATION) == NM_NETWORKMANAGER_COMPILATION_SYSTEMD
 +
 +#include <netinet/in.h>
 +#include <string.h>
 +#include <stdio.h>
 +#include <errno.h>
 +#include <elf.h>
 +#ifdef HAVE_SYS_AUXV_H
 +#include <sys/auxv.h>
 +#endif
 +#include <unistd.h>
 +#include <sys/syscall.h>
 +
 +#include <net/if_arp.h>
 +
 +/* Missing in Linux 3.2.0, in Ubuntu 12.04 */
 +#ifndef BPF_XOR
 +#define BPF_XOR 0xa0
 +#endif
 +
 +/*****************************************************************************/
 +
 +/* Can't include both net/if.h and linux/if.h; so have to define this here */
 +#ifndef IFNAMSIZ
 +#define IFNAMSIZ 16
 +#endif
 +
 +#ifndef MAX_HANDLE_SZ
 +#define MAX_HANDLE_SZ 128
 +#endif
 +
 +
 +/*
 + * Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export
 + * CLOCK_BOOTTIME even though the kernel supports it, so provide a
 + * local definition
 + */
 +#ifndef CLOCK_BOOTTIME
 +#define CLOCK_BOOTTIME 7
 +#endif
 +
 +#include "sd-id128.h"
 +#include "sparse-endian.h"
 +#include "async.h"
 +#include "util.h"
 +
 +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 */
 +
Simple merge
Simple merge
index 0000000,20e7e51..97d20f7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1863 +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;
+ }
index 0000000,ed6a092..c2c9948
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,413 +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)
Simple merge
@@@ -467,20 -469,7 +472,8 @@@ do 
  #define UID_INVALID ((uid_t) -1)
  #define GID_INVALID ((gid_t) -1)
  #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)                                         \
index 0000000,d5d98d8..a88ca45
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,103 +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
index 0000000,42f473b..e9753ad
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,49 +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
index 0000000,d55b348..50728f1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,318 +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;
+ }
index 0000000,1c044b1..820ac21
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,42 +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_;
index 0000000,4554ef2..109d003
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,136 +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)
     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 <stdint.h>
- #include <stdio.h>
- #include <string.h>
 +#include "nm-sd-adapt.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);
  }
Simple merge
@@@ -278,10 -276,9 +278,10 @@@ char **strv_split_newlines(const char *
          return l;
  }
  
 +#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);
          *t = l;
          l = NULL;
  
-         return 0;
+         return (int) n;
  }
 +#endif /* NM_IGNORED */
  
  char *strv_join(char **l, const char *separator) {
          char *r, *e;
Simple merge
Simple merge
    along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
  
- #include <string.h>
- #include <unistd.h>
 +#include "nm-sd-adapt.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
  
  #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"
- #include "exit-status.h"
- #include "hashmap.h"
- #include "env-util.h"
- #include "fileio.h"
- #include "device-nodes.h"
 +#if 0 /* NM_IGNORED */
+ #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 "gunicode.h"
+ #include "util.h"
 +#if 0 /* NM_IGNORED */
  #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 */
  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;
@@@ -6824,4 -6766,109 +6817,110 @@@ int fgetxattr_malloc(int fd, const cha
                          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 */
    along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
  
 +#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"
@@@ -440,7 -432,7 +438,9 @@@ int get_files_in_directory(const char *
  
  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);
index 0000000,2f5b9b3..39a1593
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,153 +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);
+ }
index 0000000,4012cd4..b8238fe
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,360 +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);
+ }
index 0000000,284cc67..885082e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,92 +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__)
index 0000000,12a6599..b68dce1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,84 +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;
+ }
index 0000000,74ee13a..b1e428e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,27 +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);
index 0000000,7486b4c..9b0cf04
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,117 +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;
+ }
index 0000000,517b162..063eaa2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,71 +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);
index 0000000,66af22e..de91600
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,647 +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;
+ }
index 0000000,2d2c776..56d17cf
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,99 +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);
index 0000000,112001e..e6c22d5
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,26 +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)
index 0000000,19e5cc5..974fd8b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,128 +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,
+ };
index 0000000,95b96bf..8a21ae5
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,529 +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;
+ }
index 0000000,06949a1..7f765e9
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,734 +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;
+ }
index 0000000,e7cad9b..a478277
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,31 +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)
@@@ -30,8 -28,7 +30,8 @@@
  #include "sd-id128.h"
  #include "random-util.h"
  
- _public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) {
 +#if 0 /* NM_IGNORED */
+ _public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
          unsigned n;
  
          assert_return(s, NULL);
@@@ -381,10 -379,8 +381,9 @@@ int dns_name_concat(const char *a, cons
          return 0;
  }
  
- unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
 +#if 0 /* NM_IGNORED */
+ 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);
@@@ -57,8 -54,7 +57,8 @@@ static inline int dns_name_is_valid(con
          return 1;
  }
  
- unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]);
 +#if 0 /* NM_IGNORED */
+ 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;
  
Simple merge
index 0000000,adcb2c7..6ec863c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,55 +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
Simple merge
index 0000000,308d42c..b6a7132
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,74 +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);