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

drflynn at avaya.com drflynn at avaya.com
Fri Dec 19 14:28:27 UTC 2014


From: Dennis Flynn <drflynn at avaya.com>

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 provided to include this new
Auto-Attach 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.

Signed-off-by: Ludovic Beliveau <ludovic.beliveau at windriver.com>
Signed-off-by: Dennis Flynn <drflynn at avaya.com>

diff --git a/NOTICE b/NOTICE
index a213190..dbbb707 100644
--- a/NOTICE
+++ b/NOTICE
@@ -25,3 +25,16 @@ Copyright (C) 2006-2013 Free Software Foundation, Inc.
 
 Rapid Spanning Tree Protocol (RSTP) implementation
 Copyright (c) 2011-2014 M3S, Srl - Italy
+
+LLDP implementation
+Copyright (c) 2008, 2012 Vincent Bernat <bernat at luffy.cx>
+
+LLDP includes code used from the Net::CDP project based on the ISC license
+Copyright (c) 2014 Michael Chapman
+
+LLDP includes code used from the ladvd project based on the ISC license
+Copyright (c) 2008, 2009, 2010 Sten Spans <sten at blinkenlights.nl>
+
+Auto Attach implementation
+Copyright (c) 2014 WindRiver, Inc
+Copyright (c) 2014 Avaya, Inc
diff --git a/debian/copyright.in b/debian/copyright.in
index 811a378..a9aaa5b 100644
--- a/debian/copyright.in
+++ b/debian/copyright.in
@@ -17,6 +17,10 @@ Upstream Copyright Holders:
 	Copyright (C) 2000 The NetBSD Foundation, Inc.
 	Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 	Copyright (c) 1982, 1986, 1990, 1993 The Regents of the University of California.
+	Copyright (c) 2008, 2012 Vincent Bernat <bernat at luffy.cx>
+	Copyright (c) 2014 Michael Chapman
+	Copyright (c) 2014 WindRiver, Inc.
+	Copyright (c) 2014 Avaya, Inc.
 
 License:
 
@@ -204,6 +208,17 @@ License:
     gives unlimited permission to copy and/or distribute it,
     with or without modifications, as long as this notice is preserved.
 
+* The following files are licensed under the ISC-license
+
+    lib/lldp/aa-structs.h
+    lib/lldp/lldpd.c
+    lib/lldp/lldpd.h
+    lib/lldp/lldpd-structs.c
+    lib/lldp/lldpd-structs.h
+    lib/lldp/lldp.c
+    lib/lldp/lldp-tlv.h
+    lib/lldp/lldp-const.h
+
 * All other components of this package are licensed under
   The Apache License Version 2.0.
 
diff --git a/lib/automake.mk b/lib/automake.mk
index 2a5844b..33a7461 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -160,6 +160,8 @@ lib_libopenvswitch_la_SOURCES = \
 	lib/ovs-atomic-pthreads.h \
 	lib/ovs-atomic-x86_64.h \
 	lib/ovs-atomic.h \
+	lib/ovs-lldp.c \
+	lib/ovs-lldp.h \
 	lib/ovs-rcu.c \
 	lib/ovs-rcu.h \
 	lib/ovs-router.h \
@@ -267,7 +269,15 @@ lib_libopenvswitch_la_SOURCES = \
 	lib/vswitch-idl.c \
 	lib/vswitch-idl.h \
 	lib/vtep-idl.c \
-	lib/vtep-idl.h
+	lib/vtep-idl.h \
+	lib/lldp/aa-structs.h \
+	lib/lldp/lldp.c \
+	lib/lldp/lldp-const.h \
+	lib/lldp/lldp-tlv.h \
+	lib/lldp/lldpd.c \
+	lib/lldp/lldpd.h \
+	lib/lldp/lldpd-structs.c \
+	lib/lldp/lldpd-structs.h
 
 if WIN32
 lib_libopenvswitch_la_SOURCES += \
