The 802.1 and 802.3 standards list ways for communicating important PoE,
Maximum Frame Size, and other important link-level features that peers
may want to look at. This commit provides a port of the LLDPD project's
protocol decode for 802.1 and 802.3 TLVs of interest, preserving the
implementation as closely as possible to the source materials.

The supported decodes will allow decoding and displaying:

 * IEEE 802.1-2005 (Port VLAN and Protocol Configuration)
 * IEEE 802.3at (Power over Ethernet plus)
 * IEEE 802.3bt (4-pair PoE)

The commit supports displaying this data in human readable and json
formats.

Signed-off-by: Changliang Wu <[email protected]>
---
 NEWS                     |   2 +
 lib/lldp/lldp-const.h    |   5 +
 lib/lldp/lldp.c          | 169 ++++++++++++++++++++-
 lib/lldp/lldpd-structs.c |  33 +++++
 lib/lldp/lldpd-structs.h |  69 +++++++++
 lib/lldp/lldpd.c         |   5 +
 lib/ovs-lldp.c           | 312 +++++++++++++++++++++++++++++++++++++++
 7 files changed, 593 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 8259fdd2e..6643d6efd 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,8 @@ Post-v3.6.0
        to modify the database data through this transaction.
    - ovs-vsctl:
      * Added 'lldp/neighbor' command that displays lldp neighbor information.
+   - lldp:
+     * Added support for decoding dot1 and dot3 TLVs.
 
 
 v3.6.0 - 18 Aug 2025
diff --git a/lib/lldp/lldp-const.h b/lib/lldp/lldp-const.h
index 8c5c0733e..e03c77f71 100644
--- a/lib/lldp/lldp-const.h
+++ b/lib/lldp/lldp-const.h
@@ -98,6 +98,11 @@
 #define LLDP_DOT3_POWER_8023AT_TYPE1 1
 #define LLDP_DOT3_POWER_8023AT_TYPE2 2
 
+/* 802.3bt additions */
+#define LLDP_DOT3_POWER_8023BT_OFF 0
+#define LLDP_DOT3_POWER_8023BT_TYPE3 1
+#define LLDP_DOT3_POWER_8023BT_TYPE4 2
+
 /* Dot3 power source */
 #define LLDP_DOT3_POWER_SOURCE_UNKNOWN 0
 #define LLDP_DOT3_POWER_SOURCE_PRIMARY 1
diff --git a/lib/lldp/lldp.c b/lib/lldp/lldp.c
index 86e0381d6..5ba18d750 100644
--- a/lib/lldp/lldp.c
+++ b/lib/lldp/lldp.c
@@ -18,6 +18,8 @@
  */
 
 #include <config.h>
+#include "lldp-const.h"
+#include "lldp-tlv.h"
 #include "lldpd.h"
 #include <errno.h>
 #include <time.h>
@@ -373,6 +375,12 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int 
s,
     void *b;
     struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map = NULL;
     u_int8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH];
+
+    struct lldpd_vlan *vlan = NULL;
+    struct lldpd_pi *pi = NULL;
+    struct lldpd_ppvid *ppvid;
+    int vlan_len;
+
     struct lldpd_mgmt *mgmt;
     u_int8_t addr_str_length, addr_str_buffer[32];
     u_int8_t addr_family, addr_length, *addr_ptr, iface_subtype;
@@ -385,6 +393,9 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int 
s,
 
     port = xzalloc(sizeof *port);
     ovs_list_init(&port->p_isid_vlan_maps);
+    ovs_list_init(&port->p_vlans);
+    ovs_list_init(&port->p_ppvids);
+    ovs_list_init(&port->p_pids);
 
     length = s;
     pos = (u_int8_t*) frame;
