Changliang Wu <changliang...@smartx.com> writes:

> New appctl 'lldp/neighbor' displays lldp neighbor information.
> Support json output with --format json --pretty
>
> Example outputs would be:
> $ ovs-appctl lldp/neighbor
> -------------------------------------------------------------------------------
> LLDP neighbor:
> -------------------------------------------------------------------------------
> Interface: ens13np0
>   Chassis:
>     Chassis ID:  38:a9:1c:18:a1:0a
>     SysName:     7F-jieru-chuan
>     SysDescr:    H3C Comware Platform Software
>     MgmtIP:      192.168.67.234
>     MgmtIface:   635
>     Capability:  Bridge, on
>     Capability:  Router, on
>   Port:
>     PortID:      GigabitEthernet1/0/15
>     PortDescr:   GigabitEthernet1/0/15 Interface
>     TTL:         121
>     MFS:         10240
>     PMD autoneg:  supported: yes, enabled: yes
>       Adv:       10Base-T, HD: yes, FD: yes
>       Adv:       100Base-TX, HD: yes, FD: yes
>       Adv:       1000Base-T, HD: no, FD: yes
>       MAU oper type:     30
>     MDI Power:   supported: yes, enabled: no, pair control: no
>       Device type:       PSE
>   VLAN:           1, pvid: yes, VLAN 0001
> -------------------------------------------------------------------------------
>
> $ ovs-appctl --format json --pretty lldp/neighbor
> {
>   "lldp": {
>     "interface": [
>       {
>         "ens13np0": {
>           "chassis": {
>             "7F-jieru-chuan": {
>               "capability": [
>                 {
>                   "enabled": true,
>                   "type": "Bridge"},
>                 {
>                   "enabled": true,
>                   "type": "Router"}],
>               "descr": "H3C Comware Platform Software",
>               "id": {
>                 "type": "mac",
>                 "value": "38:a9:1c:18:a1:0a"},
>               "mgmt-iface": [
>                 635],
>               "mgmt-ip": [
>                 "192.168.67.234"]}},
>           "port": {
>             "auto-negotiation": {
>               "advertised": [
>                 {
>                   "fd": true,
>                   "hd": true,
>                   "type": "10Base-T"},
>                 {
>                   "fd": true,
>                   "hd": true,
>                   "type": "100Base-TX"},
>                 {
>                   "fd": true,
>                   "hd": false,
>                   "type": "1000Base-T"}],
>               "current": 30,
>               "enabled": true,
>               "supported": true},
>             "desc": "GigabitEthernet1/0/15 Interface",
>             "id": {
>               "type": "ifname",
>               "value": "GigabitEthernet1/0/15"},
>             "mfs": 10240,
>             "power": {
>               "device-type": "PSE",
>               "enabled": false,
>               "paircontrol": false,
>               "supported": true},
>             "ttl": 121},
>           "ppid": {
>             "enabled": false,
>             "supported": false},
>           "vlan": {
>             "pvid": true,
>             "value": "VLAN 0001",
>             "vlan-id": 1}}}]}}
>
> Signed-off-by: Changliang Wu <changliang...@smartx.com>
>
>  V2: fix code lint and build warn
>  V3: fix more static analyze error
>  V4: fix build error
>  V5: add json output, add NEWS
>  V6: add dot1 and dot3, fix review problems

As expected, the dot1 and dot3 additions have made this difficult to
review.

Please create a separate patch.  You will also need to mention it in the
NEWS file as a separate item as well.

I didn't review much either part (either the output formatting or the
dot1/3 support), because there's too much going on.

