From: Gustav Wiklander Date: Thu, 21 Jun 2018 08:49:37 +0000 (+0200) Subject: Add support for PD PoE negotiation. X-Git-Tag: 1.0.2~13^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5334d8c8a459b5e1385327bea5ee527e498e4b9b;p=thirdparty%2Flldpd.git Add support for PD PoE negotiation. Power requests refer to the power at the PSE. Thus the loss offset caused by the cable has to be added to the power request. Also the power received from the PSE must subtract the cable loss to be compatible with lldp. There are three TLVs for CDPv2 PoE negotiation. Power Consumption: Current maximum power consumption of PD. Power Request: Wanted maximum power consumption of PD. Power Available: Power output from PSE. Only used if lldp PoE is not supported by switch. A cisco switch which does support both lldp and cdp will use the protocol which is first to transmit a package. Change-Id: Ib45ae582799c50a8d839120c28a5080e582c66e9 --- diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index 1914569c..3f5733b8 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -986,8 +986,10 @@ lldpd_dot3_power_pd_pse(struct lldpd_hardware *hardware) TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { if (port->p_hidden_in) continue; - if (port->p_protocol != LLDPD_MODE_LLDP) + + if (port->p_protocol != LLDPD_MODE_LLDP && port->p_protocol != LLDPD_MODE_CDPV2) continue; + if (port->p_power.devicetype != LLDP_DOT3_POWER_PSE) continue; if (!selected_port || port->p_lastupdate > selected_port->p_lastupdate) @@ -1001,6 +1003,13 @@ lldpd_dot3_power_pd_pse(struct lldpd_hardware *hardware) hardware->h_lport.p_power.allocated = selected_port->p_power.allocated; levent_schedule_pdu(hardware); } + +#ifdef ENABLE_CDP + if (selected_port && selected_port->p_cdp_power.management_id != hardware->h_lport.p_cdp_power.management_id) { + hardware->h_lport.p_cdp_power.management_id = selected_port->p_cdp_power.management_id; + } +#endif + #endif } diff --git a/src/daemon/protocols/cdp.c b/src/daemon/protocols/cdp.c index ac76b9ff..4a14ff0a 100644 --- a/src/daemon/protocols/cdp.c +++ b/src/daemon/protocols/cdp.c @@ -19,6 +19,17 @@ #include "lldpd.h" #include "frame.h" +/* + * CDP Requests Power at the switch output and therefore has to take into + * account the loss in the PoE cable. This is done by the switch automatically + * if lldp is used as the protocol. + */ +#define CDP_CLASS_3_MAX_PSE_POE 154 /* 15.4W Max PoE at PSE class 3 */ +#define CDP_SWTICH_DEFAULT_POE_PD 130 /* 13.W default PoE at PD */ +#define CDP_SWTICH_DEFAULT_POE_PSE 154 /* 15.4W default PoE at PSE */ +#define CDP_SWITCH_POE_CLASS_4_OFFSET 45 /* 4.5W max loss from cable */ +#define CDP_SWITCH_POE_CLASS_3_OFFSET 24 /* 2.4W max loss from cable */ + #if defined (ENABLE_CDP) || defined (ENABLE_FDP) #include @@ -201,7 +212,51 @@ cdp_send(struct lldpd *global, POKE_END_CDP_TLV)) goto toobig; -#ifdef ENABLE_LLDPMED +#ifdef ENABLE_DOT3 + if ((version >= 2) && + (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) && + (port->p_power.devicetype == LLDP_DOT3_POWER_PD) && + (port->p_power.requested > 0) && + (port->p_power.requested <= 655)) { + u_int16_t requested; + u_int16_t consumption; + + if (port->p_power.requested != port->p_power.allocated) { + port->p_cdp_power.request_id++; + log_debug("cdp", "requested: %d, allocated:%d", port->p_power.requested, port->p_power.allocated); + } + consumption = port->p_power.allocated ? port->p_power.allocated : CDP_SWTICH_DEFAULT_POE_PD; + if (consumption > 130) { + consumption += CDP_SWITCH_POE_CLASS_4_OFFSET; + } else { + consumption += CDP_SWITCH_POE_CLASS_3_OFFSET; + } + if (port->p_power.requested > 130) { /* Class 4 */ + requested = port->p_power.requested + CDP_SWITCH_POE_CLASS_4_OFFSET; + } else { /* Class 3 */ + requested = port->p_power.requested + CDP_SWITCH_POE_CLASS_3_OFFSET; + } + if (!( + POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) && + POKE_UINT16(consumption * 100) && + POKE_END_CDP_TLV)) + goto toobig; + /* Avoid request id 0 from overflow */ + if (!port->p_cdp_power.request_id) { + port->p_cdp_power.request_id = 1; + } + if (!port->p_cdp_power.management_id) { + port->p_cdp_power.management_id = 1; + } + if (!( + POKE_START_CDP_TLV(CDP_TLV_POWER_REQUESTED) && + POKE_UINT16(port->p_cdp_power.request_id) && + POKE_UINT16(port->p_cdp_power.management_id) && + POKE_UINT32(requested * 100) && + POKE_END_CDP_TLV)) + goto toobig; + } +#elif defined(ENABLE_LLDPMED) /* Power use */ if ((version >= 2) && port->p_med_cap_enabled && @@ -214,7 +269,8 @@ cdp_send(struct lldpd *global, POKE_END_CDP_TLV)) goto toobig; } -#endif +#endif + (void)POKE_SAVE(end); /* Compute len and checksum */ @@ -320,6 +376,10 @@ cdp_decode(struct lldpd *cfg, char *frame, int s, hardware->h_ifname); goto malformed; } + + /* This is the correct length of the CDP + LLC packets */ + length = len_eth; + PEEK_DISCARD(6); /* Skip beginning of LLC */ proto = PEEK_UINT16; if (proto != LLC_PID_CDP) { @@ -370,6 +430,7 @@ cdp_decode(struct lldpd *cfg, char *frame, int s, } tlv_type = PEEK_UINT16; tlv_len = PEEK_UINT16 - 4; + (void)PEEK_SAVE(tlv); if ((tlv_len < 0) || (length < tlv_len)) { log_warnx("cdp", "incorrect size in CDP/FDP TLV header for frame " @@ -522,6 +583,30 @@ cdp_decode(struct lldpd *cfg, char *frame, int s, TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries); break; +#endif +#ifdef ENABLE_DOT3 + case CDP_TLV_POWER_AVAILABLE: + CHECK_TLV_SIZE(12, "Power Available"); + /* check if it is a respone to a request id */ + if (PEEK_UINT16 > 0) { + port->p_cdp_power.management_id = PEEK_UINT16; + port->p_power.allocated = PEEK_UINT32; + port->p_power.allocated /= 100; + port->p_power.supported = 1; + port->p_power.enabled = 1; + port->p_power.devicetype = LLDP_DOT3_POWER_PSE; + port->p_power.powertype = LLDP_DOT3_POWER_8023AT_TYPE2; + log_debug("cdp", "Allocated power %d00", port->p_power.allocated); + if (port->p_power.allocated > CDP_CLASS_3_MAX_PSE_POE) { + port->p_power.allocated -= CDP_SWITCH_POE_CLASS_4_OFFSET; + } else if (port->p_power.allocated > CDP_SWITCH_POE_CLASS_3_OFFSET ) { + port->p_power.allocated -= CDP_SWITCH_POE_CLASS_3_OFFSET; + } else { + port->p_power.allocated = 0; + } + port->p_power.requested = hardware->h_lport.p_power.requested; + } + break; #endif default: log_debug("cdp", "unknown CDP/FDP TLV type (%d) received on %s", diff --git a/src/daemon/protocols/cdp.h b/src/daemon/protocols/cdp.h index e2d26306..73f127cd 100644 --- a/src/daemon/protocols/cdp.h +++ b/src/daemon/protocols/cdp.h @@ -44,7 +44,9 @@ enum { CDP_TLV_SOFTWARE = 5, CDP_TLV_PLATFORM = 6, CDP_TLV_NATIVEVLAN = 10, - CDP_TLV_POWER_CONSUMPTION = 16 + CDP_TLV_POWER_CONSUMPTION = 0x10, + CDP_TLV_POWER_REQUESTED = 0x19, + CDP_TLV_POWER_AVAILABLE = 0x1A }; #define CDP_ADDRESS_PROTO_IP 0xcc diff --git a/src/lldpd-structs.h b/src/lldpd-structs.h index 162ab0af..4f1a42c1 100644 --- a/src/lldpd-structs.h +++ b/src/lldpd-structs.h @@ -129,6 +129,13 @@ struct lldpd_dot3_power { MARSHAL(lldpd_dot3_power); #endif +#ifdef ENABLE_CDP +struct cdpv2_power { + u_int16_t request_id; + u_int16_t management_id; +}; +#endif + enum { LLDPD_AF_UNSPEC = 0, LLDPD_AF_IPV4, @@ -277,6 +284,10 @@ struct lldpd_port { struct lldpd_med_power p_med_power; #endif +#ifdef ENABLE_CDP + struct cdpv2_power p_cdp_power; +#endif + #ifdef ENABLE_DOT1 u_int16_t p_pvid; TAILQ_HEAD(, lldpd_vlan) p_vlans;