@@ -569,9 +580,163 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, 
int s,
             PEEK_BYTES(orgid, sizeof orgid);
             tlv_subtype = PEEK_UINT8;
             if (memcmp(dot1, orgid, sizeof orgid) == 0) {
-                hardware->h_rx_unrecognized_cnt++;
+                switch (tlv_subtype) {
+                case LLDP_TLV_DOT1_VLANNAME:
+                    CHECK_TLV_SIZE(7, "VLAN");
+                    vlan = xzalloc(sizeof *vlan);
+                    vlan->v_vid = PEEK_UINT16;
+                    vlan_len = PEEK_UINT8;
+                    CHECK_TLV_SIZE(7 + vlan_len, "VLAN");
+                    vlan->v_name = xzalloc(vlan_len + 1);
+                    PEEK_BYTES(vlan->v_name, vlan_len);
+                    ovs_list_push_back(&port->p_vlans, &vlan->v_entries);
+                    vlan = NULL;
+                    break;
+
+                case LLDP_TLV_DOT1_PVID:
+                    CHECK_TLV_SIZE(6, "PVID");
+                    port->p_pvid = PEEK_UINT16;
+                    break;
+
+                case LLDP_TLV_DOT1_PPVID:
+                    CHECK_TLV_SIZE(7, "PPVID");
+                    /* Validation needed. */
+                    /* PPVID has to be unique if more than
+                       one PPVID TLVs are received  -
+                       discard if duplicate. */
+                    /* If support bit is not set and
+                       enabled bit is set - PPVID TLV is
+                       considered error and discarded. */
+                    ppvid = xzalloc(sizeof *ppvid);
+                    ppvid->p_cap_status = PEEK_UINT8;
+                    ppvid->p_ppvid = PEEK_UINT16;
+                    if (ppvid->p_ppvid > 4096) {
+                        VLOG_WARN("PPVID value %d invalid on %s",
+                                  ppvid->p_ppvid, hardware->h_ifname);
+                        free(ppvid);
+                    } else {
+                        ovs_list_push_back(&port->p_ppvids, &ppvid->p_entries);
+                    }
+                    break;
+
+                case LLDP_TLV_DOT1_PI:
+                    /* Validation needed. */
+                    /* PI has to be unique if more than
+                       one PI TLVs are received  - discard
+                       if duplicate ?? */
+                    pi = xzalloc(sizeof *pi);
+                    pi->p_pi_len = PEEK_UINT8;
+                    CHECK_TLV_SIZE(5 + pi->p_pi_len, "PI");
+                    pi->p_pi = xzalloc(pi->p_pi_len);
+                    PEEK_BYTES(pi->p_pi, pi->p_pi_len);
+                    ovs_list_push_back(&port->p_pids, &pi->p_entries);
+                    pi = NULL;
+                    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(9, "MAC/PHY");
+                    port->p_macphy.autoneg_support = PEEK_UINT8;
+                    port->p_macphy.autoneg_enabled =
+                        (port->p_macphy.autoneg_support & 0x2) >> 1;
+                    port->p_macphy.autoneg_support =
+                        port->p_macphy.autoneg_support & 0x1;
+                    port->p_macphy.autoneg_advertised = PEEK_UINT16;
+                    port->p_macphy.mau_type = PEEK_UINT16;
+                    break;
+
+                case LLDP_TLV_DOT3_LA:
+                    CHECK_TLV_SIZE(9, "Link aggregation");
+                    PEEK_DISCARD_UINT8;
+                    port->p_aggregid = PEEK_UINT32;
+                    break;
+
+                case LLDP_TLV_DOT3_MFS:
+                    CHECK_TLV_SIZE(6, "MFS");
+                    port->p_mfs = PEEK_UINT16;
+                    break;
+
+                case LLDP_TLV_DOT3_POWER:
+                    CHECK_TLV_SIZE(7, "Power");
+                    port->p_power.devicetype = PEEK_UINT8;
+                    port->p_power.supported =
+                        (port->p_power.devicetype & 0x2) >> 1;
+                    port->p_power.enabled = (port->p_power.devicetype & 0x4) >>
+                                            2;
+                    port->p_power.paircontrol =
+                        (port->p_power.devicetype & 0x8) >> 3;
+                    port->p_power.devicetype = (port->p_power.devicetype & 0x1)
+                                                   ? LLDP_DOT3_POWER_PSE
+                                                   : LLDP_DOT3_POWER_PD;
+                    port->p_power.pairs = PEEK_UINT8;
+                    port->p_power.class = PEEK_UINT8;
+                    /* 802.3at? */
+                    if (tlv_size >= 12) {
+                        port->p_power.powertype = PEEK_UINT8;
+                        port->p_power.source =
+                            (port->p_power.powertype & (1 << 5 | 1 << 4)) >> 4;
+                        port->p_power.priority =
+                            (port->p_power.powertype & (1 << 1 | 1 << 0));
+                        port->p_power.powertype =
+                            (port->p_power.powertype & (1 << 7))
+                                ? LLDP_DOT3_POWER_8023AT_TYPE1
+                                : LLDP_DOT3_POWER_8023AT_TYPE2;
+                        port->p_power.requested = PEEK_UINT16;
+                        port->p_power.allocated = PEEK_UINT16;
+                    } else {
+                        port->p_power.powertype = LLDP_DOT3_POWER_8023AT_OFF;
+                    }
+                    /* 802.3bt? */
+                    if (tlv_size >= 29) {
+                        port->p_power.requested_a = PEEK_UINT16;
+                        port->p_power.requested_b = PEEK_UINT16;
+                        port->p_power.allocated_a = PEEK_UINT16;
+                        port->p_power.allocated_b = PEEK_UINT16;
+                        port->p_power.pse_status = PEEK_UINT16;
+                        port->p_power.pd_status =
+                            (port->p_power.pse_status & (1 << 13 | 1 << 12)) >>
+                            12;
+                        port->p_power.pse_pairs_ext =
+                            (port->p_power.pse_status & (1 << 11 | 1 << 10)) >>
+                            10;
+                        port->p_power.class_a = (port->p_power.pse_status &
+                                                 (1 << 9 | 1 << 8 | 1 << 7)) >>
+                                                7;
+                        port->p_power.class_b = (port->p_power.pse_status &
+                                                 (1 << 6 | 1 << 5 | 1 << 4)) >>
+                                                4;
+                        port->p_power.class_ext =
+                            (port->p_power.pse_status & 0xf);
+                        port->p_power.pse_status =
+                            (port->p_power.pse_status & (1 << 15 | 1 << 14)) >>
+                            14;
+                        port->p_power.type_ext = PEEK_UINT8;
+                        port->p_power.pd_load = (port->p_power.type_ext & 0x1);
+                        port->p_power.type_ext =
+                            ((port->p_power.type_ext &
+                              (1 << 3 | 1 << 2 | 1 << 1)) +
+                             1);
+                        port->p_power.pse_max = PEEK_UINT16;
+                    } else {
+                        port->p_power.type_ext = LLDP_DOT3_POWER_8023BT_OFF;
+                    }
+                    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.c b/lib/lldp/lldpd-structs.c
index a8c7fad09..3a886e78d 100644
--- a/lib/lldp/lldpd-structs.c
+++ b/lib/lldp/lldpd-structs.c
@@ -113,6 +113,37 @@ lldpd_aa_maps_cleanup(struct lldpd_port *port)
     }
 }
 
