New appctl 'lldp/neighbor' displays lldp neighbor informations.
Support json output with --format json --pretty

Example outputs would be:
ovs-appctl lldp/neighbor
-------------------------------------------------------------------------------
LLDP neighbors:
-------------------------------------------------------------------------------
Interface: ens5
  Chassis:
    Chassis ID:  9c:71:3a:58:05:b4
    SysName:     n-203
    SysDescr:    CentOS Linux 7 (Core) Linux 4.19.90-2307.3.0.el7......
    MgmtIP:      4.4.4.203
    MgmtIface:   13
    MgmtIP:      fe80::9e71:3aff:fe58:5b4
    MgmtIface:   2
    Capability:  Bridge, off
    Capability:  Router, off
    Capability:  Wlan, off
    Capability:  Station, on
  Port:
    PortID:      fe:54:00:80:ea:68
    PortDescr:   vnet74
    TTL:         120
-------------------------------------------------------------------------------
Interface: ens14np0
  Chassis:
    Chassis ID:  1c:34:da:88:27:c0
    SysName:     7F-67.239
    SysDescr:    MSN2700,Onyx,SWv3.8.2008
    MgmtIP:      192.168.67.239
    MgmtIface:   0
    Capability:  Bridge, on
    Capability:  Router, off
  Port:
    PortID:      Eth1/7/1
    PortDescr:
    TTL:         120
-------------------------------------------------------------------------------

ovs-appctl lldp/neighbor ens14np0
-------------------------------------------------------------------------------
LLDP neighbours:
-------------------------------------------------------------------------------
Interface: ens14np0
  Chassis:
    Chassis ID:  1c:34:da:88:27:c0
    SysName:     7F-67.239
    SysDescr:    MSN2700,Onyx,SWv3.8.2008
    MgmtIP:      192.168.67.239
    MgmtIface:   0
    Capability:  Bridge, on
    Capability:  Router, off
  Port:
    PortID:      Eth1/7/1
    PortDescr:
    TTL:         120
-------------------------------------------------------------------------------

ovs-appctl --format json --pretty lldp/neighbor ens14np0
{
  "lldp": {
    "interface": [
      {
        "ens14np0": {
          "chassis": {
            "7F-67.239": {
              "capability": [
                {
                  "enabled": true,
                  "type": "Bridge"},
                {
                  "enabled": false,
                  "type": "Router"}],
              "descr": "openvswitch 3.5.90",
              "id": {
                "type": "mac",
                "value": "1c:34:da:88:27:c0"},
              "mgmt-iface": [
                0],
              "mgmt-ip": [
                "192.168.67.239"]}},
          "port": {
            "desc": " ",
            "id": {
              "type": "ifname",
              "value": "Eth1/7/1"},
            "ttl": 120}}}]}}

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
     (apologize for spam emails, found tests via github action and fully tested)
 V5: add json output, add NEWS

 NEWS                       |   1 +
 lib/ovs-lldp.c             | 329 +++++++++++++++++++++++++++++++++++++
 vswitchd/ovs-vswitchd.8.in |   4 +
 3 files changed, 334 insertions(+)

diff --git a/NEWS b/NEWS
index e6da3b122..bc398ea95 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,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 infomations.
    - SSL/TLS:
      * Support for deprecated TLSv1 and TLSv1.1 protocols on OpenFlow and
        database connections is now removed.
diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
index 2d13e971e..5ab5d27af 100644
--- a/lib/ovs-lldp.c
+++ b/lib/ovs-lldp.c
@@ -34,7 +34,9 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stdlib.h>
+#include "lldp/lldp-const.h"
 #include "openvswitch/dynamic-string.h"
+#include "openvswitch/json.h"
 #include "flow.h"
 #include "openvswitch/list.h"
 #include "lldp/lldpd.h"
@@ -324,6 +326,284 @@ aa_print_isid_status(struct ds *ds, struct lldp *lldp) 
OVS_REQUIRES(mutex)
     }
 }
 
+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");
+        char *id = NULL;
+        if (port->p_chassis->c_id_len > 0) {
+            chassisid_to_string(port->p_chassis->c_id,
+                                port->p_chassis->c_id_len, &id);
+        }
+        ds_put_format(ds, "    Chassis ID:\t %s\n", 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);
+        free(id);
+
+        struct lldpd_mgmt *mgmt;
+        struct in6_addr ip;
+        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;
+            }
+            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) {
+            id = NULL;
+            if (port->p_id_len > 0) {
+                chassisid_to_string((uint8_t *) port->p_id, port->p_id_len,
+                                    &id);
+            }
+            ds_put_format(ds, "    PortID:\t %s\n", id ?: none_str);
+            free(id);
+        } else {
+            ds_put_format(ds, "    PortID:\t %s\n", 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);
+        if (port->p_mfs) {
+            ds_put_format(ds, "    MFS:\t %d\n", port->p_mfs);
+        }
+    }
+}
+
+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();
+
+        char *id = NULL;
+        if (port->p_chassis->c_id_len > 0) {
+            chassisid_to_string(port->p_chassis->c_id,
+                                port->p_chassis->c_id_len, &id);
+        }
+
+        json_object_put(chassis_id_json, "type", json_string_create("mac"));
+        json_object_put(chassis_id_json, "value",
+                        json_string_create(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));
+
+        free(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) {
+            id = NULL;
+            if (port->p_id_len > 0) {
+                chassisid_to_string((uint8_t *) port->p_id, port->p_id_len,
+                                    &id);
+            }
+            json_object_put(port_id_json, "type", json_string_create("mac"));
+            json_object_put(port_id_json, "value",
+                            json_string_create(id ?: none_str));
+
+            free(id);
+        } else {
+            json_object_put(port_id_json, "type",
+                            json_string_create("ifname"));
+            json_object_put(port_id_json, "value",
+                            json_string_create(port->p_id ?: 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));
+        if (port->p_mfs) {
+            json_object_put(port_json, "mfs",
+                            json_integer_create(port->p_mfs));
+        }
+
+        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)
@@ -382,6 +662,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 neighbors:\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.
  */
@@ -649,6 +976,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
diff --git a/vswitchd/ovs-vswitchd.8.in b/vswitchd/ovs-vswitchd.8.in
index 98e58951d..e4a6f7e46 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 neighbor on \fIinterface\fR.
+If \fIinterface\fR is not specified, then 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
-- 
2.43.5

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

Reply via email to