[ovs-dev] [PATCH 1/3] auto-attach: Initial support for Auto-Attach standard

Flynn, Dennis R (Dennis) drflynn at avaya.com
Wed Oct 1 14:42:52 UTC 2014


This commit provides the initial delivery of support for the Auto-Attach
standard to Open vSwitch. This standard describes a compact method of using
IEEE 802.1AB Link Layer Discovery Protocol (LLDP) with a IEEE 802.1aq Shortest
Path Bridging (SPB) network to automatically attach network devices not
supporting IEEE 802.1ah to individual services in a SPB network. Specifically
this commit adds base LLDP support to OVS along with the LLDP extension
required to support Auto-Attach.

The base LLDP code within this commit is adapted from the open source LLDPD
project headed by Vincent Bernat. This base code is augmented with OVS specific
logic which integrates LLDP into OVS and which extends LLDP to support
Auto-Attach. The required build system changes are included to configure and
build OVS with this new feature.

This is the first of a series of commits. Subsequent commits will be provided
to complete the task of adding Auto-Attach to OVS.

diff --git a/configure.ac b/configure.ac
index 35e884a..7a332ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -63,6 +63,7 @@ OVS_CHECK_DOT
OVS_CHECK_IF_PACKET
OVS_CHECK_IF_DL
OVS_CHECK_STRTOK_R
+OVS_CHECK_AUTO_ATTACH
AC_CHECK_DECLS([sys_siglist], [], [], [[#include <signal.h>]])
AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec],
   [], [], [[#include <sys/stat.h>]])
diff --git a/lib/aa-structs.h b/lib/aa-structs.h
new file mode 100644
index 0000000..216b6eb
--- /dev/null
+++ b/lib/aa-structs.h
@@ -0,0 +1,46 @@
+/* aa-structs.h */
+/* contains tlv structures for various auto attach functionality */
+
+/* Copyright (c) 2014 Avaya, Inc
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef AA_STRUCTS_H
+#define AA_STRUCTS_H
+
+struct lldp_aa_element_system_id {
+    u_int8_t       system_mac[6];
+    unsigned short conn_type:4;
+    unsigned short smlt_id:12;
+    u_int8_t       mlt_id[2];
+};
+
+struct lldpd_aa_element_tlv {
+    unsigned short                      type:4;
+    unsigned short                      mgmt_vlan:12;
+    struct lldp_aa_element_system_id system_id;
+};
+
+struct lldpd_aa_isid_vlan_map_data {
+    unsigned short status:4;
+    unsigned short vlan:12;
+    u_int8_t       isid[3];
+};
+
+struct lldpd_aa_isid_vlan_maps_tlv {
+    TAILQ_ENTRY(lldpd_aa_isid_vlan_maps_tlv) m_entries;
+    struct lldpd_aa_isid_vlan_map_data       isid_vlan_data;
+};
+
+#endif
diff --git a/lib/automake.mk b/lib/automake.mk
index b14a510..9999439 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -279,6 +279,28 @@ lib_libopenvswitch_la_SOURCES += \
    lib/stream-unix.c
endif

+if ENABLE_AUTO_ATTACH
+lib_lldp_SOURCES = \
+    lib/ovs-lldp.c \
+    lib/ovs-lldp.h \
+    lib/lldpd.c \
+    lib/lldpd-structs.c \
+    lib/lldpd-structs.h \
+    lib/aa-structs.h \
+    lib/lldp.c \
+    lib/lldpd.h \
+    lib/lldp-frame.c \
+    lib/lldp-frame.h \
+    lib/lldp-const.h \
+    lib/lldp-tlv.h \
+    lib/marshal.h \
+    lib/queue.h
+
+lib_libopenvswitch_la_SOURCES += $(lib_lldp_SOURCES)
+endif
+
+
+
EXTRA_DIST += \
    lib/stdio.h.in \
    lib/string.h.in
diff --git a/lib/lldp-const.h b/lib/lldp-const.h
new file mode 100644
index 0000000..7181f12
--- /dev/null
+++ b/lib/lldp-const.h
@@ -0,0 +1,229 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat at luffy.cx<mailto:bernat at luffy.cx>>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LLDP_H
+#define _LLDP_H
+
+/* Definitions prefixed by `LLDP_` are constants from LLDP
+ * specifications. Definitions prefixed by `LLDPD_` are custom
+ * constants that are useful in the context of lldpd and its clients.
+ */
+
+/* Chassis ID subtype */
+#define LLDP_CHASSISID_SUBTYPE_CHASSIS    1
+#define LLDP_CHASSISID_SUBTYPE_IFALIAS    2
+#define LLDP_CHASSISID_SUBTYPE_PORT 3
+#define LLDP_CHASSISID_SUBTYPE_LLADDR     4
+#define LLDP_CHASSISID_SUBTYPE_ADDR 5
+#define LLDP_CHASSISID_SUBTYPE_IFNAME     6
+#define LLDP_CHASSISID_SUBTYPE_LOCAL 7
+
+/* Port ID subtype */
+#define LLDP_PORTID_SUBTYPE_UNKNOWN 0
+#define LLDP_PORTID_SUBTYPE_IFALIAS 1
+#define LLDP_PORTID_SUBTYPE_PORT    2
+#define LLDP_PORTID_SUBTYPE_LLADDR  3
+#define LLDP_PORTID_SUBTYPE_ADDR    4
+#define LLDP_PORTID_SUBTYPE_IFNAME  5
+#define LLDP_PORTID_SUBTYPE_AGENTCID 6
+#define LLDP_PORTID_SUBTYPE_LOCAL   7
+#define LLDP_PORTID_SUBTYPE_MAX           LLDP_PORTID_SUBTYPE_LOCAL
+
+/* Operational MAU Type field, from RFC 3636 */
+#define LLDP_DOT3_MAU_AUI      1
+#define LLDP_DOT3_MAU_10BASE5       2
+#define LLDP_DOT3_MAU_FOIRL         3
+#define LLDP_DOT3_MAU_10BASE2       4
+#define LLDP_DOT3_MAU_10BASET       5
+#define LLDP_DOT3_MAU_10BASEFP      6
+#define LLDP_DOT3_MAU_10BASEFB      7
+#define LLDP_DOT3_MAU_10BASEFL      8
+#define LLDP_DOT3_MAU_10BROAD36           9
+#define LLDP_DOT3_MAU_10BASETHD           10
+#define LLDP_DOT3_MAU_10BASETFD           11
+#define LLDP_DOT3_MAU_10BASEFLHD    12
+#define LLDP_DOT3_MAU_10BASEFLFD    13
+#define LLDP_DOT3_MAU_10BASET4      14
+#define LLDP_DOT3_MAU_100BASETXHD   15
+#define LLDP_DOT3_MAU_100BASETXFD   16
+#define LLDP_DOT3_MAU_100BASEFXHD   17
+#define LLDP_DOT3_MAU_100BASEFXFD   18
+#define LLDP_DOT3_MAU_100BASET2HD   19
+#define LLDP_DOT3_MAU_100BASET2FD   20
+#define LLDP_DOT3_MAU_1000BASEXHD   21
+#define LLDP_DOT3_MAU_1000BASEXFD   22
+#define LLDP_DOT3_MAU_1000BASELXHD  23
+#define LLDP_DOT3_MAU_1000BASELXFD  24
+#define LLDP_DOT3_MAU_1000BASESXHD  25
+#define LLDP_DOT3_MAU_1000BASESXFD  26
+#define LLDP_DOT3_MAU_1000BASECXHD  27
+#define LLDP_DOT3_MAU_1000BASECXFD  28
+#define LLDP_DOT3_MAU_1000BASETHD   29
+#define LLDP_DOT3_MAU_1000BASETFD   30
+#define LLDP_DOT3_MAU_10GIGBASEX    31
+#define LLDP_DOT3_MAU_10GIGBASELX4  32
+#define LLDP_DOT3_MAU_10GIGBASER    33
+#define LLDP_DOT3_MAU_10GIGBASEER   34
+#define LLDP_DOT3_MAU_10GIGBASELR   35
+#define LLDP_DOT3_MAU_10GIGBASESR   36
+#define LLDP_DOT3_MAU_10GIGBASEW    37
+#define LLDP_DOT3_MAU_10GIGBASEEW   38
+#define LLDP_DOT3_MAU_10GIGBASELW   39
+#define LLDP_DOT3_MAU_10GIGBASESW   40
+
+/* Dot3 Power Devicetype */
+#define LLDP_DOT3_POWER_PSE    1
+#define LLDP_DOT3_POWER_PD     2
+
+/* Dot3 Power Pairs (RFC 3621) */
+#define LLDP_DOT3_POWERPAIRS_SIGNAL 1
+#define LLDP_DOT3_POWERPAIRS_SPARE  2
+
+/* Dot3 Power type (for 802.3at) */
+#define LLDP_DOT3_POWER_8023AT_OFF  0
+#define LLDP_DOT3_POWER_8023AT_TYPE1 1
+#define LLDP_DOT3_POWER_8023AT_TYPE2 2
+
+/* Dot3 power source */
+#define LLDP_DOT3_POWER_SOURCE_UNKNOWN    0
+#define LLDP_DOT3_POWER_SOURCE_PRIMARY    1
+#define LLDP_DOT3_POWER_SOURCE_PSE  1
+#define LLDP_DOT3_POWER_SOURCE_BACKUP     2
+#define LLDP_DOT3_POWER_SOURCE_LOCAL 2
+#define LLDP_DOT3_POWER_SOURCE_BOTH 3
+
+/* Dot3 power priority */
+#define LLDP_DOT3_POWER_PRIO_UNKNOWN 0
+#define LLDP_DOT3_POWER_PRIO_CRITICAL     1
+#define LLDP_DOT3_POWER_PRIO_HIGH   2
+#define LLDP_DOT3_POWER_PRIO_LOW    3
+
+/* PMD Auto-Negotiation Advertised Capability field, from RFC 3636 */
+#define LLDP_DOT3_LINK_AUTONEG_OTHER      0x8000
+#define LLDP_DOT3_LINK_AUTONEG_10BASE_T        0x4000
+#define LLDP_DOT3_LINK_AUTONEG_10BASET_FD 0x2000
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_T4 0x1000
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_TX 0x0800
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD    0x0400
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_T2 0x0200
+#define LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD    0x0100
+#define LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE  0x0080
+#define LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE 0x0040
+#define LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE 0x0020
+#define LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE 0x0010
+#define LLDP_DOT3_LINK_AUTONEG_1000BASE_X 0x0008
+#define LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD    0x0004
+#define LLDP_DOT3_LINK_AUTONEG_1000BASE_T 0x0002
+#define LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD    0x0001
+
+/* Capabilities */
+#define LLDP_CAP_OTHER         0x01
+#define LLDP_CAP_REPEATER 0x02
+#define LLDP_CAP_BRIDGE        0x04
+#define LLDP_CAP_WLAN          0x08
+#define LLDP_CAP_ROUTER        0x10
+#define LLDP_CAP_TELEPHONE     0x20
+#define LLDP_CAP_DOCSIS        0x40
+#define LLDP_CAP_STATION  0x80
+
+#define LLDP_PPVID_CAP_SUPPORTED    (1 << 1)
+#define LLDP_PPVID_CAP_ENABLED      (1 << 2)
+
+/* see http://www.iana.org/assignments/address-family-numbers */
+#define LLDP_MGMT_ADDR_NONE    0
+#define LLDP_MGMT_ADDR_IP4     1
+#define LLDP_MGMT_ADDR_IP6     2
+
+#define LLDP_MGMT_IFACE_UNKNOWN 1
+#define LLDP_MGMT_IFACE_IFINDEX 2
+#define LLDP_MGMT_IFACE_SYSPORT     3
+
+#define LLDP_MED_CLASS_I  1
+#define LLDP_MED_CLASS_II 2
+#define LLDP_MED_CLASS_III     3
+#define LLDP_MED_NETWORK_DEVICE     4
+
+/* LLDP MED application ttpes */
+#define LLDP_MED_APPTYPE_UNDEFINED        0
+#define LLDP_MED_APPTYPE_VOICE            1
+#define LLDP_MED_APPTYPE_VOICESIGNAL      2
+#define LLDP_MED_APPTYPE_GUESTVOICE       3
+#define LLDP_MED_APPTYPE_GUESTVOICESIGNAL 4
+#define LLDP_MED_APPTYPE_SOFTPHONEVOICE        5
+#define LLDP_MED_APPTYPE_VIDEOCONFERENCE  6
+#define LLDP_MED_APPTYPE_VIDEOSTREAM      7
+#define LLDP_MED_APPTYPE_VIDEOSIGNAL      8
+#define LLDP_MED_APPTYPE_LAST             LLDP_MED_APPTYPE_VIDEOSIGNAL
+
+/* LLDP MED location formats */
+#define LLDP_MED_LOCFORMAT_COORD    1
+#define LLDP_MED_LOCFORMAT_CIVIC    2
+#define LLDP_MED_LOCFORMAT_ELIN           3
+#define LLDP_MED_LOCFORMAT_LAST           LLDP_MED_LOCFORMAT_ELIN
+
+#define LLDP_MED_LOCATION_GEOID_WGS84          1
+#define LLDP_MED_LOCATION_GEOID_NAD83          2
+#define LLDP_MED_LOCATION_GEOID_NAD83_MLLW     3
+
+#define LLDP_MED_LOCATION_ALTITUDE_UNIT_METER  1
+#define LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR  2
+
+/* LLDP MED power related constants */
+#define LLDP_MED_POW_TYPE_PSE       1
+#define LLDP_MED_POW_TYPE_PD        2
+#define LLDP_MED_POW_TYPE_RESERVED  3
+
+#define LLDP_MED_POW_SOURCE_UNKNOWN 1
+#define LLDP_MED_POW_SOURCE_PRIMARY 2
+#define LLDP_MED_POW_SOURCE_BACKUP  3
+#define LLDP_MED_POW_SOURCE_RESERVED 4
+#define LLDP_MED_POW_SOURCE_PSE           5
+#define LLDP_MED_POW_SOURCE_LOCAL   6
+#define LLDP_MED_POW_SOURCE_BOTH    7
+
+#define LLDP_MED_POW_PRIO_UNKNOWN   0
+#define LLDP_MED_POW_PRIO_CRITICAL  1
+#define LLDP_MED_POW_PRIO_HIGH      2
+#define LLDP_MED_POW_PRIO_LOW       3
+
+/* LLDP MED capabilities */
+#define LLDP_MED_CAP_CAP  0x01
+#define LLDP_MED_CAP_POLICY    0x02
+#define LLDP_MED_CAP_LOCATION  0x04
+#define LLDP_MED_CAP_MDI_PSE   0x08
+#define LLDP_MED_CAP_MDI_PD    0x10
+#define LLDP_MED_CAP_IV        0x20
+
+/* Protocol constants for multi-protocol lldpd */
+#define LLDPD_MODE_LLDP        1
+#define LLDPD_MODE_CDPV1  2
+#define LLDPD_MODE_CDPV2  3
+#define LLDPD_MODE_SONMP  4
+#define LLDPD_MODE_EDP         5
+#define LLDPD_MODE_FDP         6
+#define LLDPD_MODE_MAX         LLDPD_MODE_FDP
+
+
+/* Bond slave src mac type constants */
+#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN   0
+#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_REAL 1
+#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO 2
+#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED     3
+#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED 4
+#define LLDP_BOND_SLAVE_SRC_MAC_TYPE_MAX LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED
+
+#endif /* _LLDP_H */
diff --git a/lib/lldp-frame.c b/lib/lldp-frame.c
new file mode 100644
index 0000000..b86d7aa
--- /dev/null
+++ b/lib/lldp-frame.c
@@ -0,0 +1,67 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2009 Vincent Bernat <bernat at luffy.cx<mailto:bernat at luffy.cx>>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include "lldpd.h"
+
+u_int16_t
+frame_checksum(const u_char *cp, int len, int cisco)
+{
+    unsigned int sum = 0, v = 0;
+    int oddbyte = 0;
+
+    /* We compute in network byte order */
+    while ((len -= 2) >= 0) {
+        sum += *cp++ << 8;
+        sum += *cp++;
+    }
+    if ((oddbyte = len & 1) != 0)
+        v = *cp;
+
+    /* The remaining byte seems to be handled oddly by Cisco. From function
+     * dissect_cdp() in wireshark. 2014/6/14,zhengy at yealink.com:
+     *
+     * CDP doesn't adhere to RFC 1071 section 2. (B). It incorrectly assumes
+     * checksums are calculated on a big endian platform, therefore i.s.o.
+     * padding odd sized data with a zero byte _at the end_ it sets the last
+     * big endian _word_ to contain the last network _octet_. This byteswap
+     * has to be done on the last octet of network data before feeding it to
+     * the Internet checksum routine.
+     * CDP checksumming code has a bug in the addition of this last _word_
+     * as a signed number into the long word intermediate checksum. When
+     * reducing this long to word size checksum an off-by-one error can be
+     * made. This off-by-one error is compensated for in the last _word_ of
+     * the network data.
+     */
+    if (oddbyte) {
+        if (cisco) {
+            if (v & 0x80) {
+                sum += 0xff << 8;
+                sum += v - 1;
+            } else {
+                sum += v;
+            }
+        } else {
+            sum += v << 8;
+        }
+    }
+
+    sum = (sum >> 16) + (sum & 0xffff);
+    sum += sum >> 16;
+    sum = ntohs(sum);
+    return (0xffff & ~sum);
+}
diff --git a/lib/lldp-frame.h b/lib/lldp-frame.h
new file mode 100644
index 0000000..de4adbb
--- /dev/null
+++ b/lib/lldp-frame.h
@@ -0,0 +1,137 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2009 Vincent Bernat <bernat at luffy.cx<mailto:bernat at luffy.cx>>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRAME_H
+#define _FRAME_H
+
+static union {
+    uint8_t f_uint8;
+    uint16_t f_uint16;
+    uint32_t f_uint32;
+} types;
+
+/* This set of macro are used to build packets. The current position in buffer
+ * is `pos'. The length of the remaining space in buffer is `length'. `type'
+ * should be a member of `types'. This was stolen from ladvd which was adapted
+ * from Net::CDP. */
+
+#define POKE(value, type, func)              \
+    ((length >= sizeof(type)) &&          \
+        (                      \
+            type = func(value),          \
+            memcpy(pos, &type, sizeof(type)), \
+            length -= sizeof(type),          \
+            pos += sizeof(type),          \
+            1                  \
+        ) \
+    )
+#define POKE_UINT8(value) POKE(value, types.f_uint8, )
+#define POKE_UINT16(value) POKE(value, types.f_uint16, htons)
+#define POKE_UINT32(value) POKE(value, types.f_uint32, htonl)
+#define POKE_BYTES(value, bytes)                   \
+    ((length >= (bytes)) &&                       \
+        (                               \
+            memcpy(pos, value, bytes),               \
+            length -= (bytes),                   \
+            pos += (bytes),                       \
+            1                           \
+        ) \
+    )
+#define POKE_SAVE(where)            \
+    (where = pos, 1)
+#define POKE_RESTORE(where)            \
+    do {                    \
+        if ((where) > pos)            \
+            length -= ((where) - pos);    \
+        else                    \
+            length += (pos - (where));    \
+        pos = (where);                \
+    } while(0)
+
+/* This set of macro are used to parse packets. The same variable as for POKE_*
+ * are used. There is no check on boundaries. */
+
+#define PEEK(type, func)                \
+    (                        \
+        memcpy(&type, pos, sizeof(type)),    \
+        length -= sizeof(type),            \
+        pos += sizeof(type),            \
+        func(type)                \
+    )
+#define PEEK_UINT8 PEEK(types.f_uint8, )
+#define PEEK_UINT16 PEEK(types.f_uint16, ntohs)
+#define PEEK_UINT32 PEEK(types.f_uint32, ntohl)
+#define PEEK_BYTES(value, bytes)                   \
+    do {                               \
+        memcpy(value, pos, bytes);               \
+        length -= (bytes);                   \
+        pos += (bytes);                       \
+    } while (0)
+#define PEEK_DISCARD(bytes)            \
+    do {                    \
+        length -= (bytes);        \
+        pos += (bytes);            \
+    } while (0)
+#define PEEK_DISCARD_UINT8 PEEK_DISCARD(1)
+#define PEEK_DISCARD_UINT16 PEEK_DISCARD(2)
+#define PEEK_DISCARD_UINT32 PEEK_DISCARD(3)
+#define PEEK_CMP(value, bytes)            \
+     (length -= (bytes),            \
+     pos += (bytes),            \
+     memcmp(pos-bytes, value, bytes))
+#define PEEK_SAVE POKE_SAVE
+#define PEEK_RESTORE POKE_RESTORE
+
+/* LLDP specific. We need a `tlv' pointer. */
+#define POKE_START_LLDP_TLV(type)  \
+    (               \
+        tlv = pos,           \
+        POKE_UINT16(type << 9)       \
+    )
+#define POKE_END_LLDP_TLV                       \
+    (                               \
+        memcpy(&types.f_uint16, tlv, sizeof(uint16_t)),           \
+        types.f_uint16 |= htons((pos - (tlv + 2)) & 0x01ff),    \
+        memcpy(tlv, &types.f_uint16, sizeof(uint16_t)),           \
+        1                               \
+    )
+
+/* Same for CDP */
+#define POKE_START_CDP_TLV(type)  \
+    (               \
+        (void)POKE_UINT16(type),  \
+        tlv = pos,           \
+        POKE_UINT16(0)           \
+    )
+#define POKE_END_CDP_TLV                       \
+    (                               \
+        types.f_uint16 = htons(pos - tlv + 2),               \
+        memcpy(tlv, &types.f_uint16, sizeof(uint16_t)),           \
+        1                               \
+    )
+
+/* Same for EDP */
+#define POKE_START_EDP_TLV(type)       \
+    (                   \
+        (void)POKE_UINT8(EDP_TLV_MARKER), \
+        (void)POKE_UINT8(type),       \
+        tlv = pos,               \
+        POKE_UINT16(0)               \
+    )
+#define POKE_END_EDP_TLV POKE_END_CDP_TLV
+
+#endif /* _FRAME_H */
diff --git a/lib/lldp-tlv.h b/lib/lldp-tlv.h
new file mode 100644
index 0000000..4249d58
--- /dev/null
+++ b/lib/lldp-tlv.h
@@ -0,0 +1,83 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat at luffy.cx<mailto:bernat at luffy.cx>>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LLDP_TLV_H
+#define _LLDP_TLV_H
+
+
+#define LLDP_MULTICAST_ADDR    {        \
+    0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e  \
+}
+
+#define LLDP_TLV_END            0
+#define LLDP_TLV_CHASSIS_ID     1
+#define LLDP_TLV_PORT_ID        2
+#define LLDP_TLV_TTL            3
+#define LLDP_TLV_PORT_DESCR     4
+#define LLDP_TLV_SYSTEM_NAME    5
+#define LLDP_TLV_SYSTEM_DESCR   6
+#define LLDP_TLV_SYSTEM_CAP     7
+#define LLDP_TLV_MGMT_ADDR      8
+#define LLDP_TLV_ORG            127
+
+#define LLDP_TLV_ORG_DOT1       {0x00, 0x80, 0xc2}
+#define LLDP_TLV_ORG_DOT3       {0x00, 0x12, 0x0f}
+#define LLDP_TLV_ORG_MED        {0x00, 0x12, 0xbb}
+#define LLDP_TLV_ORG_AVAYA      {0x00, 0x40, 0x0D}
+#define LLDP_TLV_ORG_DCBX       {0x00, 0x1b, 0x21}
+
+#define LLDP_TLV_DOT1_PVID      1
+#define LLDP_TLV_DOT1_PPVID     2
+#define LLDP_TLV_DOT1_VLANNAME  3
+#define LLDP_TLV_DOT1_PI        4
+
+#define LLDP_TLV_DOT3_MAC       1
+#define LLDP_TLV_DOT3_POWER     2
+#define LLDP_TLV_DOT3_LA        3
+#define LLDP_TLV_DOT3_MFS       4
+
+#define LLDP_TLV_MED_CAP        1
+#define LLDP_TLV_MED_POLICY     2
+#define LLDP_TLV_MED_LOCATION   3
+#define LLDP_TLV_MED_MDI        4
+#define LLDP_TLV_MED_IV_HW      5
+#define LLDP_TLV_MED_IV_FW      6
+#define LLDP_TLV_MED_IV_SW      7
+#define LLDP_TLV_MED_IV_SN      8
+#define LLDP_TLV_MED_IV_MANUF   9
+#define LLDP_TLV_MED_IV_MODEL   10
+#define LLDP_TLV_MED_IV_ASSET   11
+
+#endif
+
+#ifdef ENABLE_AUTO_ATTACH
+#define LLDP_TLV_AA_ELEMENT_SUBTYPE           0x08
+#define LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE   0x09
+#define LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH   32
+#define LLDP_TLV_AA_ELEM_TYPE_UNKNOWN         1
+#define LLDP_TLV_AA_ELEM_TYPE_SERVER          2
+#define LLDP_TLV_AA_ELEM_TYPE_PROXY           3
+#define LLDP_TLV_AA_ELEM_TYPE_UNTAG_CLIENT    4
+#define LLDP_TLV_AA_ELEM_TYPE_TAG_CLIENT      5
+#define LLDP_TLV_AA_ELEM_TYPE_SERV_NO_AUTH    6
+#define LLDP_TLV_AA_ELEM_TYPE_PROXY_NO_AUTH   7
+#define LLDP_TLV_AA_ELEM_CONN_TYPE_SINGLE     0
+#define LLDP_TLV_AA_ELEM_CONN_TYPE_MLT        1
+#define LLDP_TLV_AA_ELEM_CONN_TYPE_SLT        2
+#define LLDP_TLV_AA_ELEM_CONN_TYPE_SMLT       3
+
+#endif
diff --git a/lib/lldp.c b/lib/lldp.c
new file mode 100644
index 0000000..3d40f42
--- /dev/null
+++ b/lib/lldp.c
@@ -0,0 +1,1259 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat at luffy.cx<mailto:bernat at luffy.cx>>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <config.h>
+#include "lldpd.h"
+#ifndef ENABLE_AUTO_ATTACH
+#include "frame.h"
+#else
+#include "lldp-frame.h"
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#ifdef ENABLE_AUTO_ATTACH
+#include "compiler.h"
+
+VLOG_DEFINE_THIS_MODULE(lldp);
+#endif
+
+inline static int
+lldpd_af_to_lldp_proto(int af)
+{
+    switch (af) {
+    case LLDPD_AF_IPV4:
+        return LLDP_MGMT_ADDR_IP4;
+    case LLDPD_AF_IPV6:
+        return LLDP_MGMT_ADDR_IP6;
+    default:
+        return LLDP_MGMT_ADDR_NONE;
+    }
+}
+
+inline static int
+lldpd_af_from_lldp_proto(int proto)
+{
+    switch (proto) {
+    case LLDP_MGMT_ADDR_IP4:
+        return LLDPD_AF_IPV4;
+    case LLDP_MGMT_ADDR_IP6:
+        return LLDPD_AF_IPV6;
+    default:
+        return LLDPD_AF_UNSPEC;
+    }
+}
+
+#ifndef ENABLE_AUTO_ATTACH
+int
+lldp_send(struct lldpd *global,
+          struct lldpd_hardware *hardware)
+#else
+int
+lldp_send(struct lldpd *global OVS_UNUSED,
+          struct lldpd_hardware *hardware,
+          u_int8_t *p)
+#endif
+{
+    struct lldpd_port *port;
+    struct lldpd_chassis *chassis;
+    struct lldpd_frame *frame;
+    int length;
+    u_int8_t *packet, *pos, *tlv;
+    struct lldpd_mgmt *mgmt;
+    int proto;
+
+#ifndef ENABLE_AUTO_ATTACH
+    u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR;
+#endif
+#ifdef ENABLE_DOT1
+    const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1;
+    struct lldpd_vlan *vlan;
+    struct lldpd_ppvid *ppvid;
+    struct lldpd_pi *pi;
+#endif
+#ifdef ENABLE_DOT3
+    const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3;
+#endif
+#ifdef ENABLE_LLDPMED
+    int i;
+    const u_int8_t med[] = LLDP_TLV_ORG_MED;
+#endif
+#ifdef ENABLE_AUTO_ATTACH
+    const u_int8_t avaya[] = LLDP_TLV_ORG_AVAYA;
+    struct lldpd_aa_isid_vlan_maps_tlv *vlan_isid_map;
+    u_int8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH];
+#endif
+
+    log_debug("lldp", "send LLDP PDU to %s mtu=%d",
+        hardware->h_ifname,hardware->h_mtu);
+
+    port = &hardware->h_lport;
+    chassis = port->p_chassis;
+#ifndef ENABLE_AUTO_ATTACH
+    length = hardware->h_mtu;
+    if ((packet = (u_int8_t*)calloc(1, length)) == NULL)
+        return ENOMEM;
+#else
+
+        /* The ethernet header is filled in elsewhere, we must save room for it. */
+    length = hardware->h_mtu-sizeof(struct ether_header);
+         packet = p;
+    log_debug("lldp", "LLDP PDU send to %s mtu %d incoming with ptr=%p",
+                          hardware->h_ifname, hardware->h_mtu, packet);
+#endif
+    pos = packet;
+
+
+#ifndef ENABLE_AUTO_ATTACH
+    /* If auto attach is enabled, we must construct our own Ethernet header
+     * and add it to the packet. */
+    if (!(
+        /* First comes the LLDP multicast address. */
+        POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
+        /* Next, the source MAC address is added. */
+        POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
+        /* Finally, the LLDP frame type is added. */
+        POKE_UINT16(ETHERTYPE_LLDP)))
+        goto toobig;
+#endif
+    /* Chassis ID */
+    if (!(
+        POKE_START_LLDP_TLV(LLDP_TLV_CHASSIS_ID) &&
+        POKE_UINT8(chassis->c_id_subtype) &&
+        POKE_BYTES(chassis->c_id, chassis->c_id_len) &&
+        POKE_END_LLDP_TLV))
+        goto toobig;
+
+    /* Port ID */
+    if (!(
+        POKE_START_LLDP_TLV(LLDP_TLV_PORT_ID) &&
+        POKE_UINT8(port->p_id_subtype) &&
+        POKE_BYTES(port->p_id, port->p_id_len) &&
+        POKE_END_LLDP_TLV))
+        goto toobig;
+
+    /* Time to live */
+    if (!(
+        POKE_START_LLDP_TLV(LLDP_TLV_TTL) &&
+        POKE_UINT16(chassis->c_ttl) &&
+        POKE_END_LLDP_TLV))
+        goto toobig;
+
+    /* System name */
+    if (chassis->c_name && *chassis->c_name != '\0') {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_NAME) &&
+            POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+
+    /* System description (skip it if empty) */
+    if (chassis->c_descr && *chassis->c_descr != '\0') {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_DESCR) &&
+            POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+
+    /* System capabilities */
+    if (!(
+        POKE_START_LLDP_TLV(LLDP_TLV_SYSTEM_CAP) &&
+        POKE_UINT16(chassis->c_cap_available) &&
+        POKE_UINT16(chassis->c_cap_enabled) &&
+        POKE_END_LLDP_TLV))
+        goto toobig;
+
+    /* Management addresses */
+    TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) {
+        proto = lldpd_af_to_lldp_proto(mgmt->m_family);
+#ifndef ENABLE_AUTO_ATTACH
+        assert(proto != LLDP_MGMT_ADDR_NONE);
+#endif
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) &&
+            /* Size of the address, including its type */
+            POKE_UINT8(mgmt->m_addrsize + 1) &&
+            POKE_UINT8(proto) &&
+            POKE_BYTES(&mgmt->m_addr, mgmt->m_addrsize)))
+            goto toobig;
+
+        /* Interface port type, OID */
+        if (mgmt->m_iface == 0) {
+            if (!(
+                /* We don't know the management interface */
+                POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) &&
+                POKE_UINT32(0)))
+                goto toobig;
+        } else {
+            if (!(
+                /* We have the index of the management interface */
+                POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) &&
+                POKE_UINT32(mgmt->m_iface)))
+                goto toobig;
+        }
+        if (!(
+            /* We don't provide an OID for management */
+            POKE_UINT8(0) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+
+    /* Port description */
+    if (port->p_descr && *port->p_descr != '\0') {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_PORT_DESCR) &&
+            POKE_BYTES(port->p_descr, strlen(port->p_descr)) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+
+#ifdef ENABLE_DOT1
+    /* Port VLAN ID */
+    if(port->p_pvid != 0) {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+            POKE_BYTES(dot1, sizeof(dot1)) &&
+            POKE_UINT8(LLDP_TLV_DOT1_PVID) &&
+            POKE_UINT16(port->p_pvid) &&
+            POKE_END_LLDP_TLV)) {
+            goto toobig;
+        }
+    }
+    /* Port and Protocol VLAN IDs */
+    TAILQ_FOREACH(ppvid, &port->p_ppvids, p_entries) {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+            POKE_BYTES(dot1, sizeof(dot1)) &&
+            POKE_UINT8(LLDP_TLV_DOT1_PPVID) &&
+            POKE_UINT8(ppvid->p_cap_status) &&
+            POKE_UINT16(ppvid->p_ppvid) &&
+            POKE_END_LLDP_TLV)) {
+            goto toobig;
+        }
+    }
+    /* VLANs */
+    TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+            POKE_BYTES(dot1, sizeof(dot1)) &&
+            POKE_UINT8(LLDP_TLV_DOT1_VLANNAME) &&
+            POKE_UINT16(vlan->v_vid) &&
+            POKE_UINT8(strlen(vlan->v_name)) &&
+            POKE_BYTES(vlan->v_name, strlen(vlan->v_name)) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+    /* Protocol Identities */
+    TAILQ_FOREACH(pi, &port->p_pids, p_entries) {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+            POKE_BYTES(dot1, sizeof(dot1)) &&
+            POKE_UINT8(LLDP_TLV_DOT1_PI) &&
+            POKE_UINT8(pi->p_pi_len) &&
+            POKE_BYTES(pi->p_pi, pi->p_pi_len) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+#endif
+
+#ifdef ENABLE_DOT3
+    /* Aggregation status */
+    if (!(
+        POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+        POKE_BYTES(dot3, sizeof(dot3)) &&
+        POKE_UINT8(LLDP_TLV_DOT3_LA) &&
+        /* Bit 0 = capability ; Bit 1 = status */
+        POKE_UINT8((port->p_aggregid) ? 3:1) &&
+        POKE_UINT32(port->p_aggregid) &&
+        POKE_END_LLDP_TLV))
+        goto toobig;
+
+    /* MAC/PHY */
+    if (!(
+        POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+        POKE_BYTES(dot3, sizeof(dot3)) &&
+        POKE_UINT8(LLDP_TLV_DOT3_MAC) &&
+        POKE_UINT8(port->p_macphy.autoneg_support |
+           (port->p_macphy.autoneg_enabled << 1)) &&
+        POKE_UINT16(port->p_macphy.autoneg_advertised) &&
+        POKE_UINT16(port->p_macphy.mau_type) &&
+        POKE_END_LLDP_TLV))
+        goto toobig;
+
+    /* MFS */
+    if (port->p_mfs) {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+            POKE_BYTES(dot3, sizeof(dot3)) &&
+            POKE_UINT8(LLDP_TLV_DOT3_MFS) &&
+            POKE_UINT16(port->p_mfs) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+    /* Power */
+    if (port->p_power.devicetype) {
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+            POKE_BYTES(dot3, sizeof(dot3)) &&
+            POKE_UINT8(LLDP_TLV_DOT3_POWER) &&
+            POKE_UINT8((
+                (((2 - port->p_power.devicetype)    %(1<< 1))<<0) |
+                (( port->p_power.supported          %(1<< 1))<<1) |
+                (( port->p_power.enabled            %(1<< 1))<<2) |
+                (( port->p_power.paircontrol        %(1<< 1))<<3))) &&
+            POKE_UINT8(port->p_power.pairs) &&
+            POKE_UINT8(port->p_power.class)))
+            goto toobig;
+        /* 802.3at */
+        if (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) {
+            if (!(
+                POKE_UINT8((
+                    (((port->p_power.powertype ==
+                        LLDP_DOT3_POWER_8023AT_TYPE1)?1:0) << 7) |
+                     (((port->p_power.devicetype ==
+                        LLDP_DOT3_POWER_PSE)?0:1) << 6) |
+                     ((port->p_power.source   %(1<< 2))<<4) |
+                     ((port->p_power.priority %(1<< 2))<<0))) &&
+                  POKE_UINT16(port->p_power.requested) &&
+                  POKE_UINT16(port->p_power.allocated)))
+                goto toobig;
+        }
+        if (!(POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+#endif
+
+#ifdef ENABLE_LLDPMED
+    if (port->p_med_cap_enabled) {
+        /* LLDP-MED cap */
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+            POKE_BYTES(med, sizeof(med)) &&
+            POKE_UINT8(LLDP_TLV_MED_CAP) &&
+            POKE_UINT16(chassis->c_med_cap_available) &&
+            POKE_UINT8(chassis->c_med_type) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+
+        /* LLDP-MED inventory */
+#define LLDP_INVENTORY(value, subtype)  \
+        if (value) {    \
+            if (!(  \
+              POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&  \
+              POKE_BYTES(med, sizeof(med)) &&   \
+              POKE_UINT8(subtype) &&    \
+              POKE_BYTES(value,     \
+                (strlen(value)>32)?32:strlen(value)) && \
+              POKE_END_LLDP_TLV))   \
+                goto toobig;    \
+        }
+
+        if (port->p_med_cap_enabled & LLDP_MED_CAP_IV) {
+            LLDP_INVENTORY(chassis->c_med_hw,
+                LLDP_TLV_MED_IV_HW);
+            LLDP_INVENTORY(chassis->c_med_fw,
+                LLDP_TLV_MED_IV_FW);
+            LLDP_INVENTORY(chassis->c_med_sw,
+                LLDP_TLV_MED_IV_SW);
+            LLDP_INVENTORY(chassis->c_med_sn,
+                LLDP_TLV_MED_IV_SN);
+            LLDP_INVENTORY(chassis->c_med_manuf,
+                LLDP_TLV_MED_IV_MANUF);
+            LLDP_INVENTORY(chassis->c_med_model,
+                LLDP_TLV_MED_IV_MODEL);
+            LLDP_INVENTORY(chassis->c_med_asset,
+                LLDP_TLV_MED_IV_ASSET);
+        }
+
+        /* LLDP-MED location */
+        for (i = 0; i < LLDP_MED_LOCFORMAT_LAST; i++) {
+            if (port->p_med_location[i].format == i + 1) {
+                if (!(
+                    POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+                    POKE_BYTES(med, sizeof(med)) &&
+                    POKE_UINT8(LLDP_TLV_MED_LOCATION) &&
+                    POKE_UINT8(port->p_med_location[i].format) &&
+                    POKE_BYTES(port->p_med_location[i].data,
+                    port->p_med_location[i].data_len) &&
+                    POKE_END_LLDP_TLV))
+                    goto toobig;
+            }
+        }
+
+        /* LLDP-MED network policy */
+        for (i = 0; i < LLDP_MED_APPTYPE_LAST; i++) {
+            if (port->p_med_policy[i].type == i + 1) {
+                if (!(
+                    POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+                    POKE_BYTES(med, sizeof(med)) &&
+                    POKE_UINT8(LLDP_TLV_MED_POLICY) &&
+                    POKE_UINT32((
+                    ((port->p_med_policy[i].type     %(1<< 8))<<24) |
+                    ((port->p_med_policy[i].unknown  %(1<< 1))<<23) |
+                    ((port->p_med_policy[i].tagged   %(1<< 1))<<22) |
+                      /*((0                              %(1<< 1))<<21) |*/
+                    ((port->p_med_policy[i].vid      %(1<<12))<< 9) |
+                    ((port->p_med_policy[i].priority %(1<< 3))<< 6) |
+                    ((port->p_med_policy[i].dscp     %(1<< 6))<< 0) )) &&
+                    POKE_END_LLDP_TLV))
+                    goto toobig;
+            }
+        }
+
+        /* LLDP-MED POE-MDI */
+        if ((port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PSE) ||
+            (port->p_med_power.devicetype == LLDP_MED_POW_TYPE_PD)) {
+            int devicetype = 0, source = 0;
+            if (!(
+                  POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+                  POKE_BYTES(med, sizeof(med)) &&
+                  POKE_UINT8(LLDP_TLV_MED_MDI)))
+                goto toobig;
+            switch (port->p_med_power.devicetype) {
+            case LLDP_MED_POW_TYPE_PSE:
+                devicetype = 0;
+                switch (port->p_med_power.source) {
+                case LLDP_MED_POW_SOURCE_PRIMARY: source = 1; break;
+                case LLDP_MED_POW_SOURCE_BACKUP: source = 2; break;
+                case LLDP_MED_POW_SOURCE_RESERVED: source = 3; break;
+                default: source = 0; break;
+                }
+                break;
+            case LLDP_MED_POW_TYPE_PD:
+                devicetype = 1;
+                switch (port->p_med_power.source) {
+                case LLDP_MED_POW_SOURCE_PSE: source = 1; break;
+                case LLDP_MED_POW_SOURCE_LOCAL: source = 2; break;
+                case LLDP_MED_POW_SOURCE_BOTH: source = 3; break;
+                default: source = 0; break;
+                }
+                break;
+            }
+            if (!(
+                  POKE_UINT8((
+                ((devicetype                   %(1<< 2))<<6) |
+                ((source                       %(1<< 2))<<4) |
+                ((port->p_med_power.priority   %(1<< 4))<<0) )) &&
+                  POKE_UINT16(port->p_med_power.val) &&
+                  POKE_END_LLDP_TLV))
+                goto toobig;
+        }
+    }
+#endif
+
+#ifdef ENABLE_AUTO_ATTACH
+    /* Add Auto Attach tlvs to packet */
+        /* AA-ELEMENT */
+    if (port->p_element.type != 0) {
+        u_int8_t aa_element_first_byte;
+        u_int8_t aa_element_second_byte = 0;
+        u_int8_t aa_elem_sys_id_first_byte;
+        u_int8_t aa_elem_sys_id_second_byte;
+        /* .type should be first 4 most significant bits, so bitwise OR that
+         * with the first 4 bits of the 12-bit-wide .mgmt_vlan */
+        aa_element_first_byte = ((port->p_element.type & 0xF) << 4) |
+        ((port->p_element.mgmt_vlan >> 8) & 0xF);
+        /* second byte should just be the remaining 8 bits of .mgmt_vlan
+         * aa_element_second_byte = port->p_element.mgmt_vlan & 0x0FF;
+         * .conn_type should be 4 most sig. bits, so bitwise OR that
+         * with the first 4 bits of the 12-bit-wide .smlt_id */
+        aa_elem_sys_id_first_byte =
+            ((port->p_element.system_id.conn_type & 0xF) << 4) |
+            ((port->p_element.system_id.smlt_id >> 8) & 0xF);
+        /* second byte should just be the remaining 8 bits of .smlt_id */
+        aa_elem_sys_id_second_byte = port->p_element.system_id.smlt_id & 0x0FF;
+
+        if (!(
+            POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+            POKE_BYTES(avaya, sizeof(avaya)) &&
+            POKE_UINT8(LLDP_TLV_AA_ELEMENT_SUBTYPE) &&
+            POKE_UINT8(aa_element_first_byte) &&
+            POKE_UINT8(aa_element_second_byte) &&
+            POKE_BYTES(&port->p_element.system_id.system_mac,
+            sizeof(port->p_element.system_id.system_mac)) &&
+            POKE_UINT8(aa_elem_sys_id_first_byte) &&
+            POKE_UINT8(aa_elem_sys_id_second_byte) &&
+            POKE_BYTES(&port->p_element.system_id.mlt_id,
+            sizeof(port->p_element.system_id.mlt_id)) &&
+            POKE_END_LLDP_TLV))
+            goto toobig;
+    }
+    if ( ! TAILQ_EMPTY(&port->p_isid_vlan_maps) ) {
+        int j;
+        for( j=0; j < LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH; j++ ) {
+        msg_auth_digest[j] = 0;
+        }
+        if (!(
+        POKE_START_LLDP_TLV(LLDP_TLV_ORG) &&
+        POKE_BYTES(avaya, sizeof(avaya)) &&
+        POKE_UINT8(LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE) &&
+          POKE_BYTES(msg_auth_digest,
+              sizeof(msg_auth_digest))
+        ))
+            goto toobig;
+
+        TAILQ_FOREACH(vlan_isid_map, &port->p_isid_vlan_maps, m_entries) {
+        u_int16_t status_vlan_word;
+        status_vlan_word = (vlan_isid_map->isid_vlan_data.status << 12) |
+            vlan_isid_map->isid_vlan_data.vlan;
+        if (!(
+            POKE_UINT16(status_vlan_word) &&
+            POKE_BYTES(&vlan_isid_map->isid_vlan_data.isid,
+              sizeof(vlan_isid_map->isid_vlan_data.isid))
+            ))
+            goto toobig;
+        }
+        if (! (POKE_END_LLDP_TLV) )
+        goto toobig;
+    }
+#endif
+
+    /* END */
+    if (!(
+        POKE_START_LLDP_TLV(LLDP_TLV_END) &&
+        POKE_END_LLDP_TLV))
+        goto toobig;
+
+#ifndef ENABLE_AUTO_ATTACH
+    if (interfaces_send_helper(global, hardware,
+        (char *)packet, pos - packet) == -1) {
+        log_warn("lldp", "unable to send packet on real device for %s",
+            hardware->h_ifname);
+        free(packet);
+        return ENETDOWN;
+    }
+#endif
+
+    hardware->h_tx_cnt++;
+
+    /* We assume that LLDP frame is the reference */
+    if ((frame = (struct lldpd_frame*)
+        malloc(sizeof(int) + pos - packet)) != NULL) {
+        frame->size = pos - packet;
+                length = frame->size;
+        memcpy(&frame->frame, packet, frame->size);
+        if ((hardware->h_lport.p_lastframe == NULL) ||
+            (hardware->h_lport.p_lastframe->size != frame->size) ||
+          (memcmp(hardware->h_lport.p_lastframe->frame, frame->frame,
+            frame->size) != 0)) {
+            free(hardware->h_lport.p_lastframe);
+            hardware->h_lport.p_lastframe = frame;
+            hardware->h_lport.p_lastchange = time(NULL);
+        } else
+            free(frame);
+    }
+
+#ifndef ENABLE_AUTO_ATTACH
+        free(packet);
+        return 0;
+#else
+    return length;
+
+#endif
+toobig:
+    free(packet);
+    return E2BIG;
+}
+
+#define CHECK_TLV_SIZE(x, name)                   \
+    do { if (tlv_size < (x)) {               \
+        log_warnx("lldp", name " TLV too short received on %s",    \
+          hardware->h_ifname);               \
+        goto malformed;                   \
+    } } while (0)
+
+#ifndef ENABLE_AUTO_ATTACH
+int
+lldp_decode(struct lldpd *cfg, char *frame, int s,
+    struct lldpd_hardware *hardware,
+    struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+#else
+int
+lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s,
+    struct lldpd_hardware *hardware,
+    struct lldpd_chassis **newchassis, struct lldpd_port **newport)
+#endif
+{
+    struct lldpd_chassis *chassis;
+    struct lldpd_port *port;
+    const char lldpaddr[] = LLDP_MULTICAST_ADDR;
+    const char dot1[] = LLDP_TLV_ORG_DOT1;
+    const char dot3[] = LLDP_TLV_ORG_DOT3;
+    const char med[] = LLDP_TLV_ORG_MED;
+    const char avaya_oid[] = LLDP_TLV_ORG_AVAYA;
+    const char dcbx[] = LLDP_TLV_ORG_DCBX;
+    char orgid[3];
+    int length, gotend = 0, ttl_received = 0;
+    int tlv_size, tlv_type, tlv_subtype;
+    u_int8_t *pos, *tlv;
+    char *b;
+#ifdef ENABLE_DOT1
+    struct lldpd_vlan *vlan = NULL;
+    int vlan_len;
+    struct lldpd_ppvid *ppvid;
+    struct lldpd_pi *pi = NULL;
+#endif
+#ifdef ENABLE_AUTO_ATTACH
+    struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map = NULL;
+    u_int8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH];
+#endif
+    struct lldpd_mgmt *mgmt;
+    int af;
+    u_int8_t addr_str_length, addr_str_buffer[32];
+    u_int8_t addr_family, addr_length, *addr_ptr, iface_subtype;
+    u_int32_t iface_number, iface;
+
+    log_debug("lldp", "receive LLDP PDU on %s",
+        hardware->h_ifname);
+
+    if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
+        log_warn("lldp", "failed to allocate remote chassis");
+        return -1;
+    }
+    TAILQ_INIT(&chassis->c_mgmt);
+    if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
+        log_warn("lldp", "failed to allocate remote port");
+        free(chassis);
+        return -1;
+    }
+#ifdef ENABLE_DOT1
+    TAILQ_INIT(&port->p_vlans);
+    TAILQ_INIT(&port->p_ppvids);
+    TAILQ_INIT(&port->p_pids);
+#endif
+#ifdef ENABLE_AUTO_ATTACH
+    TAILQ_INIT(&port->p_isid_vlan_maps);
+#endif
+
+    length = s;
+    pos = (u_int8_t*)frame;
+
+    if (length < 2*ETHER_ADDR_LEN + sizeof(u_int16_t)) {
+        log_warnx("lldp", "too short frame received on %s",
+            hardware->h_ifname);
+        goto malformed;
+    }
+    if (PEEK_CMP(lldpaddr, ETHER_ADDR_LEN) != 0) {
+        log_info("lldp", "frame not targeted at LLDP multicast address "
+            "received on %s", hardware->h_ifname);
+        goto malformed;
+    }
+    PEEK_DISCARD(ETHER_ADDR_LEN);    /* Skip source address */
+    if (PEEK_UINT16 != ETHERTYPE_LLDP) {
+        log_info("lldp", "non LLDP frame received on %s",
+            hardware->h_ifname);
+        goto malformed;
+    }
+
+    while (length && (!gotend)) {
+        if (length < 2) {
+            log_warnx("lldp", "tlv header too short received on %s",
+                hardware->h_ifname);
+            goto malformed;
+        }
+        tlv_size = PEEK_UINT16;
+        tlv_type = tlv_size >> 9;
+        tlv_size = tlv_size & 0x1ff;
+        (void)PEEK_SAVE(tlv);
+        if (length < tlv_size) {
+            log_warnx("lldp", "frame too short for tlv received on %s",
+                hardware->h_ifname);
+            goto malformed;
+        }
+        switch (tlv_type) {
+        case LLDP_TLV_END:
+            if (tlv_size != 0) {
+                log_warnx("lldp", "lldp end received with size not null on %s",
+                    hardware->h_ifname);
+                goto malformed;
+            }
+            if (length)
+                log_debug("lldp", "extra data after lldp end on %s",
+                    hardware->h_ifname);
+            gotend = 1;
+            break;
+        case LLDP_TLV_CHASSIS_ID:
+        case LLDP_TLV_PORT_ID:
+            CHECK_TLV_SIZE(2, "Port Id");
+            tlv_subtype = PEEK_UINT8;
+            if ((tlv_subtype == 0) || (tlv_subtype > 7)) {
+                log_warnx("lldp", "unknown subtype for tlv id received on %s",
+                    hardware->h_ifname);
+                goto malformed;
+            }
+            if ((b = (char *)calloc(1, tlv_size - 1)) == NULL) {
+                log_warn("lldp", "unable to allocate memory for id tlv "
+                    "received on %s",
+                    hardware->h_ifname);
+                goto malformed;
+            }
+            PEEK_BYTES(b, tlv_size - 1);
+            if (tlv_type == LLDP_TLV_PORT_ID) {
+                port->p_id_subtype = tlv_subtype;
+                port->p_id = b;
+                port->p_id_len = tlv_size - 1;
+            } else {
+                chassis->c_id_subtype = tlv_subtype;
+                chassis->c_id = b;
+                chassis->c_id_len = tlv_size - 1;
+            }
+            break;
+        case LLDP_TLV_TTL:
+            CHECK_TLV_SIZE(2, "TTL");
+            chassis->c_ttl = PEEK_UINT16;
+            ttl_received = 1;
+            break;
+        case LLDP_TLV_PORT_DESCR:
+        case LLDP_TLV_SYSTEM_NAME:
+        case LLDP_TLV_SYSTEM_DESCR:
+            if (tlv_size < 1) {
+                log_debug("lldp", "empty tlv received on %s",
+                    hardware->h_ifname);
+                break;
+            }
+            if ((b = (char *)calloc(1, tlv_size + 1)) == NULL) {
+                log_warn("lldp", "unable to allocate memory for string tlv "
+                    "received on %s",
+                    hardware->h_ifname);
+                goto malformed;
+            }
+            PEEK_BYTES(b, tlv_size);
+            if (tlv_type == LLDP_TLV_PORT_DESCR)
+                port->p_descr = b;
+            else if (tlv_type == LLDP_TLV_SYSTEM_NAME)
+                chassis->c_name = b;
+            else chassis->c_descr = b;
+            break;
+        case LLDP_TLV_SYSTEM_CAP:
+            CHECK_TLV_SIZE(4, "System capabilities");
+            chassis->c_cap_available = PEEK_UINT16;
+            chassis->c_cap_enabled = PEEK_UINT16;
+            break;
+        case LLDP_TLV_MGMT_ADDR:
+            CHECK_TLV_SIZE(1, "Management address");
+            addr_str_length = PEEK_UINT8;
+            CHECK_TLV_SIZE(addr_str_length, "Management address");
+            PEEK_BYTES(addr_str_buffer, addr_str_length);
+            addr_length = addr_str_length - 1;
+            addr_family = addr_str_buffer[0];
+            addr_ptr = &addr_str_buffer[1];
+            CHECK_TLV_SIZE(5, "Management address");
+            iface_subtype = PEEK_UINT8;
+            iface_number = PEEK_UINT32;
+
+            af = lldpd_af_from_lldp_proto(addr_family);
+            if (af == LLDPD_AF_UNSPEC)
+                break;
+            if (iface_subtype == LLDP_MGMT_IFACE_IFINDEX)
+                iface = iface_number;
+            else
+                iface = 0;
+            mgmt = lldpd_alloc_mgmt(af, addr_ptr, addr_length, iface);
+            if (mgmt == NULL) {
+#ifndef ENABLE_AUTO_ATTACH
+                assert(errno == ENOMEM);
+#endif
+                log_warn("lldp", "unable to allocate memory "
+                            "for management address");
+                        goto malformed;
+            }
+            TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
+            break;
+        case LLDP_TLV_ORG:
+            CHECK_TLV_SIZE(4, "Organisational");
+            PEEK_BYTES(orgid, sizeof(orgid));
+            tlv_subtype = PEEK_UINT8;
+            if (memcmp(dot1, orgid, sizeof(orgid)) == 0) {
+#ifndef ENABLE_DOT1
+                hardware->h_rx_unrecognized_cnt++;
+#else
+                /* Dot1 */
+                switch (tlv_subtype) {
+                case LLDP_TLV_DOT1_VLANNAME:
+                    CHECK_TLV_SIZE(7, "VLAN");
+                    if ((vlan = (struct lldpd_vlan *)calloc(1,
+                            sizeof(struct lldpd_vlan))) == NULL) {
+                        log_warn("lldp", "unable to alloc vlan "
+                            "structure for "
+                            "tlv received on %s",
+                            hardware->h_ifname);
+                        goto malformed;
+                    }
+                    vlan->v_vid = PEEK_UINT16;
+                    vlan_len = PEEK_UINT8;
+                    CHECK_TLV_SIZE(7 + vlan_len, "VLAN");
+                    if ((vlan->v_name =
+                        (char *)calloc(1, vlan_len + 1)) == NULL) {
+                        log_warn("lldp", "unable to alloc vlan name for "
+                            "tlv received on %s",
+                            hardware->h_ifname);
+                        goto malformed;
+                    }
+                    PEEK_BYTES(vlan->v_name, vlan_len);
+                    TAILQ_INSERT_TAIL(&port->p_vlans,
+                        vlan, v_entries);
+                    vlan = NULL;
+                    break;
+                case LLDP_TLV_DOT1_PVID:
+                    CHECK_TLV_SIZE(6, "PVID");
+                    port->p_pvid = PEEK_UINT16;
+                    break;
+                case LLDP_TLV_DOT1_PPVID:
+                    CHECK_TLV_SIZE(7, "PPVID");
+                    /* validation needed */
+                    /* PPVID has to be unique if more than
+                       one PPVID TLVs are received  -
+                       discard if duplicate */
+                    /* if support bit is not set and
+                       enabled bit is set - PPVID TLV is
+                       considered error  and discarded */
+                    /* if PPVID > 4096 - bad and discard */
+                    if ((ppvid = (struct lldpd_ppvid *)calloc(1,
+                            sizeof(struct lldpd_ppvid))) == NULL) {
+                        log_warn("lldp", "unable to alloc ppvid "
+                            "structure for "
+                            "tlv received on %s",
+                            hardware->h_ifname);
+                        goto malformed;
+                    }
+                    ppvid->p_cap_status = PEEK_UINT8;
+                    ppvid->p_ppvid = PEEK_UINT16;
+                    TAILQ_INSERT_TAIL(&port->p_ppvids,
+                        ppvid, p_entries);
+                    break;
+                case LLDP_TLV_DOT1_PI:
+                    /* validation needed */
+                    /* PI has to be unique if more than
+                       one PI TLVs are received  - discard
+                       if duplicate ?? */
+                    CHECK_TLV_SIZE(5, "PI");
+                    if ((pi = (struct lldpd_pi *)calloc(1,
+                            sizeof(struct lldpd_pi))) == NULL) {
+                        log_warn("lldp", "unable to alloc PI "
+                            "structure for "
+                            "tlv received on %s",
+                            hardware->h_ifname);
+                        goto malformed;
+                    }
+                    pi->p_pi_len = PEEK_UINT8;
+                    CHECK_TLV_SIZE(1 + pi->p_pi_len, "PI");
+                    if ((pi->p_pi =
+                        (char *)calloc(1, pi->p_pi_len)) == NULL) {
+                        log_warn("lldp", "unable to alloc pid name for "
+                            "tlv received on %s",
+                            hardware->h_ifname);
+                        goto malformed;
+                    }
+                    PEEK_BYTES(pi->p_pi, pi->p_pi_len);
+                    TAILQ_INSERT_TAIL(&port->p_pids,
+                        pi, p_entries);
+                    pi = NULL;
+                    break;
+                default:
+                    /* Unknown Dot1 TLV, ignore it */
+                    hardware->h_rx_unrecognized_cnt++;
+                }
+#endif
+            } else if (memcmp(dot3, orgid, sizeof(orgid)) == 0) {
+#ifndef ENABLE_DOT3
+                hardware->h_rx_unrecognized_cnt++;
+#else
+                /* Dot3 */
+                switch (tlv_subtype) {
+                case LLDP_TLV_DOT3_MAC:
+                    CHECK_TLV_SIZE(9, "MAC/PHY");
+                    port->p_macphy.autoneg_support = PEEK_UINT8;
+                    port->p_macphy.autoneg_enabled =
+                        (port->p_macphy.autoneg_support & 0x2) >> 1;
+                    port->p_macphy.autoneg_support =
+                        port->p_macphy.autoneg_support & 0x1;
+                    port->p_macphy.autoneg_advertised =
+                        PEEK_UINT16;
+                    port->p_macphy.mau_type = PEEK_UINT16;
+                    break;
+                case LLDP_TLV_DOT3_LA:
+                    CHECK_TLV_SIZE(9, "Link aggregation");
+                    PEEK_DISCARD_UINT8;
+                    port->p_aggregid = PEEK_UINT32;
+                    break;
+                case LLDP_TLV_DOT3_MFS:
+                    CHECK_TLV_SIZE(6, "MFS");
+                    port->p_mfs = PEEK_UINT16;
+                    break;
+                case LLDP_TLV_DOT3_POWER:
+                    CHECK_TLV_SIZE(7, "Power");
+                    port->p_power.devicetype = PEEK_UINT8;
+                    port->p_power.supported =
+                        (port->p_power.devicetype & 0x2) >> 1;
+                    port->p_power.enabled =
+                        (port->p_power.devicetype & 0x4) >> 2;
+                    port->p_power.paircontrol =
+                        (port->p_power.devicetype & 0x8) >> 3;
+                    port->p_power.devicetype =
+                        (port->p_power.devicetype & 0x1)?
+                        LLDP_DOT3_POWER_PSE:LLDP_DOT3_POWER_PD;
+                    port->p_power.pairs = PEEK_UINT8;
+                    port->p_power.class = PEEK_UINT8;
+                    /* 802.3at? */
+                    if (tlv_size >= 12) {
+                        port->p_power.powertype = PEEK_UINT8;
+                        port->p_power.source =
+                            (port->p_power.powertype & (1<<5 | 1<<4)) >> 4;
+                        port->p_power.priority =
+                            (port->p_power.powertype & (1<<1 | 1<<0));
+                        port->p_power.powertype =
+                            (port->p_power.powertype & (1<<7))?
+                            LLDP_DOT3_POWER_8023AT_TYPE1:
+                            LLDP_DOT3_POWER_8023AT_TYPE2;
+                        port->p_power.requested = PEEK_UINT16;
+                        port->p_power.allocated = PEEK_UINT16;
+                    } else
+                        port->p_power.powertype =
+                            LLDP_DOT3_POWER_8023AT_OFF;
+                    break;
+                default:
+                    /* Unknown Dot3 TLV, ignore it */
+                    hardware->h_rx_unrecognized_cnt++;
+                }
+#endif
+            } else if (memcmp(med, orgid, sizeof(orgid)) == 0) {
+
+                /* LLDP-MED */
+#ifndef ENABLE_LLDPMED
+                hardware->h_rx_unrecognized_cnt++;
+#else
+                u_int32_t policy;
+                unsigned loctype;
+                unsigned power;
+
+                switch (tlv_subtype) {
+                case LLDP_TLV_MED_CAP:
+                    CHECK_TLV_SIZE(7, "LLDP-MED capabilities");
+                    chassis->c_med_cap_available = PEEK_UINT16;
+                    chassis->c_med_type = PEEK_UINT8;
+                    port->p_med_cap_enabled |=
+                        LLDP_MED_CAP_CAP;
+                    break;
+                case LLDP_TLV_MED_POLICY:
+                    CHECK_TLV_SIZE(8, "LLDP-MED policy");
+                    policy = PEEK_UINT32;
+                    if (((policy >> 24) < 1) ||
+                        ((policy >> 24) > LLDP_MED_APPTYPE_LAST)) {
+                        log_info("lldp", "unknown policy field %d "
+                            "received on %s",
+                            policy,
+                            hardware->h_ifname);
+                        break;
+                    }
+                    port->p_med_policy[(policy >> 24) - 1].type =
+                        (policy >> 24);
+                    port->p_med_policy[(policy >> 24) - 1].unknown =
+                        ((policy & 0x800000) != 0);
+                    port->p_med_policy[(policy >> 24) - 1].tagged =
+                        ((policy & 0x400000) != 0);
+                    port->p_med_policy[(policy >> 24) - 1].vid =
+                        (policy & 0x001FFE00) >> 9;
+                    port->p_med_policy[(policy >> 24) - 1].priority =
+                        (policy & 0x1C0) >> 6;
+                    port->p_med_policy[(policy >> 24) - 1].dscp =
+                        policy & 0x3F;
+                    port->p_med_cap_enabled |=
+                        LLDP_MED_CAP_POLICY;
+                    break;
+                case LLDP_TLV_MED_LOCATION:
+                    CHECK_TLV_SIZE(5, "LLDP-MED Location");
+                    loctype = PEEK_UINT8;
+                    if ((loctype < 1) ||
+                        (loctype > LLDP_MED_LOCFORMAT_LAST)) {
+                        log_info("lldp", "unknown location type "
+                            "received on %s",
+                            hardware->h_ifname);
+                        break;
+                    }
+                    if ((port->p_med_location[loctype - 1].data =
+                        (char*)malloc(tlv_size - 5)) == NULL) {
+                        log_warn("lldp", "unable to allocate memory "
+                            "for LLDP-MED location for "
+                            "frame received on %s",
+                            hardware->h_ifname);
+                        goto malformed;
+                    }
+                    PEEK_BYTES(port->p_med_location[loctype - 1].data,
+                        tlv_size - 5);
+                    port->p_med_location[loctype - 1].data_len =
+                        tlv_size - 5;
+                    port->p_med_location[loctype - 1].format = loctype;
+                    port->p_med_cap_enabled |=
+                        LLDP_MED_CAP_LOCATION;
+                    break;
+                case LLDP_TLV_MED_MDI:
+                    CHECK_TLV_SIZE(7, "LLDP-MED PoE-MDI");
+                    power = PEEK_UINT8;
+                    switch (power & 0xC0) {
+                    case 0x0:
+                        port->p_med_power.devicetype = LLDP_MED_POW_TYPE_PSE;
+                        port->p_med_cap_enabled |=
+                            LLDP_MED_CAP_MDI_PSE;
+                        switch (power & 0x30) {
+                        case 0x0:
+                            port->p_med_power.source =
+                                LLDP_MED_POW_SOURCE_UNKNOWN;
+                            break;
+                        case 0x10:
+                            port->p_med_power.source =
+                                LLDP_MED_POW_SOURCE_PRIMARY;
+                            break;
+                        case 0x20:
+                            port->p_med_power.source =
+                                LLDP_MED_POW_SOURCE_BACKUP;
+                            break;
+                        default:
+                            port->p_med_power.source =
+                                LLDP_MED_POW_SOURCE_RESERVED;
+                        }
+                        break;
+                    case 0x40:
+                        port->p_med_power.devicetype = LLDP_MED_POW_TYPE_PD;
+                        port->p_med_cap_enabled |=
+                            LLDP_MED_CAP_MDI_PD;
+                        switch (power & 0x30) {
+                        case 0x0:
+                            port->p_med_power.source =
+                                LLDP_MED_POW_SOURCE_UNKNOWN;
+                            break;
+                        case 0x10:
+                            port->p_med_power.source =
+                                LLDP_MED_POW_SOURCE_PSE;
+                            break;
+                        case 0x20:
+                            port->p_med_power.source =
+                                LLDP_MED_POW_SOURCE_LOCAL;
+                            break;
+                        default:
+                            port->p_med_power.source =
+                                LLDP_MED_POW_SOURCE_BOTH;
+                        }
+                        break;
+                    default:
+                        port->p_med_power.devicetype =
+                            LLDP_MED_POW_TYPE_RESERVED;
+                    }
+                    if ((power & 0x0F) > LLDP_MED_POW_PRIO_LOW)
+                        port->p_med_power.priority =
+                            LLDP_MED_POW_PRIO_UNKNOWN;
+                    else
+                        port->p_med_power.priority =
+                            power & 0x0F;
+                    port->p_med_power.val = PEEK_UINT16;
+                    break;
+                case LLDP_TLV_MED_IV_HW:
+                case LLDP_TLV_MED_IV_SW:
+                case LLDP_TLV_MED_IV_FW:
+                case LLDP_TLV_MED_IV_SN:
+                case LLDP_TLV_MED_IV_MANUF:
+                case LLDP_TLV_MED_IV_MODEL:
+                case LLDP_TLV_MED_IV_ASSET:
+                    if (tlv_size <= 4)
+                        b = NULL;
+                    else {
+                        if ((b = (char*)malloc(tlv_size - 3)) ==
+                            NULL) {
+                            log_warn("lldp", "unable to allocate "
+                                "memory for LLDP-MED "
+                                "inventory for frame "
+                                "received on %s",
+                                hardware->h_ifname);
+                            goto malformed;
+                        }
+                        PEEK_BYTES(b, tlv_size - 4);
+                        b[tlv_size - 4] = '\0';
+                    }
+                    switch (tlv_subtype) {
+                    case LLDP_TLV_MED_IV_HW:
+                        chassis->c_med_hw = b;
+                        break;
+                    case LLDP_TLV_MED_IV_FW:
+                        chassis->c_med_fw = b;
+                        break;
+                    case LLDP_TLV_MED_IV_SW:
+                        chassis->c_med_sw = b;
+                        break;
+                    case LLDP_TLV_MED_IV_SN:
+                        chassis->c_med_sn = b;
+                        break;
+                    case LLDP_TLV_MED_IV_MANUF:
+                        chassis->c_med_manuf = b;
+                        break;
+                    case LLDP_TLV_MED_IV_MODEL:
+                        chassis->c_med_model = b;
+                        break;
+                    case LLDP_TLV_MED_IV_ASSET:
+                        chassis->c_med_asset = b;
+                        break;
+                    }
+                    port->p_med_cap_enabled |=
+                        LLDP_MED_CAP_IV;
+                    break;
+                default:
+                    /* Unknown LLDP MED, ignore it */
+                    hardware->h_rx_unrecognized_cnt++;
+                }
+#endif /* ENABLE_LLDPMED */
+            } else if (memcmp(avaya_oid, orgid, sizeof(orgid)) == 0) {
+#ifndef ENABLE_AUTO_ATTACH
+                hardware->h_rx_unrecognized_cnt++;
+#else
+                u_int16_t aa_element_word;
+                u_int16_t aa_status_vlan_word;
+                u_int16_t aa_system_id_word;
+                unsigned short num_mappings;
+
+                switch(tlv_subtype) {
+                case LLDP_TLV_AA_ELEMENT_SUBTYPE:
+                    aa_element_word = PEEK_UINT16;
+                    /* type is first 4 most-significant bits */
+                    port->p_element.type = aa_element_word >> 12;
+                    /* mgmt_vlan is last 12 bits */
+                    port->p_element.mgmt_vlan = aa_element_word & 0x0FFF;
+                    log_info("auto_attach", "Element type: %X, Mgmt vlan: %X",
+                            port->p_element.type,
+                            port->p_element.mgmt_vlan);
+                    PEEK_BYTES(&port->p_element.system_id.system_mac,
+                        sizeof(port->p_element.system_id.system_mac));
+                    log_info("auto_attach",
+                        "System mac: 0x%.2X%.2X%.2X%.2X%.2X%.2X",
+                        port->p_element.system_id.system_mac[0],
+                        port->p_element.system_id.system_mac[1],
+                        port->p_element.system_id.system_mac[2],
+                        port->p_element.system_id.system_mac[3],
+                        port->p_element.system_id.system_mac[4],
+                        port->p_element.system_id.system_mac[5]);
+                    aa_system_id_word = PEEK_UINT16;
+                    port->p_element.system_id.conn_type =
+                        aa_system_id_word >> 12;
+                    port->p_element.system_id.smlt_id =
+                        aa_system_id_word & 0x0FFF;
+                    PEEK_BYTES(&port->p_element.system_id.mlt_id,
+                        sizeof(port->p_element.system_id.mlt_id));
+
+                           break;
+                case LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE:
+                    PEEK_BYTES(&msg_auth_digest,
+                        sizeof(msg_auth_digest));
+                    /* Subtract off tlv type and length (2Bytes) + OUI (3B) +
+                     * Subtype (1B) + MSG DIGEST (32B).
+                     */
+                    num_mappings = tlv_size - 4 -
+                        LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH;
+                    if( (num_mappings % 5) != 0 ) {
+                        log_info("auto_attach",
+                        "malformed vlan-isid mappings tlv received.");
+                    } else {
+                        num_mappings /= 5; /* Each mapping is 5 Bytes */
+                        for( ; num_mappings > 0; num_mappings-- ) {
+                        if ((isid_vlan_map =
+                            (struct lldpd_aa_isid_vlan_maps_tlv *) calloc(1,
+                             sizeof(struct lldpd_aa_isid_vlan_maps_tlv))
+                            )
+                             == NULL ) {
+                            log_warnx("lldp", "unable to allocate memory "
+                            "for aa_isid_vlan_maps_tlv struct");
+                            goto malformed;
+                        }
+                        aa_status_vlan_word = PEEK_UINT16;
+                        /* Status is first 4 most-significant bits. */
+                        isid_vlan_map->isid_vlan_data.status =
+                            aa_status_vlan_word >> 12;
+                        /* Vlan is last 12 bits */
+                        isid_vlan_map->isid_vlan_data.vlan =
+                            aa_status_vlan_word & 0x0FFF;
+                        PEEK_BYTES(&isid_vlan_map->isid_vlan_data.isid,
+                            sizeof(isid_vlan_map->isid_vlan_data.isid));
+                        TAILQ_INSERT_TAIL(&port->p_isid_vlan_maps,
+                            isid_vlan_map, m_entries);
+                        log_info("auto_attach", "Vlan<->Isid received. "
+                            "Vlan: 0x%X(%d) Isid: 0x%.2X%.2X%.2X(%d)",
+                                isid_vlan_map->isid_vlan_data.vlan,
+                                isid_vlan_map->isid_vlan_data.vlan,
+                                isid_vlan_map->isid_vlan_data.isid[0],
+                                isid_vlan_map->isid_vlan_data.isid[1],
+                                isid_vlan_map->isid_vlan_data.isid[2],
+                                (isid_vlan_map->isid_vlan_data.isid[0]<<16) |
+                                (isid_vlan_map->isid_vlan_data.isid[1]<<8) |
+                                (isid_vlan_map->isid_vlan_data.isid[2])
+                        );
+                        isid_vlan_map = NULL;
+                        }
+                    }
+                           break;
+                default:
+                    hardware->h_rx_unrecognized_cnt++;
+                    log_info("auto_attach",
+                        "Unrecogised tlv subtype received");
+                    break;
+                }
+#endif
+            } else if (memcmp(dcbx, orgid, sizeof(orgid)) == 0) {
+                log_debug("lldp", "unsupported DCBX tlv received on %s "
+                    "- ignore", hardware->h_ifname);
+                hardware->h_rx_unrecognized_cnt++;
+            } else {
+                log_info("lldp", "unknown org tlv [%02x:%02x:%02x] received "
+                    "on %s", orgid[0], orgid[1], orgid[2], hardware->h_ifname);
+                hardware->h_rx_unrecognized_cnt++;
+            }
+            break;
+        default:
+            log_warnx("lldp", "unknown tlv (%d) received on %s",
+                tlv_type, hardware->h_ifname);
+            goto malformed;
+        }
+        if (pos > tlv + tlv_size) {
+            log_warnx("lldp", "BUG: already past TLV!");
+            goto malformed;
+        }
+        PEEK_DISCARD(tlv + tlv_size - pos);
+    }
+
+    /* Some random check */
+    if ((chassis->c_id == NULL) ||
+        (port->p_id == NULL) ||
+        (!ttl_received) ||
+        (gotend == 0)) {
+        log_warnx("lldp", "some mandatory tlv are missing for frame received "
+            "on %s", hardware->h_ifname);
+        goto malformed;
+    }
+    *newchassis = chassis;
+    *newport = port;
+    return 1;
+malformed:
+#ifdef ENABLE_DOT1
+    free(vlan);
+    free(pi);
+#endif
+    lldpd_chassis_cleanup(chassis, 1);
+    lldpd_port_cleanup(port, 1);
+    free(port);
+    return -1;
+}
diff --git a/lib/lldpd-structs.c b/lib/lldpd-structs.c
new file mode 100644
index 0000000..6f441f5
--- /dev/null
+++ b/lib/lldpd-structs.c
@@ -0,0 +1,197 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat at luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include "lldpd.h"
+#include "lldpd-structs.h"
+#ifndef ENABLE_AUTO_ATTACH
+#include "log.h"
+#endif
+
+#ifdef ENABLE_AUTO_ATTACH
+VLOG_DEFINE_THIS_MODULE(lldpd_structs);
+#endif
+
+void
+lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *chassis)
+{
+    struct lldpd_mgmt *mgmt, *mgmt_next;
+
+    log_debug("alloc", "cleanup management addresses for chassis %s",
+        chassis->c_name ? chassis->c_name : "(unknwon)");
+
+    for (mgmt = TAILQ_FIRST(&chassis->c_mgmt);
+         mgmt != NULL;
+         mgmt = mgmt_next) {
+        mgmt_next = TAILQ_NEXT(mgmt, m_entries);
+        free(mgmt);
+    }
+    TAILQ_INIT(&chassis->c_mgmt);
+}
+
+void
+lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
+{
+    lldpd_chassis_mgmt_cleanup(chassis);
+    log_debug("alloc", "cleanup chassis %s",
+        chassis->c_name ? chassis->c_name : "(unknwon)");
+#ifdef ENABLE_LLDPMED
+    free(chassis->c_med_hw);
+    free(chassis->c_med_sw);
+    free(chassis->c_med_fw);
+    free(chassis->c_med_sn);
+    free(chassis->c_med_manuf);
+    free(chassis->c_med_model);
+    free(chassis->c_med_asset);
+#endif
+    free(chassis->c_id);
+    free(chassis->c_name);
+    free(chassis->c_descr);
+    if (all)
+        free(chassis);
+}
+
+#ifdef ENABLE_DOT1
+void
+lldpd_vlan_cleanup(struct lldpd_port *port)
+{
+    struct lldpd_vlan *vlan, *vlan_next;
+    for (vlan = TAILQ_FIRST(&port->p_vlans);
+         vlan != NULL;
+         vlan = vlan_next) {
+        free(vlan->v_name);
+        vlan_next = TAILQ_NEXT(vlan, v_entries);
+        free(vlan);
+    }
+    TAILQ_INIT(&port->p_vlans);
+}
+
+void
+lldpd_ppvid_cleanup(struct lldpd_port *port)
+{
+    struct lldpd_ppvid *ppvid, *ppvid_next;
+    for (ppvid = TAILQ_FIRST(&port->p_ppvids);
+         ppvid != NULL;
+         ppvid = ppvid_next) {
+            ppvid_next = TAILQ_NEXT(ppvid, p_entries);
+            free(ppvid);
+    }
+    TAILQ_INIT(&port->p_ppvids);
+}
+
+void
+lldpd_pi_cleanup(struct lldpd_port *port)
+{
+    struct lldpd_pi *pi, *pi_next;
+    for (pi = TAILQ_FIRST(&port->p_pids);
+         pi != NULL;
+         pi = pi_next) {
+            free(pi->p_pi);
+            pi_next = TAILQ_NEXT(pi, p_entries);
+            free(pi);
+    }
+    TAILQ_INIT(&port->p_pids);
+}
+#endif
+
+/* Cleanup a remote port. The before last argument, `expire` is a function that
+ * should be called when a remote port is removed. If the last argument is 1,
+ * all remote ports are removed.
+ */
+void
+lldpd_remote_cleanup(struct lldpd_hardware *hardware,
+    void(*expire)(struct lldpd_hardware *, struct lldpd_port *),
+    int all)
+{
+    struct lldpd_port *port, *port_next;
+    int del;
+    time_t now = time(NULL);
+
+    log_debug("alloc", "cleanup remote port on %s",
+        hardware->h_ifname);
+    for (port = TAILQ_FIRST(&hardware->h_rports);
+         port != NULL;
+         port = port_next) {
+        port_next = TAILQ_NEXT(port, p_entries);
+        del = all;
+        if (!all && expire &&
+            (now >= port->p_lastupdate + port->p_chassis->c_ttl)) {
+            hardware->h_ageout_cnt++;
+            hardware->h_delete_cnt++;
+            del = 1;
+        }
+        if (del) {
+            if (expire) expire(hardware, port);
+            /* This TAILQ_REMOVE is dangerous. It should not be
+             * called while in liblldpctl because we don't have a
+             * real list. It is only needed to be called when we
+             * don't delete the entire list. */
+            if (!all)
+                TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
+            lldpd_port_cleanup(port, 1);
+            free(port);
+        }
+    }
+    if (all)
+        TAILQ_INIT(&hardware->h_rports);
+}
+
+/* If `all' is true, clear all information, including information that
+   are not refreshed periodically. Port should be freed manually. */
+void
+lldpd_port_cleanup(struct lldpd_port *port, int all)
+{
+#ifdef ENABLE_LLDPMED
+    int i;
+    if (all)
+        for (i=0; i < LLDP_MED_LOCFORMAT_LAST; i++)
+            free(port->p_med_location[i].data);
+#endif
+#ifdef ENABLE_DOT1
+    lldpd_vlan_cleanup(port);
+    lldpd_ppvid_cleanup(port);
+    lldpd_pi_cleanup(port);
+#endif
+    /* We set these to NULL so we don't free wrong memory */
+
+    free(port->p_id);
+    port->p_id = NULL;
+    free(port->p_descr);
+    port->p_descr = NULL;
+    if (all) {
+        free(port->p_lastframe);
+        /* Chassis may not have been attributed, yet.*/
+        if (port->p_chassis) {
+            port->p_chassis->c_refcount--;
+            port->p_chassis = NULL;
+        }
+    }
+}
+
+void
+lldpd_config_cleanup(struct lldpd_config *config)
+{
+    log_debug("alloc", "general configuration cleanup");
+    free(config->c_mgmt_pattern);
+    free(config->c_cid_pattern);
+    free(config->c_iface_pattern);
+    free(config->c_platform);
+    free(config->c_description);
+}
diff --git a/lib/lldpd-structs.h b/lib/lldpd-structs.h
new file mode 100644
index 0000000..8aa1ac8
--- /dev/null
+++ b/lib/lldpd-structs.h
@@ -0,0 +1,473 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat at luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LLDPD_STRUCTS_H
+#define _LLDPD_STRUCTS_H
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+/* This is not very convenient, but we need net/if.h for IFNAMSIZ and others but
+ * we may also need linux/if.h in some modules. And they conflict each others.
+ */
+#ifdef HOST_OS_LINUX
+# include <linux/if.h>
+#else
+# include <net/if.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <sys/queue.h>
+
+#ifndef ENABLE_AUTO_ATTACH
+#include "compat/compat.h"
+#endif
+#include "marshal.h"
+#include "lldp-const.h"
+
+
+/* Add definitions for Auto Attach tlv structures */
+#ifdef ENABLE_AUTO_ATTACH
+#include "aa-structs.h"
+#endif
+
+#ifdef ENABLE_DOT1
+struct lldpd_ppvid {
+    TAILQ_ENTRY(lldpd_ppvid)    p_entries;
+    u_int8_t                    p_cap_status;
+    u_int16_t                   p_ppvid;
+};
+MARSHAL_BEGIN(lldpd_ppvid)
+MARSHAL_TQE(lldpd_ppvid, p_entries)
+MARSHAL_END(lldpd_ppvid);
+
+struct lldpd_vlan {
+    TAILQ_ENTRY(lldpd_vlan) v_entries;
+    char                    *v_name;
+    u_int16_t               v_vid;
+};
+MARSHAL_BEGIN(lldpd_vlan)
+MARSHAL_TQE(lldpd_vlan, v_entries)
+MARSHAL_STR(lldpd_vlan, v_name)
+MARSHAL_END(lldpd_vlan);
+
+struct lldpd_pi {
+    TAILQ_ENTRY(lldpd_pi)   p_entries;
+    char                    *p_pi;
+    int                     p_pi_len;
+};
+MARSHAL_BEGIN(lldpd_pi)
+MARSHAL_TQE(lldpd_pi, p_entries)
+MARSHAL_FSTR(lldpd_pi, p_pi, p_pi_len)
+MARSHAL_END(lldpd_pi);
+#endif
+
+#ifdef ENABLE_LLDPMED
+struct lldpd_med_policy {
+    u_int8_t         index; /* Not used. */
+    u_int8_t         type;
+    u_int8_t         unknown;
+    u_int8_t         tagged;
+    u_int16_t        vid;
+    u_int8_t         priority;
+    u_int8_t         dscp;
+};
+MARSHAL(lldpd_med_policy);
+
+struct lldpd_med_loc {
+    u_int8_t         index; /* Not used. */
+    u_int8_t         format;
+    char             *data;
+    int              data_len;
+};
+MARSHAL_BEGIN(lldpd_med_loc)
+MARSHAL_FSTR(lldpd_med_loc, data, data_len)
+MARSHAL_END(lldpd_med_loc);
+
+struct lldpd_med_power {
+    u_int8_t         devicetype; /* PD or PSE */
+    u_int8_t         source;
+    u_int8_t         priority;
+    u_int16_t        val;
+};
+MARSHAL(lldpd_med_power);
+#endif
+
+#ifdef ENABLE_DOT3
+struct lldpd_dot3_macphy {
+    u_int8_t         autoneg_support;
+    u_int8_t         autoneg_enabled;
+    u_int16_t        autoneg_advertised;
+    u_int16_t        mau_type;
+};
+
+struct lldpd_dot3_power {
+    u_int8_t        devicetype;
+    u_int8_t        supported;
+    u_int8_t        enabled;
+    u_int8_t        paircontrol;
+    u_int8_t        pairs;
+    u_int8_t        class;
+    u_int8_t        powertype; /* If set to LLDP_DOT3_POWER_8023AT_OFF,
+                                * following fields have no meaning */
+    u_int8_t        source;
+    u_int8_t        priority;
+    u_int16_t       requested;
+    u_int16_t       allocated;
+};
+MARSHAL(lldpd_dot3_power);
+#endif
+
+enum {
+    LLDPD_AF_UNSPEC = 0,
+    LLDPD_AF_IPV4,
+    LLDPD_AF_IPV6,
+    LLDPD_AF_LAST
+};
+
+inline static int
+lldpd_af(int af)
+{
+    switch (af) {
+    case LLDPD_AF_IPV4: return AF_INET;
+    case LLDPD_AF_IPV6: return AF_INET6;
+    case LLDPD_AF_LAST: return AF_MAX;
+    default: return AF_UNSPEC;
+    }
+}
+
+#define LLDPD_MGMT_MAXADDRSIZE    16 /* sizeof(struct in6_addr) */
+struct lldpd_mgmt {
+    TAILQ_ENTRY(lldpd_mgmt) m_entries;
+    int                m_family;
+    union {
+        struct in_addr      inet;
+        struct in6_addr     inet6;
+        u_int8_t            octets[LLDPD_MGMT_MAXADDRSIZE];
+    } m_addr;
+    size_t                  m_addrsize;
+    u_int32_t               m_iface;
+};
+MARSHAL_BEGIN(lldpd_mgmt)
+MARSHAL_TQE(lldpd_mgmt, m_entries)
+MARSHAL_END(lldpd_mgmt);
+
+struct lldpd_chassis {
+    TAILQ_ENTRY(lldpd_chassis) c_entries;
+    u_int16_t   c_refcount;   /* Reference count by ports */
+    u_int16_t   c_index;      /* Monotonic index */
+    u_int8_t    c_protocol;    /* Protocol used to get this chassis */
+    u_int8_t    c_id_subtype;
+    char        *c_id;
+    int         c_id_len;
+    char        *c_name;
+    char        *c_descr;
+
+    u_int16_t   c_cap_available;
+    u_int16_t   c_cap_enabled;
+
+    u_int16_t   c_ttl;
+
+    TAILQ_HEAD(, lldpd_mgmt) c_mgmt;
+
+#ifdef ENABLE_LLDPMED
+    u_int16_t       c_med_cap_available;
+    u_int8_t        c_med_type;
+    char            *c_med_hw;
+    char            *c_med_fw;
+    char            *c_med_sw;
+    char            *c_med_sn;
+    char            *c_med_manuf;
+    char            *c_med_model;
+    char            *c_med_asset;
+#endif
+
+};
+/* WARNING: any change to this structure should also be reflected into
+   `lldpd_copy_chassis()` which is not using marshaling. */
+MARSHAL_BEGIN(lldpd_chassis)
+MARSHAL_IGNORE(lldpd_chassis, c_entries.tqe_next)
+MARSHAL_IGNORE(lldpd_chassis, c_entries.tqe_prev)
+MARSHAL_FSTR(lldpd_chassis, c_id, c_id_len)
+MARSHAL_STR(lldpd_chassis, c_name)
+MARSHAL_STR(lldpd_chassis, c_descr)
+MARSHAL_SUBTQ(lldpd_chassis, lldpd_mgmt, c_mgmt)
+#ifdef ENABLE_LLDPMED
+MARSHAL_STR(lldpd_chassis, c_med_hw)
+MARSHAL_STR(lldpd_chassis, c_med_fw)
+MARSHAL_STR(lldpd_chassis, c_med_sw)
+MARSHAL_STR(lldpd_chassis, c_med_sn)
+MARSHAL_STR(lldpd_chassis, c_med_manuf)
+MARSHAL_STR(lldpd_chassis, c_med_model)
+MARSHAL_STR(lldpd_chassis, c_med_asset)
+#endif
+MARSHAL_END(lldpd_chassis);
+
+
+struct lldpd_port {
+    TAILQ_ENTRY(lldpd_port) p_entries;
+    struct lldpd_chassis    *p_chassis; /* Attached chassis */
+    time_t              p_lastchange; /* Time of last change of values */
+    time_t              p_lastupdate; /* Time of last update received */
+    struct lldpd_frame  *p_lastframe; /* Frame received during last update */
+    u_int8_t            p_protocol;   /* Protocol used to get this port */
+    u_int8_t            p_hidden_in:1; /* Considered as hidden for reception */
+    u_int8_t            p_hidden_out:2; /* Considered as hidden for emission */
+    /* Important: all fields that should be ignored to check if a port has
+     * been changed should be before p_id_subtype. Check
+     * `lldpd_reset_timer()`. */
+    u_int8_t            p_id_subtype;
+    char                *p_id;
+    int                 p_id_len;
+    char                *p_descr;
+    u_int16_t           p_mfs;
+
+#ifdef ENABLE_DOT3
+    /* Dot3 stuff */
+    u_int32_t                   p_aggregid;
+    struct lldpd_dot3_macphy    p_macphy;
+    struct lldpd_dot3_power     p_power;
+#endif
+
+#ifdef ENABLE_LLDPMED
+    u_int16_t                   p_med_cap_enabled;
+    struct lldpd_med_policy     p_med_policy[LLDP_MED_APPTYPE_LAST];
+    struct lldpd_med_loc        p_med_location[LLDP_MED_LOCFORMAT_LAST];
+    struct lldpd_med_power      p_med_power;
+#endif
+
+#ifdef ENABLE_DOT1
+    u_int16_t                   p_pvid;
+    TAILQ_HEAD(, lldpd_vlan)    p_vlans;
+    TAILQ_HEAD(, lldpd_ppvid)   p_ppvids;
+    TAILQ_HEAD(, lldpd_pi)      p_pids;
+#endif
+#ifdef ENABLE_AUTO_ATTACH
+    struct lldpd_aa_element_tlv p_element;
+    TAILQ_HEAD(, lldpd_aaisid_vlan_maps_tlv) p_isid_vlan_maps;
+#endif
+};
+MARSHAL_BEGIN(lldpd_port)
+MARSHAL_TQE(lldpd_port, p_entries)
+MARSHAL_POINTER(lldpd_port, lldpd_chassis, p_chassis)
+MARSHAL_IGNORE(lldpd_port, p_lastframe)
+MARSHAL_FSTR(lldpd_port, p_id, p_id_len)
+MARSHAL_STR(lldpd_port, p_descr)
+#ifdef ENABLE_LLDPMED
+MARSHAL_SUBSTRUCT(lldpd_port, lldpd_med_loc, p_med_location[0])
+MARSHAL_SUBSTRUCT(lldpd_port, lldpd_med_loc, p_med_location[1])
+MARSHAL_SUBSTRUCT(lldpd_port, lldpd_med_loc, p_med_location[2])
+#endif
+#ifdef ENABLE_DOT1
+MARSHAL_SUBTQ(lldpd_port, lldpd_vlan, p_vlans)
+MARSHAL_SUBTQ(lldpd_port, lldpd_ppvid, p_ppvids)
+MARSHAL_SUBTQ(lldpd_port, lldpd_pi, p_pids)
+#endif
+MARSHAL_END(lldpd_port);
+
+/* Used to modify some port related settings */
+struct lldpd_port_set {
+    char *ifname;
+#ifdef ENABLE_LLDPMED
+    struct lldpd_med_policy *med_policy;
+    struct lldpd_med_loc    *med_location;
+    struct lldpd_med_power  *med_power;
+#endif
+#ifdef ENABLE_DOT3
+    struct lldpd_dot3_power *dot3_power;
+#endif
+};
+MARSHAL_BEGIN(lldpd_port_set)
+MARSHAL_STR(lldpd_port_set, ifname)
+#ifdef ENABLE_LLDPMED
+MARSHAL_POINTER(lldpd_port_set, lldpd_med_policy, med_policy)
+MARSHAL_POINTER(lldpd_port_set, lldpd_med_loc,    med_location)
+MARSHAL_POINTER(lldpd_port_set, lldpd_med_power,  med_power)
+#endif
+#ifdef ENABLE_DOT3
+MARSHAL_POINTER(lldpd_port_set, lldpd_dot3_power, dot3_power)
+#endif
+MARSHAL_END(lldpd_port_set);
+
+/* Smart mode / Hide mode */
+#define SMART_INCOMING_FILTER     (1<<0) /* Incoming filtering enabled */
+#define SMART_INCOMING_ONE_PROTO  (1<<1) /* On reception, keep only 1 proto */
+#define SMART_INCOMING_ONE_NEIGH  (1<<2) /* On recep., keep only 1 neighbor */
+#define SMART_OUTGOING_FILTER     (1<<3) /* Outgoing filtering enabled */
+#define SMART_OUTGOING_ONE_PROTO  (1<<4) /* On emission, keep only one proto */
+#define SMART_OUTGOING_ONE_NEIGH  (1<<5) /* On emission, consider only
+                                            one neighbor */
+#define SMART_INCOMING (SMART_INCOMING_FILTER |    \
+             SMART_INCOMING_ONE_PROTO | \
+             SMART_INCOMING_ONE_NEIGH)
+#define SMART_OUTGOING (SMART_OUTGOING_FILTER |        \
+            SMART_OUTGOING_ONE_PROTO |    \
+            SMART_OUTGOING_ONE_NEIGH)
+
+struct lldpd_config {
+    int c_paused;           /* lldpd is paused */
+    int c_tx_interval;      /* Transmit interval */
+    int c_smart;            /* Bitmask for smart configuration (see SMART_*) */
+    int c_receiveonly;      /* Receive only mode */
+    int c_max_neighbors;    /* Maximum number of neighbors (per protocol) */
+
+    char *c_mgmt_pattern;   /* Pattern to match a management address */
+    char *c_cid_pattern;    /* Pattern to match interfaces to use for chassis
+                             * ID */
+    char *c_iface_pattern;  /* Pattern to match interfaces to use */
+
+    char *c_platform;       /* Override platform description (for CDP) */
+    char *c_description;    /* Override chassis description */
+    char *c_hostname;       /* Override system name */
+    int c_advertise_version; /* Should the precise version be advertised? */
+    int c_set_ifdescr;      /* Set interface description */
+    int c_promisc;          /* Interfaces should be in promiscuous mode */
+
+#ifdef ENABLE_LLDPMED
+    int c_noinventory;      /* Don't send inventory with LLDP-MED */
+    int c_enable_fast_start; /* enable fast start */
+    int c_tx_fast_init;     /* Num of lldpd lldppdu's for fast start */
+    int c_tx_fast_interval; /* Time intr between sends during fast start */
+#endif
+    int c_tx_hold;          /* Transmit hold */
+    int c_bond_slave_src_mac_type; /* Src mac type in lldp frames over bond
+                                    * slaves */
+    int c_lldp_portid_type; /* The PortID type */
+};
+MARSHAL_BEGIN(lldpd_config)
+MARSHAL_STR(lldpd_config, c_mgmt_pattern)
+MARSHAL_STR(lldpd_config, c_cid_pattern)
+MARSHAL_STR(lldpd_config, c_iface_pattern)
+MARSHAL_STR(lldpd_config, c_platform)
+MARSHAL_STR(lldpd_config, c_description)
+MARSHAL_STR(lldpd_config, c_hostname)
+MARSHAL_END(lldpd_config);
+
+struct lldpd_frame {
+    int size;
+    unsigned char frame[1];
+};
+
+struct lldpd_hardware;
+struct lldpd;
+struct lldpd_ops {
+    int(*send)(struct lldpd *,
+           struct lldpd_hardware*,
+           char *, size_t); /* Function to send a frame */
+    int(*recv)(struct lldpd *,
+           struct lldpd_hardware*,
+           int, char *, size_t); /* Function to receive a frame */
+    int(*cleanup)(struct lldpd *, struct lldpd_hardware *); /* Cleanup
+                                                             * function. */
+};
+
+/* An interface is uniquely identified by h_ifindex, h_ifname and h_ops. This
+ * means if an interface becomes enslaved, it will be considered as a new
+ * interface. The same applies for renaming and we include the index in case of
+ * renaming to an existing interface. */
+struct lldpd_hardware {
+    TAILQ_ENTRY(lldpd_hardware) h_entries;
+
+    struct lldpd    *h_cfg; /* Pointer to main configuration */
+    void            *h_recv;    /* FD for reception */
+    int             h_sendfd;   /* FD for sending, only used by h_ops */
+    int             h_mangle;   /* 1 if we have to mangle the MAC address */
+    struct lldpd_ops *h_ops; /* Hardware-dependent functions */
+    void            *h_data;    /* Hardware-dependent data */
+    void            *h_timer;   /* Timer for this port */
+
+    int             h_mtu;
+    int             h_flags;    /* Packets will be sent only
+                                * if IFF_RUNNING. Will be
+                                * removed if this is left
+                                * to 0. */
+    int             h_ifindex;  /* Interface index, used by SNMP */
+    char            h_ifname[IFNAMSIZ]; /* Should be unique */
+    u_int8_t        h_lladdr[ETHER_ADDR_LEN];
+
+    u_int64_t       h_tx_cnt;
+    u_int64_t       h_rx_cnt;
+    u_int64_t       h_rx_discarded_cnt;
+    u_int64_t       h_rx_unrecognized_cnt;
+    u_int64_t       h_ageout_cnt;
+    u_int64_t       h_insert_cnt;
+    u_int64_t       h_delete_cnt;
+    u_int64_t       h_drop_cnt;
+
+    u_int16_t       h_lport_cksum; /* Checksum on local port to see if there
+                                      * is a change */
+    struct lldpd_port h_lport;  /* Port attached to this hardware port */
+    TAILQ_HEAD(, lldpd_port) h_rports; /* Remote ports */
+
+#ifdef ENABLE_LLDPMED
+    int h_tx_fast; /* current tx fast start count */
+#endif
+};
+MARSHAL_BEGIN(lldpd_hardware)
+MARSHAL_IGNORE(lldpd_hardware, h_entries.tqe_next)
+MARSHAL_IGNORE(lldpd_hardware, h_entries.tqe_prev)
+MARSHAL_IGNORE(lldpd_hardware, h_ops)
+MARSHAL_IGNORE(lldpd_hardware, h_data)
+MARSHAL_IGNORE(lldpd_hardware, h_cfg)
+MARSHAL_SUBSTRUCT(lldpd_hardware, lldpd_port, h_lport)
+MARSHAL_SUBTQ(lldpd_hardware, lldpd_port, h_rports)
+MARSHAL_END(lldpd_hardware);
+
+struct lldpd_interface {
+    TAILQ_ENTRY(lldpd_interface) next;
+    char *name;
+};
+MARSHAL_BEGIN(lldpd_interface)
+MARSHAL_TQE(lldpd_interface, next)
+MARSHAL_STR(lldpd_interface, name)
+MARSHAL_END(lldpd_interface);
+TAILQ_HEAD(lldpd_interface_list, lldpd_interface);
+MARSHAL_TQ(lldpd_interface_list, lldpd_interface);
+
+struct lldpd_neighbor_change {
+    char *ifname;
+#define NEIGHBOR_CHANGE_DELETED -1
+#define NEIGHBOR_CHANGE_ADDED    1
+#define NEIGHBOR_CHANGE_UPDATED  0
+    int state;
+    struct lldpd_port *neighbor;
+};
+MARSHAL_BEGIN(lldpd_neighbor_change)
+MARSHAL_STR(lldpd_neighbor_change, ifname)
+MARSHAL_POINTER(lldpd_neighbor_change, lldpd_port, neighbor)
+MARSHAL_END(lldpd_neighbor_change);
+
+/* Cleanup functions */
+void lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *);
+void lldpd_chassis_cleanup(struct lldpd_chassis *, int);
+void lldpd_remote_cleanup(struct lldpd_hardware *,
+    void(*expire)(struct lldpd_hardware *, struct lldpd_port *),
+    int);
+void lldpd_port_cleanup(struct lldpd_port *, int);
+void lldpd_config_cleanup(struct lldpd_config *);
+#ifdef ENABLE_DOT1
+void lldpd_ppvid_cleanup(struct lldpd_port *);
+void lldpd_vlan_cleanup(struct lldpd_port *);
+void lldpd_pi_cleanup(struct lldpd_port *);
+#endif
+
+#endif
diff --git a/lib/lldpd.c b/lib/lldpd.c
new file mode 100644
index 0000000..a0c6964
--- /dev/null
+++ b/lib/lldpd.c
@@ -0,0 +1,1714 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat at luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include "lldpd.h"
+#ifndef ENABLE_AUTO_ATTACH
+#include "trace.h"
+#else
+#include "compiler.h"
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <libgen.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/if_ether.h>
+#include <pwd.h>
+#include <grp.h>
+#include <inttypes.h>
+
+#ifdef ENABLE_AUTO_ATTACH
+VLOG_DEFINE_THIS_MODULE(lldpd);
+#endif
+
+static struct protocol protos[] =
+{
+    { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
+      LLDP_MULTICAST_ADDR },
+#ifdef ENABLE_CDP
+    { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
+      CDP_MULTICAST_ADDR },
+    { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
+      CDP_MULTICAST_ADDR },
+#endif
+#ifdef ENABLE_SONMP
+    { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
+      SONMP_MULTICAST_ADDR },
+#endif
+#ifdef ENABLE_EDP
+    { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
+      EDP_MULTICAST_ADDR },
+#endif
+#ifdef ENABLE_FDP
+    { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
+      FDP_MULTICAST_ADDR },
+#endif
+    { 0, 0, "any", ' ', NULL, NULL, NULL,
+      {0,0,0,0,0,0} }
+};
+
+void lldpd_assign_cfg_to_protocols(struct lldpd *cfg);
+void lldpd_assign_cfg_to_protocols(struct lldpd *cfg)
+{
+    cfg->g_protocols = protos;
+}
+
+#ifndef ENABLE_AUTO_ATTACH
+static char        **saved_argv;
+#endif
+#ifdef HAVE___PROGNAME
+extern const char    *__progname;
+#else
+# define __progname "lldpd"
+#endif
+
+#ifndef ENABLE_AUTO_ATTACH
+static void
+usage(void)
+{
+    fprintf(stderr, "Usage:   %s [OPTIONS ...]\n", __progname);
+    fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
+
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "-d       Do not daemonize.\n");
+    fprintf(stderr, "-r       Receive-only mode\n");
+    fprintf(stderr, "-i       Disable LLDP-MED inventory TLV transmission.\n");
+    fprintf(stderr, "-k       Disable advertising of kernel release, "
+        "version, machine.\n");
+    fprintf(stderr, "-S descr Override the default system description.\n");
+    fprintf(stderr, "-P name  Override the default hardware platform.\n");
+    fprintf(stderr, "-m IP    Specify the IPv4 management addresses of this "
+        "system.\n");
+    fprintf(stderr, "-u file  Specify the Unix-domain socket used for "
+            "communication with lldpctl(8).\n");
+    fprintf(stderr, "-H mode  Specify the behaviour when detecting multiple "
+        "neighbors.\n");
+    fprintf(stderr, "-I iface Limit interfaces to use.\n");
+#ifdef ENABLE_LLDPMED
+    fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' "
+        "should be one of:\n");
+    fprintf(stderr, "             1 Generic Endpoint (Class I)\n");
+    fprintf(stderr, "             2 Media Endpoint (Class II)\n");
+    fprintf(stderr, "             3 Communication Device Endpoints (Class III)"
+        "\n");
+    fprintf(stderr, "             4 Network Connectivity Device\n");
+#endif
+#ifdef USE_SNMP
+    fprintf(stderr, "-x       Enable SNMP subagent.\n");
+#endif
+    fprintf(stderr, "\n");
+
+#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || \
+    defined ENABLE_SONMP
+    fprintf(stderr, "Additional protocol support.\n");
+#ifdef ENABLE_CDP
+    fprintf(stderr, "-c       Enable the support of CDP protocol. (Cisco)\n");
+#endif
+#ifdef ENABLE_EDP
+    fprintf(stderr, "-e       Enable the support of EDP protocol. "
+            "(Extreme)\n");
+#endif
+#ifdef ENABLE_FDP
+    fprintf(stderr, "-f       Enable the support of FDP protocol. "
+            "(Foundry)\n");
+#endif
+#ifdef ENABLE_SONMP
+    fprintf(stderr, "-s       Enable the support of SONMP protocol. "
+            "(Nortel)\n");
+#endif
+
+    fprintf(stderr, "\n");
+#endif
+
+    fprintf(stderr, "see manual page lldpd(8) for more information\n");
+    exit(1);
+}
+#endif
+
+struct lldpd_hardware *
+lldpd_get_hardware(struct lldpd *cfg, char *name, int index,
+                   struct lldpd_ops *ops)
+{
+    struct lldpd_hardware *hardware;
+    TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+        if ((strcmp(hardware->h_ifname, name) == 0) &&
+            (hardware->h_ifindex == index) &&
+            ((!ops) || (ops == hardware->h_ops)))
+            break;
+    }
+    return hardware;
+}
+
+struct lldpd_hardware *
+lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
+{
+    struct lldpd_hardware *hardware;
+
+    log_debug("alloc", "allocate a new local hardware interface (%s)", name);
+
+    if ((hardware = (struct lldpd_hardware *)
+        calloc(1, sizeof(struct lldpd_hardware))) == NULL)
+        return NULL;
+
+    hardware->h_cfg = cfg;
+#ifndef ENABLE_AUTO_ATTACH
+    strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
+#else
+    ovs_strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
+#endif
+    hardware->h_ifindex = index;
+    hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
+    hardware->h_lport.p_chassis->c_refcount++;
+    TAILQ_INIT(&hardware->h_rports);
+
+#ifdef ENABLE_LLDPMED
+    if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
+        hardware->h_lport.p_med_cap_enabled = LLDP_MED_CAP_CAP;
+        if (!cfg->g_config.c_noinventory)
+            hardware->h_lport.p_med_cap_enabled |= LLDP_MED_CAP_IV;
+    }
+#endif
+#ifdef ENABLE_DOT1
+    TAILQ_INIT(&hardware->h_lport.p_vlans);
+    TAILQ_INIT(&hardware->h_lport.p_ppvids);
+    TAILQ_INIT(&hardware->h_lport.p_pids);
+#endif
+
+#ifndef ENABLE_AUTO_ATTACH
+    levent_hardware_init(hardware);
+#endif
+    return hardware;
+}
+
+struct lldpd_mgmt *
+lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
+{
+    struct lldpd_mgmt *mgmt;
+
+    log_debug("alloc", "allocate a new management address (family: %d)"
+            , family);
+
+    if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) {
+        errno = EAFNOSUPPORT;
+        return NULL;
+    }
+    if (addrsize > LLDPD_MGMT_MAXADDRSIZE) {
+        errno = EOVERFLOW;
+        return NULL;
+    }
+    mgmt = calloc(1, sizeof(struct lldpd_mgmt));
+    if (mgmt == NULL) {
+        errno = ENOMEM;
+        return NULL;
+    }
+    mgmt->m_family = family;
+#ifndef ENABLE_AUTO_ATTACH
+    assert(addrsize <= LLDPD_MGMT_MAXADDRSIZE);
+#endif
+    memcpy(&mgmt->m_addr, addrptr, addrsize);
+    mgmt->m_addrsize = addrsize;
+    mgmt->m_iface = iface;
+    return mgmt;
+}
+
+void
+lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
+{
+    log_debug("alloc", "cleanup hardware port %s", hardware->h_ifname);
+
+    lldpd_port_cleanup(&hardware->h_lport, 1);
+    if (hardware->h_ops && hardware->h_ops->cleanup)
+        hardware->h_ops->cleanup(cfg, hardware);
+#ifndef ENABLE_AUTO_ATTACH
+    levent_hardware_release(hardware);
+#endif
+    free(hardware);
+}
+
+static void
+lldpd_display_neighbors(struct lldpd *cfg)
+{
+    struct lldpd_hardware *hardware;
+    if (!cfg->g_config.c_set_ifdescr) return;
+    TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+        struct lldpd_port *port;
+        char *description;
+        const char *neighbor = NULL;
+        unsigned neighbors = 0;
+        TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+            if (SMART_HIDDEN(port)) continue;
+            neighbors++;
+            neighbor = port->p_chassis->c_name;
+        }
+        if (neighbors == 0)
+#ifdef ENABLE_AUTO_ATTACH
+            /* TODO need replacement for priv_iface_description */
+                        ;
+#else
+            priv_iface_description(hardware->h_ifname, "");
+#endif
+        else if (neighbors == 1 && neighbor && *neighbor != '\0') {
+            if (asprintf(&description, "%s", neighbor) != -1) {
+#ifdef ENABLE_AUTO_ATTACH
+                /* TODO need replacement for priv_iface_description */
+#else
+                priv_iface_description(hardware->h_ifname, description);
+#endif
+                free(description);
+            }
+        } else {
+            if (asprintf(&description, "%d neighbor%s",
+                neighbors, (neighbors > 1)?"s":"") != -1) {
+#ifdef ENABLE_AUTO_ATTACH
+                /* TODO need replacement for priv_iface_description */
+#else
+                priv_iface_description(hardware->h_ifname, description);
+#endif
+                free(description);
+            }
+        }
+    }
+}
+
+static void
+lldpd_count_neighbors(struct lldpd *cfg)
+{
+#if HAVE_SETPROCTITLE
+    struct lldpd_chassis *chassis;
+    const char *neighbor;
+    unsigned neighbors = 0;
+    TAILQ_FOREACH(chassis, &cfg->g_chassis, c_entries) {
+        neighbors++;
+        neighbor = chassis->c_name;
+    }
+    neighbors--;
+    if (neighbors == 0)
+        setproctitle("no neighbor");
+    else if (neighbors == 1 && neighbor && *neighbor != '\0')
+        setproctitle("connected to %s", neighbor);
+    else
+        setproctitle("%d neighbor%s", neighbors,
+            (neighbors > 1)?"s":"");
+#endif
+    lldpd_display_neighbors(cfg);
+}
+
+#ifndef ENABLE_AUTO_ATTACH
+static void
+notify_clients_deletion(struct lldpd_hardware *hardware,
+    struct lldpd_port *rport)
+{
+    TRACE(LLDPD_NEIGHBOR_DELETE(hardware->h_ifname,
+        rport->p_chassis->c_name,
+        rport->p_descr));
+    levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_DELETED,
+        rport);
+
+#ifdef USE_SNMP
+    agent_notify(hardware, NEIGHBOR_CHANGE_DELETED, rport);
+#endif
+}
+#else
+static void
+notify_clients_deletion(struct lldpd_hardware *hardware OVS_UNUSED,
+    struct lldpd_port *rport OVS_UNUSED)
+#endif
+{
+}
+
+#ifndef ENABLE_AUTO_ATTACH
+static void
+lldpd_reset_timer(struct lldpd *cfg)
+{
+    /* Reset timer for ports that have been changed. */
+    struct lldpd_hardware *hardware;
+    TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+        /* We need to compute a checksum of the local port. To do this,
+         * we zero out fields that are not significant, marshal the
+         * port, compute the checksum, then restore. */
+        struct lldpd_port *port = &hardware->h_lport;
+        u_int16_t cksum;
+        u_int8_t *output = NULL;
+        size_t output_len=0;
+        char save[offsetof(struct lldpd_port, p_id_subtype)];
+        memcpy(save, port, sizeof(save));
+        /* coverity[suspicious_sizeof]
+           We intentionally partially memset port */
+        memset(port, 0, sizeof(save));
+        output_len = lldpd_port_serialize(port, (void**)&output);
+        memcpy(port, save, sizeof(save));
+        if (output_len == -1) {
+            log_warnx("localchassis",
+                "unable to serialize local port %s to check for differences",
+                hardware->h_ifname);
+            continue;
+        }
+        cksum = frame_checksum(output, output_len, 0);
+        free(output);
+        if (cksum != hardware->h_lport_cksum) {
+            log_debug("localchassis",
+                "change detected for port %s, resetting its timer",
+                hardware->h_ifname);
+            hardware->h_lport_cksum = cksum;
+            levent_schedule_pdu(hardware);
+        } else {
+            log_debug("localchassis",
+                "no change detected for port %s",
+                hardware->h_ifname);
+        }
+    }
+}
+#endif
+
+void
+lldpd_cleanup(struct lldpd *cfg)
+{
+    struct lldpd_hardware *hardware, *hardware_next;
+    struct lldpd_chassis *chassis, *chassis_next;
+
+    log_debug("localchassis", "cleanup all ports");
+
+    for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
+         hardware = hardware_next) {
+        hardware_next = TAILQ_NEXT(hardware, h_entries);
+        if (!hardware->h_flags) {
+#ifndef ENABLE_AUTO_ATTACH
+            TRACE(LLDPD_INTERFACES_DELETE(hardware->h_ifname));
+#endif
+            TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
+            lldpd_remote_cleanup(hardware, notify_clients_deletion, 1);
+            lldpd_hardware_cleanup(cfg, hardware);
+        } else
+            lldpd_remote_cleanup(hardware, notify_clients_deletion, 0);
+    }
+
+    log_debug("localchassis", "cleanup all chassis");
+
+    for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
+         chassis = chassis_next) {
+        chassis_next = TAILQ_NEXT(chassis, c_entries);
+        if (chassis->c_refcount == 0) {
+            TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
+            lldpd_chassis_cleanup(chassis, 1);
+        }
+    }
+
+    lldpd_count_neighbors(cfg);
+#ifndef ENABLE_AUTO_ATTACH
+    levent_schedule_cleanup(cfg);
+#endif
+}
+
+/* Update chassis `ochassis' with values from `chassis'. The later one is not
+   expected to be part of a list! It will also be wiped from memory. */
+static void
+lldpd_move_chassis(struct lldpd_chassis *ochassis,
+    struct lldpd_chassis *chassis) {
+    struct lldpd_mgmt *mgmt, *mgmt_next;
+
+    /* We want to keep refcount, index and list stuff from the current
+     * chassis */
+    TAILQ_ENTRY(lldpd_chassis) entries;
+    int refcount = ochassis->c_refcount;
+    int index = ochassis->c_index;
+    memcpy(&entries, &ochassis->c_entries,
+        sizeof(entries));
+    lldpd_chassis_cleanup(ochassis, 0);
+
+    /* Make the copy. */
+    /* WARNING: this is a kludgy hack, we need in-place copy and cannot use
+     * marshaling. */
+    memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
+    TAILQ_INIT(&ochassis->c_mgmt);
+
+    /* Copy of management addresses */
+    for (mgmt = TAILQ_FIRST(&chassis->c_mgmt);
+         mgmt != NULL;
+         mgmt = mgmt_next) {
+        mgmt_next = TAILQ_NEXT(mgmt, m_entries);
+        TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries);
+        TAILQ_INSERT_TAIL(&ochassis->c_mgmt, mgmt, m_entries);
+    }
+
+    /* Restore saved values */
+    ochassis->c_refcount = refcount;
+    ochassis->c_index = index;
+    memcpy(&ochassis->c_entries, &entries, sizeof(entries));
+
+    /* Get rid of the new chassis */
+    free(chassis);
+}
+
+static int
+lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
+{
+    int i;
+    if (s < ETHER_ADDR_LEN)
+        return -1;
+    for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+        if (!cfg->g_protocols[i].enabled)
+            continue;
+        if (cfg->g_protocols[i].guess == NULL) {
+            if (memcmp(frame, cfg->g_protocols[i].mac, ETHER_ADDR_LEN) == 0) {
+                log_debug("decode", "guessed protocol is %s (from MAC address)",
+                    cfg->g_protocols[i].name);
+                return cfg->g_protocols[i].mode;
+            }
+        } else {
+            if (cfg->g_protocols[i].guess(frame, s)) {
+                log_debug("decode", "guessed protocol is %s (from detector "
+                    "function)",
+                    cfg->g_protocols[i].name);
+                return cfg->g_protocols[i].mode;
+            }
+        }
+    }
+    return -1;
+}
+
+static void
+lldpd_decode(struct lldpd *cfg, char *frame, int s,
+    struct lldpd_hardware *hardware)
+{
+    int i;
+    struct lldpd_chassis *chassis, *ochassis = NULL;
+    struct lldpd_port *port, *oport = NULL, *aport;
+    int guess = LLDPD_MODE_LLDP;
+    struct ether_header eheader;
+    int count = 0;
+
+    log_debug("decode", "decode a received frame on %s size %d",
+        hardware->h_ifname,s);
+
+    if (s < sizeof(struct ether_header) + 4)
+        /* Too short, just discard it */
+        return;
+
+    /* Decapsulate VLAN frames */
+    memcpy(&eheader, frame, sizeof(struct ether_header));
+    if (eheader.ether_type == htons(ETHERTYPE_VLAN)) {
+        /* VLAN decapsulation means to shift 4 bytes left the frame from
+         * offset 2*ETHER_ADDR_LEN */
+        memmove(frame + 2*ETHER_ADDR_LEN, frame + 2*ETHER_ADDR_LEN + 4,
+            s - 2*ETHER_ADDR_LEN);
+        s -= 4;
+    }
+
+    log_debug("decode", "Received frame ether header: "
+        "dst:0x%.2x%.2x%.2x%.2x%.2x%.2x "
+        "src:0x%.2x%.2x%.2x%.2x%.2x%.2x type:0x%.4x ",
+          eheader.ether_dhost[0],
+          eheader.ether_dhost[1],
+          eheader.ether_dhost[2],
+          eheader.ether_dhost[3],
+          eheader.ether_dhost[4],
+          eheader.ether_dhost[5],
+
+          eheader.ether_shost[0],
+          eheader.ether_shost[1],
+          eheader.ether_shost[2],
+          eheader.ether_shost[3],
+          eheader.ether_shost[4],
+          eheader.ether_shost[5],
+
+          ntohs(eheader.ether_type));
+
+    TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
+        if ((oport->p_lastframe != NULL) &&
+            (oport->p_lastframe->size == s) &&
+            (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
+            /* Already received the same frame */
+            log_debug("decode", "duplicate frame, no need to decode");
+            oport->p_lastupdate = time(NULL);
+            return;
+        }
+    }
+
+    guess = lldpd_guess_type(cfg, frame, s);
+    log_debug("decode", "guessed %d         enabled:%d",
+        guess,cfg->g_protocols[0].enabled);
+
+    for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+        if (!cfg->g_protocols[i].enabled)
+            continue;
+        if (cfg->g_protocols[i].mode == guess) {
+            log_debug("decode", "using decode function for %s protocol",
+                cfg->g_protocols[i].name);
+            if (cfg->g_protocols[i].decode(cfg, frame,
+                s, hardware, &chassis, &port) == -1) {
+                log_debug("decode", "function for %s protocol did not "
+                    "decode this frame",
+                    cfg->g_protocols[i].name);
+                return;
+            }
+            chassis->c_protocol = port->p_protocol =
+                cfg->g_protocols[i].mode;
+            break;
+            }
+      log_debug("decode", " %d mode:%d enabled:%d",
+          i,cfg->g_protocols[i].mode,cfg->g_protocols[i].enabled);
+    }
+    if (cfg->g_protocols[i].mode == 0) {
+        log_debug("decode", "unable to guess frame type on %s",
+            hardware->h_ifname);
+        return;
+    }
+#ifndef ENABLE_AUTO_ATTACH
+    TRACE(LLDPD_FRAME_DECODED(
+            hardware->h_ifname,
+            cfg->g_protocols[i].name,
+            chassis->c_name,
+            port->p_descr));
+#endif
+
+    /* Do we already have the same MSAP somewhere? */
+    log_debug("decode", "search for the same MSAP");
+    TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
+        if (port->p_protocol == oport->p_protocol) {
+            count++;
+            if ((port->p_id_subtype == oport->p_id_subtype) &&
+                (port->p_id_len == oport->p_id_len) &&
+                (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
+                (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
+                (chassis->c_id_len == oport->p_chassis->c_id_len) &&
+                (memcmp(chassis->c_id, oport->p_chassis->c_id,
+                chassis->c_id_len) == 0)) {
+                ochassis = oport->p_chassis;
+                log_debug("decode", "MSAP is already known");
+                break;
+            }
+        }
+    }
+    /* Do we have room for a new MSAP? */
+    if (!oport && cfg->g_config.c_max_neighbors) {
+        if (count == (cfg->g_config.c_max_neighbors - 1)) {
+        log_debug("decode",
+            "max neighbors %d reached for port %s, "
+            "dropping any new ones silently",
+            cfg->g_config.c_max_neighbors,
+            hardware->h_ifname);
+        } else if (count > cfg->g_config.c_max_neighbors - 1) {
+        log_debug("decode",
+            "too many neighbors for port %s, drop this new one",
+            hardware->h_ifname);
+        lldpd_port_cleanup(port, 1);
+        lldpd_chassis_cleanup(chassis, 1);
+        free(port);
+        return;
+        }
+    }
+    /* No, but do we already know the system? */
+    if (!oport) {
+        log_debug("decode", "MSAP is unknown, search for the chassis");
+        TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) {
+            if ((chassis->c_protocol == ochassis->c_protocol) &&
+                (chassis->c_id_subtype == ochassis->c_id_subtype) &&
+                (chassis->c_id_len == ochassis->c_id_len) &&
+                (memcmp(chassis->c_id, ochassis->c_id,
+                chassis->c_id_len) == 0))
+            break;
+        }
+    }
+
+    if (oport) {
+        /* The port is known, remove it before adding it back */
+        TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
+        lldpd_port_cleanup(oport, 1);
+        free(oport);
+    }
+    if (ochassis) {
+        lldpd_move_chassis(ochassis, chassis);
+        chassis = ochassis;
+    } else {
+        /* Chassis not known, add it */
+        log_debug("decode", "unknown chassis, add it to the list");
+        chassis->c_index = ++cfg->g_lastrid;
+        chassis->c_refcount = 0;
+        TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
+        i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
+        log_debug("decode", "%d different systems are known", i);
+    }
+    /* Add port */
+    port->p_lastchange = port->p_lastupdate = time(NULL);
+    if ((port->p_lastframe = (struct lldpd_frame *)malloc(s +
+            sizeof(struct lldpd_frame))) != NULL) {
+        port->p_lastframe->size = s;
+        memcpy(port->p_lastframe->frame, frame, s);
+    }
+    TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
+    port->p_chassis = chassis;
+    port->p_chassis->c_refcount++;
+    /* Several cases are possible :
+         1. chassis is new, its refcount was 0. It is now attached
+            to this port, its refcount is 1.
+         2. chassis already exists and was attached to another
+            port, we increase its refcount accordingly.
+         3. chassis already exists and was attached to the same
+            port, its refcount was decreased with
+            lldpd_port_cleanup() and is now increased again.
+
+       In all cases, if the port already existed, it has been
+       freed with lldpd_port_cleanup() and therefore, the refcount
+       of the chassis that was attached to it is decreased.
+    */
+    /* coverity[use_after_free]
+       TAILQ_REMOVE does the right thing */
+    i = 0; TAILQ_FOREACH(aport, &hardware->h_rports, p_entries)
+        i++;
+    log_debug("decode", "%d neighbors for %s", i,
+        hardware->h_ifname);
+
+    if (!oport) hardware->h_insert_cnt++;
+
+    /* Notify */
+    log_debug("decode", "send notifications for changes on %s",
+        hardware->h_ifname);
+    if (oport) {
+#ifndef ENABLE_AUTO_ATTACH
+        TRACE(LLDPD_NEIGHBOR_UPDATE(hardware->h_ifname,
+            chassis->c_name,
+            port->p_descr,
+            i));
+        levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_UPDATED, port);
+#endif
+#ifdef USE_SNMP
+        agent_notify(hardware, NEIGHBOR_CHANGE_UPDATED, port);
+#endif
+    } else {
+#ifndef ENABLE_AUTO_ATTACH
+        TRACE(LLDPD_NEIGHBOR_NEW(hardware->h_ifname,
+            chassis->c_name,
+            port->p_descr,
+            i));
+        levent_ctl_notify(hardware->h_ifname, NEIGHBOR_CHANGE_ADDED, port);
+#endif
+#ifdef USE_SNMP
+        agent_notify(hardware, NEIGHBOR_CHANGE_ADDED, port);
+#endif
+    }
+
+#ifdef ENABLE_LLDPMED
+    if (!oport && port->p_chassis->c_med_type) {
+        /* New neighbor, fast start */
+        if (hardware->h_cfg->g_config.c_enable_fast_start &&
+            !hardware->h_tx_fast) {
+            log_debug("decode", "%s: entering fast start due to "
+                "new neighbor", hardware->h_ifname);
+            hardware->h_tx_fast = hardware->h_cfg->g_config.c_tx_fast_init;
+        }
+
+        levent_schedule_pdu(hardware);
+    }
+#endif
+
+    log_debug("decode","%s: end of function.",__FUNCTION__);
+    return;
+}
+
+/* Get the output of lsb_release -s -d.  This is a slow function. It should be
+   called once. It return NULL if any problem happens. Otherwise, this is a
+   statically allocated buffer. The result includes the trailing \n  */
+#ifndef ENABLE_AUTO_ATTACH
+static char *
+lldpd_get_lsb_release(void) {
+    static char release[1024];
+    char *const command[] = { "lsb_release", "-s", "-d", NULL };
+    int pid, status, devnull, count;
+    int pipefd[2];
+
+    log_debug("localchassis", "grab LSB release");
+
+    if (pipe(pipefd)) {
+        log_warn("localchassis", "unable to get a pair of pipes");
+        return NULL;
+    }
+
+    pid = vfork();
+    switch (pid) {
+    case -1:
+        log_warn("localchassis", "unable to fork");
+        return NULL;
+    case 0:
+        /* Child, exec lsb_release */
+        close(pipefd[0]);
+        if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) {
+            dup2(devnull, STDIN_FILENO);
+            dup2(devnull, STDERR_FILENO);
+            dup2(pipefd[1], STDOUT_FILENO);
+            if (devnull > 2) close(devnull);
+            if (pipefd[1] > 2) close(pipefd[1]);
+            execvp("lsb_release", command);
+        }
+        _exit(127);
+        break;
+    default:
+        /* Father, read the output from the children */
+        close(pipefd[1]);
+        count = 0;
+        do {
+            status = read(pipefd[0], release+count, sizeof(release)-count);
+            if ((status == -1) && (errno == EINTR)) continue;
+            if (status > 0)
+                count += status;
+        } while (count < sizeof(release) && (status > 0));
+        if (status < 0) {
+            log_info("localchassis", "unable to read from lsb_release");
+            close(pipefd[0]);
+            waitpid(pid, &status, 0);
+            return NULL;
+        }
+        close(pipefd[0]);
+        if (count >= sizeof(release)) {
+            log_info("localchassis", "output of lsb_release is too large");
+            waitpid(pid, &status, 0);
+            return NULL;
+        }
+        status = -1;
+        if (waitpid(pid, &status, 0) != pid)
+            return NULL;
+        if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
+            log_info("localchassis", "lsb_release information not available");
+            return NULL;
+        }
+        if (!count) {
+            log_info("localchassis", "lsb_release returned an empty string");
+            return NULL;
+        }
+        release[count] = '\0';
+        return release;
+    }
+    /* Should not be here */
+    return NULL;
+}
+#endif
+
+/* Same like lldpd_get_lsb_release but reads /etc/os-release
+ * for PRETTY_NAME=. */
+#ifndef ENABLE_AUTO_ATTACH
+static char *
+lldpd_get_os_release(void) {
+    static char release[1024];
+    char line[1024];
+    char *key, *val;
+    char *ptr1 = release;
+
+    FILE *fp = fopen("/etc/os-release", "r");
+    log_debug("localchassis", "grab OS release");
+    if (!fp) {
+        log_info("localchassis", "could not open /etc/os-release");
+        return NULL;
+    }
+
+    while ((fgets(line, sizeof(line), fp) != NULL)) {
+        key = strtok_r(line, "=");
+        val = strtok_r(NULL, "=");
+
+        if (strncmp(key, "PRETTY_NAME", sizeof(line)) == 0) {
+            strlcpy(release, val, sizeof(line));
+            break;
+        }
+    }
+    fclose(fp);
+
+    /* Remove trailing newline and all " in the string. */
+    ptr1 = release + strlen(release) - 1;
+    while (ptr1 != release &&
+        ((*ptr1 == '"') || (*ptr1 == '\n'))) {
+        *ptr1 = '\0';
+        ptr1--;
+    }
+    if (release[0] == '"')
+        return release+1;
+    return release;
+}
+#endif
+
+static void
+lldpd_hide_ports(struct lldpd *cfg,
+                 struct lldpd_hardware *hardware,
+                 int mask) {
+    struct lldpd_port *port;
+    int protocols[LLDPD_MODE_MAX+1];
+    char buffer[256];
+    int i, j, k, found;
+    unsigned int min;
+
+    log_debug("smartfilter", "apply smart filter for port %s",
+        hardware->h_ifname);
+
+    /* Compute the number of occurrences of each protocol */
+    for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
+    TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
+        protocols[port->p_protocol]++;
+
+    /* Turn the protocols[] array into an array of
+       enabled/disabled protocols. 1 means enabled, 0
+       means disabled. */
+    min = (unsigned int)-1;
+    for (i = 0; i <= LLDPD_MODE_MAX; i++)
+        if (protocols[i] && (protocols[i] < min))
+            min = protocols[i];
+    found = 0;
+    for (i = 0; i <= LLDPD_MODE_MAX; i++)
+        if ((protocols[i] == min) && !found) {
+            /* If we need a tie breaker, we take
+               the first protocol only */
+            if (cfg->g_config.c_smart & mask &
+                (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO))
+                found = 1;
+            protocols[i] = 1;
+        } else protocols[i] = 0;
+
+    /* We set the p_hidden flag to 1 if the protocol is disabled */
+    TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+        if (mask == SMART_OUTGOING)
+            port->p_hidden_out = protocols[port->p_protocol]?0:1;
+        else
+            port->p_hidden_in = protocols[port->p_protocol]?0:1;
+    }
+
+    /* If we want only one neighbor, we take the first one */
+    if (cfg->g_config.c_smart & mask &
+        (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
+        found = 0;
+        TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+            if (mask == SMART_OUTGOING) {
+                if (found) port->p_hidden_out = 1;
+                if (!port->p_hidden_out)
+                    found = 1;
+            }
+            if (mask == SMART_INCOMING) {
+                if (found) port->p_hidden_in = 1;
+                if (!port->p_hidden_in)
+                    found = 1;
+            }
+        }
+    }
+
+    /* Print a debug message summarizing the operation */
+    for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
+    k = j = 0;
+    TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+        if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) ||
+              ((mask == SMART_INCOMING) && port->p_hidden_in))) {
+            k++;
+            protocols[port->p_protocol] = 1;
+        }
+        j++;
+    }
+    buffer[0] = '\0';
+    for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+        if (cfg->g_protocols[i].enabled &&
+                protocols[cfg->g_protocols[i].mode]) {
+            if (strlen(buffer) +
+                strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
+                /* Unlikely, our buffer is too small */
+                memcpy(buffer + sizeof(buffer) - 4, "...", 4);
+                break;
+            }
+            if (buffer[0])
+                strncat(buffer, ", ", 2);
+            strncat(buffer, cfg->g_protocols[i].name,
+                strlen(cfg->g_protocols[i].name));
+        }
+    }
+    log_debug("smartfilter", "%s: %s: %d visible neighbors (out of %d)",
+        hardware->h_ifname,
+        (mask == SMART_OUTGOING)?"out filter":"in filter",
+        k, j);
+    log_debug("smartfilter", "%s: protocols: %s",
+        hardware->h_ifname, buffer[0]?buffer:"(none)");
+}
+
+/* Hide unwanted ports depending on smart mode set by the user */
+static void
+lldpd_hide_all(struct lldpd *cfg)
+{
+    struct lldpd_hardware *hardware;
+
+    if (!cfg->g_config.c_smart)
+        return;
+    log_debug("smartfilter", "apply smart filter results on all ports");
+    TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+        if (cfg->g_config.c_smart & SMART_INCOMING_FILTER)
+            lldpd_hide_ports(cfg, hardware, SMART_INCOMING);
+        if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER)
+            lldpd_hide_ports(cfg, hardware, SMART_OUTGOING);
+    }
+}
+
+// TBD: AN - fd is unused since we don't go to hw to recv in OVS world
+void
+#ifndef ENABLE_AUTO_ATTACH
+lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd)
+#else
+lldpd_recv(struct lldpd *cfg,
+           struct lldpd_hardware *hardware,
+           char *buffer,
+           size_t bufSize)
+#endif
+{
+#ifndef ENABLE_AUTO_ATTACH
+    char *buffer = NULL;
+#endif
+    int n=0;
+#ifdef ENABLE_AUTO_ATTACH
+        n=bufSize;
+#endif
+    log_debug("receive", "receive a frame on %s",
+        hardware->h_ifname);
+#ifndef ENABLE_AUTO_ATTACH
+    if ((buffer = (char *)malloc(hardware->h_mtu)) == NULL) {
+        log_warn("receive", "failed to alloc reception buffer");
+        return;
+    }
+    if ((n = hardware->h_ops->recv(cfg, hardware,
+            fd, buffer,
+            hardware->h_mtu)) == -1) {
+        log_debug("receive", "discard frame received on %s",
+            hardware->h_ifname);
+        free(buffer);
+        return;
+    }
+#endif
+    if (cfg->g_config.c_paused) {
+        log_debug("receive", "paused, ignore the frame on %s",
+            hardware->h_ifname);
+#ifndef ENABLE_AUTO_ATTACH
+        free(buffer);
+#endif
+        return;
+    }
+    hardware->h_rx_cnt++;
+    log_debug("receive", "decode received frame on %s h_rx_cnt=%" PRIu64,
+        hardware->h_ifname, hardware->h_rx_cnt);
+#ifndef ENABLE_AUTO_ATTACH
+    TRACE(LLDPD_FRAME_RECEIVED(hardware->h_ifname, buffer, (size_t)n));
+#endif
+    lldpd_decode(cfg, buffer, n, hardware);
+    lldpd_hide_all(cfg); /* Immediatly hide */
+    lldpd_count_neighbors(cfg);
+#ifndef ENABLE_AUTO_ATTACH
+    free(buffer);
+#endif
+}
+
+#ifndef ENABLE_AUTO_ATTACH
+uint32_t
+lldpd_send(struct lldpd_hardware *hardware)
+#else
+uint32_t
+lldpd_send(struct lldpd_hardware *hardware, unsigned char *p)
+#endif
+{
+    struct lldpd *cfg = hardware->h_cfg;
+    struct lldpd_port *port;
+    int i, sent;
+#ifdef ENABLE_AUTO_ATTACH
+    int lldp_size = 0;
+#endif
+
+    if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused)
+        return 0;
+    if ((hardware->h_flags & IFF_RUNNING) == 0)
+        return 0;
+
+    sent = 0;
+    for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+        if (!cfg->g_protocols[i].enabled)
+            continue;
+        /* We send only if we have at least one remote system
+         * speaking this protocol or if the protocol is forced */
+        if (cfg->g_protocols[i].enabled > 1) {
+#ifndef ENABLE_AUTO_ATTACH
+            cfg->g_protocols[i].send(cfg, hardware);
+            sent++;
+            continue;
+#else
+            if ( (lldp_size = cfg->g_protocols[i].send(cfg, hardware, p))
+                  != E2BIG)
+                        {
+              sent++;
+                          continue;
+                        }
+                        else
+                        {
+                      log_debug("send", "send PDU on %s failed E2BIG",
+                                    hardware->h_ifname);
+                          continue;
+                        }
+#endif
+        }
+        TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+            /* If this remote port is disabled, we don't
+             * consider it */
+            if (port->p_hidden_out)
+                continue;
+            if (port->p_protocol ==
+                cfg->g_protocols[i].mode) {
+#ifndef ENABLE_AUTO_ATTACH
+                TRACE(LLDPD_FRAME_SEND(hardware->h_ifname,
+                    cfg->g_protocols[i].name));
+#endif
+                log_debug("send", "send PDU on %s with protocol %s",
+                    hardware->h_ifname,
+                    cfg->g_protocols[i].name);
+#ifndef ENABLE_AUTO_ATTACH
+                cfg->g_protocols[i].send(cfg,
+                    hardware);
+#else
+                lldp_size = cfg->g_protocols[i].send(cfg,
+                    hardware,p);
+#endif
+                sent++;
+                break;
+            }
+        }
+    }
+
+    if (!sent) {
+        /* Nothing was sent for this port, let's speak the first
+         * available protocol. */
+        for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
+            if (!cfg->g_protocols[i].enabled) continue;
+#ifndef ENABLE_AUTO_ATTACH
+            TRACE(LLDPD_FRAME_SEND(hardware->h_ifname,
+                cfg->g_protocols[i].name));
+#endif
+            log_debug("send", "fallback to protocol %s for %s",
+                cfg->g_protocols[i].name, hardware->h_ifname);
+#ifndef ENABLE_AUTO_ATTACH
+            cfg->g_protocols[i].send(cfg, hardware);
+#else
+            lldp_size = cfg->g_protocols[i].send(cfg,
+                hardware,p);
+#endif
+            break;
+        }
+        if (cfg->g_protocols[i].mode == 0)
+            log_warnx("send", "no protocol enabled, dunno what to send");
+    }
+#ifdef ENABLE_AUTO_ATTACH
+  return lldp_size;
+#endif
+}
+
+#ifdef ENABLE_LLDPMED
+static void
+lldpd_med(struct lldpd_chassis *chassis)
+{
+    static short int once = 0;
+    if (!once) {
+        chassis->c_med_hw = dmi_hw();
+        chassis->c_med_fw = dmi_fw();
+        chassis->c_med_sn = dmi_sn();
+        chassis->c_med_manuf = dmi_manuf();
+        chassis->c_med_model = dmi_model();
+        chassis->c_med_asset = dmi_asset();
+        once = 1;
+    }
+}
+#endif
+
+#ifndef ENABLE_AUTO_ATTACH
+static int
+lldpd_routing_enabled(struct lldpd *cfg)
+{
+    int routing;
+    if ((routing = interfaces_routing_enabled(cfg)) == -1) {
+        log_debug("localchassis", "unable to check if routing is enabled");
+        return 0;
+    }
+    return routing;
+}
+#endif
+
+#ifndef ENABLE_AUTO_ATTACH
+static void
+lldpd_exit(struct lldpd *cfg)
+{
+    struct lldpd_hardware *hardware, *hardware_next;
+    log_debug("main", "exit lldpd");
+    close(cfg->g_ctl);
+    priv_ctl_cleanup(cfg->g_ctlname);
+    log_debug("main", "cleanup hardware information");
+    for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
+         hardware = hardware_next) {
+        hardware_next = TAILQ_NEXT(hardware, h_entries);
+        log_debug("main", "cleanup interface %s", hardware->h_ifname);
+        lldpd_remote_cleanup(hardware, NULL, 1);
+        lldpd_hardware_cleanup(cfg, hardware);
+    }
+}
+#endif
+
+#ifndef ENABLE_AUTO_ATTACH
+/**
+ * Run lldpcli to configure lldpd.
+ *
+ * @return PID of running lldpcli or -1 if error.
+ */
+static pid_t
+lldpd_configure(int debug, const char *path, const char *ctlname)
+{
+    pid_t lldpcli = vfork();
+    int devnull;
+
+    char sdebug[debug + 3];
+    memset(sdebug, 'd', debug + 3);
+    sdebug[debug + 2] = '\0';
+    sdebug[0] = '-'; sdebug[1] = 's';
+    log_debug("main", "invoke %s %s", path, sdebug);
+
+    switch (lldpcli) {
+    case -1:
+        log_warn("main", "unable to fork");
+        return -1;
+    case 0:
+        /* Child, exec lldpcli */
+        if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) {
+            dup2(devnull,   STDIN_FILENO);
+            dup2(devnull,   STDOUT_FILENO);
+            if (devnull > 2) close(devnull);
+
+            execl(path, "lldpcli", sdebug,
+                "-u", ctlname,
+                "-c", SYSCONFDIR "/lldpd.conf",
+                "-c", SYSCONFDIR "/lldpd.d",
+                "resume",
+                (char *)NULL);
+            log_warn("main", "unable to execute %s", path);
+            log_warnx("main", "configuration is incomplete, lldpd needs to be "
+                "unpaused");
+        }
+        _exit(127);
+        break;
+    default:
+        /* Father, don't do anything stupid */
+        return lldpcli;
+    }
+    /* Should not be here */
+    return -1;
+}
+#endif
+
+struct intint { int a; int b; };
+static const struct intint filters[] = {
+    {  0, 0 },
+    {  1, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+          SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+    {  2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO },
+    {  3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+    {  4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER },
+    {  5, SMART_INCOMING_FILTER },
+    {  6, SMART_OUTGOING_FILTER },
+    {  7, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+          SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER |
+          SMART_OUTGOING_ONE_PROTO },
+    {  8, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+          SMART_INCOMING_ONE_NEIGH },
+    {  9, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+          SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+    { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+    { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH },
+    { 12, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+          SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+    { 13, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+          SMART_OUTGOING_FILTER },
+    { 14, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+          SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+    { 15, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+          SMART_OUTGOING_FILTER },
+    { 16, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+          SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER |
+          SMART_OUTGOING_ONE_NEIGH },
+    { 17, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+          SMART_INCOMING_ONE_NEIGH | SMART_OUTGOING_FILTER },
+    { 18, SMART_INCOMING_FILTER |
+          SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+    { 19, SMART_INCOMING_FILTER |
+          SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+    { -1, 0 }
+};
+
+#ifndef HOST_OS_OSX
+/**
+ * Tell if we have been started by upstart.
+ */
+#ifndef ENABLE_AUTO_ATTACH
+static int
+lldpd_started_by_upstart(void)
+{
+#ifdef HOST_OS_LINUX
+    const char *upstartjob = getenv("UPSTART_JOB");
+    if (!(upstartjob && !strcmp(upstartjob, "lldpd")))
+        return 0;
+    log_debug("main", "running with upstart, don't fork but stop");
+    raise(SIGSTOP);
+    un_setenv("UPSTART_JOB");
+    return 1;
+#else
+    return 0;
+#endif
+}
+#endif
+
+/**
+ * Tell if we have been started by systemd.
+ */
+#ifndef ENABLE_AUTO_ATTACH
+static int
+lldpd_started_by_systemd(void)
+{
+#ifdef HOST_OS_LINUX
+    int fd = -1;
+    const char *notifysocket = getenv("NOTIFY_SOCKET");
+    if (!notifysocket ||
+        !strchr("@/", notifysocket[0]) ||
+        strlen(notifysocket) < 2)
+        return 0;
+
+    log_debug("main", "running with systemd, don't fork but signal ready");
+    if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
+        log_warn("main", "unable to open systemd notification socket %s",
+            notifysocket);
+        return 0;
+    }
+
+    struct sockaddr_un su = { .sun_family = AF_UNIX };
+    strlcpy(su.sun_path, notifysocket, sizeof(su.sun_path));
+    if (notifysocket[0] == '@') su.sun_path[0] = 0;
+
+    struct iovec iov = {
+        .iov_base = "READY=1",
+        .iov_len = strlen("READY=1")
+    };
+    struct msghdr hdr = {
+        .msg_name = &su,
+        .msg_namelen = offsetof(struct sockaddr_un, sun_path) +
+            strlen(notifysocket),
+        .msg_iov = &iov,
+        .msg_iovlen = 1
+    };
+    u_nsetenv("NOTIFY_SOCKET");
+    if (sendmsg(fd, &hdr, MSG_NOSIGNAL) < 0) {
+        log_warn("main", "unable to send notification to systemd");
+        close(fd);
+        return 0;
+    }
+    close(fd);
+    return 1;
+#else
+    return 0;
+#endif
+}
+#endif
+#endif
+
+#ifndef ENABLE_AUTO_ATTACH
+int
+lldpd_main(int argc, char *argv[], char *envp[])
+{
+    struct lldpd *cfg;
+    struct lldpd_chassis *lchassis;
+    int ch, debug = 3;
+#ifdef USE_SNMP
+    int snmp = 0;
+    char *agentx = NULL;    /* AgentX socket */
+#endif
+    char *ctlname = LLDPD_CTL_SOCKET;
+    char *mgmtp = NULL;
+    char *cidp = NULL;
+    char *interfaces = NULL;
+    /* We do not want more options here. Please add them in lldpcli instead
+     * unless there is a very good reason. Most command-line options will
+     * get deprecated at some point. */
+    char *popt, opts[] =
+        "H:vhkrdD:xX:m:u:4:6:I:C:p:M:P:S:iL:@                    ";
+    int i, found, advertise_version = 1;
+#ifdef ENABLE_LLDPMED
+    int lldpmed = 0, noinventory = 0;
+    int enable_fast_start = 1;
+#endif
+    char *descr_override = NULL;
+    char *platform_override = NULL;
+    char *lsb_release = NULL;
+    const char *lldpcli = LLDPCLI_PATH;
+    int smart = 15;
+    int receiveonly = 0;
+    int ctl=0;
+
+#ifndef ENABLE_AUTO_ATTACH
+    /* Non privileged user */
+    struct passwd *user;
+    struct group *group;
+    uid_t uid;
+    gid_t gid;
+#endif
+
+    saved_argv = argv;
+
+#if HAVE_SETPROCTITLE_INIT
+    setproctitle_init(argc, argv, envp);
+#endif
+
+    /*
+     * Get and parse command line options
+     */
+    if ((popt = strchr(opts, '@')) != NULL) {
+        for (i=0;
+             protos[i].mode != 0 && *popt != '\0';
+             i++)
+            *(popt++) = protos[i].arg;
+        *popt = '\0';
+    }
+    while ((ch = getopt(argc, argv, opts)) != -1) {
+        switch (ch) {
+        case 'h':
+            usage();
+            break;
+        case 'v':
+            fprintf(stdout, "%s\n", PACKAGE_VERSION);
+            exit(0);
+            break;
+        case 'd':
+            debug++;
+            break;
+        case 'D':
+            log_accept(optarg);
+            break;
+        case 'r':
+            receiveonly = 1;
+            break;
+        case 'm':
+            mgmtp = optarg;
+            break;
+        case 'u':
+            ctlname = optarg;
+            break;
+        case 'I':
+            interfaces = optarg;
+            break;
+        case 'C':
+            cidp = optarg;
+            break;
+        case 'L':
+            if (strlen(optarg)) lldpcli = optarg;
+            else lldpcli = NULL;
+            break;
+        case 'k':
+            advertise_version = 0;
+            break;
+#ifdef ENABLE_LLDPMED
+        case 'M':
+            lldpmed = atoi(optarg);
+            if ((lldpmed < 1) || (lldpmed > 4)) {
+                fprintf(stderr, "-M requires an argument between 1 and 4\n");
+                usage();
+            }
+            break;
+        case 'i':
+            noinventory = 1;
+            break;
+#else
+        case 'M':
+        case 'i':
+            fprintf(stderr, "LLDP-MED support is not built-in\n");
+            usage();
+            break;
+#endif
+#ifdef USE_SNMP
+        case 'x':
+            snmp = 1;
+            break;
+        case 'X':
+            snmp = 1;
+            agentx = optarg;
+            break;
+#else
+        case 'x':
+        case 'X':
+            fprintf(stderr, "SNMP support is not built-in\n");
+            usage();
+#endif
+            break;
+                case 'S':
+            free(descr_override);
+                        descr_override = strdup(optarg);
+                        break;
+        case 'P':
+            free(platform_override);
+            platform_override = strdup(optarg);
+            break;
+        case 'H':
+            smart = atoi(optarg);
+            break;
+        default:
+            found = 0;
+            for (i=0; protos[i].mode != 0; i++) {
+                if (ch == protos[i].arg) {
+                    found = 1;
+                    protos[i].enabled++;
+                }
+            }
+            if (!found)
+                usage();
+        }
+    }
+
+    /* Set correct smart mode */
+    for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++);
+    if (filters[i].a == -1) {
+        fprintf(stderr, "Incorrect mode for -H\n");
+        usage();
+    }
+    smart = filters[i].b;
+
+    log_init(debug, __progname);
+    tzset();        /* Get timezone info before chroot */
+
+    log_debug("main", "lldpd starting...");
+
+#ifndef ENABLE_AUTO_ATTACH
+    struct passwd *pwd;
+    struct passwd **result;
+    char *buf;
+    size_t buflen;
+
+    /* Grab uid and gid to use for priv sep */
+    if ((user = getpwnam_r(PRIVSEP_USER)) == NULL)
+        fatal("main", "no " PRIVSEP_USER " user for privilege separation");
+    uid = user->pw_uid;
+    if ((group = getgrnam_r(PRIVSEP_GROUP)) == NULL)
+        fatal("main", "no " PRIVSEP_GROUP " group for privilege separation");
+    gid = group->gr_gid;
+
+    /* Create and setup socket */
+    int retry = 1;
+    log_debug("main", "creating control socket");
+    while ((ctl = ctl_create(ctlname)) == -1) {
+        if (retry-- && errno == EADDRINUSE) {
+            /* Check if a daemon is really listening */
+            int tfd;
+            log_info("main", "unable to create control socket because it "
+                "already exists");
+            log_info("main", "check if another instance is running");
+            if ((tfd = ctl_connect(ctlname)) != -1) {
+                /* Another instance is running */
+                close(tfd);
+                log_warnx("main", "another instance is running, "
+                       "please stop it");
+                fatalx("giving up");
+            } else if (errno == ECONNREFUSED) {
+                /* Nobody is listening */
+                log_info("main", "old control socket is present, clean it");
+                ctl_cleanup(ctlname);
+                continue;
+            }
+            log_warn("main", "cannot determine if another daemon is "
+                    "already running");
+            fatalx("giving up");
+        }
+        log_warn("main", "unable to create control socket");
+        fatalx("giving up");
+    }
+    if (chown(ctlname, uid, gid) == -1)
+        log_warn("main", "unable to chown control socket");
+    if (chmod(ctlname,
+        S_IRUSR | S_IWUSR | S_IXUSR |
+        S_IRGRP | S_IWGRP | S_IXGRP) == -1)
+        log_warn("main", "unable to chmod control socket");
+
+    /* Disable SIGPIPE */
+    signal(SIGPIPE, SIG_IGN);
+
+    /* Disable SIGHUP, until handlers are installed */
+    signal(SIGHUP, SIG_IGN);
+
+    /* Configuration with lldpcli */
+    if (lldpcli) {
+        log_debug("main", "invoking lldpcli for configuration");
+        if (lldpd_configure(debug, lldpcli, ctlname) == -1)
+            fatal("main", "unable to spawn lldpcli");
+    }
+#endif
+
+    /* Daemonization, unless started by upstart, systemd or launchd or debug */
+#ifndef HOST_OS_OSX
+    if (!lldpd_started_by_upstart() && !lldpd_started_by_systemd() &&
+        !debug) {
+        int pid;
+        char *spid;
+        log_debug("main", "daemonize");
+        if (daemon(0, 0) != 0)
+            fatal("main", "failed to detach daemon");
+        if ((pid = open(LLDPD_PID_FILE,
+                O_TRUNC | O_CREAT | O_WRONLY, 0666)) == -1)
+            fatal("main", "unable to open pid file " LLDPD_PID_FILE);
+        if (asprintf(&spid, "%d\n", getpid()) == -1)
+            fatal("main", "unable to create pid file " LLDPD_PID_FILE);
+        if (write(pid, spid, strlen(spid)) == -1)
+            fatal("main", "unable to write pid file " LLDPD_PID_FILE);
+        free(spid);
+        close(pid);
+    }
+#endif
+
+    /* Try to read system information from /etc/os-release if possible.
+       Fall back to lsb_release for compatibility. */
+    log_debug("main", "get OS/LSB release information");
+    lsb_release = lldpd_get_os_release();
+    if (!lsb_release) {
+        lsb_release = lldpd_get_lsb_release();
+    }
+
+    log_debug("main", "initialize privilege separation");
+#ifndef ENABLE_AUTO_ATTACH
+    priv_init(PRIVSEP_CHROOT, ctl, uid, gid);
+#endif
+
+    /* Initialization of global configuration */
+    if ((cfg = (struct lldpd *)
+        calloc(1, sizeof(struct lldpd))) == NULL)
+        fatal("main", NULL);
+
+    cfg->g_ctlname = ctlname;
+    cfg->g_ctl = ctl;
+    cfg->g_config.c_mgmt_pattern = mgmtp;
+    cfg->g_config.c_cid_pattern = cidp;
+    cfg->g_config.c_iface_pattern = interfaces;
+    cfg->g_config.c_smart = smart;
+    if (lldpcli)
+        cfg->g_config.c_paused = 1;
+    cfg->g_config.c_receiveonly = receiveonly;
+    cfg->g_config.c_tx_interval = LLDPD_TX_INTERVAL;
+    cfg->g_config.c_tx_hold = LLDPD_TX_HOLD;
+    cfg->g_config.c_max_neighbors = LLDPD_MAX_NEIGHBORS;
+#ifdef ENABLE_LLDPMED
+    cfg->g_config.c_enable_fast_start = enable_fast_start;
+    cfg->g_config.c_tx_fast_init = LLDPD_FAST_INIT;
+    cfg->g_config.c_tx_fast_interval = LLDPD_FAST_TX_INTERVAL;
+#endif
+#ifdef USE_SNMP
+    cfg->g_snmp = snmp;
+    cfg->g_snmp_agentx = agentx;
+#endif /* USE_SNMP */
+    cfg->g_config.c_bond_slave_src_mac_type = \
+        LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED;
+
+    /* Get ioctl socket */
+    log_debug("main", "get an ioctl socket");
+    if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+        fatal("main", "failed to get ioctl socket");
+
+    /* Description */
+    if (!(cfg->g_config.c_advertise_version = advertise_version) &&
+        lsb_release && lsb_release[strlen(lsb_release) - 1] == '\n')
+        lsb_release[strlen(lsb_release) - 1] = '\0';
+    cfg->g_lsb_release = lsb_release;
+        if (descr_override)
+           cfg->g_config.c_description = descr_override;
+
+    if (platform_override)
+        cfg->g_config.c_platform = platform_override;
+
+    /* Set system capabilities */
+    log_debug("main", "set system capabilities");
+    if ((lchassis = (struct lldpd_chassis*)
+        calloc(1, sizeof(struct lldpd_chassis))) == NULL)
+        fatal("localchassis", NULL);
+    lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
+        LLDP_CAP_ROUTER | LLDP_CAP_STATION;
+    TAILQ_INIT(&lchassis->c_mgmt);
+#ifdef ENABLE_LLDPMED
+    if (lldpmed > 0) {
+        if (lldpmed == LLDP_MED_CLASS_III)
+            lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
+        lchassis->c_med_type = lldpmed;
+        lchassis->c_med_cap_available = LLDP_MED_CAP_CAP |
+            LLDP_MED_CAP_IV | LLDP_MED_CAP_LOCATION |
+            LLDP_MED_CAP_POLICY | LLDP_MED_CAP_MDI_PSE | LLDP_MED_CAP_MDI_PD;
+        cfg->g_config.c_noinventory = noinventory;
+    } else
+        cfg->g_config.c_noinventory = 1;
+#endif
+
+    /* Set TTL */
+    lchassis->c_ttl = cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold;
+
+    log_debug("main", "initialize protocols");
+    cfg->g_protocols = protos;
+    for (i=0; protos[i].mode != 0; i++) {
+
+        /* With -ll, disable LLDP */
+        if (protos[i].mode == LLDPD_MODE_LLDP)
+            protos[i].enabled %= 3;
+        /* With -ccc force CDPV2, enable CDPV1 */
+        if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled == 3) {
+            protos[i].enabled = 1;
+        }
+        /* With -cc force CDPV1, enable CDPV2 */
+        if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 2) {
+            protos[i].enabled = 1;
+        }
+
+        /* With -cccc disable CDPV1, enable CDPV2 */
+        if (protos[i].mode == LLDPD_MODE_CDPV1 && protos[i].enabled >= 4) {
+            protos[i].enabled = 0;
+        }
+
+        /* With -cccc disable CDPV1, enable CDPV2; -ccccc will force CDPv2 */
+        if (protos[i].mode == LLDPD_MODE_CDPV2 && protos[i].enabled == 4) {
+            protos[i].enabled = 1;
+        }
+
+        if (protos[i].enabled > 1)
+            log_info("main", "protocol %s enabled and forced", protos[i].name);
+        else if (protos[i].enabled)
+            log_info("main", "protocol %s enabled", protos[i].name);
+        else
+            log_info("main", "protocol %s disabled", protos[i].name);
+        }
+
+    TAILQ_INIT(&cfg->g_hardware);
+    TAILQ_INIT(&cfg->g_chassis);
+    TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
+    /* We should always keep a reference to local chassis */
+    lchassis->c_refcount++;
+
+    /* Main loop */
+    log_debug("main", "start main loop");
+    levent_loop(cfg);
+    lldpd_exit(cfg);
+
+    return (0);
+}
+#endif
diff --git a/lib/lldpd.h b/lib/lldpd.h
new file mode 100644
index 0000000..419e3cd
--- /dev/null
+++ b/lib/lldpd.h
@@ -0,0 +1,468 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat at luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LLDPD_H
+#define _LLDPD_H
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifdef ENABLE_AUTO_ATTACH
+#define ETHERTYPE_LLDP 0x88cc
+#endif
+
+#ifdef HAVE_VALGRIND_VALGRIND_H
+# include <valgrind/valgrind.h>
+#else
+# define RUNNING_ON_VALGRIND 0
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+#ifdef ENABLE_AUTO_ATTACH
+#define SYSCONFDIR ""
+#define LLDPD_CTL_SOCKET ""
+#define LLDPCLI_PATH ""
+#define PRIVSEP_USER ""
+#define PRIVSEP_GROUP ""
+#define PRIVSEP_CHROOT ""
+#else
+#include "strlcpy.h"
+#endif
+
+#if HAVE_VFORK_H
+# include <vfork.h>
+#endif
+#if HAVE_WORKING_FORK
+# define vfork fork
+#endif
+
+#include "lldp-tlv.h"
+#if defined (ENABLE_CDP) || defined (ENABLE_FDP)
+#  include "cdp.h"
+#endif
+#ifdef ENABLE_SONMP
+#  include "sonmp.h"
+#endif
+#ifdef ENABLE_EDP
+#  include "edp.h"
+#endif
+
+#ifndef ENABLE_AUTO_ATTACH
+#include "../compat/compat.h"
+#include "../ctl.h"
+#include "log.h"
+#else
+
+#include "vlog.h"
+#define log_info(_str_,...) VLOG_INFO(__VA_ARGS__)
+#define log_debug(_str_,...) VLOG_DBG(__VA_ARGS__)
+#define log_warn(_str_,...) VLOG_WARN(__VA_ARGS__)
+#define log_warnx(_str_,...) VLOG_WARN(__VA_ARGS__)
+#define fatal(_str_,...) VLOG_FATAL(__VA_ARGS__)
+
+#endif
+
+#include "marshal.h"
+#include "lldpd-structs.h"
+
+
+/* We don't want to import event2/event.h. We only need those as
+   opaque structs. */
+struct event;
+struct event_base;
+
+#define SYSFS_CLASS_NET "/sys/class/net/"
+#define SYSFS_CLASS_DMI "/sys/class/dmi/id/"
+#define LLDPD_TX_INTERVAL 30
+#define LLDPD_TX_HOLD     4
+#define LLDPD_TTL         LLDPD_TX_INTERVAL * LLDPD_TX_HOLD
+#define LLDPD_TX_MSGDELAY 1
+#define LLDPD_MAX_NEIGHBORS 4
+#define LLDPD_FAST_TX_INTERVAL 1
+#define LLDPD_FAST_INIT 4
+
+#define USING_AGENTX_SUBAGENT_MODULE 1
+
+#ifndef ENABLE_AUTO_ATTACH
+#define PROTO_SEND_SIG struct lldpd *, struct lldpd_hardware *
+#else
+#define PROTO_SEND_SIG struct lldpd *, struct lldpd_hardware *,u_int8_t *
+#endif
+#define PROTO_DECODE_SIG struct lldpd *, char *, int, struct lldpd_hardware *,\
+    struct lldpd_chassis **, struct lldpd_port **
+#define PROTO_GUESS_SIG char *, int
+
+#define ALIGNED_CAST(TYPE, ATTR) ((TYPE) (void *) (ATTR))
+
+struct protocol {
+    int  mode;        /* > 0 mode identifier (unique per protocol) */
+    int  enabled;    /* Is this protocol enabled? */
+    char *name;        /* Name of protocol */
+    char arg;        /* Argument to enable this protocol */
+    int(*send)(PROTO_SEND_SIG);    /* How to send a frame */
+    int(*decode)(PROTO_DECODE_SIG); /* How to decode a frame */
+    int(*guess)(PROTO_GUESS_SIG);   /* Can be NULL, use MAC address in this
+                                     * case */
+    u_int8_t mac[ETHER_ADDR_LEN];  /* Destination MAC address used by this
+                                    * protocol */
+};
+
+#define SMART_HIDDEN(port) (port->p_hidden_in)
+
+struct lldpd {
+    int                 g_sock;
+    struct event_base   *g_base;
+#ifdef USE_SNMP
+#endif
+
+    struct lldpd_config g_config;
+
+    struct protocol     *g_protocols;
+    int                 g_lastrid;
+    struct event        *g_main_loop;
+    struct event        *g_cleanup_timer;
+#ifdef USE_SNMP
+    int                 g_snmp;
+    struct event        *g_snmp_timeout;
+    void                *g_snmp_fds;
+    char                *g_snmp_agentx;
+#endif /* USE_SNMP */
+
+    /* Unix socket handling */
+    const char          *g_ctlname;
+    int                 g_ctl;
+    struct event        *g_ctl_event;
+    struct event        *g_iface_event; /* Triggered when there is an
+                                         *  interface change */
+    struct event        *g_iface_timer_event; /* Triggered one second after
+                                               * last interface change */
+
+    char                *g_lsb_release;
+
+#define LOCAL_CHASSIS(cfg) ((struct lldpd_chassis *) \
+        (TAILQ_FIRST(&cfg->g_chassis)))
+    TAILQ_HEAD(, lldpd_chassis) g_chassis;
+    TAILQ_HEAD(, lldpd_hardware) g_hardware;
+};
+
+/* lldpd.c */
+struct lldpd_hardware    *lldpd_get_hardware(struct lldpd *,
+    char *, int, struct lldpd_ops *);
+struct lldpd_hardware    *lldpd_alloc_hardware(struct lldpd *, char *, int);
+void     lldpd_hardware_cleanup(struct lldpd*, struct lldpd_hardware *);
+struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize,
+        u_int32_t iface);
+#ifdef ENABLE_AUTO_ATTACH
+void     lldpd_recv(struct lldpd *, struct lldpd_hardware *, char *, size_t);
+#else
+void     lldpd_recv(struct lldpd *, struct lldpd_hardware *, int);
+#endif
+#ifndef ENABLE_AUTO_ATTACH
+uint32_t lldpd_send(struct lldpd_hardware *);
+#else
+uint32_t lldpd_send(struct lldpd_hardware *, unsigned char *);
+#endif
+void     lldpd_loop(struct lldpd *);
+
+#ifdef ENABLE_AUTO_ATTACH
+int     lldpd_main(int, char **);
+#else
+int     lldpd_main(int, char **, char **);
+#endif
+void     lldpd_update_localports(struct lldpd *);
+void     lldpd_cleanup(struct lldpd *);
+
+/* frame.c */
+u_int16_t frame_checksum(const u_int8_t *, int, int);
+
+/* event.c */
+void    levent_loop(struct lldpd *);
+void    levent_hardware_init(struct lldpd_hardware *);
+void    levent_hardware_add_fd(struct lldpd_hardware *, int);
+void    levent_hardware_release(struct lldpd_hardware *);
+void    levent_ctl_notify(char *, int, struct lldpd_port *);
+void    levent_send_now(struct lldpd *);
+void    levent_update_now(struct lldpd *);
+int     levent_iface_subscribe(struct lldpd *, int);
+void    levent_schedule_pdu(struct lldpd_hardware *);
+void    levent_schedule_cleanup(struct lldpd *);
+int     levent_make_socket_nonblocking(int);
+
+/* lldp.c */
+int     lldp_send(PROTO_SEND_SIG);
+int     lldp_decode(PROTO_DECODE_SIG);
+
+/* cdp.c */
+#ifdef ENABLE_CDP
+int     cdpv1_send(PROTO_SEND_SIG);
+int     cdpv2_send(PROTO_SEND_SIG);
+int     cdpv1_guess(PROTO_GUESS_SIG);
+int     cdpv2_guess(PROTO_GUESS_SIG);
+#endif
+#if defined (ENABLE_CDP) || defined (ENABLE_FDP)
+int     cdp_decode(PROTO_DECODE_SIG);
+#endif
+#ifdef ENABLE_FDP
+int     fdp_send(PROTO_SEND_SIG);
+#endif
+
+#ifdef ENABLE_SONMP
+/* sonmp.c */
+int     sonmp_send(PROTO_SEND_SIG);
+int     sonmp_decode(PROTO_DECODE_SIG);
+#endif
+
+#ifdef ENABLE_EDP
+/* edp.c */
+int     edp_send(PROTO_SEND_SIG);
+int     edp_decode(PROTO_DECODE_SIG);
+#endif
+
+/* dmi.c */
+#ifdef ENABLE_LLDPMED
+char    *dmi_hw(void);
+char    *dmi_fw(void);
+char    *dmi_sn(void);
+char    *dmi_manuf(void);
+char    *dmi_model(void);
+char    *dmi_asset(void);
+#endif
+
+#ifdef USE_SNMP
+/* agent.c */
+void    agent_shutdown(void);
+void    agent_init(struct lldpd *, char *);
+void    agent_notify(struct lldpd_hardware *, int, struct lldpd_port *);
+#endif
+
+#ifdef ENABLE_PRIVSEP
+/* agent_priv.c */
+void    agent_priv_register_domain(void);
+#endif
+
+#ifndef ENABLE_AUTO_ATTACH
+/* client.c */
+int
+client_handle_client(struct lldpd *cfg,
+    ssize_t(*send)(void *, int, void *, size_t),
+    void *,
+    enum hmsg_type type, void *buffer, size_t n,
+    int*);
+#endif
+
+/* priv.c */
+void    priv_init(const char*, int, uid_t, gid_t);
+void    priv_wait(void);
+void    priv_ctl_cleanup(const char *ctlname);
+char    *priv_gethostbyname(void);
+#ifdef HOST_OS_LINUX
+int     priv_open(char*);
+void    asroot_open(void);
+int     priv_ethtool(char*, void*, size_t);
+void    asroot_ethtool(void);
+#endif
+int     priv_iface_init(int, char *);
+int     asroot_iface_init_os(int, char *, int *);
+int     priv_iface_multicast(const char *, u_int8_t *, int);
+int     priv_iface_description(const char *, const char *);
+int     asroot_iface_description_os(const char *, const char *);
+int     priv_iface_promisc(const char*);
+int     asroot_iface_promisc_os(const char *);
+int     priv_snmp_socket(struct sockaddr_un *);
+
+enum priv_cmd {
+    PRIV_PING,
+    PRIV_DELETE_CTL_SOCKET,
+    PRIV_GET_HOSTNAME,
+    PRIV_OPEN,
+    PRIV_ETHTOOL,
+    PRIV_IFACE_INIT,
+    PRIV_IFACE_MULTICAST,
+    PRIV_IFACE_DESCRIPTION,
+    PRIV_IFACE_PROMISC,
+    PRIV_SNMP_SOCKET,
+};
+
+/* priv-seccomp.c */
+#if defined USE_SECCOMP && defined ENABLE_PRIVSEP
+int priv_seccomp_init(int, int);
+#endif
+
+/* privsep_io.c */
+enum priv_context {
+    PRIV_PRIVILEGED,
+    PRIV_UNPRIVILEGED
+};
+int     may_read(enum priv_context, void *, size_t);
+void    must_read(enum priv_context, void *, size_t);
+void    must_write(enum priv_context, const void *, size_t);
+void    priv_privileged_fd(int);
+void    priv_unprivileged_fd(int);
+int     receive_fd(enum priv_context);
+void    send_fd(enum priv_context, int);
+
+/* interfaces-*.c */
+
+/* BPF filter to get revelant information from interfaces */
+/* LLDP: "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */
+/* FDP: "ether dst 01:e0:52:cc:cc:cc" */
+/* CDP: "ether dst 01:00:0c:cc:cc:cc" */
+/* SONMP: "ether dst 01:00:81:00:01:00" */
+/* EDP: "ether dst 00:e0:2b:00:00:00" */
+/* For optimization purpose, we first check if the first bit of the
+   first byte is 1. if not, this can only be an EDP packet:
+
+   tcpdump -dd "(ether[0] & 1 = 1 and
+                 ((ether proto 0x88cc and ether dst 01:80:c2:00:00:0e) or
+                  (ether dst 01:e0:52:cc:cc:cc) or
+                  (ether dst 01:00:0c:cc:cc:cc) or
+                  (ether dst 01:00:81:00:01:00))) or
+                (ether dst 00:e0:2b:00:00:00)"
+*/
+
+#define LLDPD_FILTER_F                \
+    { 0x30, 0, 0, 0x00000000 },       \
+    { 0x54, 0, 0, 0x00000001 },       \
+    { 0x15, 0, 14, 0x00000001 },      \
+    { 0x28, 0, 0, 0x0000000c },       \
+    { 0x15, 0, 4, 0x000088cc },       \
+    { 0x20, 0, 0, 0x00000002 },       \
+    { 0x15, 0, 2, 0xc200000e },       \
+    { 0x28, 0, 0, 0x00000000 },       \
+    { 0x15, 12, 13, 0x00000180 },     \
+    { 0x20, 0, 0, 0x00000002 },       \
+    { 0x15, 0, 2, 0x52cccccc },       \
+    { 0x28, 0, 0, 0x00000000 },       \
+    { 0x15, 8, 9, 0x000001e0 },       \
+    { 0x15, 1, 0, 0x0ccccccc },       \
+    { 0x15, 0, 2, 0x81000100 },       \
+    { 0x28, 0, 0, 0x00000000 },       \
+    { 0x15, 4, 5, 0x00000100 },       \
+    { 0x20, 0, 0, 0x00000002 },       \
+    { 0x15, 0, 3, 0x2b000000 },       \
+    { 0x28, 0, 0, 0x00000000 },       \
+    { 0x15, 0, 1, 0x000000e0 },       \
+    { 0x6, 0, 0, 0x0000ffff },        \
+    { 0x6, 0, 0, 0x00000000 },
+
+/* This function is responsible to refresh information about interfaces. It is
+ * OS specific but should be present for each OS. It can use the functions in
+ * `interfaces.c` as helper by providing a list of OS-independent interface
+ * devices. */
+void     interfaces_update(struct lldpd *);
+
+/* interfaces.c */
+/* An interface cannot be both physical and (bridge or bond or vlan) */
+#define IFACE_PHYSICAL_T (1 << 0) /* Physical interface */
+#define IFACE_BRIDGE_T   (1 << 1) /* Bridge interface */
+#define IFACE_BOND_T     (1 << 2) /* Bond interface */
+#define IFACE_VLAN_T     (1 << 3) /* VLAN interface */
+#define IFACE_WIRELESS_T (1 << 4) /* Wireless interface */
+struct interfaces_device {
+    TAILQ_ENTRY(interfaces_device) next;
+    int   index;     /* Index */
+    char  *name;     /* Name */
+    char  *alias;    /* Alias */
+    char  *address;  /* MAC address */
+    char  *driver;   /* Driver (for whitelisting purpose) */
+    int   flags;     /* Flags (IFF_*) */
+    int   mtu;       /* MTU */
+    int   type;      /* Type (see IFACE_*_T) */
+    int   vlanid;    /* If a VLAN, what is the VLAN ID? */
+    struct interfaces_device *lower; /* Lower interface (for a VLAN for
+                                      * example) */
+    struct interfaces_device *upper; /* Upper interface (for a bridge or a
+                                      * bond) */
+
+    /* The following are OS specific. Should be static (no free function) */
+#ifdef HOST_OS_LINUX
+    int lower_idx;  /* Index to lower interface */
+    int upper_idx;  /* Index to upper interface */
+    int txqueue;    /* Transmit queue length */
+#endif
+};
+struct interfaces_address {
+    TAILQ_ENTRY(interfaces_address) next;
+    int index;  /* Index */
+    int flags;  /* Flags */
+    struct sockaddr_storage address; /* Address */
+
+    /* The following are OS specific. */
+    /* Nothing yet. */
+};
+TAILQ_HEAD(interfaces_device_list,  interfaces_device);
+TAILQ_HEAD(interfaces_address_list, interfaces_address);
+void interfaces_free_device(struct interfaces_device *);
+void interfaces_free_address(struct interfaces_address *);
+void interfaces_free_devices(struct interfaces_device_list *);
+void interfaces_free_addresses(struct interfaces_address_list *);
+struct interfaces_device* interfaces_indextointerface(
+    struct interfaces_device_list *, int);
+struct interfaces_device* interfaces_nametointerface(
+    struct interfaces_device_list *, const char *);
+
+void interfaces_helper_promisc(struct lldpd *,
+    struct lldpd_hardware *);
+void interfaces_helper_whitelist(struct lldpd *,
+    struct interfaces_device_list *);
+void interfaces_helper_chassis(struct lldpd *,
+    struct interfaces_device_list *);
+void interfaces_helper_add_hardware(struct lldpd *,
+    struct lldpd_hardware *);
+void interfaces_helper_physical(struct lldpd *,
+    struct interfaces_device_list *,
+    struct lldpd_ops *,
+    int(*init)(struct lldpd *, struct lldpd_hardware *));
+void interfaces_helper_port_name_desc(struct lldpd *,
+    struct lldpd_hardware *,
+    struct interfaces_device *);
+void interfaces_helper_mgmt(struct lldpd *,
+    struct interfaces_address_list *);
+#ifdef ENABLE_DOT1
+void interfaces_helper_vlan(struct lldpd *,
+    struct interfaces_device_list *);
+#endif
+int interfaces_send_helper(struct lldpd *,
+    struct lldpd_hardware *, char *, size_t);
+
+void interfaces_setup_multicast(struct lldpd *, const char *, int);
+int interfaces_routing_enabled(struct lldpd *);
+
+#ifdef HOST_OS_LINUX
+/* netlink.c */
+struct interfaces_device_list  *netlink_get_interfaces(void);
+struct interfaces_address_list *netlink_get_addresses(void);
+int netlink_subscribe_changes(void);
+#endif
+
+#ifndef HOST_OS_LINUX
+int ifbpf_phys_init(struct lldpd *, struct lldpd_hardware *);
+#endif
+
+/* pattern.c */
+int pattern_match(char *, char *, int);
+
+#endif /* _LLDPD_H */
diff --git a/lib/marshal.h b/lib/marshal.h
new file mode 100644
index 0000000..a7001ae
--- /dev/null
+++ b/lib/marshal.h
@@ -0,0 +1,156 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat at luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _MARSHAL_H
+#define _MARSHAL_H
+
+#include <stddef.h>
+#include <sys/types.h>
+
+struct marshal_info;
+enum marshal_subinfo_kind {
+    pointer,
+    substruct,
+#ifdef ENABLE_AUTO_ATTACH
+    ignorem, // ignore has a direct naming conflict in ovs (and therefore has been renamed)
+#else
+    ignore
+#endif
+};
+#define MARSHAL_INFO_POINTER 1
+#define MARSHAL_INFO_SUB     2
+struct marshal_subinfo {
+    size_t offset;       /* Offset compared to parent structure */
+    size_t offset2;      /* Ancillary offset (for related data) */
+    enum marshal_subinfo_kind kind; /* Kind of substructure */
+    struct  marshal_info *mi;
+};
+#ifdef ENABLE_AUTO_ATTACH
+#define MARSHAL_SUBINFO_NULL { .offset = 0, .offset2 = 0, .kind = ignorem, .mi = NULL }
+#else
+#define MARSHAL_SUBINFO_NULL { .offset = 0, .offset2 = 0, .kind = ignore, .mi = NULL }
+#endif
+struct marshal_info {
+    char   *name;        /* Name of structure */
+    size_t  size;        /* Size of the structure */
+#if defined __GNUC__ && __GNUC__ < 3
+    /* With gcc 2.96, flexible arrays are not supported, even with
+    * -std=gnu99. And with gcc 3.x, zero-sized arrays cannot be statically
+    * initialized (with more than one element). */
+    struct marshal_subinfo pointers[0]; /* Pointer to other structures */
+#else
+    struct marshal_subinfo pointers[]; /* Pointer to other structures */
+#endif
+};
+/* Special case for strings */
+extern struct marshal_info marshal_info_string;
+extern struct marshal_info marshal_info_fstring;
+#ifdef ENABLE_AUTO_ATTACH
+extern struct marshal_info marshal_info_ignorem;
+#else
+extern struct marshal_info marshal_info_ignore;
+#endif
+
+/* Declare a new marshal_info struct named after the type we want to
+   marshal. The marshalled type has to be a structure. */
+#define MARSHAL_INFO(type) marshal_info_##type
+#ifdef MARSHAL_EXPORT
+#define MARSHAL_HELPER_FUNCTIONS(type, ttype)             \
+    ssize_t                                    \
+    type ## _serialize(ttype *source, void *buffer) {    \
+          return marshal_serialize(type,             \
+              source, buffer);                 \
+    }                                    \
+    size_t                                     \
+    type ## _unserialize(void *buffer, size_t len,       \
+        ttype **destination) {                 \
+          void *p;                        \
+          size_t rc;                      \
+          rc = marshal_unserialize(type,             \
+              buffer, len, &p);                \
+          if (rc <= 0) return rc;                    \
+          *destination = p;                    \
+          return rc;                      \
+    }
+#define MARSHAL_BEGIN(type) struct marshal_info MARSHAL_INFO(type) = \
+    {                                          \
+          .name = #type,                             \
+          .size = sizeof(struct type),                    \
+          .pointers = {
+#define MARSHAL_ADD(_kind, type, subtype, member)         \
+    { .offset = offsetof(struct type, member),      \
+      .offset2 = 0,                            \
+      .kind = _kind,                           \
+      .mi = &MARSHAL_INFO(subtype) },
+#define MARSHAL_FSTR(type, member, len)                   \
+    { .offset = offsetof(struct type, member),      \
+      .offset2 = offsetof(struct type, len),        \
+      .kind = pointer,                         \
+      .mi = &marshal_info_fstring },
+#define MARSHAL_END(type) MARSHAL_SUBINFO_NULL }};        \
+    MARSHAL_HELPER_FUNCTIONS(type, struct type)
+#else
+#define MARSHAL_HELPER_FUNCTIONS(type, ttype)             \
+    ssize_t type ## _serialize(ttype*, void*);      \
+    size_t type ## _unserialize(void*, size_t, ttype**);
+#define MARSHAL_BEGIN(type) extern struct marshal_info MARSHAL_INFO(type);
+#define MARSHAL_ADD(...)
+#define MARSHAL_FSTR(...)
+#define MARSHAL_END(type) MARSHAL_HELPER_FUNCTIONS(type, struct type)
+#endif
+/* Shortcuts */
+#define MARSHAL_POINTER(...) MARSHAL_ADD(pointer, ##__VA_ARGS__)
+#define MARSHAL_SUBSTRUCT(...) MARSHAL_ADD(substruct, ##__VA_ARGS__)
+#define MARSHAL_STR(type, member) MARSHAL_ADD(pointer, type, string, member)
+#ifdef ENABLE_AUTO_ATTACH
+#define MARSHAL_IGNORE(type, member) MARSHAL_ADD(ignorem, type, ignorem, member)
+#else
+#define MARSHAL_IGNORE(type, member) MARSHAL_ADD(ignore, type, ignore, member)
+#endif
+#define MARSHAL_TQE(type, field)               \
+    MARSHAL_POINTER(type, type, field.tqe_next)     \
+    MARSHAL_IGNORE(type, field.tqe_prev)
+/* Support for TAILQ list is partial. Access to last and previous
+   elements is not available. Some operations are therefore not
+   possible. However, TAILQ_FOREACH is still
+   available. */
+#define MARSHAL_TQH(type, subtype)             \
+    MARSHAL_POINTER(type, subtype, tqh_first)  \
+    MARSHAL_IGNORE(type, tqh_last)
+#define MARSHAL_SUBTQ(type, subtype, field)         \
+    MARSHAL_POINTER(type, subtype, field.tqh_first) \
+    MARSHAL_IGNORE(type, field.tqh_last)
+#define MARSHAL(type)               \
+    MARSHAL_BEGIN(type)        \
+    MARSHAL_END(type)
+#define MARSHAL_TQ(type, subtype)   \
+    MARSHAL_BEGIN(type)        \
+    MARSHAL_TQH(type, subtype) \
+    MARSHAL_END(type)
+
+/* Serialization */
+ssize_t  marshal_serialize_(struct marshal_info *, void *, void **, int, void *, int)
+    __attribute__((nonnull (1, 2, 3) ));
+#define marshal_serialize(type, o, output) marshal_serialize_(&MARSHAL_INFO(type), o, output, 0, NULL, 0)
+
+/* Unserialization */
+size_t  marshal_unserialize_(struct marshal_info *, void *, size_t, void **, void*, int, int)
+    __attribute__((nonnull (1, 2, 4) ));
+#define marshal_unserialize(type, o, l, input) \
+    marshal_unserialize_(&MARSHAL_INFO(type), o, l, input, NULL, 0, 0)
+
+#endif
diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
new file mode 100644
index 0000000..2837e0e
--- /dev/null
+++ b/lib/ovs-lldp.c
@@ -0,0 +1,980 @@
+/*
+ * Copyright (c) 2014 WindRiver, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Implementation of Auto Attach.
+ * Based on sample implementation in 802.1ab.  Above copyright and license
+ * applies to all modifications.
+ * Limitations:
+ * - No support for multiple bridge.
+ * - Auto Attach state machine not implemented.
+ * - Auto Attach and LLDP code are bundled together.  The plan is to decoupled
+ *   them.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "ovs-lldp.h"
+#include "openvswitch/types.h"
+#include "flow.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "unixctl.h"
+#include "vlog.h"
+#include "lib/dynamic-string.h"
+#include "netdev.h"
+#include "lldpd.h"
+#include "lldpd-structs.h"
+#include "sys/queue.h"
+#include "util.h"
+#include "list.h"
+#include "smap.h"
+#include "poll-loop.h"
+
+VLOG_DEFINE_THIS_MODULE(ovs_lldp);
+
+#define LLDP_PROTOCOL_ID 0x0000
+#define LLDP_PROTOCOL_VERSION 0x00
+#define LLDP_TYPE_CONFIG 0x00
+#define LLDP_CHASSIS_TTL 120
+#define ETH_TYPE_LLDP 0x88cc
+#define MINIMUM_ETH_PACKET_SIZE 68
+
+#define AA_STATUS_MULTIPLE \
+    AA_STATUS(ACTIVE,2,Active) \
+    AA_STATUS(REJECT_GENERIC,3,Reject (Generic)) \
+    AA_STATUS(REJECT_AA_RES_NOTAVAIL,4,Reject (AA resources unavailable)) \
+    AA_STATUS(REJECT_INVALID,6,Reject (Invalid)) \
+    AA_STATUS(REJECT_VLAN_RES_UNAVAIL,8,Reject (VLAN resources unavailable)) \
+    AA_STATUS(REJECT_VLAN_APP_ISSUE,9,Reject (Application interaction issue)) \
+    AA_STATUS(PENDING,255,Pending)
+
+enum aa_status {
+#define AA_STATUS(NAME, VALUE, STR) AA_STATUS_##NAME = VALUE,
+    AA_STATUS_MULTIPLE
+#undef AA_STATUS
+    AA_STATUS_N_MULTIPLE
+};
+
+/* Internal structure for an Auto Attach mapping.
+ */
+struct aa_mapping_internal {
+    struct hmap_node hmap_node;
+    int64_t isid;
+    int64_t vlan;
+    void *aux;
+    enum aa_status status;
+};
+
+/* Hash map of all LLDP instances keyed by name (port at the moment).
+ */
+static struct hmap all_lldps__ = HMAP_INITIALIZER(&all_lldps__);
+static struct hmap *const all_lldps OVS_GUARDED_BY(mutex) = &all_lldps__;
+
+/* Store Auto Attach settings.  Global at the moment (but will be per bridge).
+ */
+static struct aa_settings aa_settings OVS_GUARDED_BY(mutex);
+
+/* Hash map of all the Auto Attach mappings.  Global at the moment (but will
+ * be per bridge).  Used when adding a new port to a bridge so that we can
+ * properly install all the configured mapping on the port and export them
+ * To the Auto Attach server via LLDP.
+ */
+static struct hmap all_mappings__ = HMAP_INITIALIZER(&all_mappings__);
+static struct hmap *const all_mappings OVS_GUARDED_BY(mutex) = &all_mappings__;
+
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
+
+static struct lldp_aa_element_system_id system_id_null;
+
+/* Convert an array to an integer.  I-SID are stored in an array of bytes
+ * in the LLDP hardware structrure.
+ */
+inline static uint32_t
+array_to_int(uint8_t *array, size_t len)
+{
+    uint32_t res = 0;
+    unsigned int i = 0;
+
+    ovs_assert(len <= sizeof(uint32_t));
+
+    for (i = 0; i < len; i++) {
+        res = res | (array[len - i - 1] << (i * 8));
+    }
+
+    return res;
+}
+
+/* Convert an integer to an array of byte.
+ */
+inline static void
+int_to_array(uint8_t *array, size_t len, uint32_t value)
+{
+    unsigned int i;
+
+    ovs_assert(len <= sizeof(uint32_t));
+
+    for (i = 0; i < len; i++) {
+        array[len - i - 1] = value >> (8 * i);
+    }
+}
+
+/* Convert an LLDP chassis ID to a string.
+ */
+inline static void
+chassisid_to_string(uint8_t *array, size_t len, char **str)
+{
+    unsigned int i;
+
+    *str = malloc(len * 3);
+
+    for (i = 0; i < len; i++) {
+        snprintf(&(*str)[i * 3], 4, "%02x:", array[i]);
+    }
+    (*str)[(i * 3) - 1] = '\0';
+}
+
+/* Find an Auto Attach mapping keyed by I-SID.
+ */
+static struct aa_mapping_internal *
+mapping_find_by_isid(struct lldp *lldp, const uint64_t isid) OVS_REQUIRES(mutex)
+{
+    struct aa_mapping_internal *m;
+
+    HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, hash_bytes(&isid, sizeof(isid), 0),
+                             &lldp->mappings_by_isid) {
+        if (isid == m->isid) {
+            return m;
+        }
+    }
+
+    return NULL;
+}
+
+/* Find an Auto Attach mapping keyed by aux.  aux is an opaque pointer created
+ * by the bridge that refers to an OVSDB mapping record.
+ */
+static struct aa_mapping_internal *
+mapping_find_by_aux(struct lldp *lldp, const void *aux) OVS_REQUIRES(mutex)
+{
+    struct aa_mapping_internal *m;
+
+    HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, hash_bytes(&aux, sizeof(aux), 0),
+                             &lldp->mappings_by_aux) {
+        if (aux == m->aux) {
+            return m;
+        }
+    }
+
+    return NULL;
+}
+
+/* Convert an Auto Attach request status to a string.
+ */
+static char *
+aa_status_to_str(uint8_t status)
+{
+    switch (status) {
+#define AA_STATUS(NAME, VALUE, STR) case AA_STATUS_##NAME: return #STR;
+        AA_STATUS_MULTIPLE
+#undef AA_STATUS
+        default: return "Undefined";
+    }
+}
+
+/* Display LLDP and Auto Attach statistics.
+ */
+static void
+aa_print_lldp_and_aa_stats(struct ds *ds, struct lldp *lldp) OVS_EXCLUDED(mutex)
+{
+    struct lldpd_hardware *hardware;
+
+    ds_put_format(ds, "Statistics: %s\n", lldp->name);
+
+    if (!lldp->lldpd) {
+        return;
+    }
+
+    TAILQ_FOREACH (hardware, &lldp->lldpd->g_hardware, h_entries) {
+        ds_put_format(ds, "\ttx cnt: %llu\n",
+                      (long long unsigned int) hardware->h_tx_cnt);
+        ds_put_format(ds, "\trx cnt: %llu\n",
+                      (long long unsigned int) hardware->h_rx_cnt);
+        ds_put_format(ds, "\trx discarded cnt: %llu\n",
+                      (long long unsigned int) hardware->h_rx_discarded_cnt);
+        ds_put_format(ds, "\trx unrecognized cnt: %llu\n",
+                      (long long unsigned int) hardware->h_rx_unrecognized_cnt);
+        ds_put_format(ds, "\tageout cnt: %llu\n",
+                      (long long unsigned int) hardware->h_ageout_cnt);
+        ds_put_format(ds, "\tinsert cnt: %llu\n",
+                      (long long unsigned int) hardware->h_insert_cnt);
+        ds_put_format(ds, "\tdelete cnt: %llu\n",
+                      (long long unsigned int) hardware->h_delete_cnt);
+        ds_put_format(ds, "\tdrop cnt: %llu\n",
+                      (long long unsigned int) hardware->h_drop_cnt);
+    }
+}
+
+/* Auto Attach server broadcast an LLDP message periodically.  Display
+ * the discovered server.
+ */
+static void
+aa_print_element_status(struct ds *ds, struct lldp *lldp) OVS_EXCLUDED(mutex)
+{
+    struct lldpd_hardware *hardware;
+
+    ovs_mutex_lock(&mutex);
+    ds_put_format(ds, "LLDP: %s\n", lldp->name);
+
+    if (!lldp->lldpd) {
+        return;
+    }
+
+    TAILQ_FOREACH (hardware, &lldp->lldpd->g_hardware, h_entries) {
+        struct lldpd_port *port;
+
+        TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
+            if (memcmp(&port->p_element.system_id,
+                       &system_id_null,
+                       sizeof(struct lldp_aa_element_system_id))) {
+                static char *none_str = "<None>";
+                char *id = none_str, *descr = none_str, *system = none_str;
+
+                if (port->p_chassis) {
+                    if (port->p_chassis->c_id_len > 0) {
+                        chassisid_to_string((uint8_t *) port->p_chassis->c_id,
+                                        port->p_chassis->c_id_len, &id);
+                    }
+
+                    descr = port->p_chassis->c_descr
+                        ? port->p_chassis->c_descr : none_str;
+                }
+
+                chassisid_to_string((uint8_t *) &port->p_element.system_id,
+                    sizeof(port->p_element.system_id), &system);
+
+                ds_put_format(ds, "\tAuto Attach Primary Server Id: %s\n",
+                              id);
+                ds_put_format(ds, "\tAuto Attach Primary Server Descr: %s\n",
+                              descr);
+                ds_put_format(ds, "\tAuto Attach Primary Server System Id: %s\n",
+                              system);
+            }
+        }
+    }
+
+    ovs_mutex_unlock(&mutex);
+}
+
+/* The Auto Attach server will broadcast the status of the configured mappings
+ * via LLDP.  Display the status.
+ */
+static void
+aa_print_isid_status(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex)
+{
+    ds_put_format(ds, "LLDP: %s\n", lldp->name);
+
+    if (lldp->lldpd) {
+        struct lldpd_hardware *hardware;
+        struct aa_mapping_internal *m;
+
+        TAILQ_FOREACH (hardware, &lldp->lldpd->g_hardware, h_entries) {
+            struct lldpd_port *port;
+
+            TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
+                struct lldpd_aa_isid_vlan_maps_tlv *mapping;
+
+                TAILQ_FOREACH (mapping, &port->p_isid_vlan_maps, m_entries) {
+                    uint32_t isid = array_to_int(mapping->isid_vlan_data.isid,
+                        sizeof(mapping->isid_vlan_data.isid));
+                    struct aa_mapping_internal *m =
+                        mapping_find_by_isid(lldp, isid);
+
+                    VLOG_INFO("h_rport: isid=%u, vlan=%u, status=%d",
+                             isid,
+                             mapping->isid_vlan_data.vlan,
+                             mapping->isid_vlan_data.status);
+
+                    /* Update the status of our internal state for the mapping */
+                    if (m) {
+                        VLOG_INFO("Setting status for ISID=%u to %u",
+                                 isid,
+                                 mapping->isid_vlan_data.status);
+                        m->status = mapping->isid_vlan_data.status;
+                    } else {
+                        VLOG_WARN("Couldn't find mapping for I-SID=%u", isid);
+                    }
+                }
+            }
+        }
+
+        ds_put_format(ds, "%-8s %-4s %-11s %-8s\n",
+                          "I-SID",
+                          "VLAN",
+                          "Source",
+                          "Status");
+        ds_put_format(ds, "-------- ---- ----------- --------\n");
+
+        HMAP_FOR_EACH (m, hmap_node, &lldp->mappings_by_isid) {
+            ds_put_format(ds, "%-8ld %-4ld %-11s %-11s\n",
+                              (long int) m->isid,
+                              (long int) m->vlan,
+                              "Switch",
+                              aa_status_to_str(m->status));
+        }
+    }
+}
+
+static void
+aa_unixctl_status(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                  const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+                  OVS_EXCLUDED(mutex)
+{
+    struct lldp *lldp;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        aa_print_element_status(&ds, lldp);
+    }
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+}
+
+static void
+aa_unixctl_show_isid(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                     const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+                     OVS_EXCLUDED(mutex)
+{
+    struct lldp *lldp;
+    struct ds ds = DS_EMPTY_INITIALIZER;
+
+    ovs_mutex_lock(&mutex);
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        aa_print_isid_status(&ds, lldp);
+    }
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+    ovs_mutex_unlock(&mutex);
+}
+
+static void
+aa_unixctl_statistics(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                      const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+                      OVS_EXCLUDED(mutex)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct lldp *lldp;
+
+    ovs_mutex_lock(&mutex);
+    /* Cycle through all ports and dump the stats for each one */
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        aa_print_lldp_and_aa_stats(&ds, lldp);
+    }
+    ovs_mutex_unlock(&mutex);
+
+    unixctl_command_reply(conn, ds_cstr(&ds));
+}
+
+/* An Auto Attach mapping was configured.  Populate the corresponding
+ * structures in the LLDP hardware.
+ */
+static void
+update_mapping_on_lldp(struct lldp *lldp, struct lldpd_hardware *hardware,
+                       struct aa_mapping_internal *m)
+{
+    struct lldpd_aa_isid_vlan_maps_tlv *lm;
+
+    if (hardware->h_ifname) {
+        VLOG_INFO("\t\t hardware->h_ifname=%s", hardware->h_ifname);
+    }
+
+    lm = xzalloc(sizeof(*lm));
+    int_to_array(lm->isid_vlan_data.isid,
+                 sizeof(lm->isid_vlan_data.isid) /
+                    sizeof(lm->isid_vlan_data.isid[0]),
+                 (uint32_t) m->isid);
+    lm->isid_vlan_data.vlan = m->vlan;
+
+    if (hardware->h_lport.p_isid_vlan_maps.tqh_first == NULL &&
+        hardware->h_lport.p_isid_vlan_maps.tqh_last == NULL) {
+        TAILQ_INIT(&hardware->h_lport.p_isid_vlan_maps);
+    }
+
+    TAILQ_INSERT_TAIL(&hardware->h_lport.p_isid_vlan_maps, lm, m_entries);
+
+    /* TODO Should be done in the Auto Attach state machine when a mapping goes
+     * from "pending" to "active".
+     */
+    {
+        struct bridge_aa_vlan *node = xmalloc(sizeof(*node));
+
+        node->port_name = xstrdup(hardware->h_ifname);
+        node->vlan = m->vlan;
+        node->oper = BRIDGE_AA_VLAN_OPER_ADD;
+
+        list_push_back(&lldp->active_mapping_queue, &node->list_node);
+    }
+}
+
+/* Bridge will poll the list of VLAN that needs to be auto configure based on
+ * the Auto Attach mappings that have been exchanged with the server.
+ */
+int
+aa_get_vlan_queued(struct list **list)
+{
+    struct lldp *lldp;
+
+    *list = xmalloc(sizeof(struct list));
+    list_init(*list);
+
+    ovs_mutex_lock(&mutex);
+
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        struct bridge_aa_vlan *node;
+
+        LIST_FOR_EACH(node, list_node, &lldp->active_mapping_queue) {
+            struct bridge_aa_vlan *copy;
+
+            copy = xmalloc(sizeof(*copy));
+            copy->port_name = xstrdup(node->port_name);
+            copy->vlan = node->vlan;
+            copy->oper = node->oper;
+
+            list_push_back(*list, &copy->list_node);
+
+            /* Cleanup */
+            list_remove(&node->list_node);
+            free(node->port_name);
+            free(node);
+        }
+    }
+
+    ovs_mutex_unlock(&mutex);
+
+    return 0;
+}
+
+/* Bridge will poll whether or not VLAN have been auto-configured.
+ */
+unsigned int
+aa_get_vlan_queue_size(void)
+{
+    struct lldp *lldp;
+    unsigned int size = 0;
+
+    ovs_mutex_lock(&mutex);
+
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        size += list_size(&lldp->active_mapping_queue);
+    }
+
+    ovs_mutex_unlock(&mutex);
+
+    return size;
+}
+
+/* Configure Auto Attach.
+ */
+int
+aa_configure(const struct aa_settings *s)
+{
+    struct lldp *lldp;
+
+    ovs_mutex_lock(&mutex);
+
+    /* Enabled */
+    aa_settings.enabled = s->enabled;
+
+    /* System Description */
+    if (aa_settings.system_description) {
+        free(aa_settings.system_description);
+    }
+    aa_settings.system_description = strlen(s->system_description) > 0 ?
+        xstrdup(s->system_description) : xstrdup(PACKAGE_STRING);
+
+    /* System Name */
+    if (aa_settings.system_name) {
+        free(aa_settings.system_name);
+    }
+    aa_settings.system_name = xstrdup(s->system_name);
+
+    /* Authentication Key */
+    if (aa_settings.authentication_key) {
+        free(aa_settings.authentication_key);
+    }
+    aa_settings.authentication_key = xstrdup(s->authentication_key);
+
+    /* TODO Change all instances for now */
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        struct lldpd_chassis *chassis;
+
+        TAILQ_FOREACH (chassis, &lldp->lldpd->g_chassis, c_entries) {
+            if (chassis->c_descr) {
+                free(chassis->c_descr);
+            }
+            chassis->c_descr = xstrdup(aa_settings.system_description);
+
+            if (chassis->c_name) {
+                free(chassis->c_name);
+            }
+            chassis->c_name = xstrdup(aa_settings.system_name);
+        }
+    }
+
+    ovs_mutex_unlock(&mutex);
+
+    return 0;
+}
+
+/* Add a new Auto Attach mapping.
+ */
+int
+aa_mapping_register(void *aux, const struct aa_mapping_settings *s)
+{
+    struct aa_mapping_internal *bridge_m;
+    struct lldp *lldp;
+
+    VLOG_INFO("Adding mapping ISID=%ld, VLAN=%ld, aux=%p", (long int) s->isid,
+              (long int) s->vlan, aux);
+
+    ovs_mutex_lock(&mutex);
+
+    /* TODO These mappings should be stores per bridge.  This is used
+     * When a port is added.  Auto Attach mappings need to be added on this
+     * port.
+     */
+    bridge_m = xzalloc(sizeof *bridge_m);
+    bridge_m->isid = s->isid;
+    bridge_m->vlan = s->vlan;
+    bridge_m->aux = aux;
+    bridge_m->status = AA_STATUS_PENDING;
+    hmap_insert(all_mappings, &bridge_m->hmap_node,
+        hash_bytes((const void *) &bridge_m->aux, sizeof(bridge_m->aux), 0));
+
+    /* Update mapping on the all the LLDP instances. */
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        struct lldpd_hardware *hardware;
+        struct aa_mapping_internal *m;
+
+        VLOG_INFO("\t lldp->name=%s", lldp->name);
+
+        /* Add to internal hash table - by aux. */
+        m = xzalloc(sizeof *m);
+        m->isid = s->isid;
+        m->vlan = s->vlan;
+        m->status = AA_STATUS_PENDING;
+        m->aux = aux;
+        hmap_insert(&lldp->mappings_by_isid, &m->hmap_node,
+            hash_bytes((const void *) &m->isid, sizeof(m->isid), 0));
+
+        /* Add to internal hash table - by I-SID. */
+        m = xzalloc(sizeof *m);
+        m->isid = s->isid;
+        m->vlan = s->vlan;
+        m->status = AA_STATUS_PENDING;
+        m->aux = aux;
+        hmap_insert(&lldp->mappings_by_aux, &m->hmap_node,
+            hash_bytes((const void *) &m->aux, sizeof(m->aux), 0));
+
+        /* Configure the mapping on each port of the LLDP stack. */
+        TAILQ_FOREACH (hardware, &lldp->lldpd->g_hardware, h_entries) {
+            update_mapping_on_lldp(lldp, hardware, m);
+        }
+    }
+
+    ovs_mutex_unlock(&mutex);
+
+    return 0;
+}
+
+/* Remove an existing Auto Attach mapping.
+ */
+int
+aa_mapping_unregister(void *aux)
+{
+    struct lldp *lldp;
+
+    VLOG_INFO("Removing mapping aux=%p", aux);
+
+    ovs_mutex_lock(&mutex);
+
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        struct lldpd_hardware *hardware;
+        struct aa_mapping_internal *m = mapping_find_by_aux(lldp, aux);
+
+        /* Remove from internal hash tables. */
+        if (m) {
+            struct aa_mapping_internal *p =
+                mapping_find_by_isid(lldp, m->isid);
+
+            VLOG_INFO("\t Removing mapping ISID=%ld, VLAN=%ld (lldp->name=%s)",
+                      (long int) m->isid, (long int) m->vlan, lldp->name);
+
+            if (p) {
+                hmap_remove(&lldp->mappings_by_isid, &p->hmap_node);
+                free(p);
+            }
+
+            hmap_remove(&lldp->mappings_by_aux, &m->hmap_node);
+            free(m);
+
+            /* Remove from all the lldp instances */
+            TAILQ_FOREACH (hardware, &lldp->lldpd->g_hardware, h_entries) {
+                struct lldpd_aa_isid_vlan_maps_tlv *lm;
+
+                if (hardware->h_ifname) {
+                    VLOG_INFO("\t\t hardware->h_ifname=%s", hardware->h_ifname);
+                }
+
+                TAILQ_FOREACH (lm, &hardware->h_lport.p_isid_vlan_maps,
+                               m_entries) {
+                    uint32_t isid =
+                        array_to_int(lm->isid_vlan_data.isid,
+                                     sizeof(lm->isid_vlan_data.isid));
+
+                    if (isid == (uint32_t) m->isid) {
+                        VLOG_INFO("\t\t Removing lport, isid=%u, vlan=%u", isid,
+                                  lm->isid_vlan_data.vlan);
+
+                        TAILQ_REMOVE(&hardware->h_lport.p_isid_vlan_maps, lm,
+                                     m_entries);
+
+                        /* TODO Should be done in the AA SM when a mapping goes
+                         * from "pending" to "active".
+                         */
+                        {
+                            struct bridge_aa_vlan *node =
+                                xmalloc(sizeof(*node));
+
+                            node->port_name = xstrdup(hardware->h_ifname);
+                            node->vlan = (uint32_t) m->vlan;
+                            node->oper = BRIDGE_AA_VLAN_OPER_REMOVE;
+
+                            list_push_back(&lldp->active_mapping_queue,
+                                       &node->list_node);
+                        }
+
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    ovs_mutex_unlock(&mutex);
+
+    return 0;
+}
+
+void
+lldp_init(void)
+{
+    memset(&system_id_null, 0, sizeof(system_id_null));
+    memset(&aa_settings, 0, sizeof(aa_settings));
+
+    unixctl_command_register("autoattach/status", "[bridge]", 0, 1,
+                             aa_unixctl_status, NULL);
+    unixctl_command_register("autoattach/show-isid", "[bridge]", 0, 1,
+                             aa_unixctl_show_isid, NULL);
+    unixctl_command_register("autoattach/statistics", "[bridge]", 0, 1,
+                             aa_unixctl_statistics, NULL);
+}
+
+/* Returns true if 'lldp' should process packets from 'flow'.  Sets
+ * fields in 'wc' that were used to make the determination.
+ */
+bool
+lldp_should_process_flow(const struct flow *flow, struct flow_wildcards *wc)
+{
+    memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+    return (ntohs(flow->dl_type) == ETH_TYPE_LLDP);
+}
+
+
+/* Process an LLDP packet that was received on a bridge port.
+ */
+void
+lldp_process_packet(struct lldp *lldp, const struct ofpbuf *p)
+{
+    if (lldp) {
+        struct lldpd_hardware *hardware = TAILQ_FIRST(&lldp->lldpd->g_hardware);
+        lldpd_recv(lldp->lldpd, hardware, (char *) p->data_, p->size_);
+    }
+}
+
+/* This code is called periodically to check if the LLDP module has an LLDP
+ * message it wishes to send.  It is called several times every second.
+ */
+bool
+lldp_should_send_packet(struct lldp *cfg) OVS_EXCLUDED(mutex)
+{
+    bool ret;
+
+    /* TODO check the tx is enabled for this lldp instance, and if we have
+     * a bridge-level enable/disable that must be checked here as well.
+     */
+
+    ovs_mutex_lock(&mutex);
+    ret = timer_expired(&cfg->tx_timer);
+    ovs_mutex_unlock(&mutex);
+
+    return ret;
+}
+
+/* Returns the next wake up time.
+ */
+long long int
+lldp_wake_time(const struct lldp *lldp) OVS_EXCLUDED(mutex)
+{
+    long long int retval;
+
+    if (!lldp) {
+        return LLONG_MAX;
+    }
+
+    ovs_mutex_lock(&mutex);
+    retval = lldp->tx_timer.t;
+    ovs_mutex_unlock(&mutex);
+
+    return retval;
+}
+
+/* Put the monitor thread to sleep until it's next wake time.
+ */
+void
+lldp_wait(struct lldp *lldp) OVS_EXCLUDED(mutex)
+{
+    poll_timer_wait_until(lldp_wake_time(lldp));
+}
+
+/* Prepare the LLDP packet to be sent on a bridge port.
+ */
+void lldp_put_packet(struct lldp *lldp, struct ofpbuf *packet,
+                     uint8_t eth_src[ETH_ADDR_LEN])
+                     OVS_EXCLUDED(mutex)
+{
+    struct lldpd *mylldpd = NULL;
+    struct lldpd_hardware *hardware = NULL;
+    unsigned char *lldp_ptr = 0;
+    uint32_t lldp_size = 0;
+    static const uint8_t eth_addr_lldp[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x0e};
+
+    ovs_mutex_lock(&mutex);
+
+    mylldpd = lldp->lldpd;
+    hardware = TAILQ_FIRST(&mylldpd->g_hardware);
+
+    lldp_ptr = malloc(hardware->h_mtu-ETH_HEADER_LEN);
+    if (lldp_ptr) {
+        /* Allocate MTU of space, call lldpd_send with this space
+         * on return, find end of packet, or size used by the lldpd_send
+         * do eth_compose with the size required by lldpd_send
+         * memcpy the lldp space into the packet->data space.
+         *
+         * Note, we allocate an almost MTU-size frame here for lldp to
+         * fill in and then memcpy this onto the ofpbuf. Alternatively (let's
+         * call this method 2) we could ask lldp for the required size frame it
+         * needs, and *then* allocate that space with eth_compose, thereby
+         * avoiding an extra malloc and memcpy. This alternative is more
+         * efficient, but we're not doing that here since performance at the
+         * moment is not an issue with slow-path LLDP packets that are sent
+         * typically every 30 seconds. If performance becomes an issue, we can
+         * change the way lldp send currently works,
+         * and instead have it return the required size (method 2 above).
+         */
+        memset(lldp_ptr, 0, hardware->h_mtu-ETH_HEADER_LEN);
+        lldp_size = lldpd_send(hardware, lldp_ptr);
+        if (lldp_size + ETH_HEADER_LEN < MINIMUM_ETH_PACKET_SIZE) {
+            lldp_size = MINIMUM_ETH_PACKET_SIZE;
+        }
+        eth_compose(packet, eth_addr_lldp, eth_src, ETH_TYPE_LLDP, lldp_size);
+
+        memcpy(ofpbuf_l3(packet), lldp_ptr, lldp_size);
+        free(lldp_ptr);
+    }
+
+    timer_set_duration(&lldp->tx_timer, lldp->lldpd->g_config.c_tx_interval);
+    ovs_mutex_unlock(&mutex);
+}
+
+/* Configures the LLDP stack.
+ */
+bool
+lldp_configure(struct lldp *lldp) OVS_EXCLUDED(mutex)
+{
+    ovs_mutex_lock(&mutex);
+    timer_set_expired(&lldp->tx_timer);
+    timer_set_duration(&lldp->tx_timer, LLDP_DEFAULT_TRANSMIT_INTERVAL_MS);
+    lldp->lldpd->g_config.c_tx_interval = LLDP_DEFAULT_TRANSMIT_INTERVAL_MS;
+    ovs_mutex_unlock(&mutex);
+
+    return true;
+}
+
+/* Create an LLDP stack instance.  At the moment there is one per bridge port.
+ */
+struct lldp *
+lldp_create(const struct netdev *netdev,
+            const uint32_t mtu,
+            const struct smap *cfg) OVS_EXCLUDED(mutex)
+{
+    struct lldp *lldp;
+    struct lldpd_chassis *lchassis;
+    struct lldpd_hardware *hardware;
+    struct aa_mapping_internal *m;
+
+    if (!cfg || !smap_get_bool(cfg, "enable", false)) {
+        /*lldp_unref(lldp);*/
+        VLOG_INFO("lldp not enabled %s",__FUNCTION__);
+        return NULL;
+    }
+
+    lldp = xzalloc(sizeof *lldp);
+    lldp->name = xstrdup(netdev_get_name(netdev));
+    lldp->lldpd = xzalloc(sizeof(struct lldpd));
+
+    hmap_init(&lldp->mappings_by_isid);
+    hmap_init(&lldp->mappings_by_aux);
+    list_init(&lldp->active_mapping_queue);
+
+    lchassis = xzalloc(sizeof *lchassis);
+    lchassis->c_cap_available = LLDP_CAP_BRIDGE;
+    lchassis->c_cap_enabled = LLDP_CAP_BRIDGE;
+    lchassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
+    lchassis->c_id_len = ETHER_ADDR_LEN;
+    lchassis->c_id = malloc(ETHER_ADDR_LEN);
+    netdev_get_etheraddr(netdev, (uint8_t *) lchassis->c_id);
+
+    TAILQ_INIT(&lchassis->c_mgmt);
+    lchassis->c_ttl = lldp->lldpd->g_config.c_tx_interval *
+                      lldp->lldpd->g_config.c_tx_hold;
+    lchassis->c_ttl = LLDP_CHASSIS_TTL;
+    lldpd_assign_cfg_to_protocols(lldp->lldpd);
+    lldp->lldpd->g_config.c_paused = 0;
+    lldp->lldpd->g_config.c_smart = 0;
+
+    TAILQ_INIT(&lldp->lldpd->g_chassis);
+    TAILQ_INSERT_TAIL(&lldp->lldpd->g_chassis, lchassis, c_entries);
+    memset(&(lldp->lldpd->g_config), 0, sizeof(lldp->lldpd->g_config));
+
+    if ((hardware = lldpd_alloc_hardware(lldp->lldpd,
+                                         (char *) netdev_get_name(netdev),
+                                         0)) == NULL) {
+        VLOG_WARN("Unable to allocate space for %s",
+                  (char *) netdev_get_name(netdev));
+        return NULL;
+    }
+
+    atomic_init(&lldp->ref_cnt, 1);
+    hardware->h_flags |= IFF_RUNNING;
+    hardware->h_mtu = mtu;
+    hardware->h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+    hardware->h_lport.p_id = malloc(strlen(netdev_get_name(netdev)));
+    hardware->h_lport.p_id_len = strlen(netdev_get_name(netdev));
+    memcpy(hardware->h_lport.p_id, netdev_get_name(netdev),
+           strlen(netdev_get_name(netdev)));
+
+    /* Auto Attach element tlv */
+    hardware->h_lport.p_element.type = LLDP_TLV_AA_ELEM_TYPE_PROXY;
+    hardware->h_lport.p_element.mgmt_vlan = 0;
+    memcpy(&hardware->h_lport.p_element.system_id.system_mac,
+           lchassis->c_id, lchassis->c_id_len);
+    hardware->h_lport.p_element.system_id.conn_type =
+        LLDP_TLV_AA_ELEM_CONN_TYPE_SINGLE;
+    hardware->h_lport.p_element.system_id.smlt_id = 0;
+    hardware->h_lport.p_element.system_id.mlt_id[0] = 0;
+    hardware->h_lport.p_element.system_id.mlt_id[1] = 0;
+    TAILQ_INIT(&hardware->h_lport.p_isid_vlan_maps);
+
+    TAILQ_INIT(&lldp->lldpd->g_hardware);
+    TAILQ_INSERT_TAIL(&lldp->lldpd->g_hardware, hardware, h_entries);
+
+    /* Update port with Auto Attach mappings configured. */
+    HMAP_FOR_EACH (m, hmap_node, all_mappings) {
+        struct aa_mapping_internal *p;
+
+        p = xzalloc(sizeof *p);
+        memcpy(p, m, sizeof *p);
+        hmap_insert(&lldp->mappings_by_isid, &p->hmap_node,
+                    hash_bytes((const void *) &p->isid,
+                    sizeof(p->isid), 0));
+
+        p = xzalloc(sizeof *p);
+        memcpy(p, m, sizeof *p);
+        hmap_insert(&lldp->mappings_by_aux, &p->hmap_node,
+                    hash_bytes((const void *) &p->aux,
+                    sizeof(p->aux), 0));
+
+        update_mapping_on_lldp(lldp, hardware, p);
+    }
+
+    ovs_mutex_lock(&mutex);
+    hmap_insert(all_lldps, &lldp->hmap_node,
+                hash_string(netdev_get_name(netdev), 0));
+    ovs_mutex_unlock(&mutex);
+
+    return lldp;
+}
+
+/* Free an LLDP instance.
+ */
+static void
+lldp_free(struct lldp *lldp) OVS_REQUIRES(mutex)
+{
+     hmap_remove(all_lldps, &lldp->hmap_node);
+     lldpd_cleanup(lldp->lldpd);
+     free(lldp->lldpd);
+     free(lldp->name);
+     free(lldp);
+}
+
+/* Unreference a specific LLDP instance.
+ */
+void
+lldp_unref(struct lldp *lldp)
+{
+    if (lldp) {
+        int orig;
+
+        atomic_sub(&lldp->ref_cnt, 1, &orig);
+        ovs_assert(orig > 0);
+        if (orig == 1) {
+            ovs_mutex_lock(&mutex);
+            lldp_free(lldp);
+            ovs_mutex_unlock(&mutex);
+        }
+    }
+}
+
+/* Unreference a specific LLDP instance.
+ */
+struct lldp *
+lldp_ref(const struct lldp *lldp_)
+{
+    struct lldp *lldp = CONST_CAST(struct lldp *, lldp_);
+
+    if (lldp) {
+        int orig;
+        atomic_add(&lldp->ref_cnt, 1, &orig);
+        ovs_assert(orig > 0);
+    }
+
+    return lldp;
+}
diff --git a/lib/ovs-lldp.h b/lib/ovs-lldp.h
new file mode 100644
index 0000000..0676951
--- /dev/null
+++ b/lib/ovs-lldp.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2014 Wind River Systems, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OVS_LLDP_H
+#define OVS_LLDP_H
+
+#include <config.h>
+#include <stdint.h>
+#include "ofpbuf.h"
+#include "packets.h"
+#include "lldpd.h"
+#include "timer.h"
+#include "hmap.h"
+#include "list.h"
+#include "ovs-thread.h"
+#include "lib/ovsdb-data.h"
+
+/* Transmit every LLDPD_TX_INTERVAL seconds. */
+#define LLDP_DEFAULT_TRANSMIT_INTERVAL_MS LLDPD_TX_INTERVAL * 1000
+
+struct flow_wildcards;
+struct flow;
+struct netdev;
+struct smap;
+
+/* Structure per LLDP instance (at the moment per port when enabled).
+ */
+struct lldp {
+    struct hmap_node hmap_node;   /* Node in all_lldps list. */
+    struct lldpd *lldpd;
+    char *name;                   /* Name of the port. */
+    struct timer tx_timer;        /* Send LLDP when expired. */
+    struct hmap mappings_by_isid; /* "struct" indexed by ISID */
+    struct hmap mappings_by_aux;  /* "struct" indexed by aux */
+    struct list active_mapping_queue;
+    atomic_int ref_cnt;
+};
+
+struct lldp_status {
+    /* TODO should reflect lldp stack detail */
+};
+
+/* Configuration specific to Auto Attach.
+ */
+struct aa_settings {
+    bool enabled;
+    char *system_description;
+    char *system_name;
+    char *authentication_key;
+};
+
+/* Configuration of Auto Attach mappings.
+ */
+struct aa_mapping_settings {
+    int64_t isid;
+    int64_t vlan;
+};
+
+enum bridge_aa_vlan_oper {
+   BRIDGE_AA_VLAN_OPER_UNDEF,
+   BRIDGE_AA_VLAN_OPER_ADD,
+   BRIDGE_AA_VLAN_OPER_REMOVE
+};
+
+/* Bridge Auto Attach operations.  Mostly for adding/removing VLAN on
+ * the trunk port connected to the Auto Attach server.
+ */
+struct bridge_aa_vlan {
+    struct list list_node;
+    char *port_name;
+    uint32_t vlan;
+    enum bridge_aa_vlan_oper oper;
+};
+
+void lldp_init(void);
+void lldp_wait(struct lldp *lldp) OVS_EXCLUDED(mutex);
+long long int lldp_wake_time(const struct lldp *lldp) OVS_EXCLUDED(mutex);
+void lldp_run(struct lldpd *cfg) OVS_EXCLUDED(mutex);
+bool lldp_should_send_packet(struct lldp *cfg) OVS_EXCLUDED(mutex);
+bool lldp_should_process_flow(const struct flow *flow,
+                              struct flow_wildcards *wc);
+bool lldp_configure(struct lldp *lldp) OVS_EXCLUDED(mutex);
+void lldp_process_packet(struct lldp *cfg, const struct ofpbuf *p);
+void lldp_put_packet(struct lldp *lldp, struct ofpbuf *packet,
+                     uint8_t eth_src[ETH_ADDR_LEN]);
+void lldpd_assign_cfg_to_protocols(struct lldpd *cfg);
+struct lldp * lldp_create(const struct netdev *netdev, const uint32_t mtu,
+                          const struct smap *cfg);
+struct lldp * lldp_ref(const struct lldp *lldp_);
+void lldp_unref(struct lldp *lldp);
+
+int aa_get_vlan_queued(struct list **list);
+unsigned int aa_get_vlan_queue_size(void);
+int aa_configure(const struct aa_settings *s);
+int aa_mapping_register(void *aux, const struct aa_mapping_settings *s);
+int aa_mapping_unregister(void *aux);
+
+#endif /* OVS_LLDP_H */
diff --git a/lib/packets.c b/lib/packets.c
index 65d8109..928a9e6 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -575,6 +575,7 @@ eth_compose(struct ofpbuf *b, const uint8_t eth_dst[ETH_ADDR_LEN],
     eth = ofpbuf_put_uninit(b, ETH_HEADER_LEN);
     data = ofpbuf_put_uninit(b, size);

+    memset(ofpbuf_base(b), 0, size);
     memcpy(eth->eth_dst, eth_dst, ETH_ADDR_LEN);
     memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
     eth->eth_type = htons(eth_type);
diff --git a/lib/queue.h b/lib/queue.h
new file mode 100644
index 0000000..daf4553
--- /dev/null
+++ b/lib/queue.h
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 1991, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  @(#)queue.h     8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef   _SYS_QUEUE_H_
+#define   _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list.  Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction.  Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define   LIST_HEAD(name, type)                           \
+struct name {                                       \
+    struct type *lh_first;     /* first element */             \
+}
+
+#define   LIST_HEAD_INITIALIZER(head)                          \
+    { NULL }
+
+#define   LIST_ENTRY(type)                                \
+struct {                                       \
+    struct type *le_next; /* next element */              \
+    struct type **le_prev;     /* address of previous next element */     \
+}
+
+/*
+ * List functions.
+ */
+#define   LIST_INIT(head) do {                            \
+    (head)->lh_first = NULL;                        \
+} while (/*CONSTCOND*/0)
+
+#define   LIST_INSERT_AFTER(listelm, elm, field) do {                \
+    if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)  \
+          (listelm)->field.le_next->field.le_prev =       \
+              &(elm)->field.le_next;                 \
+    (listelm)->field.le_next = (elm);                    \
+    (elm)->field.le_prev = &(listelm)->field.le_next;          \
+} while (/*CONSTCOND*/0)
+
+#define   LIST_INSERT_BEFORE(listelm, elm, field) do {               \
+    (elm)->field.le_prev = (listelm)->field.le_prev;           \
+    (elm)->field.le_next = (listelm);                    \
+    *(listelm)->field.le_prev = (elm);                   \
+    (listelm)->field.le_prev = &(elm)->field.le_next;          \
+} while (/*CONSTCOND*/0)
+
+#define   LIST_INSERT_HEAD(head, elm, field) do {                    \
+    if (((elm)->field.le_next = (head)->lh_first) != NULL)          \
+          (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+    (head)->lh_first = (elm);                       \
+    (elm)->field.le_prev = &(head)->lh_first;            \
+} while (/*CONSTCOND*/0)
+
+#define   LIST_REMOVE(elm, field) do {                         \
+    if ((elm)->field.le_next != NULL)                    \
+          (elm)->field.le_next->field.le_prev =                \
+              (elm)->field.le_prev;                  \
+    *(elm)->field.le_prev = (elm)->field.le_next;              \
+} while (/*CONSTCOND*/0)
+
+#define   LIST_FOREACH(var, head, field)                       \
+    for ((var) = ((head)->lh_first);                     \
+          (var);                                     \
+          (var) = ((var)->field.le_next))
+
+/*
+ * List access methods.
+ */
+#define   LIST_EMPTY(head)           ((head)->lh_first == NULL)
+#define   LIST_FIRST(head)           ((head)->lh_first)
+#define   LIST_NEXT(elm, field)      ((elm)->field.le_next)
+
+
+/*
+ * Singly-linked List definitions.
+ */
+#define   SLIST_HEAD(name, type)                               \
+struct name {                                       \
+    struct type *slh_first;    /* first element */             \
+}
+
+#define   SLIST_HEAD_INITIALIZER(head)                         \
+    { NULL }
+
+#define   SLIST_ENTRY(type)                               \
+struct {                                       \
+    struct type *sle_next;     /* next element */              \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define   SLIST_INIT(head) do {                           \
+    (head)->slh_first = NULL;                       \
+} while (/*CONSTCOND*/0)
+
+#define   SLIST_INSERT_AFTER(slistelm, elm, field) do {              \
+    (elm)->field.sle_next = (slistelm)->field.sle_next;        \
+    (slistelm)->field.sle_next = (elm);                  \
+} while (/*CONSTCOND*/0)
+
+#define   SLIST_INSERT_HEAD(head, elm, field) do {             \
+    (elm)->field.sle_next = (head)->slh_first;           \
+    (head)->slh_first = (elm);                      \
+} while (/*CONSTCOND*/0)
+
+#define   SLIST_REMOVE_HEAD(head, field) do {                  \
+    (head)->slh_first = (head)->slh_first->field.sle_next;          \
+} while (/*CONSTCOND*/0)
+
+#define   SLIST_REMOVE(head, elm, type, field) do {            \
+    if ((head)->slh_first == (elm)) {                    \
+          SLIST_REMOVE_HEAD((head), field);               \
+    }                                          \
+    else {                                          \
+          struct type *curelm = (head)->slh_first;        \
+          while(curelm->field.sle_next != (elm))               \
+               curelm = curelm->field.sle_next;           \
+          curelm->field.sle_next =                   \
+              curelm->field.sle_next->field.sle_next;          \
+    }                                          \
+} while (/*CONSTCOND*/0)
+
+#define   SLIST_FOREACH(var, head, field)                      \
+    for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+/*
+ * Singly-linked List access methods.
+ */
+#define   SLIST_EMPTY(head)    ((head)->slh_first == NULL)
+#define   SLIST_FIRST(head)    ((head)->slh_first)
+#define   SLIST_NEXT(elm, field)     ((elm)->field.sle_next)
+
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define   STAILQ_HEAD(name, type)                         \
+struct name {                                       \
+    struct type *stqh_first;   /* first element */             \
+    struct type **stqh_last;   /* addr of last next element */      \
+}
+
+#define   STAILQ_HEAD_INITIALIZER(head)                        \
+    { NULL, &(head).stqh_first }
+
+#define   STAILQ_ENTRY(type)                              \
+struct {                                       \
+    struct type *stqe_next;    /* next element */              \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define   STAILQ_INIT(head) do {                               \
+    (head)->stqh_first = NULL;                      \
+    (head)->stqh_last = &(head)->stqh_first;                   \
+} while (/*CONSTCOND*/0)
+
+#define   STAILQ_INSERT_HEAD(head, elm, field) do {            \
+    if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+          (head)->stqh_last = &(elm)->field.stqe_next;         \
+    (head)->stqh_first = (elm);                          \
+} while (/*CONSTCOND*/0)
+
+#define   STAILQ_INSERT_TAIL(head, elm, field) do {            \
+    (elm)->field.stqe_next = NULL;                       \
+    *(head)->stqh_last = (elm);                          \
+    (head)->stqh_last = &(elm)->field.stqe_next;               \
+} while (/*CONSTCOND*/0)
+
+#define   STAILQ_INSERT_AFTER(head, listelm, elm, field) do {        \
+    if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+          (head)->stqh_last = &(elm)->field.stqe_next;         \
+    (listelm)->field.stqe_next = (elm);                  \
+} while (/*CONSTCOND*/0)
+
+#define   STAILQ_REMOVE_HEAD(head, field) do {                 \
+    if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+          (head)->stqh_last = &(head)->stqh_first;             \
+} while (/*CONSTCOND*/0)
+
+#define   STAILQ_REMOVE(head, elm, type, field) do {           \
+    if ((head)->stqh_first == (elm)) {                   \
+          STAILQ_REMOVE_HEAD((head), field);              \
+    } else {                                   \
+          struct type *curelm = (head)->stqh_first;       \
+          while (curelm->field.stqe_next != (elm))             \
+               curelm = curelm->field.stqe_next;          \
+          if ((curelm->field.stqe_next =                  \
+               curelm->field.stqe_next->field.stqe_next) == NULL) \
+                   (head)->stqh_last = &(curelm)->field.stqe_next; \
+    }                                          \
+} while (/*CONSTCOND*/0)
+
+#define   STAILQ_FOREACH(var, head, field)                     \
+    for ((var) = ((head)->stqh_first);                   \
+          (var);                                     \
+          (var) = ((var)->field.stqe_next))
+
+#define   STAILQ_CONCAT(head1, head2) do {                     \
+    if (!STAILQ_EMPTY((head2))) {                        \
+          *(head1)->stqh_last = (head2)->stqh_first;      \
+          (head1)->stqh_last = (head2)->stqh_last;        \
+          STAILQ_INIT((head2));                      \
+    }                                          \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define   STAILQ_EMPTY(head)   ((head)->stqh_first == NULL)
+#define   STAILQ_FIRST(head)   ((head)->stqh_first)
+#define   STAILQ_NEXT(elm, field)    ((elm)->field.stqe_next)
+
+
+/*
+ * Simple queue definitions.
+ */
+#define   SIMPLEQ_HEAD(name, type)                        \
+struct name {                                       \
+    struct type *sqh_first;    /* first element */             \
+    struct type **sqh_last;    /* addr of last next element */      \
+}
+
+#define   SIMPLEQ_HEAD_INITIALIZER(head)                       \
+    { NULL, &(head).sqh_first }
+
+#define   SIMPLEQ_ENTRY(type)                             \
+struct {                                       \
+    struct type *sqe_next;     /* next element */              \
+}
+
+/*
+ * Simple queue functions.
+ */
+#define   SIMPLEQ_INIT(head) do {                              \
+    (head)->sqh_first = NULL;                       \
+    (head)->sqh_last = &(head)->sqh_first;                     \
+} while (/*CONSTCOND*/0)
+
+#define   SIMPLEQ_INSERT_HEAD(head, elm, field) do {           \
+    if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)   \
+          (head)->sqh_last = &(elm)->field.sqe_next;      \
+    (head)->sqh_first = (elm);                      \
+} while (/*CONSTCOND*/0)
+
+#define   SIMPLEQ_INSERT_TAIL(head, elm, field) do {           \
+    (elm)->field.sqe_next = NULL;                        \
+    *(head)->sqh_last = (elm);                      \
+    (head)->sqh_last = &(elm)->field.sqe_next;           \
+} while (/*CONSTCOND*/0)
+
+#define   SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {       \
+    if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+          (head)->sqh_last = &(elm)->field.sqe_next;      \
+    (listelm)->field.sqe_next = (elm);                   \
+} while (/*CONSTCOND*/0)
+
+#define   SIMPLEQ_REMOVE_HEAD(head, field) do {                \
+    if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+          (head)->sqh_last = &(head)->sqh_first;               \
+} while (/*CONSTCOND*/0)
+
+#define   SIMPLEQ_REMOVE(head, elm, type, field) do {                \
+    if ((head)->sqh_first == (elm)) {                    \
+          SIMPLEQ_REMOVE_HEAD((head), field);             \
+    } else {                                   \
+          struct type *curelm = (head)->sqh_first;        \
+          while (curelm->field.sqe_next != (elm))              \
+               curelm = curelm->field.sqe_next;           \
+          if ((curelm->field.sqe_next =                   \
+               curelm->field.sqe_next->field.sqe_next) == NULL) \
+                   (head)->sqh_last = &(curelm)->field.sqe_next; \
+    }                                          \
+} while (/*CONSTCOND*/0)
+
+#define   SIMPLEQ_FOREACH(var, head, field)                    \
+    for ((var) = ((head)->sqh_first);                    \
+          (var);                                     \
+          (var) = ((var)->field.sqe_next))
+
+/*
+ * Simple queue access methods.
+ */
+#define   SIMPLEQ_EMPTY(head)        ((head)->sqh_first == NULL)
+#define   SIMPLEQ_FIRST(head)        ((head)->sqh_first)
+#define   SIMPLEQ_NEXT(elm, field)   ((elm)->field.sqe_next)
+
+
+/*
+ * Tail queue definitions.
+ */
+#define   _TAILQ_HEAD(name, type, qual)                        \
+struct name {                                       \
+    qual type *tqh_first;      /* first element */        \
+    qual type *qual *tqh_last; /* addr of last next element */ \
+}
+#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
+
+#define   TAILQ_HEAD_INITIALIZER(head)                         \
+    { NULL, &(head).tqh_first }
+
+#define   _TAILQ_ENTRY(type, qual)                        \
+struct {                                       \
+    qual type *tqe_next;       /* next element */         \
+    qual type *qual *tqe_prev; /* address of previous next element */\
+}
+#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue functions.
+ */
+#define   TAILQ_INIT(head) do {                           \
+    (head)->tqh_first = NULL;                       \
+    (head)->tqh_last = &(head)->tqh_first;                     \
+} while (/*CONSTCOND*/0)
+
+#define   TAILQ_INSERT_HEAD(head, elm, field) do {             \
+    if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)   \
+          (head)->tqh_first->field.tqe_prev =             \
+              &(elm)->field.tqe_next;                     \
+    else                                       \
+          (head)->tqh_last = &(elm)->field.tqe_next;      \
+    (head)->tqh_first = (elm);                      \
+    (elm)->field.tqe_prev = &(head)->tqh_first;                \
+} while (/*CONSTCOND*/0)
+
+#define   TAILQ_INSERT_TAIL(head, elm, field) do {             \
+    (elm)->field.tqe_next = NULL;                        \
+    (elm)->field.tqe_prev = (head)->tqh_last;            \
+    *(head)->tqh_last = (elm);                      \
+    (head)->tqh_last = &(elm)->field.tqe_next;           \
+} while (/*CONSTCOND*/0)
+
+#define   TAILQ_INSERT_AFTER(head, listelm, elm, field) do {         \
+    if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+          (elm)->field.tqe_next->field.tqe_prev =         \
+             &(elm)->field.tqe_next;                     \
+    else                                       \
+          (head)->tqh_last = &(elm)->field.tqe_next;      \
+    (listelm)->field.tqe_next = (elm);                   \
+    (elm)->field.tqe_prev = &(listelm)->field.tqe_next;        \
+} while (/*CONSTCOND*/0)
+
+#define   TAILQ_INSERT_BEFORE(listelm, elm, field) do {              \
+    (elm)->field.tqe_prev = (listelm)->field.tqe_prev;         \
+    (elm)->field.tqe_next = (listelm);                   \
+    *(listelm)->field.tqe_prev = (elm);                  \
+    (listelm)->field.tqe_prev = &(elm)->field.tqe_next;        \
+} while (/*CONSTCOND*/0)
+
+#define   TAILQ_REMOVE(head, elm, field) do {                  \
+    if (((elm)->field.tqe_next) != NULL)                 \
+          (elm)->field.tqe_next->field.tqe_prev =         \
+              (elm)->field.tqe_prev;                 \
+    else                                       \
+          (head)->tqh_last = (elm)->field.tqe_prev;       \
+    *(elm)->field.tqe_prev = (elm)->field.tqe_next;            \
+} while (/*CONSTCOND*/0)
+
+#define   TAILQ_FOREACH(var, head, field)                      \
+    for ((var) = ((head)->tqh_first);                    \
+          (var);                                     \
+          (var) = ((var)->field.tqe_next))
+
+#define   TAILQ_FOREACH_REVERSE(var, head, headname, field)          \
+    for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
+          (var);                                     \
+          (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+#define   TAILQ_CONCAT(head1, head2, field) do {                     \
+    if (!TAILQ_EMPTY(head2)) {                      \
+          *(head1)->tqh_last = (head2)->tqh_first;        \
+          (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;    \
+          (head1)->tqh_last = (head2)->tqh_last;               \
+          TAILQ_INIT((head2));                       \
+    }                                          \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Tail queue access methods.
+ */
+#define   TAILQ_EMPTY(head)          ((head)->tqh_first == NULL)
+#define   TAILQ_FIRST(head)          ((head)->tqh_first)
+#define   TAILQ_NEXT(elm, field)          ((elm)->field.tqe_next)
+
+#define   TAILQ_LAST(head, headname) \
+    (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define   TAILQ_PREV(elm, headname, field) \
+    (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+/*
+ * Circular queue definitions.
+ */
+#define   CIRCLEQ_HEAD(name, type)                        \
+struct name {                                       \
+    struct type *cqh_first;         /* first element */        \
+    struct type *cqh_last;          /* last element */         \
+}
+
+#define   CIRCLEQ_HEAD_INITIALIZER(head)                       \
+    { (void *)&head, (void *)&head }
+
+#define   CIRCLEQ_ENTRY(type)                             \
+struct {                                       \
+    struct type *cqe_next;          /* next element */         \
+    struct type *cqe_prev;          /* previous element */          \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define   CIRCLEQ_INIT(head) do {                              \
+    (head)->cqh_first = (void *)(head);                  \
+    (head)->cqh_last = (void *)(head);                   \
+} while (/*CONSTCOND*/0)
+
+#define   CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {       \
+    (elm)->field.cqe_next = (listelm)->field.cqe_next;         \
+    (elm)->field.cqe_prev = (listelm);                   \
+    if ((listelm)->field.cqe_next == (void *)(head))           \
+          (head)->cqh_last = (elm);                  \
+    else                                       \
+          (listelm)->field.cqe_next->field.cqe_prev = (elm);   \
+    (listelm)->field.cqe_next = (elm);                   \
+} while (/*CONSTCOND*/0)
+
+#define   CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {      \
+    (elm)->field.cqe_next = (listelm);                   \
+    (elm)->field.cqe_prev = (listelm)->field.cqe_prev;         \
+    if ((listelm)->field.cqe_prev == (void *)(head))           \
+          (head)->cqh_first = (elm);                 \
+    else                                       \
+          (listelm)->field.cqe_prev->field.cqe_next = (elm);   \
+    (listelm)->field.cqe_prev = (elm);                   \
+} while (/*CONSTCOND*/0)
+
+#define   CIRCLEQ_INSERT_HEAD(head, elm, field) do {           \
+    (elm)->field.cqe_next = (head)->cqh_first;           \
+    (elm)->field.cqe_prev = (void *)(head);                    \
+    if ((head)->cqh_last == (void *)(head))                    \
+          (head)->cqh_last = (elm);                  \
+    else                                       \
+          (head)->cqh_first->field.cqe_prev = (elm);      \
+    (head)->cqh_first = (elm);                      \
+} while (/*CONSTCOND*/0)
+
+#define   CIRCLEQ_INSERT_TAIL(head, elm, field) do {           \
+    (elm)->field.cqe_next = (void *)(head);                    \
+    (elm)->field.cqe_prev = (head)->cqh_last;            \
+    if ((head)->cqh_first == (void *)(head))             \
+          (head)->cqh_first = (elm);                 \
+    else                                       \
+          (head)->cqh_last->field.cqe_next = (elm);       \
+    (head)->cqh_last = (elm);                       \
+} while (/*CONSTCOND*/0)
+
+#define   CIRCLEQ_REMOVE(head, elm, field) do {                \
+    if ((elm)->field.cqe_next == (void *)(head))               \
+          (head)->cqh_last = (elm)->field.cqe_prev;       \
+    else                                       \
+          (elm)->field.cqe_next->field.cqe_prev =              \
+              (elm)->field.cqe_prev;                 \
+    if ((elm)->field.cqe_prev == (void *)(head))               \
+          (head)->cqh_first = (elm)->field.cqe_next;      \
+    else                                       \
+          (elm)->field.cqe_prev->field.cqe_next =              \
+              (elm)->field.cqe_next;                 \
+} while (/*CONSTCOND*/0)
+
+#define   CIRCLEQ_FOREACH(var, head, field)                    \
+    for ((var) = ((head)->cqh_first);                    \
+          (var) != (const void *)(head);                  \
+          (var) = ((var)->field.cqe_next))
+
+#define   CIRCLEQ_FOREACH_REVERSE(var, head, field)            \
+    for ((var) = ((head)->cqh_last);                     \
+          (var) != (const void *)(head);                  \
+          (var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define   CIRCLEQ_EMPTY(head)        ((head)->cqh_first == (void *)(head))
+#define   CIRCLEQ_FIRST(head)        ((head)->cqh_first)
+#define   CIRCLEQ_LAST(head)         ((head)->cqh_last)
+#define   CIRCLEQ_NEXT(elm, field)   ((elm)->field.cqe_next)
+#define   CIRCLEQ_PREV(elm, field)   ((elm)->field.cqe_prev)
+
+#define CIRCLEQ_LOOP_NEXT(head, elm, field)                    \
+    (((elm)->field.cqe_next == (void *)(head))           \
+        ? ((head)->cqh_first)                       \
+        : (elm->field.cqe_next))
+#define CIRCLEQ_LOOP_PREV(head, elm, field)                    \
+    (((elm)->field.cqe_prev == (void *)(head))           \
+        ? ((head)->cqh_last)                        \
+        : (elm->field.cqe_prev))
+
+#endif    /* sys/queue.h */
diff --git a/m4/openvswitch.m4 b/m4/openvswitch.m4
index 6e24f25..f6b01b1 100644
--- a/m4/openvswitch.m4
+++ b/m4/openvswitch.m4
@@ -179,6 +179,21 @@ OpenFlow connections over SSL will not be supported.
       AC_DEFINE([HAVE_OPENSSL], [1], [Define to 1 if OpenSSL is installed.])
    fi])

+dnl Check for Auto Attach.
+AC_DEFUN([OVS_CHECK_AUTO_ATTACH],
+  [AC_ARG_ENABLE(
+     [auto-attach],
+     [AC_HELP_STRING([--enable-auto-attach],
+                     [Enable Auto Attach.])],
+     [auto_attach=true],
+     [auto_attach=false])
+   AC_SUBST([ENABLE_AUTO_ATTACH])
+   AM_CONDITIONAL([ENABLE_AUTO_ATTACH], [test x$auto_attach = xtrue])
+   if test "$auto_attach" != false; then
+      AC_DEFINE([ENABLE_AUTO_ATTACH], [1], [Define to 1 if Auto Attach is enabled.])
+
+   fi])
+
dnl Checks for libraries needed by lib/socket-util.c.
AC_DEFUN([OVS_CHECK_SOCKET_LIBS],
   [AC_CHECK_LIB([socket], [connect])
--
1.7.12.4





More information about the dev mailing list