+static void
+lldpd_dot13_lists_cleanup(struct lldpd_port *port)
+{
+    struct lldpd_ppvid *ppvid;
+    struct lldpd_vlan *vlan;
+    struct lldpd_pi *pid;
+    if (!ovs_list_is_empty(&port->p_vlans)) {
+        LIST_FOR_EACH_SAFE (vlan, v_entries, &port->p_vlans) {
+            ovs_list_remove(&vlan->v_entries);
+            free(vlan->v_name);
+            free(vlan);
+        }
+        ovs_list_init(&port->p_vlans);
+    }
+    if (!ovs_list_is_empty(&port->p_ppvids)) {
+        LIST_FOR_EACH_SAFE (ppvid, p_entries, &port->p_ppvids) {
+            ovs_list_remove(&ppvid->p_entries);
+            free(ppvid);
+        }
+        ovs_list_init(&port->p_ppvids);
+    }
+    if (!ovs_list_is_empty(&port->p_pids)) {
+        LIST_FOR_EACH_SAFE (pid, p_entries, &port->p_pids) {
+            ovs_list_remove(&pid->p_entries);
+            free(pid->p_pi);
+            free(pid);
+        }
+        ovs_list_init(&port->p_pids);
+    }
+}
+
 /* If `all' is true, clear all information, including information that
    are not refreshed periodically. Port should be freed manually. */
 void
