From: Patrik Danielsson Date: Tue, 17 Sep 2019 08:39:22 +0000 (+0200) Subject: lldp: Power via MDI TLV: Initial support for 802.3bt X-Git-Tag: 1.0.5~35^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7cfcd3b7efb961330b3d4aef1b078ed481272583;p=thirdparty%2Flldpd.git lldp: Power via MDI TLV: Initial support for 802.3bt Change-Id: Ie2cb521a2433cee64c91e5146050814276931125 --- diff --git a/NEWS b/NEWS index 9cacee82..5e4cf8d0 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ lldpd (1.0.5) is enabled. + Stricter on LLDP incoming frames validation. + Add support for VLAN-aware bridges for Linux (no range support). + + Add support for 802.3BT (no SNMP support). * Fix: + Don't clear chassis TLV on shutdown LLDPDU. diff --git a/src/client/display.c b/src/client/display.c index 02be0771..809c5db8 100644 --- a/src/client/display.c +++ b/src/client/display.c @@ -469,6 +469,63 @@ display_port(struct writer *w, lldpctl_atom_t *port, int details) 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); diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index dd4487ac..dfe71523 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -1008,6 +1008,8 @@ lldpd_dot3_power_pd_pse(struct lldpd_hardware *hardware) selected_port->p_power.allocated, hardware->h_lport.p_power.allocated); hardware->h_lport.p_power.allocated = selected_port->p_power.allocated; + hardware->h_lport.p_power.allocated_a = selected_port->p_power.allocated_a; + hardware->h_lport.p_power.allocated_b = selected_port->p_power.allocated_b; levent_schedule_pdu(hardware); } diff --git a/src/daemon/protocols/lldp.c b/src/daemon/protocols/lldp.c index 336cd600..2609d6d4 100644 --- a/src/daemon/protocols/lldp.c +++ b/src/daemon/protocols/lldp.c @@ -294,33 +294,59 @@ static int _lldp_send(struct lldpd *global, /* Power */ if (port->p_power.devicetype) { if (!( - POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + (port->p_power.type_ext != LLDP_DOT3_POWER_8023BT_OFF ? + (tlv = pos, POKE_UINT16((LLDP_TLV_ORG << 9) | (0x1d))): + POKE_START_LLDP_TLV(LLDP_TLV_ORG)) && POKE_BYTES(dot3, sizeof(dot3)) && POKE_UINT8(LLDP_TLV_DOT3_POWER) && POKE_UINT8(( - (((2 - port->p_power.devicetype) %(1<< 1))<<0) | - (( port->p_power.supported %(1<< 1))<<1) | - (( port->p_power.enabled %(1<< 1))<<2) | - (( port->p_power.paircontrol %(1<< 1))<<3))) && + (((2 - port->p_power.devicetype) %(1<< 1))<<0) | + (( port->p_power.supported %(1<< 1))<<1) | + (( port->p_power.enabled %(1<< 1))<<2) | + (( port->p_power.paircontrol %(1<< 1))<<3))) && POKE_UINT8(port->p_power.pairs) && POKE_UINT8(port->p_power.class))) goto toobig; /* 802.3at */ if (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) { if (!( - POKE_UINT8(( - (((port->p_power.powertype == + POKE_UINT8(((((port->p_power.powertype == LLDP_DOT3_POWER_8023AT_TYPE1)?1:0) << 7) | - (((port->p_power.devicetype == + (((port->p_power.devicetype == LLDP_DOT3_POWER_PSE)?0:1) << 6) | - ((port->p_power.source %(1<< 2))<<4) | - ((port->p_power.priority %(1<< 2))<<0))) && - POKE_UINT16(port->p_power.requested) && - POKE_UINT16(port->p_power.allocated))) + ((port->p_power.source %(1<< 2))<<4) | + ((port->p_power.pd_4pid %(1 << 1))<<2) | + ((port->p_power.priority %(1<< 2))<<0))) && + POKE_UINT16(port->p_power.requested) && + POKE_UINT16(port->p_power.allocated))) goto toobig; } - if (!(POKE_END_LLDP_TLV)) - goto toobig; + if (port->p_power.type_ext != LLDP_DOT3_POWER_8023BT_OFF) { + if (!( + POKE_UINT16(port->p_power.requested_a) && + POKE_UINT16(port->p_power.requested_b) && + POKE_UINT16(port->p_power.allocated_a) && + POKE_UINT16(port->p_power.allocated_b) && + POKE_UINT16(( + (port->p_power.pse_status << 14) | + (port->p_power.pd_status << 12) | + (port->p_power.pse_pairs_ext << 10) | + (port->p_power.class_a << 7) | + (port->p_power.class_b << 4) | + (port->p_power.class_ext << 0))) && + POKE_UINT8( + /* Adjust by -1 to enable 0 to mean no 802.3bt support */ + ((port->p_power.type_ext -1) << 1) | + (port->p_power.pd_load << 0)) && + POKE_UINT16(port->p_power.pse_max) && + /* Send 0 for autoclass and power down requests */ + POKE_UINT8(0) && + POKE_UINT16(0) && + POKE_UINT8(0))) + goto toobig; + } + if (!(POKE_END_LLDP_TLV)) + goto toobig; } #endif @@ -973,6 +999,35 @@ lldp_decode(struct lldpd *cfg, char *frame, int s, } 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: /* Unknown Dot3 TLV, ignore it */ diff --git a/src/lib/atoms/dot3.c b/src/lib/atoms/dot3.c index 19d62c46..97a959dd 100644 --- a/src/lib/atoms/dot3.c +++ b/src/lib/atoms/dot3.c @@ -77,6 +77,120 @@ static struct atom_map port_dot3_power_priority_map = { }, }; +static struct atom_map port_dot3_power_pd_4pid_map = { + .key = lldpctl_k_dot3_power_pd_4pid, + .map = { + { 0, "PD does not support powering both modes" }, + { 1, "PD supports powering both modes" }, + }, +}; + +static struct atom_map port_dot3_power_pse_status_map = { + .key = lldpctl_k_dot3_power_pse_status, + .map = { + { 0, "Unknown" }, + { 1, "2-pair powering" }, + { 2, "4-pair powering dual-signature PD" }, + { 3, "4-pair powering single-signature PD" }, + }, +}; + +static struct atom_map port_dot3_power_pd_status_map = { + .key = lldpctl_k_dot3_power_pd_status, + .map = { + { 0, "Unknown" }, + { 1, "2-pair powered PD" }, + { 2, "4-pair powered dual-signature PD" }, + { 3, "4-pair powered single-signature PD" }, + }, +}; + +static struct atom_map port_dot3_power_pse_pairs_ext_map = { + .key = lldpctl_k_dot3_power_pse_pairs_ext, + .map = { + { 0, "Unknown" }, + { 1, "Alternative A" }, + { 2, "Alternative B" }, + { 3, "Both alternatives" }, + }, +}; + +static struct atom_map port_dot3_power_class_a_map = { + .key = lldpctl_k_dot3_power_class_a, + .map = { + { 0, "Unknown" }, + { 1, "Class 1" }, + { 2, "Class 2" }, + { 3, "Class 3" }, + { 4, "Class 4" }, + { 5, "Class 5" }, + { 6, "Unknown" }, + { 7, "Single-signature PD or 2-pair only PSE" }, + }, +}; + +static struct atom_map port_dot3_power_class_b_map = { + .key = lldpctl_k_dot3_power_class_b, + .map = { + { 0, "Unknown" }, + { 1, "Class 1" }, + { 2, "Class 2" }, + { 3, "Class 3" }, + { 4, "Class 4" }, + { 5, "Class 5" }, + { 6, "Unknown" }, + { 7, "Single-signature PD or 2-pair only PSE" }, + }, +}; + +static struct atom_map port_dot3_power_class_ext_map = { + .key = lldpctl_k_dot3_power_class_ext, + .map = { + { 0, "Unknown" }, + { 1, "Class 1" }, + { 2, "Class 2" }, + { 3, "Class 3" }, + { 4, "Class 4" }, + { 5, "Class 5" }, + { 6, "Class 6" }, + { 7, "Class 7" }, + { 8, "Class 8" }, + { 9, "Unknown" }, + { 10, "Unknown" }, + { 11, "Unknown" }, + { 12, "Unknown" }, + { 13, "Unknown" }, + { 14, "Unknown" }, + { 15, "Dual-signature PD" }, + }, +}; + +static struct atom_map port_dot3_power_type_ext_map = { + .key = lldpctl_k_dot3_power_type_ext, + .map = { + { LLDP_DOT3_POWER_8023BT_OFF, "802.3bt off" }, + { 1, "Type 3 PSE" }, + { 2, "Type 4 PSE" }, + { 3, "Type 3 single-signature PD" }, + { 4, "Type 3 dual-signature PD" }, + { 5, "Type 4 single-signature PD" }, + { 6, "Type 4 dual-signature PD" }, + { 7, "Unknown" }, + { 8, "Unknown" }, + }, +}; + +static struct atom_map port_dot3_power_pd_load_map = { + .key = lldpctl_k_dot3_power_pd_load, + .map = { + { 0, "PD is single- or dual-signature and power on the modes is not " + "electrically isolated." }, + { 1, "PD is dual-signature and power on the modes is electrically " + "isolated" }, + }, +}; + + ATOM_MAP_REGISTER(port_dot3_power_pairs_map, 4); ATOM_MAP_REGISTER(port_dot3_power_class_map, 5); ATOM_MAP_REGISTER(port_dot3_power_priority_map, 6); @@ -125,6 +239,33 @@ _lldpctl_atom_get_str_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key) case lldpctl_k_dot3_power_priority: return map_lookup(port_dot3_power_priority_map.map, port->p_power.priority); + case lldpctl_k_dot3_power_pd_4pid: + return map_lookup(port_dot3_power_pd_4pid_map.map, + port->p_power.pd_4pid); + case lldpctl_k_dot3_power_pse_status: + return map_lookup(port_dot3_power_pse_status_map.map, + port->p_power.pse_status); + case lldpctl_k_dot3_power_pd_status: + return map_lookup(port_dot3_power_pd_status_map.map, + port->p_power.pd_status); + case lldpctl_k_dot3_power_pse_pairs_ext: + return map_lookup(port_dot3_power_pse_pairs_ext_map.map, + port->p_power.pse_pairs_ext); + case lldpctl_k_dot3_power_class_a: + return map_lookup(port_dot3_power_class_a_map.map, + port->p_power.class_a); + case lldpctl_k_dot3_power_class_b: + return map_lookup(port_dot3_power_class_b_map.map, + port->p_power.class_b); + case lldpctl_k_dot3_power_class_ext: + return map_lookup(port_dot3_power_class_ext_map.map, + port->p_power.class_ext); + case lldpctl_k_dot3_power_type_ext: + return map_lookup(port_dot3_power_type_ext_map.map, + port->p_power.type_ext); + case lldpctl_k_dot3_power_pd_load: + return map_lookup(port_dot3_power_pd_load_map.map, + port->p_power.pd_load); default: SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); return NULL; @@ -162,6 +303,35 @@ _lldpctl_atom_get_int_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key) return port->p_power.requested * 100; case lldpctl_k_dot3_power_allocated: return port->p_power.allocated * 100; + /* 802.3bt additions */ + case lldpctl_k_dot3_power_pd_4pid: + return port->p_power.pd_4pid; + case lldpctl_k_dot3_power_requested_a: + return port->p_power.requested_a * 100; + case lldpctl_k_dot3_power_requested_b: + return port->p_power.requested_b * 100; + case lldpctl_k_dot3_power_allocated_a: + return port->p_power.allocated_a * 100; + case lldpctl_k_dot3_power_allocated_b: + return port->p_power.allocated_b * 100; + case lldpctl_k_dot3_power_pse_status: + return port->p_power.pse_status; + case lldpctl_k_dot3_power_pd_status: + return port->p_power.pd_status; + case lldpctl_k_dot3_power_pse_pairs_ext: + return port->p_power.pse_pairs_ext; + case lldpctl_k_dot3_power_class_a: + return port->p_power.class_a; + case lldpctl_k_dot3_power_class_b: + return port->p_power.class_b; + case lldpctl_k_dot3_power_class_ext: + return port->p_power.class_ext; + case lldpctl_k_dot3_power_type_ext: + return port->p_power.type_ext; + case lldpctl_k_dot3_power_pd_load: + return port->p_power.pd_load; + case lldpctl_k_dot3_power_pse_max: + return port->p_power.pse_max * 100; default: return SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); } @@ -260,6 +430,49 @@ _lldpctl_atom_set_int_dot3_power(lldpctl_atom_t *atom, lldpctl_key_t key, if (value < 0) goto bad; port->p_power.requested = value / 100; return atom; + /* 802.3bt additions */ + case lldpctl_k_dot3_power_pd_4pid: + port->p_power.pd_4pid = value; + return atom; + case lldpctl_k_dot3_power_requested_a: + port->p_power.requested_a = value / 100; + return atom; + case lldpctl_k_dot3_power_requested_b: + port->p_power.requested_b = value / 100; + return atom; + case lldpctl_k_dot3_power_allocated_a: + port->p_power.allocated_a = value / 100; + return atom; + case lldpctl_k_dot3_power_allocated_b: + port->p_power.allocated_b = value / 100; + return atom; + case lldpctl_k_dot3_power_pse_status: + port->p_power.pse_status = value; + return atom; + case lldpctl_k_dot3_power_pd_status: + port->p_power.pd_status = value; + return atom; + case lldpctl_k_dot3_power_pse_pairs_ext: + port->p_power.pse_pairs_ext = value; + return atom; + case lldpctl_k_dot3_power_class_a: + port->p_power.class_a = value; + return atom; + case lldpctl_k_dot3_power_class_b: + port->p_power.class_b = value; + return atom; + case lldpctl_k_dot3_power_class_ext: + port->p_power.class_ext = value; + return atom; + case lldpctl_k_dot3_power_type_ext: + port->p_power.type_ext = value; + return atom; + case lldpctl_k_dot3_power_pd_load: + port->p_power.pd_load = value; + return atom; + case lldpctl_k_dot3_power_pse_max: + port->p_power.pse_max = value / 100; + return atom; default: SET_ERROR(atom->conn, LLDPCTL_ERR_NOT_EXIST); return NULL; diff --git a/src/lib/lldpctl.h b/src/lib/lldpctl.h index 5222cb00..9d656f16 100644 --- a/src/lib/lldpctl.h +++ b/src/lib/lldpctl.h @@ -711,6 +711,22 @@ typedef enum { lldpctl_k_dot3_power_allocated, /**< `(I,W)` 802.3AT power allocated */ lldpctl_k_dot3_power_requested, /**< `(I,W)` 802.3AT power requested */ + /* 802.3bt additions */ + lldpctl_k_dot3_power_pd_4pid, /**< `(IS,W)` 802.3BT both modes supported? */ + lldpctl_k_dot3_power_requested_a, /**< `(I,W)` 802.3BT power value requested for A */ + lldpctl_k_dot3_power_requested_b, /**< `(I,W)` 802.3BT power value requested for B */ + lldpctl_k_dot3_power_allocated_a, /**< `(I,W)` 802.3BT power value allocated for A */ + lldpctl_k_dot3_power_allocated_b, /**< `(I,W)` 802.3BT power value allocated for B */ + lldpctl_k_dot3_power_pse_status, /**< `(IS,W)` 802.3BT PSE powering status */ + lldpctl_k_dot3_power_pd_status, /**< `(IS,W)` 802.3BT PD powering status */ + lldpctl_k_dot3_power_pse_pairs_ext, /**< `(IS,W)` 802.3BT PSE power pairs */ + lldpctl_k_dot3_power_class_a, /**< `(IS,W)` 802.3BT power class for A */ + lldpctl_k_dot3_power_class_b, /**< `(IS,W)` 802.3BT power class for B */ + lldpctl_k_dot3_power_class_ext, /**< `(IS,W)` 802.3BT power class */ + lldpctl_k_dot3_power_type_ext, /**< `(IS,W)` 802.3BT power type */ + lldpctl_k_dot3_power_pd_load, /**< `(IS,W)` 802.3BT dualsig isolated? */ + lldpctl_k_dot3_power_pse_max, /**< `(I,W)` 802.3BT maximum available power */ + lldpctl_k_port_vlan_pvid = 1500, /**< `(I)` Primary VLAN ID */ lldpctl_k_port_vlans, /**< `(AL)` List of VLAN */ lldpctl_k_vlan_id, /**< `(I)` VLAN ID */ diff --git a/src/lldp-const.h b/src/lldp-const.h index 7414827a..56466840 100644 --- a/src/lldp-const.h +++ b/src/lldp-const.h @@ -140,6 +140,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/src/lldpd-structs.h b/src/lldpd-structs.h index 60187bb5..93cde031 100644 --- a/src/lldpd-structs.h +++ b/src/lldpd-structs.h @@ -125,6 +125,22 @@ struct lldpd_dot3_power { 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; }; MARSHAL(lldpd_dot3_power); #endif