]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Add support for PD PoE negotiation. 289/head
authorGustav Wiklander <gustavwi@axis.com>
Thu, 21 Jun 2018 08:49:37 +0000 (10:49 +0200)
committerGustav Wiklander <gustavwi@axis.com>
Fri, 6 Jul 2018 11:22:27 +0000 (13:22 +0200)
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

src/daemon/lldpd.c
src/daemon/protocols/cdp.c
src/daemon/protocols/cdp.h
src/lldpd-structs.h

index 1914569cb6104bc7cc8dc132a4dc18adae6e286b..3f5733b887fc93823e7aa6094a43b0bed2b2324b 100644 (file)
@@ -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
 }
 
index ac76b9ff06d5ca2eca0f09fea7a4b52bd1fa67a8..4a14ff0ab4556bc2ceb8d267bbb6f5a63b5fce57 100644 (file)
 #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 <stdio.h>
@@ -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",
index e2d26306f5d7d26a6c03046e6c6e6469a6e73505..73f127cd7d72544b3bab8902f5c5d4e71391be3d 100644 (file)
@@ -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
index 162ab0afd5234a0b6f13a5d222c934eaa8140fb7..4f1a42c11e7a571b6eb207578fb3331c0f87b96b 100644 (file)
@@ -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;