diff --git a/lib/lldp/aa-structs.h b/lib/lldp/aa-structs.h
new file mode 100644
index 0000000..f58be76
--- /dev/null
+++ b/lib/lldp/aa-structs.h
@@ -0,0 +1,49 @@
+/* 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
+
+#include <stdint.h>
+#include "list.h"
+
+struct lldp_aa_element_system_id {
+    uint8_t  system_mac[6];
+    uint16_t conn_type;
+    uint16_t smlt_id;
+    uint8_t  mlt_id[2];
+};
+
+struct lldpd_aa_element_tlv {
+    uint16_t                         type;
+    uint16_t                         mgmt_vlan;
+    struct lldp_aa_element_system_id system_id;
+};
+
+struct lldpd_aa_isid_vlan_map_data {
+    uint16_t status;
+    uint16_t vlan;
+    uint8_t  isid[3];
+};
+
+struct lldpd_aa_isid_vlan_maps_tlv {
+    struct ovs_list                     m_entries;
+    struct lldpd_aa_isid_vlan_map_data  isid_vlan_data;
+};
+
+#endif
diff --git a/lib/lldp/lldp-const.h b/lib/lldp/lldp-const.h
new file mode 100644
index 0000000..eceb612
--- /dev/null
+++ b/lib/lldp/lldp-const.h
@@ -0,0 +1,230 @@
+/* -*- 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 _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/lldp-tlv.h b/lib/lldp/lldp-tlv.h
new file mode 100644
index 0000000..237414d
--- /dev/null
+++ b/lib/lldp/lldp-tlv.h
@@ -0,0 +1,79 @@
+/* -*- 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 _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
+
+#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/lldp.c b/lib/lldp/lldp.c
new file mode 100644
index 0000000..ba42892
--- /dev/null
+++ b/lib/lldp/lldp.c
@@ -0,0 +1,751 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat at luffy.cx>
+ * Copyright (c) 2014 Michael Chapman
+ *
+ * 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"
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include "compiler.h"
+#include "packets.h"
+#include "ofpbuf.h"
+
+VLOG_DEFINE_THIS_MODULE(lldp);
+
+/* 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. The original
+ * author of those macros, Michael Chapman, has relicensed those macros under
+ * the ISC license.
+ */
+
+#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                                                    \
+    )
+
+#define CHECK_TLV_SIZE(x, name)                             \
+    do {                                                    \
+        if (tlv_size < (x)) {                               \
+            VLOG_WARN(name " TLV too short received on %s", \
+                      hardware->h_ifname);                  \
+            goto malformed;                                 \
+        }                                                   \
+    } while (0)
+
+static union {
+    uint8_t  f_uint8;
+    ovs_be16 f_uint16;
+    ovs_be32 f_uint32;
+} types;
+
+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;
+    }
+}
+
+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;
+    }
+}
+
+int
+lldp_send(struct lldpd *global OVS_UNUSED,
+          struct lldpd_hardware *hardware,
+          struct ofpbuf *p)
+{
+    struct lldpd_port *port;
+    struct lldpd_chassis *chassis;
+    struct lldpd_frame *frame;
+    uint8_t *packet, *pos, *tlv;
+    struct lldpd_mgmt *mgmt;
+    int length, proto;
+    const uint8_t avaya[] = LLDP_TLV_ORG_AVAYA;
+    struct lldpd_aa_isid_vlan_maps_tlv *vlan_isid_map;
+    uint8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH];
+
+    port = &hardware->h_lport;
+    chassis = port->p_chassis;
+
+    /* The ethernet header is filled in elsewhere, we must save room for it. */
+    length = hardware->h_mtu - sizeof(struct eth_header);
+    packet = ofpbuf_l3(p);
+    VLOG_DBG("LLDP PDU send to %s mtu %d incoming with ptr=%p",
+              hardware->h_ifname, hardware->h_mtu, packet);
+    pos = packet;
+
+    /*
+     * Make room in ofpbuf for chassis ID, Port ID, System Name, System Descr,
+     * System Cap
+     */
+    pos = ofpbuf_put_uninit(p, sizeof chassis->c_id_subtype +
+                               chassis->c_id_len +
+                               sizeof port->p_id_subtype +
+                               port->p_id_len +
+                               sizeof chassis->c_ttl +
+                               strlen(chassis->c_name) +
+                               strlen(chassis->c_descr) +
+                               sizeof chassis->c_cap_available +
+                               sizeof chassis->c_cap_enabled + 12);
+
+    /* 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;
+    }
+
+    LIST_FOR_EACH (mgmt, m_entries, &chassis->c_mgmt.m_entries) {
+       /*
+        * Make room for 1 mgmt interface
+        */
+        ofpbuf_put_uninit(p, 2 + sizeof(uint8_t) +
+                             sizeof(uint8_t) +
+                             mgmt->m_addrsize +
+                             sizeof(uint8_t) +
+                             sizeof(uint32_t) +
+                             sizeof(uint8_t));
+
+        proto = lldpd_af_to_lldp_proto(mgmt->m_family);
+        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') {
+        /* make room for port descr */
+        ofpbuf_put_uninit(p, 2 + strlen(port->p_descr));
+
+        if (!(POKE_START_LLDP_TLV(LLDP_TLV_PORT_DESCR) &&
+              POKE_BYTES(port->p_descr, strlen(port->p_descr)) &&
+              POKE_END_LLDP_TLV)) {
+            goto toobig;
+        }
+    }
+
+    /* 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;
+
+        /* Element 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;
+
+        /* make room for element type tlv */
+        ofpbuf_put_uninit(p, 2 + sizeof avaya +
+                             sizeof(uint8_t) +
+                             sizeof aa_element_first_byte +
+                             sizeof aa_element_second_byte +
+                             sizeof port->p_element.system_id.system_mac +
+                             sizeof aa_elem_sys_id_first_byte +
+                             sizeof aa_elem_sys_id_second_byte +
+                             sizeof port->p_element.system_id.mlt_id);
+
+        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 (!list_is_empty(&port->p_isid_vlan_maps.m_entries)) {
+        int j;
+
+       /*
+        * make room for aa_isid_digest
+        */
+        ofpbuf_put_uninit(p, 2 + sizeof avaya +
+                             sizeof(uint8_t) +
+                             sizeof msg_auth_digest);
+
+        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;
+        }
+
+        LIST_FOR_EACH (vlan_isid_map,
+                       m_entries,
+                       &hardware->h_lport.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;
+
+            /*
+             * Make room for one isid-vlan mapping
+             */
+            ofpbuf_put_uninit(p, sizeof status_vlan_word +
+                                 sizeof vlan_isid_map->isid_vlan_data.isid);
+
+            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;
+        }
+    }
+
+    /* Make room for the End TLV 0x0000 */
+    ofpbuf_put_uninit(p, sizeof(uint16_t));
+
+    /* END */
+    if (!(POKE_START_LLDP_TLV(LLDP_TLV_END) &&
+          POKE_END_LLDP_TLV)) {
+        goto toobig;
+    }
+
+    hardware->h_tx_cnt++;
+
+    /* We assume that LLDP frame is the reference */
+    if ((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);
+        }
+    }
+
+    return length;
+
+toobig:
+    free(packet);
+
+    return E2BIG;
+}
+
+int
+lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s,
+    struct lldpd_hardware *hardware, struct lldpd_chassis **newchassis,
+    struct lldpd_port **newport)
+{
+    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, af;
+    int tlv_size, tlv_type, tlv_subtype;
+    u_int8_t *pos, *tlv;
+    char *b;
+    struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map = NULL;
+    u_int8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH];
+    struct lldpd_mgmt *mgmt;
+    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;
+
+    VLOG_DBG("receive LLDP PDU on %s", hardware->h_ifname);
+
+    if ((chassis = calloc(1, sizeof *chassis)) == NULL) {
+        VLOG_WARN("failed to allocate remote chassis");
+        return -1;
+    }
+    list_init(&chassis->c_mgmt.m_entries);
+
+    if ((port = calloc(1, sizeof *port)) == NULL) {
+        VLOG_WARN("failed to allocate remote port");
+        free(chassis);
+        return -1;
+    }
+    list_init(&port->p_isid_vlan_maps.m_entries);
+
+    length = s;
+    pos = (u_int8_t*) frame;
+
+    if (length < 2 * ETH_ADDR_LEN + sizeof(u_int16_t)) {
+        VLOG_WARN("too short frame received on %s", hardware->h_ifname);
+        goto malformed;
+    }
+    if (PEEK_CMP(lldpaddr, ETH_ADDR_LEN) != 0) {
+        VLOG_INFO("frame not targeted at LLDP multicast address "
+                  "received on %s", hardware->h_ifname);
+        goto malformed;
+    }
+    PEEK_DISCARD(ETH_ADDR_LEN); /* Skip source address */
+    if (PEEK_UINT16 != ETHERTYPE_LLDP) {
+        VLOG_INFO("non LLDP frame received on %s", hardware->h_ifname);
+        goto malformed;
+    }
+
+    while (length && (!gotend)) {
+        if (length < 2) {
+            VLOG_WARN("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) {
+            VLOG_WARN("frame too short for tlv received on %s",
+                      hardware->h_ifname);
+            goto malformed;
+        }
+
+        switch (tlv_type) {
+        case LLDP_TLV_END:
+            if (tlv_size != 0) {
+                VLOG_WARN("lldp end received with size not null on %s",
+                          hardware->h_ifname);
+                goto malformed;
+            }
+            if (length) {
+                VLOG_DBG("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)) {
+                VLOG_WARN("unknown subtype for tlv id received on %s",
+                          hardware->h_ifname);
+                goto malformed;
+            }
+            if ((b = (char *) calloc(1, tlv_size - 1)) == NULL) {
+                VLOG_WARN("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) {
+                VLOG_DBG("empty tlv received on %s", hardware->h_ifname);
+                break;
+            }
+            if ((b = (char *) calloc(1, tlv_size + 1)) == NULL) {
+                VLOG_WARN("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;
+            }
+            iface = iface_subtype == LLDP_MGMT_IFACE_IFINDEX ?
+                iface_number : 0;
+            mgmt = lldpd_alloc_mgmt(af, addr_ptr, addr_length, iface);
+            if (mgmt == NULL) {
+                VLOG_WARN("unable to allocate memory for management address");
+                goto malformed;
+            }
+            list_push_back(&chassis->c_mgmt.m_entries, &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) {
+                hardware->h_rx_unrecognized_cnt++;
+            } else if (memcmp(dot3, orgid, sizeof orgid) == 0) {
+                hardware->h_rx_unrecognized_cnt++;
+            } else if (memcmp(med, orgid, sizeof orgid) == 0) {
+                /* LLDP-MED */
+                hardware->h_rx_unrecognized_cnt++;
+            } else if (memcmp(avaya_oid, orgid, sizeof orgid) == 0) {
+                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;
+                    VLOG_INFO("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);
+                    VLOG_INFO("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) {
+                        VLOG_INFO("malformed vlan-isid mappings tlv received");
+                        goto malformed;
+                    }
+
+                    num_mappings /= 5; /* Each mapping is 5 Bytes */
+                    for(; num_mappings > 0; num_mappings--) {
+                        isid_vlan_map = (struct lldpd_aa_isid_vlan_maps_tlv *)
+                            calloc(1, sizeof *isid_vlan_map);
+                        if (!isid_vlan_map) {
+                            VLOG_WARN("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);
+                        list_push_back(
+                            (struct ovs_list *) &port->p_isid_vlan_maps,
+                            (struct ovs_list *) isid_vlan_map);
+                        isid_vlan_map = NULL;
+                    }
+                    break;
+
+                default:
+                    hardware->h_rx_unrecognized_cnt++;
+                    VLOG_INFO("Unrecogised tlv subtype received");
+                    break;
+                }
+            } else if (memcmp(dcbx, orgid, sizeof orgid) == 0) {
+                VLOG_DBG("unsupported DCBX tlv received on %s "
+                         "- ignore", hardware->h_ifname);
+                hardware->h_rx_unrecognized_cnt++;
+            } else {
+                VLOG_INFO("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:
+            VLOG_WARN("unknown tlv (%d) received on %s",
+                      tlv_type,
+                      hardware->h_ifname);
+            goto malformed;
+        }
+        if (pos > tlv + tlv_size) {
+            VLOG_WARN("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)) {
+        VLOG_WARN("some mandatory tlv are missing for frame received "
+                  "on %s", hardware->h_ifname);
+        goto malformed;
+    }
+    *newchassis = chassis;
+    *newport = port;
+    return 1;
+
+malformed:
+    lldpd_chassis_cleanup(chassis, 1);
+    lldpd_port_cleanup(port, 1);
+    free(port);
+    return -1;
+}
diff --git a/lib/lldp/lldpd-structs.c b/lib/lldp/lldpd-structs.c
new file mode 100644
index 0000000..7a434ff
--- /dev/null
+++ b/lib/lldp/lldpd-structs.c
@@ -0,0 +1,130 @@
+/* -*- 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-structs.h"
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include "lldpd.h"
+
+VLOG_DEFINE_THIS_MODULE(lldpd_structs);
+
+void
+lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *chassis)
+{
+    struct lldpd_mgmt *mgmt, *mgmt_next;
+
+    VLOG_DBG("cleanup management addresses for chassis %s",
+             chassis->c_name ? chassis->c_name : "(unknown)");
+
+    LIST_FOR_EACH_SAFE (mgmt,
+                        mgmt_next,
+                        m_entries,
+                        &chassis->c_mgmt.m_entries) {
+       list_remove(&mgmt->m_entries);
+       free(mgmt);
+    }
+
+    list_init(&chassis->c_mgmt.m_entries);
+}
+
+void
+lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
+{
+    lldpd_chassis_mgmt_cleanup(chassis);
+    VLOG_DBG("cleanup chassis %s",
+             chassis->c_name ? chassis->c_name : "(unkwnon)");
+    free(chassis->c_id);
+    free(chassis->c_name);
+    free(chassis->c_descr);
+    if (all) {
+        free(chassis);
+    }
+}
+
+/* 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 *hw,
+                     void(*expire)(struct lldpd_hardware *,
+                                   struct lldpd_port *),
+                     int all)
+{
+    struct lldpd_port *port, *port_next;
+    int del;
+    time_t now = time(NULL);
+
+    VLOG_DBG("cleanup remote port on %s", hw->h_ifname);
+    LIST_FOR_EACH_SAFE (port, port_next, p_entries, &hw->h_rports.p_entries) {
+        del = all;
+        if (!all && expire &&
+            (now >= port->p_lastupdate + port->p_chassis->c_ttl)) {
+            hw->h_ageout_cnt++;
+            hw->h_delete_cnt++;
+            del = 1;
+        }
+        if (del) {
+            if (expire) {
+                expire(hw, port);
+            }
+
+            if (!all) {
+                list_remove(&port->p_entries);
+            }
+            lldpd_port_cleanup(port, 1);
+            free(port);
+        }
+    }
+    if (all) {
+        list_init(&hw->h_rports.p_entries);
+    }
+}
+
+/* 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)
+{
+    /* 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)
+{
+    VLOG_DBG("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/lldp/lldpd-structs.h b/lib/lldp/lldpd-structs.h
new file mode 100644
index 0000000..98ebd52
--- /dev/null
+++ b/lib/lldp/lldpd-structs.h
@@ -0,0 +1,228 @@
+/* -*- 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
+
+#include <net/if.h>
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+#include <sys/socket.h>
+#include <sys/types.h>
+#include "aa-structs.h"
+#include "lldp-const.h"
+#include "packets.h"
+
+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 {
+    struct ovs_list 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;
+};
+
+struct lldpd_chassis {
+    struct ovs_list list;
+    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;
+
+    struct lldpd_mgmt  c_mgmt;
+};
+/* WARNING: any change to this structure should also be reflected into
+   `lldpd_copy_chassis()` which is not using marshaling. */
+
+struct lldpd_port {
+    struct ovs_list      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 hidden for reception */
+    u_int8_t             p_hidden_out:2; /* Considered 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;
+    struct lldpd_aa_element_tlv        p_element;
+    struct lldpd_aa_isid_vlan_maps_tlv p_isid_vlan_maps;
+};
+
+/* Used to modify some port related settings */
+struct lldpd_port_set {
+    char *ifname;
+};
+
+/* 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 */
+    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 */
+};
+
+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 */
+};
+
+/* 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 {
+    struct ovs_list   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[ETH_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 */
+    struct lldpd_port h_rports; /* Remote ports */
+};
+
+struct lldpd_interface;
+struct lldpd_interface_list;
+
+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;
+};
+
+/* 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 *);
+
+#endif
diff --git a/lib/lldp/lldpd.c b/lib/lldp/lldpd.c
new file mode 100644
index 0000000..9c8173e
--- /dev/null
+++ b/lib/lldp/lldpd.c
@@ -0,0 +1,655 @@
+/* -*- 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"
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#ifndef _WIN32
+#include <grp.h>
+#include <libgen.h>
+#include <netinet/if_ether.h>
+#include <pwd.h>
+#include <sys/select.h>
+#include <sys/utsname.h>
+#endif
+#include "compiler.h"
+#include "list.h"
+#include "packets.h"
+
+VLOG_DEFINE_THIS_MODULE(lldpd);
+
+static struct protocol protos[] =
+{
+    { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
+      LLDP_MULTICAST_ADDR },
+    { 0, 0, "any", ' ', NULL, NULL, NULL,
+    { 0,0,0,0,0,0 } }
+};
+
+void lldpd_assign_cfg_to_protocols(struct lldpd *cfg)
+{
+    cfg->g_protocols = protos;
+}
+
+struct lldpd_hardware *
+lldpd_get_hardware(struct lldpd *cfg, char *name, int index,
+                   struct lldpd_ops *ops)
+{
+    struct lldpd_hardware *hw;
+
+    LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware.h_entries) {
+        if ((strcmp(hw->h_ifname, name) == 0) &&
+            (hw->h_ifindex == index) &&
+            ((!ops) || (ops == hw->h_ops))) {
+            return hw;
+        }
+    }
+
+    return NULL;
+}
+
+struct lldpd_hardware *
+lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index)
+{
+    struct lldpd_hardware *hw;
+
+    VLOG_DBG("allocate a new local hardware interface (%s)", name);
+
+    if ((hw = (struct lldpd_hardware *) calloc(1, sizeof *hw)) == NULL) {
+        return NULL;
+    }
+
+    hw->h_cfg = cfg;
+    ovs_strlcpy(hw->h_ifname, name, sizeof hw->h_ifname);
+    hw->h_ifindex = index;
+    hw->h_lport.p_chassis = (struct lldpd_chassis *)
+    list_front(&cfg->g_chassis.list);
+    hw->h_lport.p_chassis->c_refcount++;
+    list_init(&hw->h_rports.p_entries);
+
+    return hw;
+}
+
+struct lldpd_mgmt *
+lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
+{
+    struct lldpd_mgmt *mgmt;
+
+    VLOG_DBG("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 *mgmt);
+    if (mgmt == NULL) {
+        errno = ENOMEM;
+        return NULL;
+    }
+    mgmt->m_family = family;
+    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)
+{
+    VLOG_DBG("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);
+    }
+    free(hardware);
+}
+
+void
+lldpd_cleanup(struct lldpd *cfg)
+{
+    struct lldpd_hardware *hw, *hw_next;
+    struct lldpd_chassis *chassis, *chassis_next;
+
+    VLOG_DBG("cleanup all ports");
+
+    LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware.h_entries) {
+        if (!hw->h_flags) {
+            list_remove(&hw->h_entries);
+            lldpd_remote_cleanup(hw, NULL, 1);
+            lldpd_hardware_cleanup(cfg, hw);
+        } else {
+            lldpd_remote_cleanup(hw, NULL, 0);
+        }
+    }
+
+    VLOG_DBG("cleanup all chassis");
+
+    LIST_FOR_EACH_SAFE (chassis, chassis_next, list, &cfg->g_chassis.list) {
+        if (chassis->c_refcount == 0) {
+            list_remove(&chassis->list);
+            lldpd_chassis_cleanup(chassis, 1);
+        }
+    }
+}
+
+/* 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;
+    int refcount = ochassis->c_refcount;
+    int index = ochassis->c_index;
+    struct ovs_list listcopy;
+
+    /* We want to keep refcount, index and list stuff from the current chassis
+     */
+    memcpy(&listcopy, &ochassis->list, sizeof listcopy);
+    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 *ochassis);
+    list_init(&ochassis->c_mgmt.m_entries);
+
+    /* Copy of management addresses */
+    LIST_FOR_EACH_SAFE (mgmt,
+                        mgmt_next,
+                        m_entries,
+                        &chassis->c_mgmt.m_entries) {
+        list_remove(&mgmt->m_entries);
+        list_insert(&ochassis->c_mgmt.m_entries, &mgmt->m_entries);
+    }
+
+    /* Restore saved values */
+    ochassis->c_refcount = refcount;
+    ochassis->c_index = index;
+    memcpy(&ochassis->list, &listcopy, sizeof ochassis->list);
+
+    /* Get rid of the new chassis */
+    free(chassis);
+}
+
+static int
+lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
+{
+    int i;
+
+    if (s < ETH_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, ETH_ADDR_LEN) == 0) {
+                VLOG_DBG("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)) {
+                VLOG_DBG("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 *hw)
+{
+    size_t listsize, i;
+    struct lldpd_chassis *chassis, *ochassis = NULL;
+    struct lldpd_port *port, *oport;
+    int guess = LLDPD_MODE_LLDP;
+    struct eth_header eheader;
+    int count = 0;
+    int found = 0;
+
+    VLOG_DBG("decode a received frame on %s size %d", hw->h_ifname,s);
+
+    if (s < sizeof(struct eth_header) + 4) {
+        /* Too short, just discard it */
+        return;
+    }
+
+    /* Decapsulate VLAN frames */
+    memcpy(&eheader, frame, sizeof eheader);
+    if (eheader.eth_type == htons(ETH_TYPE_VLAN)) {
+        /* VLAN decapsulation means to shift 4 bytes left the frame from
+         * offset 2 * ETH_ADDR_LEN
+         */
+        memmove(frame + 2 * ETH_ADDR_LEN, frame + 2 * ETH_ADDR_LEN + 4,
+                s - 2 * ETH_ADDR_LEN);
+        s -= 4;
+    }
+
+    LIST_FOR_EACH (oport, p_entries, &hw->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 */
+            VLOG_DBG("duplicate frame, no need to decode");
+            oport->p_lastupdate = time(NULL);
+            return;
+        }
+    }
+
+    guess = lldpd_guess_type(cfg, frame, s);
+    VLOG_DBG("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) {
+            VLOG_DBG("using decode function for %s protocol",
+                cfg->g_protocols[i].name);
+            if (cfg->g_protocols[i].decode(cfg, frame, s, hw, &chassis, &port)
+                    == -1) {
+                VLOG_DBG("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;
+      }
+      VLOG_DBG(" %"PRIuSIZE "mode:%d enabled:%d",
+               i, cfg->g_protocols[i].mode, cfg->g_protocols[i].enabled);
+    }
+    if (cfg->g_protocols[i].mode == 0) {
+        VLOG_DBG("unable to guess frame type on %s", hw->h_ifname);
+        return;
+    }
+
+    /* Do we already have the same MSAP somewhere? */
+    VLOG_DBG("search for the same MSAP");
+
+    LIST_FOR_EACH (oport, p_entries, &hw->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;
+                VLOG_DBG("MSAP is already known");
+                found = 1;
+                break;
+            }
+        }
+    }
+
+    if (!found) {
+       oport = NULL;
+    }
+
+    /* 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)) {
+            VLOG_DBG("max neighbors %d reached for port %s, "
+                     "dropping any new ones silently",
+                     cfg->g_config.c_max_neighbors,
+                     hw->h_ifname);
+        } else if (count > cfg->g_config.c_max_neighbors - 1) {
+            VLOG_DBG("too many neighbors for port %s, drop this new one",
+                     hw->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) {
+        int found = 0;
+        VLOG_DBG("MSAP is unknown, search for the chassis");
+
+        LIST_FOR_EACH (ochassis, list, &cfg->g_chassis.list) {
+                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)) {
+                    found=1;
+                    break;
+                }
+        }
+
+        if (!found) {
+            ochassis = NULL;
+        }
+    }
+
+    if (oport) {
+        /* The port is known, remove it before adding it back */
+        list_remove(&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 */
+        VLOG_DBG("unknown chassis, add it to the list");
+        chassis->c_index = ++cfg->g_lastrid;
+        chassis->c_refcount = 0;
+        list_push_back(&cfg->g_chassis.list, &chassis->list);
+        listsize = list_size(&cfg->g_chassis.list);
+        VLOG_DBG("%"PRIuSIZE " different systems are known", listsize);
+    }
+
+    /* Add port */
+    port->p_lastchange = port->p_lastupdate = time(NULL);
+    if ((port->p_lastframe = malloc(s + sizeof(struct lldpd_frame))) != NULL) {
+        port->p_lastframe->size = s;
+        memcpy(port->p_lastframe->frame, frame, s);
+    }
+    list_insert(&hw->h_rports.p_entries, &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 = list_size((struct ovs_list *) &hw->h_rports);
+    VLOG_DBG("%"PRIuSIZE " neighbors for %s", i, hw->h_ifname);
+
+    if (!oport)  {
+        hw->h_insert_cnt++;
+    }
+
+    return;
+}
+
+static void
+lldpd_hide_ports(struct lldpd *cfg,
+                 struct lldpd_hardware *hw,
+                 int mask) {
+    struct lldpd_port *port;
+    int protocols[LLDPD_MODE_MAX + 1];
+    char buffer[256];
+    int i, j, k, found = 0;
+    unsigned int min;
+
+    VLOG_DBG("apply smart filter for port %s", hw->h_ifname);
+
+    /* Compute the number of occurrences of each protocol */
+    for (i = 0; i <= LLDPD_MODE_MAX; i++) {
+        protocols[i] = 0;
+    }
+
+    LIST_FOR_EACH (port, p_entries, &hw->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];
+        }
+    }
+    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 */
+    LIST_FOR_EACH (port, p_entries, &hw->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;
+
+        LIST_FOR_EACH (port, p_entries, &hw->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;
+    LIST_FOR_EACH (port, p_entries, &hw->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));
+            }
+        }
+    }
+    VLOG_DBG("%s: %s: %d visible neighbors (out of %d)",
+             hw->h_ifname,
+             (mask == SMART_OUTGOING) ? "out filter" : "in filter",
+             k, j);
+    VLOG_DBG("%s: protocols: %s",
+             hw->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 *hw;
+
+    if (!cfg->g_config.c_smart) {
+        return;
+    }
+
+    VLOG_DBG("apply smart filter results on all ports");
+
+    LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware.h_entries) {
+        if (cfg->g_config.c_smart & SMART_INCOMING_FILTER) {
+            lldpd_hide_ports(cfg, hw, SMART_INCOMING);
+        }
+        if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER) {
+            lldpd_hide_ports(cfg, hw, SMART_OUTGOING);
+        }
+    }
+}
+
+void
+lldpd_recv(struct lldpd *cfg,
+           struct lldpd_hardware *hw,
+           char *buffer,
+           size_t bufSize)
+{
+    int n = bufSize;
+
+    VLOG_DBG("receive a frame on %s", hw->h_ifname);
+    if (cfg->g_config.c_paused) {
+        VLOG_DBG("paused, ignore the frame on %s", hw->h_ifname);
+        return;
+    }
+    hw->h_rx_cnt++;
+    VLOG_DBG("decode received frame on %s h_rx_cnt=%" PRIu64,
+             hw->h_ifname, hw->h_rx_cnt);
+    lldpd_decode(cfg, buffer, n, hw);
+    lldpd_hide_all(cfg); /* Immediatly hide */
+}
+
+uint32_t
+lldpd_send(struct lldpd_hardware *hw, struct ofpbuf *p)
+{
+    struct lldpd *cfg = hw->h_cfg;
+    struct lldpd_port *port;
+    int i, sent = 0;
+    int lldp_size = 0;
+
+    if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) {
+        return 0;
+    }
+#ifndef _WIN32
+    if ((hw->h_flags & IFF_RUNNING) == 0) {
+        return 0;
+    }
+#endif
+
+    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) {
+            if ((lldp_size = cfg->g_protocols[i].send(cfg, hw, p)) != E2BIG) {
+                sent++;
+                continue;
+            } else {
+                VLOG_DBG("send PDU on %s failed E2BIG", hw->h_ifname);
+                continue;
+            }
+        }
+
+        LIST_FOR_EACH (port, p_entries, &hw->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) {
+                VLOG_DBG("send PDU on %s with protocol %s",
+                         hw->h_ifname, cfg->g_protocols[i].name);
+                lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
+                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;
+            }
+            VLOG_DBG("fallback to protocol %s for %s",
+                     cfg->g_protocols[i].name, hw->h_ifname);
+            lldp_size = cfg->g_protocols[i].send(cfg, hw, p);
+            break;
+        }
+        if (cfg->g_protocols[i].mode == 0) {
+            VLOG_WARN("no protocol enabled, dunno what to send");
+        }
+    }
+
+    return lldp_size;
+}
diff --git a/lib/lldp/lldpd.h b/lib/lldp/lldpd.h
new file mode 100644
index 0000000..f142180
--- /dev/null
+++ b/lib/lldp/lldpd.h
@@ -0,0 +1,120 @@
+/* -*- 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
+
+#ifndef _WIN32
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#endif
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include "list.h"
+#include "lldpd-structs.h"
+#include "lldp-tlv.h"
+#include "packets.h"
+#include "openvswitch/vlog.h"
+#include "ofpbuf.h"
+
+#define SYSCONFDIR       ""
+#define LLDPD_CTL_SOCKET ""
+#define LLDPCLI_PATH     ""
+#define PRIVSEP_USER     ""
+#define PRIVSEP_GROUP    ""
+#define PRIVSEP_CHROOT   ""
+
+#define ETHERTYPE_LLDP 0x88cc
+
+struct event;
+struct event_base;
+
+#define LLDPD_TX_INTERVAL      5
+#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
+
+#define PROTO_SEND_SIG struct lldpd *, struct lldpd_hardware *,struct ofpbuf *
+#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[ETH_ADDR_LEN];  /* Destination MAC address used by this
+                                  * protocol
+                                  */
+};
+
+#define SMART_HIDDEN(port) (port->p_hidden_in)
+
+struct lldpd {
+    int                 g_sock;
+    struct lldpd_config g_config;
+    struct protocol     *g_protocols;
+    int                 g_lastrid;
+
+    /* Unix socket handling */
+    const char          *g_ctlname;
+    int                 g_ctl;
+
+    char                *g_lsb_release;
+
+    struct lldpd_chassis g_chassis;
+    struct 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);
+void lldpd_recv(struct lldpd *, struct lldpd_hardware *, char *, size_t);
+uint32_t lldpd_send(struct lldpd_hardware *, struct ofpbuf *);
+void lldpd_loop(struct lldpd *);
+
+int lldpd_main(int, char **);
+void lldpd_update_localports(struct lldpd *);
+void lldpd_cleanup(struct lldpd *);
+
+void lldpd_assign_cfg_to_protocols(struct lldpd *);
+
+/* lldp.c */
+int lldp_send(PROTO_SEND_SIG);
+int lldp_decode(PROTO_DECODE_SIG);
+
+#endif /* _LLDPD_H */
diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
new file mode 100644
index 0000000..9ff4fae
--- /dev/null
+++ b/lib/ovs-lldp.c
@@ -0,0 +1,985 @@
+/*
+ * 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 "ovs-lldp.h"
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include "dynamic-string.h"
+#include "flow.h"
+#include "list.h"
+#include "lldp/lldpd.h"
+#include "lldp/lldpd-structs.h"
+#include "netdev.h"
+#include "ofpbuf.h"
+#include "openvswitch/types.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "smap.h"
+#include "unixctl.h"
+#include "util.h"
+#include "openvswitch/vlog.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
+};
+
+/* 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 ovs_list     active_mapping_queue;
+    struct ovs_refcount ref_cnt;
+};
+
+/* Internal structure for an Auto Attach mapping.
+ */
+struct aa_mapping_internal {
+    struct hmap_node hmap_node_isid;
+    struct hmap_node hmap_node_aux;
+    int64_t          isid;
+    int64_t          vlan;
+    void             *aux;
+    enum aa_status   status;
+};
+
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
+
+/* 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__;
+
+/* 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 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.
+ */
+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.
+ */
+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.
+ */
+static void
+chassisid_to_string(uint8_t *array, size_t len, char **str)
+{
+    unsigned int i;
+
+    *str = xmalloc(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_isid,
+                             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_aux, hash_pointer(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_REQUIRES(mutex)
+{
+    struct lldpd_hardware *hw;
+
+    ds_put_format(ds, "Statistics: %s\n", lldp->name);
+
+    if (!lldp->lldpd) {
+        return;
+    }
+
+    LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) {
+        ds_put_format(ds, "\ttx cnt: %"PRIu64"\n", hw->h_tx_cnt);
+        ds_put_format(ds, "\trx cnt: %"PRIu64"\n", hw->h_rx_cnt);
+        ds_put_format(ds, "\trx discarded cnt: %"PRIu64"\n",
+                      hw->h_rx_discarded_cnt);
+        ds_put_format(ds, "\trx unrecognized cnt: %"PRIu64"\n",
+                      hw->h_rx_unrecognized_cnt);
+        ds_put_format(ds, "\tageout cnt: %"PRIu64"\n", hw->h_ageout_cnt);
+        ds_put_format(ds, "\tinsert cnt: %"PRIu64"\n", hw->h_insert_cnt);
+        ds_put_format(ds, "\tdelete cnt: %"PRIu64"\n", hw->h_delete_cnt);
+        ds_put_format(ds, "\tdrop cnt: %"PRIu64"\n", hw->h_drop_cnt);
+    }
+}
+
+static void
+aa_print_element_status_port(struct ds *ds, struct lldpd_hardware *hw)
+{
+    struct lldpd_port *port;
+
+    LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) {
+        if (memcmp(&port->p_element.system_id,
+                   &system_id_null,
+                   sizeof port->p_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);
+
+            free(id);
+            free(system);
+        }
+    }
+}
+
+/* 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_REQUIRES(mutex)
+{
+    struct lldpd_hardware *hw;
+
+    ds_put_format(ds, "LLDP: %s\n", lldp->name);
+
+    if (!lldp->lldpd) {
+        return;
+    }
+
+    LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) {
+        aa_print_element_status_port(ds, hw);
+    }
+}
+
+static void
+aa_print_isid_status_port_isid(struct lldp *lldp, struct lldpd_port *port)
+    OVS_REQUIRES(mutex)
+{
+    struct lldpd_aa_isid_vlan_maps_tlv *mapping;
+
+    if (list_is_empty(&port->p_isid_vlan_maps.m_entries)) {
+        return;
+    }
+
+    LIST_FOR_EACH (mapping, m_entries, &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);
+        }
+    }
+}
+
+static void
+aa_print_isid_status_port(struct lldp *lldp, struct lldpd_hardware *hw)
+    OVS_REQUIRES(mutex)
+{
+    struct lldpd_port *port;
+
+    LIST_FOR_EACH (port, p_entries, &hw->h_rports.p_entries) {
+        aa_print_isid_status_port_isid(lldp, port);
+    }
+}
+
+/* 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)
+{
+    struct lldpd_hardware *hw;
+    struct aa_mapping_internal *m;
+
+    if (!lldp->lldpd) {
+        return;
+    }
+
+    ds_put_format(ds, "LLDP: %s\n", lldp->name);
+
+    LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) {
+        aa_print_isid_status_port(lldp, hw);
+    }
+
+    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_isid, &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;
+
+    ovs_mutex_lock(&mutex);
+
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        aa_print_element_status(&ds, lldp);
+    }
+    unixctl_command_reply(conn, ds_cstr(&ds));
+    ds_destroy(&ds);
+
+    ovs_mutex_unlock(&mutex);
+}
+
+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 = xzalloc(sizeof *lm);
+
+    if (hardware->h_ifname) {
+        VLOG_INFO("\t\t hardware->h_ifname=%s", hardware->h_ifname);
+    }
+
+    int_to_array(lm->isid_vlan_data.isid,
+                 ARRAY_SIZE(lm->isid_vlan_data.isid),
+                 (uint32_t) m->isid);
+    lm->isid_vlan_data.vlan = m->vlan;
+
+    list_push_back(&hardware->h_lport.p_isid_vlan_maps.m_entries,
+                   &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 ovs_list *list)
+{
+    struct lldp *lldp;
+
+    ovs_mutex_lock(&mutex);
+
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        struct bridge_aa_vlan *node, *node_next;
+
+        LIST_FOR_EACH_SAFE (node,
+                            node_next,
+                            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);
+
+    /* TODO Change all instances for now */
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        struct lldpd_chassis *chassis;
+
+        LIST_FOR_EACH (chassis, list, &lldp->lldpd->g_chassis.list) {
+            /* System Description */
+            if (chassis->c_descr) {
+                free(chassis->c_descr);
+            }
+            chassis->c_descr = s->system_description[0] ?
+                xstrdup(s->system_description) : xstrdup(PACKAGE_STRING);
+
+            /* System Name */
+            if (chassis->c_name) {
+                free(chassis->c_name);
+            }
+            chassis->c_name = xstrdup(s->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_isid,
+                hash_bytes((const void *) &bridge_m->isid,
+                           sizeof bridge_m->isid,
+                           0));
+
+    /* Update mapping on the all the LLDP instances. */
+    HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
+        struct lldpd_hardware *hw;
+        struct aa_mapping_internal *m;
+
+        VLOG_INFO("\t lldp->name=%s", lldp->name);
+
+        if (mapping_find_by_isid(lldp, s->isid)) {
+            continue;
+        }
+
+        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_isid,
+                    hash_bytes((const void *) &m->isid,
+                               sizeof m->isid,
+                               0));
+        hmap_insert(&lldp->mappings_by_aux,
+                    &m->hmap_node_aux,
+                    hash_pointer(m->aux, 0));
+
+        /* Configure the mapping on each port of the LLDP stack. */
+        LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) {
+            update_mapping_on_lldp(lldp, hw, m);
+        }
+    }
+
+    ovs_mutex_unlock(&mutex);
+
+    return 0;
+}
+
+static void
+aa_mapping_unregister_mapping(struct lldp *lldp,
+                              struct lldpd_hardware *hw,
+                              struct aa_mapping_internal *m)
+{
+    struct lldpd_aa_isid_vlan_maps_tlv *lm, *lm_next;
+
+    LIST_FOR_EACH_SAFE (lm,
+                        lm_next,
+                        m_entries,
+                        &hw->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);
+
+            list_remove(&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(hw->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;
+        }
+    }
+}
+
+/* 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 *hw;
+        struct aa_mapping_internal *m = mapping_find_by_aux(lldp, aux);
+        int64_t isid_tmp = -1, vlan_tmp = -1;
+
+        /* Remove from internal hash tables. */
+        if (m) {
+            struct aa_mapping_internal *p =
+                mapping_find_by_isid(lldp, m->isid);
+
+            isid_tmp = m->isid;
+            vlan_tmp = m->vlan;
+            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_isid);
+            }
+
+            hmap_remove(&lldp->mappings_by_aux, &m->hmap_node_aux);
+            free(m);
+
+            /* Remove from all the lldp instances */
+            LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware.h_entries) {
+                if (hw->h_ifname) {
+                    VLOG_INFO("\t\t hardware->h_ifname=%s", hw->h_ifname);
+                }
+
+                aa_mapping_unregister_mapping(lldp, hw, m);
+            }
+
+            if (isid_tmp >= 0 && vlan_tmp >= 0) {
+                /* Remove from the all_mappings */
+                HMAP_FOR_EACH (m, hmap_node_isid, all_mappings) {
+                    if (m && isid_tmp == m->isid && vlan_tmp == m->vlan) {
+                         hmap_remove(all_mappings, &m->hmap_node_isid);
+                         break;
+                    }
+                }
+            }
+        }
+    }
+
+    ovs_mutex_unlock(&mutex);
+
+    return 0;
+}
+
+void
+lldp_init(void)
+{
+    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)
+{
+    return (flow->dl_type == htons(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) {
+        lldpd_recv(lldp->lldpd,
+                   (struct lldpd_hardware *)
+                       lldp->lldpd->g_hardware.h_entries.next,
+                   (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;
+
+    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.
+ */
+long long int
+lldp_wait(struct lldp *lldp) OVS_EXCLUDED(mutex)
+{
+    long long int wake_time = lldp_wake_time(lldp);
+    poll_timer_wait_until(wake_time);
+    return wake_time;
+}
+
+/* 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 = lldp->lldpd;
+    struct lldpd_hardware *hw = (struct lldpd_hardware *)
+        mylldpd->g_hardware.h_entries.next;
+    uint32_t lldp_size = 0;
+    static const uint8_t eth_addr_lldp[6] =
+        {0x01, 0x80, 0xC2, 0x00, 0x00, 0x0e};
+
+    ovs_mutex_lock(&mutex);
+
+    eth_compose(packet, eth_addr_lldp, eth_src, ETH_TYPE_LLDP, 0);
+
+    lldp_size = lldpd_send(hw, packet);
+    if (lldp_size + ETH_HEADER_LEN < MINIMUM_ETH_PACKET_SIZE) {
+        lldp_size = MINIMUM_ETH_PACKET_SIZE;
+    }
+
+    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)
+{
+    if (lldp) {
+        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 *hw;
+    struct aa_mapping_internal *m;
+
+    if (!cfg || !smap_get_bool(cfg, "enable", false)) {
+        return NULL;
+    }
+
+    lldp = xzalloc(sizeof *lldp);
+    lldp->name = xstrdup(netdev_get_name(netdev));
+    lldp->lldpd = xzalloc(sizeof *lldp->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 = ETH_ADDR_LEN;
+    lchassis->c_id = xmalloc(ETH_ADDR_LEN);
+    netdev_get_etheraddr(netdev, (uint8_t *) lchassis->c_id);
+
+    list_init(&lchassis->c_mgmt.m_entries);
+    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);
+    list_init(&lldp->lldpd->g_chassis.list);
+    list_push_back(&lldp->lldpd->g_chassis.list, &lchassis->list);
+
+    if ((hw = 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));
+        out_of_memory();
+    }
+
+    ovs_refcount_init(&lldp->ref_cnt);
+#ifndef _WIN32
+    hw->h_flags |= IFF_RUNNING;
+#endif
+    hw->h_mtu = mtu;
+    hw->h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+    hw->h_lport.p_id = xstrdup(netdev_get_name(netdev));
+    /* p_id is not necessarily a null terminated string. */
+    hw->h_lport.p_id_len = strlen(netdev_get_name(netdev));
+
+    /* Auto Attach element tlv */
+    hw->h_lport.p_element.type = LLDP_TLV_AA_ELEM_TYPE_TAG_CLIENT;
+    hw->h_lport.p_element.mgmt_vlan = 0;
+    memcpy(&hw->h_lport.p_element.system_id.system_mac,
+           lchassis->c_id, lchassis->c_id_len);
+    hw->h_lport.p_element.system_id.conn_type =
+        LLDP_TLV_AA_ELEM_CONN_TYPE_SINGLE;
+    hw->h_lport.p_element.system_id.smlt_id = 0;
+    hw->h_lport.p_element.system_id.mlt_id[0] = 0;
+    hw->h_lport.p_element.system_id.mlt_id[1] = 0;
+    list_init(&hw->h_lport.p_isid_vlan_maps.m_entries);
+    list_init(&lldp->lldpd->g_hardware.h_entries);
+    list_push_back(&lldp->lldpd->g_hardware.h_entries, &hw->h_entries);
+
+    ovs_mutex_lock(&mutex);
+
+    /* Update port with Auto Attach mappings configured. */
+    HMAP_FOR_EACH (m, hmap_node_isid, all_mappings) {
+        struct aa_mapping_internal *p;
+
+        if (mapping_find_by_isid(lldp, m->isid)) {
+            continue;
+        }
+
+        p = xmemdup(m, sizeof *p);
+        hmap_insert(&lldp->mappings_by_isid,
+                    &p->hmap_node_isid,
+                    hash_bytes((const void *) &p->isid,
+                               sizeof p->isid,
+                               0));
+        hmap_insert(&lldp->mappings_by_aux,
+                    &p->hmap_node_aux,
+                    hash_pointer(p->aux, 0));
+
+        update_mapping_on_lldp(lldp, hw, p);
+    }
+
+    hmap_insert(all_lldps, &lldp->hmap_node,
+                hash_string(netdev_get_name(netdev), 0));
+
+    ovs_mutex_unlock(&mutex);
+
+    return lldp;
+}
+
+/* Unreference a specific LLDP instance.
+ */
+void
+lldp_unref(struct lldp *lldp)
+{
+    if (!lldp) {
+        return;
+    }
+
+    ovs_mutex_lock(&mutex);
+    if (ovs_refcount_unref_relaxed(&lldp->ref_cnt) != 1) {
+        ovs_mutex_unlock(&mutex);
+        return;
+    }
+
+    hmap_remove(all_lldps, &lldp->hmap_node);
+    ovs_mutex_unlock(&mutex);
+
+    lldpd_cleanup(lldp->lldpd);
+    free(lldp->lldpd);
+    free(lldp->name);
+    free(lldp);
+}
+
+/* Unreference a specific LLDP instance.
+ */
+struct lldp *
+lldp_ref(const struct lldp *lldp_)
+{
+    struct lldp *lldp = CONST_CAST(struct lldp *, lldp_);
+    if (lldp) {
+        ovs_refcount_ref(&lldp->ref_cnt);
+    }
+    return lldp;
+}
diff --git a/lib/ovs-lldp.h b/lib/ovs-lldp.h
new file mode 100644
index 0000000..ad790a0
--- /dev/null
+++ b/lib/ovs-lldp.h
@@ -0,0 +1,96 @@
+/*
+ * 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 <stdint.h>
+#include "hmap.h"
+#include "list.h"
+#include "lldp/lldpd.h"
+#include "ofpbuf.h"
+#include "ovsdb-data.h"
+#include "ovs-thread.h"
+#include "packets.h"
+#include "timer.h"
+
+/* Transmit every LLDPD_TX_INTERVAL seconds. */
+#define LLDP_DEFAULT_TRANSMIT_INTERVAL_MS LLDPD_TX_INTERVAL * 1000
+
+struct lldp;
+struct flow_wildcards;
+struct flow;
+struct netdev;
+struct smap;
+
+struct lldp_status {
+    /* TODO should reflect lldp stack detail */
+    char *stackdetail; /* Added because MSVC doesn't like empty structs */
+};
+
+/* Configuration specific to Auto Attach.
+ */
+struct aa_settings {
+    char *system_description;
+    char *system_name;
+};
+
+/* 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 ovs_list list_node;
+    char *port_name;
+    uint32_t vlan;
+    enum bridge_aa_vlan_oper oper;
+};
+
+void lldp_init(void);
+long long int lldp_wait(struct lldp *lldp);
+long long int lldp_wake_time(const struct lldp *lldp);
+void lldp_run(struct lldpd *cfg);
+bool lldp_should_send_packet(struct lldp *cfg);
+bool lldp_should_process_flow(const struct flow *flow);
+bool lldp_configure(struct lldp *lldp);
+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 ovs_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 */
-- 
1.8.3.1




More information about the dev mailing list