From daeeb398b8d09d7558f18482d668c2318c392f89 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Sun, 19 Nov 2017 16:38:33 +0100 Subject: [PATCH] dot3: as PD device, echo back PSE allocated value Dot3 power TLV contains an allocated value and a requested value. When PSE allocates some power and says so in its TLV, PD device is expected to echo back (within 10 seconds) the received value in its own TLV. We handle this part automatically. Fix #243 Fix #249 --- NEWS | 1 + src/daemon/lldpd.c | 32 +++++++++++++++++++++++++ tests/integration/test_dot3.py | 44 +++++++++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 40635946..b1f9c298 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ lldpd (0.9.9) * Changes: + lldpcli can now display local interfaces with LLDP data sent on each of them ("show interfaces"). + + As Dot3 PD device, echo back allocated value from PSE device. * Fix: + Don't remove interfaces when they are released from a bridge. + Don't use "expect stop" with Upstart. It's buggy. diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index 34387008..35d71174 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -944,6 +944,37 @@ lldpd_hide_all(struct lldpd *cfg) } } +/* If PD device and PSE allocated power, echo back this change. If we have + * several LLDP neighbors, we use the latest updated. */ +static void +lldpd_dot3_power_pd_pse(struct lldpd_hardware *hardware) +{ +#ifdef ENABLE_DOT3 + struct lldpd_port *port, *selected_port = NULL; + /* Are we a PD device? */ + if (hardware->h_lport.p_power.devicetype != LLDP_DOT3_POWER_PD) + return; + TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { + if (port->p_hidden_in) + continue; + if (port->p_protocol != LLDPD_MODE_LLDP) + continue; + if (port->p_power.devicetype != LLDP_DOT3_POWER_PSE) + continue; + if (!selected_port || port->p_lastupdate > selected_port->p_lastupdate) + selected_port = port; + } + if (selected_port->p_power.allocated != hardware->h_lport.p_power.allocated) { + log_info("receive", "for %s, PSE told us allocated is now %d instead of %d", + hardware->h_ifname, + selected_port->p_power.allocated, + hardware->h_lport.p_power.allocated); + hardware->h_lport.p_power.allocated = selected_port->p_power.allocated; + levent_schedule_pdu(hardware); + } +#endif +} + void lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd) { @@ -981,6 +1012,7 @@ lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd) TRACE(LLDPD_FRAME_RECEIVED(hardware->h_ifname, buffer, (size_t)n)); lldpd_decode(cfg, buffer, n, hardware); lldpd_hide_all(cfg); /* Immediatly hide */ + lldpd_dot3_power_pd_pse(hardware); lldpd_count_neighbors(cfg); free(buffer); } diff --git a/tests/integration/test_dot3.py b/tests/integration/test_dot3.py index f24e0372..6f311953 100644 --- a/tests/integration/test_dot3.py +++ b/tests/integration/test_dot3.py @@ -1,5 +1,4 @@ import pytest -import pyroute2 import shlex import time @@ -57,3 +56,46 @@ class TestLldpDot3(object): for k, v in out.items() if k.startswith(pfx)} assert out == expected + + def test_autoneg_power(self, links, lldpd, lldpcli, namespaces): + links(namespaces(1), namespaces(2)) + with namespaces(1): + lldpd() + with namespaces(2): + lldpd() + result = lldpcli( + *shlex.split("configure dot3 power pd " + "supported enabled paircontrol " + "powerpairs spare " + "class class-3 " + "type 1 source both priority low " + "requested 20000 allocated 5000")) + assert result.returncode == 0 + time.sleep(2) + with namespaces(1): + # Did we receive the request? + out = lldpcli("-f", "keyvalue", "show", "neighbors", "details") + assert out['lldp.eth0.port.power.requested'] == '20000' + assert out['lldp.eth0.port.power.allocated'] == '5000' + # Send an answer we agree to give almost that (this part + # cannot be automated, lldpd cannot take this decision). + result = lldpcli( + *shlex.split("configure dot3 power pse " + "supported enabled paircontrol powerpairs " + "spare class class-3 " + "type 1 source primary priority high " + "requested 20000 allocated 19000")) + assert result.returncode == 0 + time.sleep(2) + with namespaces(2): + # Did we receive that? + out = lldpcli("-f", "keyvalue", "show", "neighbors", "details") + assert out['lldp.eth1.port.power.requested'] == '20000' + assert out['lldp.eth1.port.power.allocated'] == '19000' + with namespaces(1): + # Did we get an echo back? This part is handled + # automatically by lldpd: we confirm we received the + # answer "immediately". + out = lldpcli("-f", "keyvalue", "show", "neighbors", "details") + assert out['lldp.eth0.port.power.requested'] == '20000' + assert out['lldp.eth0.port.power.allocated'] == '19000' -- 2.39.5