summaryrefslogtreecommitdiffstats
path: root/src/client/display.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/display.c')
-rw-r--r--src/client/display.c1039
1 files changed, 1039 insertions, 0 deletions
diff --git a/src/client/display.c b/src/client/display.c
new file mode 100644
index 0000000..6e3ec78
--- /dev/null
+++ b/src/client/display.c
@@ -0,0 +1,1039 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "../log.h"
+#include "client.h"
+
+static void
+display_cap(struct writer *w, lldpctl_atom_t *chassis, u_int8_t bit, const char *symbol)
+{
+ if (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_cap_available) & bit) {
+ tag_start(w, "capability", "Capability");
+ tag_attr(w, "type", "", symbol);
+ tag_attr(w, "enabled", "",
+ (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_cap_enabled) &
+ bit) ?
+ "on" :
+ "off");
+ tag_end(w);
+ }
+}
+
+static void
+display_med_capability(struct writer *w, long int available, int cap,
+ const char *symbol)
+{
+ if (available & cap) {
+ tag_start(w, "capability", "Capability");
+ tag_attr(w, "type", "", symbol);
+ tag_attr(w, "available", "", "yes");
+ tag_end(w);
+ }
+}
+
+static void
+display_med(struct writer *w, lldpctl_atom_t *port, lldpctl_atom_t *chassis)
+{
+ lldpctl_atom_t *medpolicies, *medpolicy;
+ lldpctl_atom_t *medlocations, *medlocation;
+ lldpctl_atom_t *caelements, *caelement;
+ lldpctl_atom_t *medpower;
+ long int cap = lldpctl_atom_get_int(chassis, lldpctl_k_chassis_med_cap);
+ const char *type;
+
+ if (lldpctl_atom_get_int(chassis, lldpctl_k_chassis_med_type) <= 0) return;
+
+ tag_start(w, "lldp-med", "LLDP-MED");
+
+ tag_datatag(w, "device-type", "Device Type",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_type));
+
+ display_med_capability(w, cap, LLDP_MED_CAP_CAP, "Capabilities");
+ display_med_capability(w, cap, LLDP_MED_CAP_POLICY, "Policy");
+ display_med_capability(w, cap, LLDP_MED_CAP_LOCATION, "Location");
+ display_med_capability(w, cap, LLDP_MED_CAP_MDI_PSE, "MDI/PSE");
+ display_med_capability(w, cap, LLDP_MED_CAP_MDI_PD, "MDI/PD");
+ display_med_capability(w, cap, LLDP_MED_CAP_IV, "Inventory");
+
+ /* LLDP MED policies */
+ medpolicies = lldpctl_atom_get(port, lldpctl_k_port_med_policies);
+ lldpctl_atom_foreach(medpolicies, medpolicy)
+ {
+ if (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_type) <= 0)
+ continue;
+
+ tag_start(w, "policy", "LLDP-MED Network Policy for");
+ tag_attr(w, "apptype", "",
+ lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_type));
+ tag_attr(w, "defined", "Defined",
+ (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_unknown) >
+ 0) ?
+ "no" :
+ "yes");
+
+ if (lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_tagged) > 0) {
+ int vid =
+ lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_vid);
+ tag_start(w, "vlan", "VLAN");
+ if (vid == 0) {
+ tag_attr(w, "vid", "", "priority");
+ } else if (vid == 4095) {
+ tag_attr(w, "vid", "", "reserved");
+ } else {
+ tag_attr(w, "vid", "",
+ lldpctl_atom_get_str(medpolicy,
+ lldpctl_k_med_policy_vid));
+ }
+ tag_end(w);
+ }
+
+ tag_datatag(w, "priority", "Priority",
+ lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_priority));
+ /* Also give a numeric value */
+ int pcp =
+ lldpctl_atom_get_int(medpolicy, lldpctl_k_med_policy_priority);
+ char spcp[2] = { pcp + '0', '\0' };
+ tag_datatag(w, "pcp", "PCP", spcp);
+ tag_datatag(w, "dscp", "DSCP Value",
+ lldpctl_atom_get_str(medpolicy, lldpctl_k_med_policy_dscp));
+
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(medpolicies);
+
+ /* LLDP MED locations */
+ medlocations = lldpctl_atom_get(port, lldpctl_k_port_med_locations);
+ lldpctl_atom_foreach(medlocations, medlocation)
+ {
+ int format =
+ lldpctl_atom_get_int(medlocation, lldpctl_k_med_location_format);
+ if (format <= 0) continue;
+ tag_start(w, "location", "LLDP-MED Location Identification");
+ tag_attr(w, "type", "Type",
+ lldpctl_atom_get_str(medlocation, lldpctl_k_med_location_format));
+
+ switch (format) {
+ case LLDP_MED_LOCFORMAT_COORD:
+ tag_attr(w, "geoid", "Geoid",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_geoid));
+ tag_datatag(w, "lat", "Latitude",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_latitude));
+ tag_datatag(w, "lon", "Longitude",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_longitude));
+ tag_start(w, "altitude", "Altitude");
+ tag_attr(w, "unit", "",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_altitude_unit));
+ tag_data(w,
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_altitude));
+ tag_end(w);
+ break;
+ case LLDP_MED_LOCFORMAT_CIVIC:
+ tag_datatag(w, "country", "Country",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_country));
+ caelements = lldpctl_atom_get(medlocation,
+ lldpctl_k_med_location_ca_elements);
+ lldpctl_atom_foreach(caelements, caelement)
+ {
+ type = lldpctl_atom_get_str(caelement,
+ lldpctl_k_med_civicaddress_type);
+ tag_datatag(w, totag(type), type,
+ lldpctl_atom_get_str(caelement,
+ lldpctl_k_med_civicaddress_value));
+ }
+ lldpctl_atom_dec_ref(caelements);
+ break;
+ case LLDP_MED_LOCFORMAT_ELIN:
+ tag_datatag(w, "ecs", "ECS ELIN",
+ lldpctl_atom_get_str(medlocation,
+ lldpctl_k_med_location_elin));
+ break;
+ }
+
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(medlocations);
+
+ /* LLDP MED power */
+ medpower = lldpctl_atom_get(port, lldpctl_k_port_med_power);
+ if (lldpctl_atom_get_int(medpower, lldpctl_k_med_power_type) > 0) {
+ tag_start(w, "poe", "Extended Power-over-Ethernet");
+
+ tag_datatag(w, "device-type", "Power Type & Source",
+ lldpctl_atom_get_str(medpower, lldpctl_k_med_power_type));
+ tag_datatag(w, "source", "Power Source",
+ lldpctl_atom_get_str(medpower, lldpctl_k_med_power_source));
+ tag_datatag(w, "priority", "Power priority",
+ lldpctl_atom_get_str(medpower, lldpctl_k_med_power_priority));
+ tag_datatag(w, "power", "Power Value",
+ lldpctl_atom_get_str(medpower, lldpctl_k_med_power_val));
+
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(medpower);
+
+ /* LLDP MED inventory */
+ do {
+ const char *hw =
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_hw);
+ const char *sw =
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_sw);
+ const char *fw =
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_fw);
+ const char *sn =
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_med_inventory_sn);
+ const char *manuf = lldpctl_atom_get_str(chassis,
+ lldpctl_k_chassis_med_inventory_manuf);
+ const char *model = lldpctl_atom_get_str(chassis,
+ lldpctl_k_chassis_med_inventory_model);
+ const char *asset = lldpctl_atom_get_str(chassis,
+ lldpctl_k_chassis_med_inventory_asset);
+ if (!(hw || sw || fw || sn || manuf || model || asset)) break;
+
+ tag_start(w, "inventory", "Inventory");
+ tag_datatag(w, "hardware", "Hardware Revision", hw);
+ tag_datatag(w, "software", "Software Revision", sw);
+ tag_datatag(w, "firmware", "Firmware Revision", fw);
+ tag_datatag(w, "serial", "Serial Number", sn);
+ tag_datatag(w, "manufacturer", "Manufacturer", manuf);
+ tag_datatag(w, "model", "Model", model);
+ tag_datatag(w, "asset", "Asset ID", asset);
+ tag_end(w);
+ } while (0);
+
+ tag_end(w);
+}
+
+static void
+display_chassis(struct writer *w, lldpctl_atom_t *chassis, int details)
+{
+ lldpctl_atom_t *mgmts, *mgmt;
+
+ tag_start(w, "chassis", "Chassis");
+ tag_start(w, "id", "ChassisID");
+ tag_attr(w, "type", "",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_id_subtype));
+ tag_data(w, lldpctl_atom_get_str(chassis, lldpctl_k_chassis_id));
+ tag_end(w);
+ tag_datatag(w, "name", "SysName",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_name));
+ if (details == DISPLAY_BRIEF) {
+ tag_end(w);
+ return;
+ }
+ tag_datatag(w, "descr", "SysDescr",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_descr));
+
+ /* Management addresses */
+ mgmts = lldpctl_atom_get(chassis, lldpctl_k_chassis_mgmt);
+ lldpctl_atom_foreach(mgmts, mgmt)
+ {
+ tag_datatag(w, "mgmt-ip", "MgmtIP",
+ lldpctl_atom_get_str(mgmt, lldpctl_k_mgmt_ip));
+ if (lldpctl_atom_get_int(mgmt, lldpctl_k_mgmt_iface_index))
+ tag_datatag(w, "mgmt-iface", "MgmtIface",
+ lldpctl_atom_get_str(mgmt, lldpctl_k_mgmt_iface_index));
+ }
+ lldpctl_atom_dec_ref(mgmts);
+
+ /* Capabilities */
+ display_cap(w, chassis, LLDP_CAP_OTHER, "Other");
+ display_cap(w, chassis, LLDP_CAP_REPEATER, "Repeater");
+ display_cap(w, chassis, LLDP_CAP_BRIDGE, "Bridge");
+ display_cap(w, chassis, LLDP_CAP_ROUTER, "Router");
+ display_cap(w, chassis, LLDP_CAP_WLAN, "Wlan");
+ display_cap(w, chassis, LLDP_CAP_TELEPHONE, "Tel");
+ display_cap(w, chassis, LLDP_CAP_DOCSIS, "Docsis");
+ display_cap(w, chassis, LLDP_CAP_STATION, "Station");
+
+ tag_end(w);
+}
+
+static void
+display_custom_tlvs(struct writer *w, lldpctl_atom_t *neighbor)
+{
+ lldpctl_atom_t *custom_list, *custom;
+ int have_custom_tlvs = 0;
+ size_t i, len, slen;
+ const uint8_t *oui, *oui_info;
+ char buf[1600]; /* should be enough for printing */
+
+ custom_list = lldpctl_atom_get(neighbor, lldpctl_k_custom_tlvs);
+ lldpctl_atom_foreach(custom_list, custom)
+ {
+ /* This tag gets added only once, if there are any custom TLVs */
+ if (!have_custom_tlvs) {
+ tag_start(w, "unknown-tlvs", "Unknown TLVs");
+ have_custom_tlvs++;
+ }
+ len = 0;
+ oui = lldpctl_atom_get_buffer(custom, lldpctl_k_custom_tlv_oui, &len);
+ len = 0;
+ oui_info = lldpctl_atom_get_buffer(custom,
+ lldpctl_k_custom_tlv_oui_info_string, &len);
+ if (!oui) continue;
+ tag_start(w, "unknown-tlv", "TLV");
+
+ /* Add OUI as attribute */
+ snprintf(buf, sizeof(buf), "%02X,%02X,%02X", oui[0], oui[1], oui[2]);
+ tag_attr(w, "oui", "OUI", buf);
+ snprintf(buf, sizeof(buf), "%d",
+ (int)lldpctl_atom_get_int(custom,
+ lldpctl_k_custom_tlv_oui_subtype));
+ tag_attr(w, "subtype", "SubType", buf);
+ snprintf(buf, sizeof(buf), "%d", (int)len);
+ tag_attr(w, "len", "Len", buf);
+ if (len > 0) {
+ for (slen = 0, i = 0; i < len; ++i)
+ slen += snprintf(buf + slen,
+ sizeof(buf) > slen ? sizeof(buf) - slen : 0,
+ "%02X%s", oui_info[i], ((i < len - 1) ? "," : ""));
+ tag_data(w, buf);
+ }
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(custom_list);
+
+ if (have_custom_tlvs) tag_end(w);
+}
+
+static void
+display_autoneg(struct writer *w, int advertised, int bithd, int bitfd,
+ const char *desc)
+{
+ if (!((advertised & bithd) || (advertised & bitfd))) return;
+
+ tag_start(w, "advertised", "Adv");
+ tag_attr(w, "type", "", desc);
+ if (bitfd != bithd) {
+ tag_attr(w, "hd", "HD", (advertised & bithd) ? "yes" : "no");
+ tag_attr(w, "fd", "FD", (advertised & bitfd) ? "yes" : "no");
+ }
+ tag_end(w);
+}
+
+static void
+display_port(struct writer *w, lldpctl_atom_t *port, int details)
+{
+ int vlan_tx_tag;
+ char buf[5]; /* should be enough for printing */
+
+ tag_start(w, "port", "Port");
+ tag_start(w, "id", "PortID");
+ tag_attr(w, "type", "", lldpctl_atom_get_str(port, lldpctl_k_port_id_subtype));
+ tag_data(w, lldpctl_atom_get_str(port, lldpctl_k_port_id));
+ tag_end(w);
+
+ tag_datatag(w, "descr", "PortDescr",
+ lldpctl_atom_get_str(port, lldpctl_k_port_descr));
+
+ if ((vlan_tx_tag = lldpctl_atom_get_int(port, lldpctl_k_port_vlan_tx)) != -1) {
+ tag_start(w, "vlanTX", "VlanTX");
+ snprintf(buf, sizeof(buf), "%d", vlan_tx_tag & 0xfff);
+ tag_attr(w, "id", "VID", buf);
+ snprintf(buf, sizeof(buf), "%d", (vlan_tx_tag >> 13) & 0x7);
+ tag_attr(w, "prio", "Prio", buf);
+ snprintf(buf, sizeof(buf), "%d", (vlan_tx_tag >> 12) & 0x1);
+ tag_attr(w, "dei", "DEI", buf);
+ tag_end(w);
+ }
+
+ if (details && lldpctl_atom_get_int(port, lldpctl_k_port_ttl) > 0)
+ tag_datatag(w, "ttl", "TTL",
+ lldpctl_atom_get_str(port, lldpctl_k_port_ttl));
+
+ /* Dot3 */
+ if (details == DISPLAY_DETAILS) {
+ tag_datatag(w, "mfs", "MFS",
+ lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mfs));
+ tag_datatag(w, "aggregation", "Port is aggregated. PortAggregID",
+ lldpctl_atom_get_str(port, lldpctl_k_port_dot3_aggregid));
+
+ long int autoneg_support, autoneg_enabled, autoneg_advertised, mautype;
+ autoneg_support =
+ lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_support);
+ autoneg_enabled =
+ lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_enabled);
+ autoneg_advertised =
+ lldpctl_atom_get_int(port, lldpctl_k_port_dot3_autoneg_advertised);
+ mautype = lldpctl_atom_get_int(port, lldpctl_k_port_dot3_mautype);
+ if (autoneg_support > 0 || autoneg_enabled > 0 || mautype > 0) {
+ tag_start(w, "auto-negotiation", "PMD autoneg");
+ tag_attr(w, "supported", "supported",
+ (autoneg_support > 0) ? "yes" : "no");
+ tag_attr(w, "enabled", "enabled",
+ (autoneg_enabled > 0) ? "yes" : "no");
+
+ if (autoneg_enabled > 0) {
+ if (autoneg_advertised < 0) autoneg_advertised = 0;
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_10BASE_T,
+ LLDP_DOT3_LINK_AUTONEG_10BASET_FD, "10Base-T");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD, "100Base-TX");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD, "100Base-T2");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
+ LLDP_DOT3_LINK_AUTONEG_100BASE_T4, "100Base-T4");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD, "1000Base-X");
+ display_autoneg(w, autoneg_advertised,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
+ LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD, "1000Base-T");
+ }
+ tag_datatag(w, "current", "MAU oper type",
+ lldpctl_atom_get_str(port, lldpctl_k_port_dot3_mautype));
+ tag_end(w);
+ }
+
+ lldpctl_atom_t *dot3_power =
+ lldpctl_atom_get(port, lldpctl_k_port_dot3_power);
+ int devicetype =
+ lldpctl_atom_get_int(dot3_power, lldpctl_k_dot3_power_devicetype);
+ if (devicetype > 0) {
+ tag_start(w, "power", "MDI Power");
+ tag_attr(w, "supported", "Supported",
+ (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_supported) > 0) ?
+ "yes" :
+ "no");
+ tag_attr(w, "enabled", "Enabled",
+ (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_enabled) > 0) ?
+ "yes" :
+ "no");
+ tag_attr(w, "paircontrol", "Pair control",
+ (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_paircontrol) > 0) ?
+ "yes" :
+ "no");
+ tag_start(w, "device-type", "Device type");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_devicetype));
+ ;
+ tag_end(w);
+ tag_start(w, "pairs", "Power pairs");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pairs));
+ tag_end(w);
+ tag_start(w, "class", "Class");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_class));
+ tag_end(w);
+
+ /* 802.3at */
+ if (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_type) >
+ LLDP_DOT3_POWER_8023AT_OFF) {
+ tag_start(w, "power-type", "Power type");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_type));
+ tag_end(w);
+
+ tag_start(w, "source", "Power Source");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_source));
+ tag_end(w);
+
+ tag_start(w, "priority", "Power Priority");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_priority));
+ tag_end(w);
+
+ tag_start(w, "requested", "PD requested power Value");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_requested));
+ tag_end(w);
+
+ tag_start(w, "allocated", "PSE allocated power Value");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_allocated));
+ tag_end(w);
+ }
+
+ /* 802.3bt */
+ if (lldpctl_atom_get_int(dot3_power,
+ lldpctl_k_dot3_power_type_ext) >
+ LLDP_DOT3_POWER_8023BT_OFF) {
+ tag_start(w, "requested-a", "Requested mode A");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_requested_a));
+ tag_end(w);
+ tag_start(w, "requested-b", "Requested mode B");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_requested_b));
+ tag_end(w);
+ tag_start(w, "allocated-a", "Allocated alternative A");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_allocated_a));
+ tag_end(w);
+ tag_start(w, "allocated-b", "Allocated alternative B");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_allocated_b));
+ tag_end(w);
+ tag_start(w, "pse-powering-status",
+ "PSE powering status");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pse_status));
+ tag_end(w);
+ tag_start(w, "pd-powering-status",
+ "PD powering status");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pd_status));
+ tag_end(w);
+ tag_start(w, "power-pairs-ext", "Power pairs extra");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pse_pairs_ext));
+ tag_end(w);
+ tag_start(w, "power-class-ext-a", "Class extra A");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_class_a));
+ tag_end(w);
+ tag_start(w, "power-class-ext-b", "Class extra B");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_class_b));
+ tag_end(w);
+ tag_start(w, "power-class-ext", "Class extra");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_class_ext));
+ tag_end(w);
+ tag_start(w, "power-type-ext", "Power type extra");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_type_ext));
+ tag_end(w);
+ tag_start(w, "pd-load", "PD load");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pd_load));
+ tag_end(w);
+ tag_start(w, "max-power",
+ "PSE maximum available power");
+ tag_data(w,
+ lldpctl_atom_get_str(dot3_power,
+ lldpctl_k_dot3_power_pse_max));
+ tag_end(w);
+ }
+
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(dot3_power);
+ }
+
+ tag_end(w);
+}
+
+static void
+display_local_ttl(struct writer *w, lldpctl_conn_t *conn, int details)
+{
+ char *ttl;
+ long int tx_hold;
+ long int tx_interval;
+
+ lldpctl_atom_t *configuration;
+ configuration = lldpctl_get_configuration(conn);
+ if (!configuration) {
+ log_warnx("lldpctl", "not able to get configuration. %s",
+ lldpctl_last_strerror(conn));
+ return;
+ }
+
+ tx_hold = lldpctl_atom_get_int(configuration, lldpctl_k_config_tx_hold);
+ tx_interval =
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_tx_interval_ms);
+
+ tx_interval = (tx_interval * tx_hold + 999) / 1000;
+
+ if (asprintf(&ttl, "%lu", tx_interval) == -1) {
+ log_warnx("lldpctl", "not enough memory to build TTL.");
+ goto end;
+ }
+
+ tag_start(w, "ttl", "TTL");
+ tag_attr(w, "ttl", "", ttl);
+ tag_end(w);
+ free(ttl);
+end:
+ lldpctl_atom_dec_ref(configuration);
+}
+
+static void
+display_vlans(struct writer *w, lldpctl_atom_t *port)
+{
+ lldpctl_atom_t *vlans, *vlan;
+ int foundpvid = 0;
+ int pvid, vid;
+
+ pvid = lldpctl_atom_get_int(port, lldpctl_k_port_vlan_pvid);
+
+ vlans = lldpctl_atom_get(port, lldpctl_k_port_vlans);
+ lldpctl_atom_foreach(vlans, vlan)
+ {
+ vid = lldpctl_atom_get_int(vlan, lldpctl_k_vlan_id);
+
+ tag_start(w, "vlan", "VLAN");
+ tag_attr(w, "vlan-id", "",
+ lldpctl_atom_get_str(vlan, lldpctl_k_vlan_id));
+ if (pvid == vid) {
+ tag_attr(w, "pvid", "pvid", "yes");
+ foundpvid = 1;
+ } else {
+ tag_attr(w, "pvid", "pvid", "no");
+ }
+ tag_data(w, lldpctl_atom_get_str(vlan, lldpctl_k_vlan_name));
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(vlans);
+
+ if (!foundpvid && pvid > 0) {
+ tag_start(w, "vlan", "VLAN");
+ tag_attr(w, "vlan-id", "",
+ lldpctl_atom_get_str(port, lldpctl_k_port_vlan_pvid));
+ tag_attr(w, "pvid", "pvid", "yes");
+ tag_end(w);
+ }
+}
+
+static void
+display_ppvids(struct writer *w, lldpctl_atom_t *port)
+{
+ lldpctl_atom_t *ppvids, *ppvid;
+ ppvids = lldpctl_atom_get(port, lldpctl_k_port_ppvids);
+ lldpctl_atom_foreach(ppvids, ppvid)
+ {
+ int status = lldpctl_atom_get_int(ppvid, lldpctl_k_ppvid_status);
+ tag_start(w, "ppvid", "PPVID");
+ if (lldpctl_atom_get_int(ppvid, lldpctl_k_ppvid_id) > 0)
+ tag_attr(w, "value", "",
+ lldpctl_atom_get_str(ppvid, lldpctl_k_ppvid_id));
+ tag_attr(w, "supported", "supported",
+ (status & LLDP_PPVID_CAP_SUPPORTED) ? "yes" : "no");
+ tag_attr(w, "enabled", "enabled",
+ (status & LLDP_PPVID_CAP_ENABLED) ? "yes" : "no");
+ tag_end(w);
+ }
+ lldpctl_atom_dec_ref(ppvids);
+}
+
+static void
+display_pids(struct writer *w, lldpctl_atom_t *port)
+{
+ lldpctl_atom_t *pids, *pid;
+ pids = lldpctl_atom_get(port, lldpctl_k_port_pis);
+ lldpctl_atom_foreach(pids, pid)
+ {
+ const char *pi = lldpctl_atom_get_str(pid, lldpctl_k_pi_id);
+ if (pi && strlen(pi) > 0) tag_datatag(w, "pi", "PI", pi);
+ }
+ lldpctl_atom_dec_ref(pids);
+}
+
+static const char *
+display_age(time_t lastchange)
+{
+ static char sage[30];
+ int age = (int)(time(NULL) - lastchange);
+ if (snprintf(sage, sizeof(sage), "%d day%s, %02d:%02d:%02d",
+ age / (60 * 60 * 24), (age / (60 * 60 * 24) > 1) ? "s" : "",
+ (age / (60 * 60)) % 24, (age / 60) % 60, age % 60) >= sizeof(sage))
+ return "too much";
+ else
+ return sage;
+}
+
+void
+display_local_chassis(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ int details)
+{
+ tag_start(w, "local-chassis", "Local chassis");
+
+ lldpctl_atom_t *chassis = lldpctl_get_local_chassis(conn);
+ display_chassis(w, chassis, details);
+ if (details == DISPLAY_DETAILS) {
+ display_med(w, NULL, chassis);
+ }
+ lldpctl_atom_dec_ref(chassis);
+
+ tag_end(w);
+}
+
+void
+display_interface(lldpctl_conn_t *conn, struct writer *w, int hidden,
+ lldpctl_atom_t *iface, lldpctl_atom_t *port, int details, int protocol)
+{
+ int local = 0;
+
+ if (!hidden && lldpctl_atom_get_int(port, lldpctl_k_port_hidden)) return;
+
+ /* user might have specified protocol to filter on display */
+ if ((protocol != LLDPD_MODE_MAX) &&
+ (protocol != lldpctl_atom_get_int(port, lldpctl_k_port_protocol)))
+ return;
+
+ /* Infer local / remote port from the port index (remote == 0) */
+ local = lldpctl_atom_get_int(port, lldpctl_k_port_index) > 0 ? 1 : 0;
+
+ lldpctl_atom_t *chassis = lldpctl_atom_get(port, lldpctl_k_port_chassis);
+
+ tag_start(w, "interface", "Interface");
+ tag_attr(w, "name", "", lldpctl_atom_get_str(iface, lldpctl_k_interface_name));
+ if (!local) {
+ tag_attr(w, "via", "via",
+ lldpctl_atom_get_str(port, lldpctl_k_port_protocol));
+ if (details > DISPLAY_BRIEF) {
+ tag_attr(w, "rid", "RID",
+ lldpctl_atom_get_str(chassis, lldpctl_k_chassis_index));
+ tag_attr(w, "age", "Time",
+ display_age(
+ lldpctl_atom_get_int(port, lldpctl_k_port_age)));
+ }
+ } else {
+ tag_datatag(w, "status", "Administrative status",
+ lldpctl_atom_get_str(port, lldpctl_k_port_status));
+ }
+
+ display_chassis(w, chassis, details);
+ display_port(w, port, details);
+ if (details && local && conn) display_local_ttl(w, conn, details);
+ if (details == DISPLAY_DETAILS) {
+ display_vlans(w, port);
+ display_ppvids(w, port);
+ display_pids(w, port);
+ display_med(w, port, chassis);
+ }
+
+ lldpctl_atom_dec_ref(chassis);
+
+ display_custom_tlvs(w, port);
+
+ tag_end(w);
+}
+
+/**
+ * Display information about interfaces.
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer.
+ * @param env Environment from which we may find the list of ports.
+ * @param hidden Whatever to show hidden ports.
+ * @param details Level of details we need (DISPLAY_*).
+ */
+void
+display_interfaces(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ int hidden, int details)
+{
+ lldpctl_atom_t *iface;
+ int protocol = LLDPD_MODE_MAX;
+ const char *proto_str;
+
+ /* user might have specified protocol to filter display results */
+ proto_str = cmdenv_get(env, "protocol");
+
+ if (proto_str) {
+ log_debug("display", "filter protocol: %s ", proto_str);
+
+ protocol = 0;
+ for (lldpctl_map_t *protocol_map =
+ lldpctl_key_get_map(lldpctl_k_port_protocol);
+ protocol_map->string; protocol_map++) {
+ if (!strcasecmp(proto_str, protocol_map->string)) {
+ protocol = protocol_map->value;
+ break;
+ }
+ }
+ }
+
+ tag_start(w, "lldp", "LLDP neighbors");
+ while ((iface = cmd_iterate_on_interfaces(conn, env))) {
+ lldpctl_atom_t *port;
+ lldpctl_atom_t *neighbors;
+ lldpctl_atom_t *neighbor;
+ port = lldpctl_get_port(iface);
+ neighbors = lldpctl_atom_get(port, lldpctl_k_port_neighbors);
+ lldpctl_atom_foreach(neighbors, neighbor)
+ {
+ display_interface(conn, w, hidden, iface, neighbor, details,
+ protocol);
+ }
+ lldpctl_atom_dec_ref(neighbors);
+ lldpctl_atom_dec_ref(port);
+ }
+ tag_end(w);
+}
+
+/**
+ * Display information about local interfaces.
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer.
+ * @param hidden Whatever to show hidden ports.
+ * @param env Environment from which we may find the list of ports.
+ * @param details Level of details we need (DISPLAY_*).
+ */
+void
+display_local_interfaces(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
+ int hidden, int details)
+{
+ lldpctl_atom_t *iface;
+ int protocol = LLDPD_MODE_MAX;
+
+ tag_start(w, "lldp", "LLDP interfaces");
+ while ((iface = cmd_iterate_on_interfaces(conn, env))) {
+ lldpctl_atom_t *port;
+ port = lldpctl_get_port(iface);
+ display_interface(conn, w, hidden, iface, port, details, protocol);
+ lldpctl_atom_dec_ref(port);
+ }
+ tag_end(w);
+}
+
+static void
+display_stat(struct writer *w, const char *tag, const char *descr,
+ long unsigned int cnt)
+{
+ char buf[20] = {};
+
+ tag_start(w, tag, descr);
+ snprintf(buf, sizeof(buf), "%lu", cnt);
+ tag_attr(w, tag, "", buf);
+ tag_end(w);
+}
+
+void
+display_interface_stats(lldpctl_conn_t *conn, struct writer *w, lldpctl_atom_t *port)
+{
+ tag_start(w, "interface", "Interface");
+ tag_attr(w, "name", "", lldpctl_atom_get_str(port, lldpctl_k_port_name));
+
+ display_stat(w, "tx", "Transmitted",
+ lldpctl_atom_get_int(port, lldpctl_k_tx_cnt));
+ display_stat(w, "rx", "Received", lldpctl_atom_get_int(port, lldpctl_k_rx_cnt));
+
+ display_stat(w, "rx_discarded_cnt", "Discarded",
+ lldpctl_atom_get_int(port, lldpctl_k_rx_discarded_cnt));
+
+ display_stat(w, "rx_unrecognized_cnt", "Unrecognized",
+ lldpctl_atom_get_int(port, lldpctl_k_rx_unrecognized_cnt));
+
+ display_stat(w, "ageout_cnt", "Ageout",
+ lldpctl_atom_get_int(port, lldpctl_k_ageout_cnt));
+
+ display_stat(w, "insert_cnt", "Inserted",
+ lldpctl_atom_get_int(port, lldpctl_k_insert_cnt));
+
+ display_stat(w, "delete_cnt", "Deleted",
+ lldpctl_atom_get_int(port, lldpctl_k_delete_cnt));
+
+ tag_end(w);
+}
+
+/**
+ * Display interface stats
+ *
+ * @param conn Connection to lldpd.
+ * @param w Writer.
+ * @param env Environment from which we may find the list of ports.
+ */
+void
+display_interfaces_stats(lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env)
+{
+ lldpctl_atom_t *iface;
+ int summary = 0;
+ u_int64_t h_tx_cnt = 0;
+ u_int64_t h_rx_cnt = 0;
+ u_int64_t h_rx_discarded_cnt = 0;
+ u_int64_t h_rx_unrecognized_cnt = 0;
+ u_int64_t h_ageout_cnt = 0;
+ u_int64_t h_insert_cnt = 0;
+ u_int64_t h_delete_cnt = 0;
+
+ if (cmdenv_get(env, "summary")) summary = 1;
+
+ tag_start(w, "lldp", (summary ? "LLDP Global statistics" : "LLDP statistics"));
+ while ((iface = cmd_iterate_on_interfaces(conn, env))) {
+ lldpctl_atom_t *port;
+ port = lldpctl_get_port(iface);
+ if (!summary)
+ display_interface_stats(conn, w, port);
+ else {
+ h_tx_cnt += lldpctl_atom_get_int(port, lldpctl_k_tx_cnt);
+ h_rx_cnt += lldpctl_atom_get_int(port, lldpctl_k_rx_cnt);
+ h_rx_discarded_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_rx_discarded_cnt);
+ h_rx_unrecognized_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_rx_unrecognized_cnt);
+ h_ageout_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_ageout_cnt);
+ h_insert_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_insert_cnt);
+ h_delete_cnt +=
+ lldpctl_atom_get_int(port, lldpctl_k_delete_cnt);
+ }
+ lldpctl_atom_dec_ref(port);
+ }
+
+ if (summary) {
+ tag_start(w, "summary", "Summary of stats");
+ display_stat(w, "tx", "Transmitted", h_tx_cnt);
+ display_stat(w, "rx", "Received", h_rx_cnt);
+ display_stat(w, "rx_discarded_cnt", "Discarded", h_rx_discarded_cnt);
+
+ display_stat(w, "rx_unrecognized_cnt", "Unrecognized",
+ h_rx_unrecognized_cnt);
+
+ display_stat(w, "ageout_cnt", "Ageout", h_ageout_cnt);
+
+ display_stat(w, "insert_cnt", "Inserted", h_insert_cnt);
+
+ display_stat(w, "delete_cnt", "Deleted", h_delete_cnt);
+ tag_end(w);
+ }
+ tag_end(w);
+}
+
+static const char *
+N(const char *str)
+{
+ if (str == NULL || strlen(str) == 0) return "(none)";
+ return str;
+}
+
+void
+display_configuration(lldpctl_conn_t *conn, struct writer *w)
+{
+ lldpctl_atom_t *configuration;
+
+ configuration = lldpctl_get_configuration(conn);
+ if (!configuration) {
+ log_warnx("lldpctl", "not able to get configuration. %s",
+ lldpctl_last_strerror(conn));
+ return;
+ }
+
+ tag_start(w, "configuration", "Global configuration");
+ tag_start(w, "config", "Configuration");
+
+ tag_datatag(w, "tx-delay", "Transmit delay",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_interval));
+ tag_datatag(w, "tx-delay-ms", "Transmit delay in milliseconds",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_interval_ms));
+ tag_datatag(w, "tx-hold", "Transmit hold",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_tx_hold));
+ tag_datatag(w, "max-neighbors", "Maximum number of neighbors",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_max_neighbors));
+ tag_datatag(w, "rx-only", "Receive mode",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_receiveonly) ? "yes" :
+ "no");
+ tag_datatag(w, "mgmt-pattern", "Pattern for management addresses",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_mgmt_pattern)));
+ tag_datatag(w, "iface-pattern", "Interface pattern",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_iface_pattern)));
+ tag_datatag(w, "perm-iface-pattern", "Permanent interface pattern",
+ N(lldpctl_atom_get_str(configuration,
+ lldpctl_k_config_perm_iface_pattern)));
+ tag_datatag(w, "cid-pattern", "Interface pattern for chassis ID",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_cid_pattern)));
+ tag_datatag(w, "cid-string", "Override chassis ID with",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_cid_string)));
+ tag_datatag(w, "description", "Override description with",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_description)));
+ tag_datatag(w, "platform", "Override platform with",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_platform)));
+ tag_datatag(w, "hostname", "Override system name with",
+ N(lldpctl_atom_get_str(configuration, lldpctl_k_config_hostname)));
+ tag_datatag(w, "capabilities", "Override system capabilities",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_chassis_cap_override) ?
+ "yes" :
+ "no");
+ tag_datatag(w, "advertise-version", "Advertise version",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_advertise_version) ?
+ "yes" :
+ "no");
+ tag_datatag(w, "ifdescr-update", "Update interface descriptions",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_ifdescr_update) ?
+ "yes" :
+ "no");
+ tag_datatag(w, "iface-promisc", "Promiscuous mode on managed interfaces",
+ lldpctl_atom_get_int(configuration, lldpctl_k_config_iface_promisc) ?
+ "yes" :
+ "no");
+ tag_datatag(w, "lldpmed-no-inventory", "Disable LLDP-MED inventory",
+ (lldpctl_atom_get_int(configuration,
+ lldpctl_k_config_lldpmed_noinventory) == 0) ?
+ "no" :
+ "yes");
+ tag_datatag(w, "lldpmed-faststart", "LLDP-MED fast start mechanism",
+ (lldpctl_atom_get_int(configuration, lldpctl_k_config_fast_start_enabled) ==
+ 0) ?
+ "no" :
+ "yes");
+ tag_datatag(w, "lldpmed-faststart-interval", "LLDP-MED fast start interval",
+ N(lldpctl_atom_get_str(configuration,
+ lldpctl_k_config_fast_start_interval)));
+ tag_datatag(w, "bond-slave-src-mac-type",
+ "Source MAC for LLDP frames on bond slaves",
+ lldpctl_atom_get_str(configuration,
+ lldpctl_k_config_bond_slave_src_mac_type));
+ tag_datatag(w, "lldp-portid-type", "Port ID TLV subtype for LLDP frames",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_lldp_portid_type));
+ tag_datatag(w, "lldp-agent-type", "Agent type",
+ lldpctl_atom_get_str(configuration, lldpctl_k_config_lldp_agent_type));
+
+ tag_end(w);
+ tag_end(w);
+
+ lldpctl_atom_dec_ref(configuration);
+}