From aa3dbd5059b86e17563e2dd85840ee44b0ceefd0 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 8 Oct 2019 19:35:41 +0200 Subject: [PATCH] lldp: when receiving a shutdown LLDPU, don't clear chassis information The chassis may be shared with another port. When the MSAP is known and we receive a shutdown LLDPDU, just leave the original chassis as is instead of copying information from the new chassis to the old chassis. Fix #348. --- NEWS | 2 + src/daemon/lldpd.c | 9 ++- tests/integration/requirements.txt | 1 + tests/integration/test_basic.py | 90 ++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 7c96206e..2bef25c8 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ lldpd (1.0.5) + On Linux, only register protocol handler for LLDP when only LLDP is enabled. + Stricter on LLDP incoming frames validation. + * Fix: + + Don't clear chassis TLV on shutdown LLDPDU. lldpd (1.0.4) * Changes: diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index 1d92dd3c..dd4487ac 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -662,7 +662,14 @@ lldpd_decode(struct lldpd *cfg, char *frame, int s, free(oport); } if (ochassis) { - lldpd_move_chassis(ochassis, chassis); + if (port->p_ttl == 0) { + /* Shutdown LLDPDU is special. We do not want to replace + * the chassis. Free the new chassis (which is mostly empty) */ + log_debug("decode", "received a shutdown LLDPDU"); + lldpd_chassis_cleanup(chassis, 1); + } else { + lldpd_move_chassis(ochassis, chassis); + } chassis = ochassis; } else { /* Chassis not known, add it */ diff --git a/tests/integration/requirements.txt b/tests/integration/requirements.txt index 17e21ae1..15dc91c2 100644 --- a/tests/integration/requirements.txt +++ b/tests/integration/requirements.txt @@ -1,3 +1,4 @@ pyroute2==0.5.2 pytest==3.10.1 pytest-xdist==1.26.1 +scapy==2.4.3 diff --git a/tests/integration/test_basic.py b/tests/integration/test_basic.py index e0f1329d..89d98756 100644 --- a/tests/integration/test_basic.py +++ b/tests/integration/test_basic.py @@ -1,6 +1,8 @@ import time import pytest import pyroute2 +import scapy.all +import scapy.contrib.lldp def test_one_neighbor(lldpd1, lldpd, lldpcli, namespaces): @@ -375,3 +377,91 @@ def test_set_interface_alias(lldpd1, lldpd, lldpcli, namespaces): ipr = pyroute2.IPRoute() link = ipr.link('get', ifname='eth0')[0] assert link.get_attr('IFLA_IFALIAS') == 'lldpd: connected to ns-2.example.com' + + +def test_lldpdu_shutdown(lldpd, lldpcli, namespaces, links): + links(namespaces(1), namespaces(2)) + links(namespaces(1), namespaces(2)) + with namespaces(1): + lldpd() + # From https://github.com/vincentbernat/lldpd/issues/348 + frm_fa01 = scapy.all.Ether( + src='04:fe:7f:00:00:01', + dst=scapy.contrib.lldp.LLDP_NEAREST_BRIDGE_MAC) / \ + scapy.contrib.lldp.LLDPDUChassisID( + subtype=scapy.contrib.lldp.LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, + id=b'\x04\xfe\x7f\x00\x00\x00') / \ + scapy.contrib.lldp.LLDPDUPortID( + subtype=scapy.contrib.lldp.LLDPDUPortID.SUBTYPE_INTERFACE_NAME, + id='Fa0/1') / \ + scapy.contrib.lldp.LLDPDUTimeToLive(ttl=65535) / \ + scapy.contrib.lldp.LLDPDUSystemName( + system_name='this info should not disappear') / \ + scapy.contrib.lldp.LLDPDUEndOfLLDPDU() + frm_fa01 = frm_fa01.build() + frm_fa01 = scapy.all.Ether(frm_fa01) + + frm_fa02 = scapy.all.Ether( + src='04:fe:7f:00:00:02', + dst=scapy.contrib.lldp.LLDP_NEAREST_BRIDGE_MAC) / \ + scapy.contrib.lldp.LLDPDUChassisID( + subtype=scapy.contrib.lldp.LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, + id=b'\x04\xfe\x7f\x00\x00\x00') / \ + scapy.contrib.lldp.LLDPDUPortID( + subtype=scapy.contrib.lldp.LLDPDUPortID.SUBTYPE_INTERFACE_NAME, + id='Fa0/2') / \ + scapy.contrib.lldp.LLDPDUTimeToLive(ttl=65535) / \ + scapy.contrib.lldp.LLDPDUSystemName( + system_name='this info should not disappear') / \ + scapy.contrib.lldp.LLDPDUEndOfLLDPDU() + frm_fa02 = frm_fa02.build() + frm_fa02 = scapy.all.Ether(frm_fa02) + + frm_shut_fa01 = scapy.all.Ether( + src='04:fe:7f:00:00:01', + dst=scapy.contrib.lldp.LLDP_NEAREST_BRIDGE_MAC) / \ + scapy.contrib.lldp.LLDPDUChassisID( + subtype=scapy.contrib.lldp.LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, + id=b'\x04\xfe\x7f\x00\x00\x00') / \ + scapy.contrib.lldp.LLDPDUPortID( + subtype=scapy.contrib.lldp.LLDPDUPortID.SUBTYPE_INTERFACE_NAME, + id='Fa0/1') / \ + scapy.contrib.lldp.LLDPDUTimeToLive(ttl=0) / \ + scapy.contrib.lldp.LLDPDUEndOfLLDPDU() + frm_shut_fa01 = frm_shut_fa01.build() + frm_shut_fa01 = scapy.all.Ether(frm_shut_fa01) + + with namespaces(2): + scapy.all.sendp(frm_fa01, iface='eth1') + scapy.all.sendp(frm_fa02, iface='eth3') + time.sleep(2) + with namespaces(1): + out = lldpcli("-f", "keyvalue", "show", "neighbors") + del out['lldp.eth0.age'] + del out['lldp.eth2.age'] + assert out == { + "lldp.eth0.via": "LLDP", + "lldp.eth0.rid": "1", + "lldp.eth0.chassis.mac": "04:fe:7f:00:00:00", + "lldp.eth0.chassis.name": "this info should not disappear", + "lldp.eth0.port.ifname": "Fa0/1", + "lldp.eth0.port.ttl": "65535", + "lldp.eth2.via": "LLDP", + "lldp.eth2.rid": "1", + "lldp.eth2.chassis.mac": "04:fe:7f:00:00:00", + "lldp.eth2.chassis.name": "this info should not disappear", + "lldp.eth2.port.ifname": "Fa0/2", + "lldp.eth2.port.ttl": "65535"} + with namespaces(2): + scapy.all.sendp(frm_shut_fa01, iface='eth1') + time.sleep(2) + with namespaces(1): + out = lldpcli("-f", "keyvalue", "show", "neighbors") + del out['lldp.eth2.age'] + assert out == { + "lldp.eth2.via": "LLDP", + "lldp.eth2.rid": "1", + "lldp.eth2.chassis.mac": "04:fe:7f:00:00:00", + "lldp.eth2.chassis.name": "this info should not disappear", + "lldp.eth2.port.ifname": "Fa0/2", + "lldp.eth2.port.ttl": "65535"} -- 2.39.5