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