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