> ---
>  NEWS                       |   1 +
>  lib/lldp/lldp-const.h      |  41 ++-
>  lib/lldp/lldp.c            |  76 ++++-
>  lib/lldp/lldpd-structs.h   |  24 ++
>  lib/ovs-lldp.c             | 643 ++++++++++++++++++++++++++++++++++++-
>  tests/automake.mk          |   1 +
>  tests/ovs-lldp.at          | 125 +++++++
>  tests/testsuite.at         |   1 +
>  vswitchd/ovs-vswitchd.8.in |   4 +
>  9 files changed, 897 insertions(+), 19 deletions(-)
>  create mode 100644 tests/ovs-lldp.at
>
> diff --git a/NEWS b/NEWS
> index d7231fabc..c66b41e64 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -15,6 +15,7 @@ Post-v3.5.0
>         core file size.
>     - ovs-appctl:
>       * Added JSON output support to the 'ovs/route/show' command.
> +     * Added 'lldp/neighbor' command that displays lldp neighbor infomation.
>     - SSL/TLS:
>       * Support for deprecated TLSv1 and TLSv1.1 protocols on OpenFlow and
>         database connections is now removed.
> diff --git a/lib/lldp/lldp-const.h b/lib/lldp/lldp-const.h
> index 8c5c0733e..7b9c74149 100644
> --- a/lib/lldp/lldp-const.h
> +++ b/lib/lldp/lldp-const.h
> @@ -85,6 +85,11 @@
>  #define LLDP_DOT3_MAU_10GIGBASELW  39
>  #define LLDP_DOT3_MAU_10GIGBASESW  40
>  
> +#define LLDP_DOT3_POWER_PORT_CLASS    (1 << 0)
> +#define LLDP_DOT3_POWER_SUPPORT       (1 << 1)
> +#define LLDP_DOT3_POWER_ENABLED       (1 << 2)
> +#define LLDP_DOT3_POWER_PAIRCONTROL   (1 << 3)
> +
>  /* Dot3 Power Devicetype */
>  #define LLDP_DOT3_POWER_PSE 1
>  #define LLDP_DOT3_POWER_PD  2
> @@ -112,23 +117,27 @@
>  #define LLDP_DOT3_POWER_PRIO_HIGH     2
>  #define LLDP_DOT3_POWER_PRIO_LOW      3
>  
> +/* Auto-Negotiation Support/Status field, from RFC 4836 */
> +#define LLDP_DOT3_LINK_AUTONEG_SUPPORT  (1 << 0)
> +#define LLDP_DOT3_LINK_AUTONEG_ENABLED  (1 << 1)
> +
>  /* 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
> +#define LLDP_DOT3_LINK_AUTONEG_OTHER          0x8000
> +#define LLDP_DOT3_LINK_AUTONEG_10BASE_T       0x4000
> +#define LLDP_DOT3_LINK_AUTONEG_10BASE_T_FD    0x2000
> +#define LLDP_DOT3_LINK_AUTONEG_100BASE_T4     0x1000
> +#define LLDP_DOT3_LINK_AUTONEG_100BASE_TX     0x0800
> +#define LLDP_DOT3_LINK_AUTONEG_100BASE_TX_FD  0x0400
> +#define LLDP_DOT3_LINK_AUTONEG_100BASE_T2     0x0200
> +#define LLDP_DOT3_LINK_AUTONEG_100BASE_T2_FD  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_X_FD  0x0004
> +#define LLDP_DOT3_LINK_AUTONEG_1000BASE_T     0x0002
> +#define LLDP_DOT3_LINK_AUTONEG_1000BASE_T_FD  0x0001
>  
>  /* Capabilities */
>  #define LLDP_CAP_OTHER     0x01
> diff --git a/lib/lldp/lldp.c b/lib/lldp/lldp.c
> index 6fdcfef56..136247100 100644
> --- a/lib/lldp/lldp.c
> +++ b/lib/lldp/lldp.c
> @@ -18,6 +18,7 @@
>   */
>  
>  #include <config.h>
> +#include "lldp-tlv.h"
>  #include "lldpd.h"
>  #include <errno.h>
>  #include <time.h>
> @@ -567,10 +568,81 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, 
> int s,
>              CHECK_TLV_SIZE(1 + sizeof orgid, "Organisational");
>              PEEK_BYTES(orgid, sizeof orgid);
>              tlv_subtype = PEEK_UINT8;
> +            uint8_t tlv_org_size;
>              if (memcmp(dot1, orgid, sizeof orgid) == 0) {
> -                hardware->h_rx_unrecognized_cnt++;
> +                switch (tlv_subtype) {
> +                case LLDP_TLV_DOT1_PVID:
> +                    CHECK_TLV_SIZE(2, "Dot1 Port VLAN ID");
> +                    port->p_dot1.pvid = PEEK_UINT16;
> +                    port->p_dot1_enable |= (1 << LLDP_TLV_DOT1_PVID);
> +                    break;
> +                case LLDP_TLV_DOT1_PPVID:
> +                    CHECK_TLV_SIZE(2, "Dot1 Port And Protocol VLAN ID");
> +                    port->p_dot1.ppvid = PEEK_UINT16;
> +                    port->p_dot1_enable |= (1 << LLDP_TLV_DOT1_PPVID);
> +                    break;
> +                case LLDP_TLV_DOT1_VLANNAME:
> +                case LLDP_TLV_DOT1_PI:
> +                    if (tlv_subtype == LLDP_TLV_DOT1_VLANNAME) {
> +                        CHECK_TLV_SIZE(2, "DOT1 VLAN ID");
> +                        port->p_dot1.vlan_id = PEEK_UINT16;
> +                        port->p_dot1_enable |= (1 << LLDP_TLV_DOT1_VLANNAME);
> +                    }
> +                    CHECK_TLV_SIZE(1, "DOT1 VLAN Name/PI Length");
> +                    tlv_org_size = PEEK_UINT8;
> +                    if (tlv_org_size < 1) {
> +                        VLOG_DBG("empty dot1 vlan name/pi tlv received on 
> %s",
> +                                 hardware->h_ifname);
> +                        goto malformed;
> +                    }
> +                    CHECK_TLV_SIZE(tlv_org_size, "DOT1 VLAN Name/PI");
> +                    b = xzalloc(tlv_org_size + 1);
> +                    PEEK_BYTES(b, tlv_org_size);
> +                    if (tlv_subtype == LLDP_TLV_DOT1_VLANNAME) {
> +                        free(port->p_dot1.vlan_name);
> +                        port->p_dot1.vlan_name = b;
> +                    } else if (tlv_subtype == LLDP_TLV_DOT1_PI) {
> +                        free(port->p_dot1.pi);
> +                        port->p_dot1.pi = b;
> +                        port->p_dot1_enable |= (1 << LLDP_TLV_DOT1_PI);
> +                    }
> +                    break;
> +                default:
> +                    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++;
> +                }
>              } else if (memcmp(dot3, orgid, sizeof orgid) == 0) {
> -                hardware->h_rx_unrecognized_cnt++;
> +                switch (tlv_subtype) {
> +                case LLDP_TLV_DOT3_MAC:
> +                    CHECK_TLV_SIZE(5, "DOT3 MAC");
> +                    port->p_dot3.auto_nego = PEEK_UINT8;
> +                    port->p_dot3.pmd_auto_nego = PEEK_UINT16;
> +                    port->p_dot3.mau = PEEK_UINT16;
> +                    port->p_dot3_enable |= (1 << LLDP_TLV_DOT3_MAC);
> +                    break;
> +                case LLDP_TLV_DOT3_POWER:
> +                    CHECK_TLV_SIZE(3, "DOT3 MDI");
> +                    port->p_dot3.mdi = PEEK_UINT8;
> +                    port->p_dot3.pse = PEEK_UINT8;
> +                    port->p_dot3.power_class = PEEK_UINT8;
> +                    port->p_dot3_enable |= (1 << LLDP_TLV_DOT3_POWER);
> +                    break;
> +                case LLDP_TLV_DOT3_LA: break;
> +                case LLDP_TLV_DOT3_MFS:
> +                    CHECK_TLV_SIZE(3, "DOT3 MFS");
> +                    port->p_dot3.mfs = PEEK_UINT16;
> +                    port->p_dot3_enable |= (1 << LLDP_TLV_DOT3_MFS);
> +                    break;
> +                default:
> +                    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++;
> +                }
>              } else if (memcmp(med, orgid, sizeof orgid) == 0) {
>                  /* LLDP-MED */
>                  hardware->h_rx_unrecognized_cnt++;
> diff --git a/lib/lldp/lldpd-structs.h b/lib/lldp/lldpd-structs.h
> index fe5d5f9f8..bb8939db1 100644
> --- a/lib/lldp/lldpd-structs.h
> +++ b/lib/lldp/lldpd-structs.h
> @@ -79,6 +79,24 @@ struct lldpd_chassis {
>  /* WARNING: any change to this structure should also be reflected into
>     `lldpd_copy_chassis()` which is not using marshaling. */
>  
> +struct lldpd_tlv_dot1 {
> +    u_int16_t       pvid;       /* Port VLAN identifier */
> +    u_int16_t       ppvid;      /* Port and protocol VLAN identifier */
> +    char            *vlan_name; /* VLAN Name in LLDP_TLV_DOT1_VLANNAME */
> +    u_int16_t       vlan_id;    /* VLAN ID in LLDP_TLV_DOT1_VLANNAME */
> +    char            *pi;        /* protocol identity */
> +};
> +
> +struct lldpd_tlv_dot3 {
> +    u_int8_t  auto_nego;     /* auto-negotiation support/status  */
> +    u_int16_t pmd_auto_nego; /* PMD auto-negotiation advertised capability */
> +    u_int16_t mau;           /* Operational MAU type */
> +    u_int8_t  mdi;           /* MDI power support */
> +    u_int8_t  pse;           /* PSE power pair */
> +    u_int8_t  power_class;   /* Port class */
> +    u_int16_t mfs;           /* Maximum 802.3 frame size */
> +};
> +
>  struct lldpd_port {
>      struct ovs_list      p_entries;
>      struct lldpd_chassis *p_chassis; /* Attached chassis */
> @@ -98,6 +116,12 @@ struct lldpd_port {
>      char                 *p_descr;
>      u_int16_t            p_mfs;
>      struct lldpd_aa_element_tlv        p_element;
> +    /* Bitmask for dot1 tlv received */
> +    u_int16_t                          p_dot1_enable;
> +    struct lldpd_tlv_dot1              p_dot1;
> +    /* Bitmask for dot3 tlv received */
> +    u_int16_t                          p_dot3_enable;
> +    struct lldpd_tlv_dot3              p_dot3;
>      struct ovs_list p_isid_vlan_maps; /* Contains "struct 
> lldpd_aa_isid_vlan_maps_tlv"s. */
>  };
>  
> diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
> index 152777248..388ea7f33 100644
> --- a/lib/ovs-lldp.c
> +++ b/lib/ovs-lldp.c
> @@ -35,8 +35,11 @@
>  #include <stdbool.h>
>  #include <stdlib.h>
>  #include "openvswitch/dynamic-string.h"
> +#include "openvswitch/json.h"
>  #include "flow.h"
>  #include "openvswitch/list.h"
> +#include "lldp/lldp-const.h"
> +#include "lldp/lldp-tlv.h"
>  #include "lldp/lldpd.h"
>  #include "lldp/lldpd-structs.h"
>  #include "netdev.h"
> @@ -310,6 +313,596 @@ aa_print_isid_status(struct ds *ds, struct lldp *lldp) 
> OVS_REQUIRES(mutex)
>      }
>  }
>  
> +static void
> +lldp_print_neighbor_port_dot1(struct ds *ds, struct lldpd_port *port)
> +{
> +    if (port->p_dot1_enable &
> +        ((1 << LLDP_TLV_DOT1_PVID) | (1 << LLDP_TLV_DOT1_VLANNAME))) {
> +        ds_put_format(ds, "  VLAN: \t");
> +    }
> +    if (port->p_dot1_enable & (1 << LLDP_TLV_DOT1_PVID)) {
> +        ds_put_format(ds, "  %d, ", port->p_dot1.pvid);
> +        if (!(port->p_dot1_enable & (1 << LLDP_TLV_DOT1_VLANNAME)) &&
> +            port->p_dot1.pvid > 0) {
> +            ds_put_format(ds, "pvid: yes");
> +        }
> +    }
> +    if (port->p_dot1_enable & (1 << LLDP_TLV_DOT1_VLANNAME)) {
> +        ds_put_format(ds, "pvid: %s",
> +                      port->p_dot1.pvid == port->p_dot1.vlan_id ? "yes"
> +                                                                : "no");
> +        if (port->p_dot1.vlan_name) {
> +            ds_put_format(ds, ", %s", port->p_dot1.vlan_name);
> +        }
> +    }
> +    ds_put_format(ds, "\n");
> +}
> +
> +static void
> +lldp_print_neighbor_port_dot1_json(struct json *interface_item_json,
> +                                   struct lldpd_port *port)
> +{
> +    struct json *vlan_json = NULL;
> +    if (port->p_dot1_enable & (1 << LLDP_TLV_DOT1_PVID)) {
> +        if (!vlan_json) {
> +            vlan_json = json_object_create();
> +        }
> +        json_object_put(vlan_json, "vlan-id",
> +                        json_integer_create(port->p_dot1.pvid));
> +
> +        if (!(port->p_dot1_enable & (1 << LLDP_TLV_DOT1_VLANNAME)) &&
> +            port->p_dot1.pvid > 0) {
> +            json_object_put(vlan_json, "pvid", json_boolean_create(true));
> +        }
> +    }
> +    if (port->p_dot1_enable & (1 << LLDP_TLV_DOT1_VLANNAME)) {
> +        if (!vlan_json) {
> +            vlan_json = json_object_create();
> +        }
> +        json_object_put(
> +            vlan_json, "pvid",
> +            json_boolean_create(
> +                port->p_dot1.pvid == port->p_dot1.vlan_id ? true : false));
> +        if (port->p_dot1.vlan_name) {
> +            json_object_put(vlan_json, "value",
> +                            json_string_create(port->p_dot1.vlan_name));
> +        }
> +    }
> +    if (vlan_json) {
> +        json_object_put(interface_item_json, "vlan", vlan_json);
> +    }
> +
> +    if (port->p_dot1_enable & (1 << LLDP_TLV_DOT1_PPVID)) {
> +        struct json *ppid_json = json_object_create();
> +        json_object_put(ppid_json, "supported",
> +                        port->p_dot1.ppvid & LLDP_PPVID_CAP_SUPPORTED
> +                            ? json_boolean_create(true)
> +                            : json_boolean_create(false));
> +        json_object_put(ppid_json, "enabled",
> +                        port->p_dot1.ppvid & LLDP_PPVID_CAP_ENABLED
> +                            ? json_boolean_create(true)
> +                            : json_boolean_create(false));
> +        json_object_put(interface_item_json, "ppid", ppid_json);
> +    }
> +}
> +
> +static void
> +lldp_dot3_autoneg_advertised(struct ds *ds, uint16_t pmd_auto_nego, int 
> bithd,
> +                             int bitfd, const char *type)
> +{
> +    if (!((pmd_auto_nego & bithd) || (pmd_auto_nego & bitfd))) {
> +        return;
> +    }
> +
> +    ds_put_format(ds, "      Adv:\t %s", type);
> +    if (bithd != bitfd) {
> +        ds_put_format(ds, ", HD: %s, FD: %s\n",
> +                      (pmd_auto_nego & bithd) ? "yes" : "no",
> +                      (pmd_auto_nego & bitfd) ? "yes" : "no");
> +    }
> +}
> +
> +static void
> +lldp_dot3_autoneg_advertised_json(struct json **advertised_json,
> +                             uint16_t pmd_auto_nego, int bithd, int bitfd,
> +                             const char *type)
> +{
> +    if (!((pmd_auto_nego & bithd) || (pmd_auto_nego & bitfd))) {
> +        return;
> +    }
> +
> +    if (!*advertised_json) {
> +        *advertised_json = json_array_create_empty();
> +    }
> +
> +    struct json *advertised_item_json = json_object_create();
> +    json_object_put(advertised_item_json, "type", json_string_create(type));
> +    if (bithd != bitfd) {
> +        json_object_put(advertised_item_json, "hd",
> +                        json_boolean_create((pmd_auto_nego & bithd) ? true
> +                                                                    : 
> false));
> +        json_object_put(advertised_item_json, "fd",
> +                        json_boolean_create((pmd_auto_nego & bitfd) ? true
> +                                                                    : 
> false));
> +    }
> +    json_array_add(*advertised_json, advertised_item_json);
> +}
> +
> +static void
> +lldp_print_neighbor_port_dot3(struct ds *ds, struct lldpd_port *port)
> +{
> +    if (port->p_dot3_enable & (1 << LLDP_TLV_DOT3_MFS)) {
> +        ds_put_format(ds, "    MFS:\t %d\n", port->p_dot3.mfs);
> +    }
> +
> +    if (port->p_dot3_enable & (1 << LLDP_TLV_DOT3_MAC)) {
> +        ds_put_format(
> +            ds, "    PMD autoneg:  supported: %s, enabled: %s\n",
> +            port->p_dot3.auto_nego & LLDP_DOT3_LINK_AUTONEG_SUPPORT ? "yes"
> +                                                                    : "no",
> +            port->p_dot3.auto_nego & LLDP_DOT3_LINK_AUTONEG_ENABLED ? "yes"
> +                                                                    : "no");
> +
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_10BASE_T,
> +                                     LLDP_DOT3_LINK_AUTONEG_10BASE_T_FD,
> +                                     "10Base-T");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
> +                                     LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
> +                                     "100Base-T4");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
> +                                     LLDP_DOT3_LINK_AUTONEG_100BASE_TX_FD,
> +                                     "100Base-TX");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
> +                                     LLDP_DOT3_LINK_AUTONEG_100BASE_T2_FD,
> +                                     "100Base-T2");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
> +                                     LLDP_DOT3_LINK_AUTONEG_1000BASE_X_FD,
> +                                     "1000Base-X");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
> +                                     LLDP_DOT3_LINK_AUTONEG_1000BASE_T_FD,
> +                                     "1000Base-T");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE,
> +                                     LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE,
> +                                     "FDX_PAUSE");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE,
> +                                     LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE,
> +                                     "FDX_APAUSE");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE,
> +                                     LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE,
> +                                     "FDX_SPAUSE");
> +        lldp_dot3_autoneg_advertised(ds, port->p_dot3.pmd_auto_nego,
> +                                     LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE,
> +                                     LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE,
> +                                     "FDX_BPAUSE");
> +
> +        ds_put_format(ds, "      MAU oper type:\t %d\n", port->p_dot3.mau);
> +    }
> +
> +    if (port->p_dot3_enable & (1 << LLDP_TLV_DOT3_POWER)) {
> +        ds_put_format(
> +            ds,
> +            "    MDI Power:\t supported: %s, enabled: %s, pair control: 
> %s\n",
> +            port->p_dot3.mdi & LLDP_DOT3_POWER_SUPPORT ? "yes" : "no",
> +            port->p_dot3.mdi & LLDP_DOT3_POWER_ENABLED ? "yes" : "no",
> +            port->p_dot3.mdi & LLDP_DOT3_POWER_PAIRCONTROL ? "yes" : "no");
> +
> +        ds_put_format(ds, "      Device type:\t %s\n",
> +                      port->p_dot3.mdi & LLDP_DOT3_POWER_PORT_CLASS ? "PSE"
> +                                                                    : "PD");
> +    }
> +}
> +
> +static void
> +lldp_print_neighbor_port_dot3_json(struct json *port_json,
> +                                   struct lldpd_port *port)
> +{
> +    if (port->p_dot3_enable & (1 << LLDP_TLV_DOT3_MAC)) {
> +        struct json *auto_nego_json = json_object_create();
> +        json_object_put(auto_nego_json, "supported",
> +                        port->p_dot3.auto_nego & 
> LLDP_DOT3_LINK_AUTONEG_SUPPORT
> +                            ? json_boolean_create(true)
> +                            : json_boolean_create(false));
> +        json_object_put(auto_nego_json, "enabled",
> +                        port->p_dot3.auto_nego & 
> LLDP_DOT3_LINK_AUTONEG_ENABLED
> +                            ? json_boolean_create(true)
> +                            : json_boolean_create(false));
> +
> +        struct json *advertised_json = NULL;
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_10BASE_T,
> +                                          LLDP_DOT3_LINK_AUTONEG_10BASE_T_FD,
> +                                          "10Base-T");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
> +                                          LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
> +                                          "100Base-T4");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
> +                                          
> LLDP_DOT3_LINK_AUTONEG_100BASE_TX_FD,
> +                                          "100Base-TX");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
> +                                          
> LLDP_DOT3_LINK_AUTONEG_100BASE_T2_FD,
> +                                          "100Base-T2");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
> +                                          
> LLDP_DOT3_LINK_AUTONEG_1000BASE_X_FD,
> +                                          "1000Base-X");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
> +                                          
> LLDP_DOT3_LINK_AUTONEG_1000BASE_T_FD,
> +                                          "1000Base-T");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE,
> +                                          LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE,
> +                                          "FDX_PAUSE");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE,
> +                                          LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE,
> +                                          "FDX_APAUSE");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE,
> +                                          LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE,
> +                                          "FDX_SPAUSE");
> +        lldp_dot3_autoneg_advertised_json(&advertised_json,
> +                                          port->p_dot3.pmd_auto_nego,
> +                                          LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE,
> +                                          LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE,
> +                                          "FDX_BPAUSE");
> +        if (advertised_json) {
> +            json_object_put(auto_nego_json, "advertised", advertised_json);
> +        }
> +
> +        json_object_put(auto_nego_json, "current",
> +                        json_integer_create(port->p_dot3.mau));
> +
> +        json_object_put(port_json, "auto-negotiation", auto_nego_json);
> +    }
> +
> +    if (port->p_dot3_enable & (1 << LLDP_TLV_DOT3_POWER)) {
> +        struct json *power_json = json_object_create();
> +
> +        json_object_put(
> +            power_json, "device-type",
> +            json_string_create(
> +                port->p_dot3.mdi & LLDP_DOT3_POWER_PORT_CLASS ? "PSE" : 
> "PD"));
> +
> +        json_object_put(
> +            power_json, "supported",
> +            json_boolean_create(
> +                port->p_dot3.mdi & LLDP_DOT3_POWER_SUPPORT ? true : false));
> +
> +        json_object_put(
> +            power_json, "enabled",
> +            json_boolean_create(
> +                port->p_dot3.mdi & LLDP_DOT3_POWER_ENABLED ? true : false));
> +
> +        json_object_put(power_json, "paircontrol",
> +                        json_boolean_create(port->p_dot3.mdi &
> +                                                    
> LLDP_DOT3_POWER_PAIRCONTROL
> +                                                ? true
> +                                                : false));
> +
> +        json_object_put(port_json, "power", power_json);
> +    }
> +
> +    if (port->p_dot3_enable & (1 << LLDP_TLV_DOT3_MFS)) {
> +        json_object_put(port_json, "mfs",
> +                        json_integer_create(port->p_dot3.mfs));
> +    }
> +}
> +
> +static void
> +lldp_print_neighbor_port(struct ds *ds, struct lldpd_hardware *hw)
> +{
> +    struct lldpd_port *port;
> +    const char *none_str = "<None>";
> +
> +    LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
> +        if (!port->p_chassis) {
> +            continue;
> +        }
> +        ds_put_format(ds, "  Chassis:\n");
> +
> +        struct ds id = DS_EMPTY_INITIALIZER;
> +        if (port->p_chassis->c_id_len > 0) {
> +            ds_put_hex_with_delimiter(&id, port->p_chassis->c_id,
> +                                      port->p_chassis->c_id_len, ":");
> +        }
> +        ds_put_format(ds, "    Chassis ID:\t %s\n",
> +                      id.length ? ds_cstr_ro(&id) : none_str);
> +        ds_put_format(ds, "    SysName:\t %s\n",
> +                      strlen(port->p_chassis->c_name) ? 
> port->p_chassis->c_name
> +                                                      : none_str);
> +        ds_put_format(ds, "    SysDescr:\t %s\n",
> +                      strlen(port->p_chassis->c_descr)
> +                          ? port->p_chassis->c_descr
> +                          : none_str);
> +        ds_destroy(&id);
> +
> +        struct lldpd_mgmt *mgmt;
> +        LIST_FOR_EACH (mgmt, m_entries, &port->p_chassis->c_mgmt) {
> +            struct in6_addr ip;
> +            switch (mgmt->m_family) {
> +            case LLDPD_AF_IPV4:
> +                in6_addr_set_mapped_ipv4(&ip, mgmt->m_addr.inet.s_addr);
> +                break;
> +            case LLDPD_AF_IPV6: ip = mgmt->m_addr.inet6; break;
> +            default: continue;
> +            }
> +            ds_put_format(ds, "    MgmtIP:\t ");
> +            ipv6_format_mapped(&ip, ds);
> +            ds_put_format(ds, "\n");
> +            ds_put_format(ds, "    MgmtIface:\t %d\n", mgmt->m_iface);
> +        }
> +
> +        if (port->p_chassis->c_cap_available & LLDP_CAP_BRIDGE) {
> +            ds_put_format(ds, "    Capability:\t Bridge, %s\n",
> +                          port->p_chassis->c_cap_enabled & LLDP_CAP_BRIDGE
> +                              ? "on"
> +                              : "off");
> +        }
> +        if (port->p_chassis->c_cap_available & LLDP_CAP_ROUTER) {
> +            ds_put_format(ds, "    Capability:\t Router, %s\n",
> +                          port->p_chassis->c_cap_enabled & LLDP_CAP_ROUTER
> +                              ? "on"
> +                              : "off");
> +        }
> +        if (port->p_chassis->c_cap_available & LLDP_CAP_WLAN) {
> +            ds_put_format(
> +                ds, "    Capability:\t Wlan, %s\n",
> +                port->p_chassis->c_cap_enabled & LLDP_CAP_WLAN ? "on" : 
> "off");
> +        }
> +        if (port->p_chassis->c_cap_available & LLDP_CAP_STATION) {
> +            ds_put_format(ds, "    Capability:\t Station, %s\n",
> +                          port->p_chassis->c_cap_enabled & LLDP_CAP_STATION
> +                              ? "on"
> +                              : "off");
> +        }
> +
> +        ds_put_format(ds, "  Port:\n");
> +
> +        if (port->p_id_subtype == LLDP_PORTID_SUBTYPE_LLADDR) {
> +            ds_init(&id);
> +            if (port->p_id_len > 0) {
> +                ds_put_hex_with_delimiter(&id, (uint8_t *) port->p_id,
> +                                          port->p_id_len, ":");
> +            }
> +            ds_put_format(ds, "    PortID:\t %s\n",
> +                          id.length ? ds_cstr_ro(&id) : none_str);
> +            ds_destroy(&id);
> +        } else {
> +            ds_put_format(ds, "    PortID:\t %.*s\n",
> +                          (int) (port->p_id ? port->p_id_len
> +                                            : strlen(none_str)),
> +                          port->p_id ?: none_str);
> +        }
> +        ds_put_format(ds, "    PortDescr:\t %s\n",
> +                      strlen(port->p_descr) ? port->p_descr : none_str);
> +        ds_put_format(ds, "    TTL:\t %d\n", port->p_chassis->c_ttl);
> +
> +        lldp_print_neighbor_port_dot3(ds, port);
> +        lldp_print_neighbor_port_dot1(ds, port);
> +    }
> +}
> +
> +static void
> +lldp_print_neighbor_port_json(struct json *interface_item_json,
> +                              struct lldpd_hardware *hw)
> +{
> +    const char *none_str = "<None>";
> +    struct lldpd_port *port;
> +
> +    LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
> +        if (!port->p_chassis) {
> +            continue;
> +        }
> +
> +        struct json *chassis_json = json_object_create();
> +        struct json *chassis_sys_json = json_object_create();
> +        struct json *chassis_id_json = json_object_create();
> +        struct json *chassis_mgmt_ip_json = json_array_create_empty();
> +        struct json *chassis_mgmt_iface_json = json_array_create_empty();
> +        struct json *chassis_capability_json = json_array_create_empty();
> +        struct json *chassis_capability_item_json;
> +        struct json *port_json = json_object_create();
> +        struct json *port_id_json = json_object_create();
> +
> +        struct ds id = DS_EMPTY_INITIALIZER;
> +        if (port->p_chassis->c_id_len > 0) {
> +            ds_put_hex_with_delimiter(&id, port->p_chassis->c_id,
> +                                      port->p_chassis->c_id_len, ":");
> +        }
> +
> +        json_object_put(chassis_id_json, "type", json_string_create("mac"));
> +        json_object_put(chassis_id_json, "value",
> +                        json_string_create(id.length ? ds_cstr_ro(&id)
> +                                                     : none_str));
> +        json_object_put(chassis_sys_json, "id", chassis_id_json);
> +
> +        json_object_put(chassis_json,
> +                        strlen(port->p_chassis->c_name)
> +                            ? port->p_chassis->c_name
> +                            : none_str,
> +                        chassis_sys_json);
> +
> +        json_object_put(chassis_sys_json, "descr",
> +                        json_string_create(strlen(port->p_chassis->c_descr)
> +                                               ? port->p_chassis->c_descr
> +                                               : none_str));
> +
> +        ds_destroy(&id);
> +
> +        struct lldpd_mgmt *mgmt;
> +        struct in6_addr ip;
> +        char addr_str[INET6_ADDRSTRLEN];
> +        LIST_FOR_EACH (mgmt, m_entries, &port->p_chassis->c_mgmt) {
> +            switch (mgmt->m_family) {
> +            case LLDPD_AF_IPV4:
> +                in6_addr_set_mapped_ipv4(&ip, mgmt->m_addr.inet.s_addr);
> +                break;
> +            case LLDPD_AF_IPV6: ip = mgmt->m_addr.inet6; break;
> +            default: continue;
> +            }
> +
> +            ipv6_string_mapped(addr_str, &ip);
> +            json_array_add(chassis_mgmt_ip_json, 
> json_string_create(addr_str));
> +            json_array_add(chassis_mgmt_iface_json,
> +                           json_integer_create(mgmt->m_iface));
> +        }
> +        json_object_put(chassis_sys_json, "mgmt-ip", chassis_mgmt_ip_json);
> +        json_object_put(chassis_sys_json, "mgmt-iface",
> +                        chassis_mgmt_iface_json);
> +
> +        if (port->p_chassis->c_cap_available & LLDP_CAP_BRIDGE) {
> +            chassis_capability_item_json = json_object_create();
> +            json_object_put(chassis_capability_item_json, "type",
> +                            json_string_create("Bridge"));
> +            json_object_put(
> +                chassis_capability_item_json, "enabled",
> +                json_boolean_create(port->p_chassis->c_cap_enabled &
> +                                    LLDP_CAP_BRIDGE));
> +            json_array_add(chassis_capability_json,
> +                           chassis_capability_item_json);
> +        }
> +        if (port->p_chassis->c_cap_available & LLDP_CAP_ROUTER) {
> +            chassis_capability_item_json = json_object_create();
> +            json_object_put(chassis_capability_item_json, "type",
> +                            json_string_create("Router"));
> +            json_object_put(
> +                chassis_capability_item_json, "enabled",
> +                json_boolean_create(port->p_chassis->c_cap_enabled &
> +                                    LLDP_CAP_ROUTER));
> +            json_array_add(chassis_capability_json,
> +                           chassis_capability_item_json);
> +        }
> +        if (port->p_chassis->c_cap_available & LLDP_CAP_WLAN) {
> +            chassis_capability_item_json = json_object_create();
> +            json_object_put(chassis_capability_item_json, "type",
> +                            json_string_create("Wlan"));
> +            json_object_put(
> +                chassis_capability_item_json, "enabled",
> +                json_boolean_create(port->p_chassis->c_cap_enabled &
> +                                    LLDP_CAP_WLAN));
> +            json_array_add(chassis_capability_json,
> +                           chassis_capability_item_json);
> +        }
> +        if (port->p_chassis->c_cap_available & LLDP_CAP_STATION) {
> +            chassis_capability_item_json = json_object_create();
> +            json_object_put(chassis_capability_item_json, "type",
> +                            json_string_create("Station"));
> +            json_object_put(
> +                chassis_capability_item_json, "enabled",
> +                json_boolean_create(port->p_chassis->c_cap_enabled &
> +                                    LLDP_CAP_STATION));
> +            json_array_add(chassis_capability_json,
> +                           chassis_capability_item_json);
> +        }
> +        json_object_put(chassis_sys_json, "capability",
> +                        chassis_capability_json);
> +
> +        if (port->p_id_subtype == LLDP_PORTID_SUBTYPE_LLADDR) {
> +            ds_init(&id);
> +            if (port->p_id_len > 0) {
> +                ds_put_hex_with_delimiter(&id, port->p_id, port->p_id_len,
> +                                          ":");
> +            }
> +            json_object_put(port_id_json, "type", json_string_create("mac"));
> +            json_object_put(port_id_json, "value",
> +                            json_string_create(id.length ? ds_cstr_ro(&id)
> +                                                         : none_str));
> +
> +            ds_destroy(&id);
> +        } else {
> +            json_object_put(port_id_json, "type",
> +                            json_string_create("ifname"));
> +            if (port->p_id) {
> +                struct ds port_ds = DS_EMPTY_INITIALIZER;
> +                ds_put_buffer(&port_ds, port->p_id, port->p_id_len);
> +                json_object_put(port_id_json, "value",
> +                                json_string_create(ds_cstr_ro(&port_ds)));
> +                ds_destroy(&port_ds);
> +            } else {
> +                json_object_put(port_id_json, "value",
> +                                json_string_create(none_str));
> +            }
> +        }
> +        json_object_put(port_json, "id", port_id_json);
> +
> +        json_object_put(port_json, "desc",
> +                        json_string_create(
> +                            strlen(port->p_descr) ? port->p_descr : 
> none_str));
> +        json_object_put(port_json, "ttl",
> +                        json_integer_create(port->p_chassis->c_ttl));
> +
> +        lldp_print_neighbor_port_dot1_json(interface_item_json, port);
> +        lldp_print_neighbor_port_dot3_json(port_json, port);
> +
> +        json_object_put(interface_item_json, "chassis", chassis_json);
> +        json_object_put(interface_item_json, "port", port_json);
> +    }
> +}
> +
> +static void
> +lldp_print_neighbor(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex)
> +{
> +    struct lldpd_hardware *hw;
> +
> +    ds_put_format(ds, "Interface: %s\n", lldp->name);
> +
> +    if (!lldp->lldpd) {
> +        return;
> +    }
> +
> +    LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) {
> +        lldp_print_neighbor_port(ds, hw);
> +    }
> +    ds_put_format(ds, 
> "------------------------------------------------------"
> +                      "-------------------------\n");
> +}
> +
> +static void
> +lldp_print_neighbor_json(struct json *interface_array_json, struct lldp 
> *lldp)
> +    OVS_REQUIRES(mutex)
> +{
> +    struct lldpd_hardware *hw;
> +    struct json *interface_item_json;
> +    struct json *interface_item_warp_json;
> +
> +    if (!lldp->lldpd) {
> +        return;
> +    }
> +
> +    LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) {
> +        interface_item_json = json_object_create();
> +        lldp_print_neighbor_port_json(interface_item_json, hw);
> +
> +        interface_item_warp_json = json_object_create();
> +        json_object_put(interface_item_warp_json, lldp->name,
> +                        interface_item_json);
> +
> +        json_array_add(interface_array_json, interface_item_warp_json);
> +    }
> +}
> +
>  static void
>  aa_unixctl_status(struct unixctl_conn *conn, int argc OVS_UNUSED,
>                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
> @@ -368,6 +961,53 @@ aa_unixctl_statistics(struct unixctl_conn *conn, int 
> argc OVS_UNUSED,
>      unixctl_command_reply(conn, ds_cstr(&ds));
>  }
>  
> +static void
> +lldp_unixctl_show_neighbor(struct unixctl_conn *conn, int argc,
> +                           const char *argv[], void *aux OVS_UNUSED)
> +    OVS_EXCLUDED(mutex)
> +{
> +    struct lldp *lldp;
> +
> +    if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
> +        struct json *lldp_json = json_object_create();
> +        struct json *interface_json = json_object_create();
> +        struct json *interface_array_json = json_array_create_empty();
> +
> +        ovs_mutex_lock(&mutex);
> +        HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
> +            if (argc > 1 && strcmp(argv[1], lldp->name)) {
> +                continue;
> +            }
> +            lldp_print_neighbor_json(interface_array_json,lldp);
> +        }
> +        ovs_mutex_unlock(&mutex);
> +
> +        json_object_put(interface_json, "interface", interface_array_json);
> +        json_object_put(lldp_json, "lldp", interface_json);
> +
> +        unixctl_command_reply_json(conn, lldp_json);
> +    } else {
> +        struct ds ds = DS_EMPTY_INITIALIZER;
> +
> +        ds_put_format(&ds, 
> "--------------------------------------------------"
> +                           "-----------------------------\nLLDP neighbor:\n"
> +                           
> "--------------------------------------------------"
> +                           "-----------------------------\n");
> +
> +        ovs_mutex_lock(&mutex);
> +        HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
> +            if (argc > 1 && strcmp(argv[1], lldp->name)) {
> +                continue;
> +            }
> +            lldp_print_neighbor(&ds, lldp);
> +        }
> +        ovs_mutex_unlock(&mutex);
> +
> +        unixctl_command_reply(conn, ds_cstr(&ds));
> +        ds_destroy(&ds);
> +    }
> +}
> +
>  /* An Auto Attach mapping was configured.  Populate the corresponding
>   * structures in the LLDP hardware.
>   */
> @@ -635,6 +1275,8 @@ lldp_init(void)
>                               aa_unixctl_show_isid, NULL);
>      unixctl_command_register("autoattach/statistics", "[bridge]", 0, 1,
>                               aa_unixctl_statistics, NULL);
> +    unixctl_command_register("lldp/neighbor", "[interface]", 0, 1,
> +                             lldp_unixctl_show_neighbor, NULL);
>  }
>  
>  /* Returns true if 'lldp' should process packets from 'flow'.  Sets
> @@ -974,4 +1616,3 @@ lldp_destroy_dummy(struct lldp *lldp)
>  
>      free(lldp);
>  }
> -
> diff --git a/tests/automake.mk b/tests/automake.mk
> index 59f538761..a453a0c4b 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -68,6 +68,7 @@ TESTSUITE_AT = \
>       tests/tunnel.at \
>       tests/tunnel-push-pop.at \
>       tests/tunnel-push-pop-ipv6.at \
> +     tests/ovs-lldp.at \
>       tests/ovs-router.at \
>       tests/lockfile.at \
>       tests/reconnect.at \
> diff --git a/tests/ovs-lldp.at b/tests/ovs-lldp.at
> new file mode 100644
> index 000000000..b45904f60
> --- /dev/null
> +++ b/tests/ovs-lldp.at
> @@ -0,0 +1,125 @@
> +AT_BANNER([ovs-lldp])
> +
> +AT_SETUP([lldp - check lldp neighbor display])
> +
> +OVS_VSWITCHD_START([])
> +
> +AT_CHECK([
> +    ovs-vsctl set bridge br0 \
> +    datapath_type=dummy
> +], [0])
> +
> +AT_CHECK([
> +    ovs-vsctl add-port br0 p1 -- set Interface p1 type=dummy \
> +    lldp:enable=true
> +], [0])
> +
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 \
> +'0180c200000ee878eecf78d588cc020704e878eecf78a80416054769676162697445746865726e6574312f302f3332060200790808746f5f37305f33310a0b424a59442d36372e3233370c1d48334320436f6d7761726520506c6174666f726d20536f6674776172650e0403140114100c0501c0a843ed020000027b00fe060080c2010bb8fe070080c202020000fe100080c203000109564c414e2030303031fe060080c2060000fe090080c2070100000000fe0900120f01036c01001efe0c00120f020101011000000000fe0600120f0428000000'])
> +
> +AT_CHECK([ovs-appctl lldp/neighbor p1], [0], [dnl
> +-------------------------------------------------------------------------------
> +LLDP neighbor:
> +-------------------------------------------------------------------------------
> +Interface: p1
> +  Chassis:
> +    Chassis ID:       e8:78:ee:cf:78:a8
> +    SysName:  BJYD-67.237
> +    SysDescr:         H3C Comware Platform Software
> +    MgmtIP:   192.168.67.237
> +    MgmtIface:        635
> +    Capability:       Bridge, on
> +    Capability:       Router, on
> +  Port:
> +    PortID:   GigabitEthernet1/0/32
> +    PortDescr:        to_70_31
> +    TTL:      121
> +    MFS:      10240
> +    PMD autoneg:  supported: yes, enabled: yes
> +      Adv:    10Base-T, HD: yes, FD: yes
> +      Adv:    100Base-TX, HD: yes, FD: yes
> +      Adv:    1000Base-T, HD: no, FD: yes
> +      MAU oper type:  30
> +    MDI Power:        supported: no, enabled: no, pair control: no
> +      Device type:    PSE
> +  VLAN:        3000, pvid: no, VLAN 0001
> +-------------------------------------------------------------------------------
> +])
> +
> +AT_CHECK([ovs-appctl --format json lldp/neighbor p1 | jq 
> .lldp.interface[\[0\]].p1], [0], [dnl
> +[{
> +  "chassis": {
> +    "BJYD-67.237": {
> +      "capability": [
> +        {
> +          "enabled": true,
> +          "type": "Bridge"
> +        },
> +        {
> +          "enabled": true,
> +          "type": "Router"
> +        }
> +      ],
> +      "descr": "H3C Comware Platform Software",
> +      "id": {
> +        "type": "mac",
> +        "value": "e8:78:ee:cf:78:a8"
> +      },
> +      "mgmt-iface": [
> +        635
> +      ],
> +      "mgmt-ip": [
> +        "192.168.67.237"
> +      ]
> +    }
> +  },
> +  "port": {
> +    "auto-negotiation": {
> +      "advertised": [
> +        {
> +          "fd": true,
> +          "hd": true,
> +          "type": "10Base-T"
> +        },
> +        {
> +          "fd": true,
> +          "hd": true,
> +          "type": "100Base-TX"
> +        },
> +        {
> +          "fd": true,
> +          "hd": false,
> +          "type": "1000Base-T"
> +        }
> +      ],
> +      "current": 30,
> +      "enabled": true,
> +      "supported": true
> +    },
> +    "desc": "to_70_31",
> +    "id": {
> +      "type": "ifname",
> +      "value": "GigabitEthernet1/0/32"
> +    },
> +    "mfs": 10240,
> +    "power": {
> +      "device-type": "PSE",
> +      "enabled": false,
> +      "paircontrol": false,
> +      "supported": false
> +    },
> +    "ttl": 121
> +  },
> +  "ppid": {
> +    "enabled": false,
> +    "supported": false
> +  },
> +  "vlan": {
> +    "pvid": false,
> +    "value": "VLAN 0001",
> +    "vlan-id": 3000
> +  }
> +}]
> +])
> +
> +AT_CLEANUP
> \ No newline at end of file
> diff --git a/tests/testsuite.at b/tests/testsuite.at
> index 9d77a9f51..f80656076 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -66,6 +66,7 @@ m4_include([tests/ofproto-dpif.at])
>  m4_include([tests/bridge.at])
>  m4_include([tests/netdev-type.at])
>  m4_include([tests/ovsdb.at])
> +m4_include([tests/ovs-lldp.at])
>  m4_include([tests/ovs-vsctl.at])
>  m4_include([tests/stp.at])
>  m4_include([tests/rstp.at])
> diff --git a/vswitchd/ovs-vswitchd.8.in b/vswitchd/ovs-vswitchd.8.in
> index 98e58951d..51b0a46b5 100644
> --- a/vswitchd/ovs-vswitchd.8.in
> +++ b/vswitchd/ovs-vswitchd.8.in
> @@ -149,6 +149,10 @@ enabled.
>  Force the fault status of the CFM module on \fIinterface\fR (or all
>  interfaces if none is given) to be \fIstatus\fR.  \fIstatus\fR can be
>  "true", "false", or "normal" which reverts to the standard behavior.
> +.IP "\fBlldp/neighbor\fR [\fIinterface\fR]"
> +Displays detailed information about LLDP neighbors on \fIinterface\fR.
> +If \fIinterface\fR is not specified, then it displays detailed information
> +about all interfaces with LLDP enabled.
>  .IP "\fBstp/tcn\fR [\fIbridge\fR]"
>  Forces a topology change event on \fIbridge\fR if it's running STP.  This
>  may cause it to send Topology Change Notifications to its peers and flush

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to