@@ -127,6 +158,8 @@ lldpd_port_cleanup(struct lldpd_port *port, bool all)
     /* Cleanup auto-attach mappings */
     lldpd_aa_maps_cleanup(port);
 
+    lldpd_dot13_lists_cleanup(port);
+
     if (all) {
         free(port->p_lastframe);
         /* Chassis may not have been attributed, yet.*/
diff --git a/lib/lldp/lldpd-structs.h b/lib/lldp/lldpd-structs.h
index 9a83d5499..308242dd2 100644
--- a/lib/lldp/lldpd-structs.h
+++ b/lib/lldp/lldpd-structs.h
@@ -45,6 +45,63 @@ lldpd_af(int af)
     }
 }
 
+struct lldpd_ppvid {
+    struct ovs_list p_entries;
+    u_int8_t        p_cap_status;
+    u_int16_t       p_ppvid;
+};
+
+struct lldpd_vlan {
+    struct ovs_list v_entries;
+    char            *v_name;
+    u_int16_t       v_vid;
+};
+
+struct lldpd_pi {
+    struct ovs_list p_entries;
+    char            *p_pi;
+    u_int32_t       p_pi_len;
+};
+
+struct lldpd_dot3_macphy {
+    u_int8_t  autoneg_support;
+    u_int8_t  autoneg_enabled;
+    u_int16_t autoneg_advertised;
+    u_int16_t mau_type;
+};
+
+struct lldpd_dot3_power {
+    u_int8_t devicetype;
+    u_int8_t supported;
+    u_int8_t enabled;
+    u_int8_t paircontrol;
+    u_int8_t pairs;
+    u_int8_t class;
+    u_int8_t powertype; /* If set to LLDP_DOT3_POWER_8023AT_OFF,
+                         * following fields have no meaning. */
+    u_int8_t  source;
+    u_int8_t  priority;
+    u_int16_t requested;
+    u_int16_t allocated;
+
+    /* For 802.3BT */
+    u_int8_t  pd_4pid;
+    u_int16_t requested_a;
+    u_int16_t requested_b;
+    u_int16_t allocated_a;
+    u_int16_t allocated_b;
+    u_int16_t pse_status;
+    u_int8_t  pd_status;
+    u_int8_t  pse_pairs_ext;
+    u_int8_t  class_a;
+    u_int8_t  class_b;
+    u_int8_t  class_ext;
+    u_int8_t  type_ext;
+    u_int8_t  pd_load;
+    u_int16_t pse_max;
+};
+
+
 #define LLDPD_MGMT_MAXADDRSIZE 16 /* sizeof(struct in6_addr) */
 struct lldpd_mgmt {
     struct ovs_list m_entries;
@@ -98,6 +155,18 @@ struct lldpd_port {
     char                 *p_descr;
     u_int16_t            p_mfs;
     struct lldpd_aa_element_tlv        p_element;
+
+    /* Dot3 stuff */
+    u_int32_t                p_aggregid;
+    struct lldpd_dot3_macphy p_macphy;
+    struct lldpd_dot3_power  p_power;
+
+    /* Dot1 stuff */
+    u_int16_t       p_pvid;
+    struct ovs_list p_vlans; /* Contains "struct lldpd_vlan"s. */
+    struct ovs_list p_ppvids; /* Contains "struct lldpd_ppvid"s. */
+    struct ovs_list p_pids;    /* Contains "struct lldpd_pi"s. */
+
     struct ovs_list p_isid_vlan_maps; /* Contains "struct 
lldpd_aa_isid_vlan_maps_tlv"s. */
 };
 
diff --git a/lib/lldp/lldpd.c b/lib/lldp/lldpd.c
index 4bff7b017..dd9ba9925 100644
--- a/lib/lldp/lldpd.c
+++ b/lib/lldp/lldpd.c
@@ -90,6 +90,11 @@ lldpd_alloc_hardware(struct lldpd *cfg, const char *name, 
int index)
     hw->h_lport.p_chassis = CONTAINER_OF(ovs_list_front(&cfg->g_chassis),
                                          struct lldpd_chassis, list);
     hw->h_lport.p_chassis->c_refcount++;
+    ovs_list_init(&hw->h_lport.p_vlans);
+    ovs_list_init(&hw->h_lport.p_ppvids);
+    ovs_list_init(&hw->h_lport.p_pids);
+    ovs_list_init(&hw->h_lport.p_isid_vlan_maps);
+
     ovs_list_init(&hw->h_rports);
 
     return hw;
diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
index d85845ba6..f755fb180 100644
--- a/lib/ovs-lldp.c
+++ b/lib/ovs-lldp.c
@@ -364,6 +364,312 @@ lldp_get_network_addr_string(char *port_id)
     return ipaddress;
 }
 
+static void
+lldp_print_neighbor_port_dot1(struct ds *ds, struct lldpd_port *port)
+{
+    struct lldpd_ppvid *ppvid;
+    struct lldpd_vlan *vlan;
+    bool foundpvid = false;
+    struct lldpd_pi *pid;
+
+    LIST_FOR_EACH (vlan, v_entries, &port->p_vlans) {
+        ds_put_format(ds, "  %-20s%d, %s", "VLAN:", vlan->v_vid,
+                      port->p_pvid == vlan->v_vid ? "pvid: yes" : "pvid: no");
+        if (port->p_pvid == vlan->v_vid) {
+            foundpvid = true;
+        }
+        if (vlan->v_name) {
+            ds_put_format(ds, ", %s", vlan->v_name);
+        }
+        ds_put_format(ds, "\n");
+    }
+    if (!foundpvid && port->p_pvid > 0) {
+        ds_put_format(ds, "  %-20s%d, %s\n", "VLAN:", port->p_pvid,
+                      "pvid: yes");
+    }
+
+    LIST_FOR_EACH (ppvid, p_entries, &port->p_ppvids) {
+        ds_put_format(ds, "  %-20s%d, ", "PPVID:", ppvid->p_ppvid);
+        ds_put_format(
+            ds, "supported: %s,enabled %s\n",
+            (ppvid->p_cap_status & LLDP_PPVID_CAP_SUPPORTED) ? "yes" : "no",
+            (ppvid->p_cap_status & LLDP_PPVID_CAP_ENABLED) ? "yes" : "no");
+    }
+
+    LIST_FOR_EACH (pid, p_entries, &port->p_pids) {
+        if (pid->p_pi && pid->p_pi_len > 0) {
+            ds_put_format(ds, "  %-20s", "PI:");
+            ds_put_buffer(ds, pid->p_pi, pid->p_pi_len);
+            ds_put_format(ds, "\n");
+        }
+    }
+}
+
+static void
+lldp_print_neighbor_port_dot1_json(struct json *neighbor_item_json,
+                                   struct lldpd_port *port)
+{
+    struct json *ppvid_json_array = json_array_create_empty();
+    struct json *vlan_json_array = json_array_create_empty();
+    struct json *pi_json_array = json_array_create_empty();
+    struct json *ppvid_json = NULL;
+    struct json *vlan_json = NULL;
+    struct json *pi_json = NULL;
+    bool foundpvid = false;
+
+    struct lldpd_ppvid *ppvid;
+    struct lldpd_vlan *vlan;
+    struct lldpd_pi *pid;
+
+    LIST_FOR_EACH (vlan, v_entries, &port->p_vlans) {
+        vlan_json = json_object_create();
+        json_object_put(vlan_json, "vlan-id",
+                        json_integer_create(vlan->v_vid));
+        json_object_put(vlan_json, "pvid",
+                        json_boolean_create(port->p_pvid == vlan->v_vid));
+        if (port->p_pvid == vlan->v_vid) {
+            foundpvid = true;
+        }
+        if (vlan->v_name) {
+            json_object_put(vlan_json, "value",
+                            json_string_create(vlan->v_name));
+        }
+        json_array_add(vlan_json_array, vlan_json);
+    }
+    if (!foundpvid && port->p_pvid > 0) {
+        vlan_json = json_object_create();
+        json_object_put(vlan_json, "vlan-id",
+                        json_integer_create(port->p_pvid));
+        json_object_put(vlan_json, "pvid", json_boolean_create(true));
+        json_array_add(vlan_json_array, vlan_json);
+    }
+    if (json_array_size(vlan_json_array) > 0) {
+        json_object_put(neighbor_item_json, "vlan", vlan_json_array);
+    }
+
+    LIST_FOR_EACH (ppvid, p_entries, &port->p_ppvids) {
+        ppvid_json = json_object_create();
+        if (ppvid->p_ppvid) {
+            json_object_put(ppvid_json, "ppvid",
+                            json_integer_create(ppvid->p_ppvid));
+        }
+        json_object_put(ppvid_json, "supported",
+                        json_boolean_create(ppvid->p_cap_status &
+                                            LLDP_PPVID_CAP_SUPPORTED));
+        json_object_put(ppvid_json, "enabled",
+                        json_boolean_create(ppvid->p_cap_status &
+                                            LLDP_PPVID_CAP_ENABLED));
+        json_array_add(ppvid_json_array, ppvid_json);
+    }
+    if (json_array_size(ppvid_json_array) > 0) {
+        json_object_put(neighbor_item_json, "ppvid", ppvid_json_array);
+    }
+
+    LIST_FOR_EACH (pid, p_entries, &port->p_pids) {
+        if (pid->p_pi && pid->p_pi_len > 0) {
+            pi_json = json_object_create();
+            struct ds pi_ds = DS_EMPTY_INITIALIZER;
+            ds_put_buffer(&pi_ds, pid->p_pi, pid->p_pi_len);
+            json_object_put(pi_json, "pi",
+                            json_string_create_nocopy(ds_steal_cstr(&pi_ds)));
+            json_array_add(pi_json_array, pi_json);
+        }
+    }
+    if (json_array_size(pi_json_array) > 0) {
+        json_object_put(neighbor_item_json, "pi", pi_json_array);
+    }
+}
+
+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, "    %-18s%s", "Adv:", 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_mfs) {
+        ds_put_format(ds, "  %-20s%d\n", "MFS:", port->p_mfs);
+    }
+
+    uint16_t autoneg_advertised = port->p_macphy.autoneg_advertised;
+    uint16_t autoneg_support = port->p_macphy.autoneg_support;
+    uint16_t autoneg_enabled = port->p_macphy.autoneg_enabled;
+    uint16_t mautype = port->p_macphy.mau_type;
+    if (autoneg_support || autoneg_enabled || mautype) {
+        ds_put_format(ds, "  PMD autoneg: supported: %s, enabled: %s\n",
+                      autoneg_support ? "yes" : "no",
+                      autoneg_enabled ? "yes" : "no");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_10BASE_T,
+                                     LLDP_DOT3_LINK_AUTONEG_10BASET_FD,
+                                     "10Base-T");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
+                                     LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
+                                     "100Base-T4");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
+                                     LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD,
+                                     "100Base-TX");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
+                                     LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD,
+                                     "100Base-T2");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
+                                     LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD,
+                                     "1000Base-X");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
+                                     LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD,
+                                     "1000Base-T");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE,
+                                     LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE,
+                                     "FDX_PAUSE");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE,
+                                     LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE,
+                                     "FDX_APAUSE");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE,
+                                     LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE,
+                                     "FDX_SPAUSE");
+        lldp_dot3_autoneg_advertised(ds, autoneg_advertised,
+                                     LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE,
+                                     LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE,
+                                     "FDX_BPAUSE");
+        ds_put_format(ds, "    %-18s%hu\n", "MAU oper type:", mautype);
+    }
+
+    if (port->p_power.devicetype) {
+        ds_put_format(ds,
+                      "  MDI Power: supported: %s, enabled: %s, pair "
+                      "control: %s\n",
+                      port->p_power.supported ? "yes" : "no",
+                      port->p_power.enabled ? "yes" : "no",
+                      port->p_power.paircontrol ? "yes" : "no");
+    }
+}
+
+static void
+lldp_print_neighbor_port_dot3_json(struct json *port_json,
+                                   struct lldpd_port *port)
+{
+    if (port->p_mfs) {
+        json_object_put(port_json, "mfs", json_integer_create(port->p_mfs));
+    }
+
+    uint16_t autoneg_advertised = port->p_macphy.autoneg_advertised;
+    uint16_t autoneg_support = port->p_macphy.autoneg_support;
+    uint16_t autoneg_enabled = port->p_macphy.autoneg_enabled;
+    uint16_t mautype = port->p_macphy.mau_type;
+    if (autoneg_support || autoneg_enabled || mautype) {
+        struct json *auto_nego_json = json_object_create();
+        struct json *advertised_json = NULL;
+
+        json_object_put(auto_nego_json, "supported",
+                        json_boolean_create(autoneg_support));
+        json_object_put(auto_nego_json, "enabled",
+                        json_boolean_create(autoneg_enabled));
+
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_10BASE_T,
+                                          LLDP_DOT3_LINK_AUTONEG_10BASET_FD,
+                                          "10Base-T");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
+                                          LLDP_DOT3_LINK_AUTONEG_100BASE_T4,
+                                          "100Base-T4");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
+                                          LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD,
+                                          "100Base-TX");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
+                                          LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD,
+                                          "100Base-T2");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
+                                          LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD,
+                                          "1000Base-X");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
+                                          LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD,
+                                          "1000Base-T");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE,
+                                          LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE,
+                                          "FDX_PAUSE");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE,
+                                          LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE,
+                                          "FDX_APAUSE");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE,
+                                          LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE,
+                                          "FDX_SPAUSE");
+        lldp_dot3_autoneg_advertised_json(&advertised_json, autoneg_advertised,
+                                          LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE,
+                                          LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE,
+                                          "FDX_BPAUSE");
+        json_object_put(auto_nego_json, "current",
+                        json_integer_create(mautype));
+
+        json_object_put(port_json, "auto-negotiation", auto_nego_json);
+    }
+
+    if (port->p_power.devicetype) {
+        struct json *power_json = json_object_create();
+
+        json_object_put(power_json, "supported",
+                        json_boolean_create(port->p_power.supported));
+        json_object_put(power_json, "enabled",
+                        json_boolean_create(port->p_power.enabled));
+        json_object_put(power_json, "paircontrol",
+                        json_boolean_create(port->p_power.paircontrol));
+        json_object_put(port_json, "power", power_json);
+    }
+}
+
 static void
 lldp_print_neighbor(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex)
 {
@@ -557,6 +863,9 @@ lldp_print_neighbor(struct ds *ds, struct lldp *lldp) 
OVS_REQUIRES(mutex)
                 ds_put_format(ds, "  %-20s%d\n",
                               "MgmtIface:", mgmt->m_iface);
             }
+
+            lldp_print_neighbor_port_dot3(ds, port);
+            lldp_print_neighbor_port_dot1(ds, port);
         }
     }
 }
@@ -817,6 +1126,9 @@ lldp_print_neighbor_json(struct json *neighbor_json, 
struct lldp *lldp)
             json_object_put(chassis_sys_json, "mgmt-iface",
                             chassis_mgmt_iface_json);
 
+            lldp_print_neighbor_port_dot1_json(neighbor_item_json, port);
+            lldp_print_neighbor_port_dot3_json(port_json, port);
+
             json_object_put(neighbor_item_json, "chassis", chassis_json);
             json_object_put(neighbor_item_json, "port", port_json);
 
-- 
2.43.5

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to