srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_lldp.cpp98
-rw-r--r--tests/test_summarize.cpp24
2 files changed, 120 insertions, 2 deletions
diff --git a/tests/test_lldp.cpp b/tests/test_lldp.cpp
new file mode 100644
index 0000000..3d48970
--- /dev/null
+++ b/tests/test_lldp.cpp
@@ -0,0 +1,98 @@
+#include <doctest/doctest.h>
+
+#include <vector>
+
+#include "packeteer/net/lldp.hpp"
+
+using namespace packeteer::net;
+
+namespace {
+
+std::vector<unsigned char> tlv(std::uint8_t type, const std::vector<unsigned char>& value) {
+ std::uint16_t header = static_cast<std::uint16_t>((type << 9) | value.size());
+ std::vector<unsigned char> out = {static_cast<unsigned char>(header >> 8),
+ static_cast<unsigned char>(header & 0xFF)};
+ out.insert(out.end(), value.begin(), value.end());
+ return out;
+}
+
+std::vector<unsigned char> lldpdu(std::optional<std::vector<unsigned char>> extra_tlv = {}) {
+ std::vector<unsigned char> bytes;
+ auto chassis_id = tlv(1, {4, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}); // subtype 4: MAC
+ auto port_id = tlv(2, {7, 'e', 't', 'h', '0'}); // subtype 7: locally assigned
+ auto ttl = tlv(3, {0x00, 0x78}); // 120 seconds
+ bytes.insert(bytes.end(), chassis_id.begin(), chassis_id.end());
+ bytes.insert(bytes.end(), port_id.begin(), port_id.end());
+ bytes.insert(bytes.end(), ttl.begin(), ttl.end());
+ if (extra_tlv) bytes.insert(bytes.end(), extra_tlv->begin(), extra_tlv->end());
+ auto end = tlv(0, {});
+ bytes.insert(bytes.end(), end.begin(), end.end());
+ return bytes;
+}
+
+} // namespace
+
+TEST_CASE("parse_lldp decodes the three mandatory TLVs") {
+ auto msg = parse_lldp(lldpdu());
+ REQUIRE(msg.has_value());
+ REQUIRE(msg->chassis_id.has_value());
+ CHECK(*msg->chassis_id == "aa:bb:cc:dd:ee:ff");
+ REQUIRE(msg->port_id.has_value());
+ CHECK(*msg->port_id == "eth0");
+ REQUIRE(msg->ttl.has_value());
+ CHECK(*msg->ttl == 120);
+ CHECK_FALSE(msg->system_name.has_value());
+}
+
+TEST_CASE("parse_lldp decodes the System Name TLV when present") {
+ auto msg = parse_lldp(lldpdu(tlv(5, {'s', 'w', 'i', 't', 'c', 'h', '1'})));
+ REQUIRE(msg.has_value());
+ REQUIRE(msg->system_name.has_value());
+ CHECK(*msg->system_name == "switch1");
+}
+
+TEST_CASE("parse_lldp skips over an unrecognized TLV without losing later ones") {
+ // Type 6 (System Description) isn't decoded, but must not break
+ // parsing of the End TLV that follows it.
+ auto msg = parse_lldp(lldpdu(tlv(6, {'d', 'e', 's', 'c'})));
+ REQUIRE(msg.has_value());
+ REQUIRE(msg->chassis_id.has_value());
+ REQUIRE(msg->port_id.has_value());
+ REQUIRE(msg->ttl.has_value());
+}
+
+TEST_CASE("parse_lldp rejects a message missing a mandatory TLV") {
+ // Chassis ID and Port ID only, no TTL - not a well-formed LLDPDU.
+ std::vector<unsigned char> bytes;
+ auto chassis_id = tlv(1, {4, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF});
+ auto port_id = tlv(2, {7, 'e', 't', 'h', '0'});
+ bytes.insert(bytes.end(), chassis_id.begin(), chassis_id.end());
+ bytes.insert(bytes.end(), port_id.begin(), port_id.end());
+ auto end = tlv(0, {});
+ bytes.insert(bytes.end(), end.begin(), end.end());
+
+ CHECK_FALSE(parse_lldp(bytes).has_value());
+}
+
+TEST_CASE("parse_lldp rejects an empty buffer") {
+ std::vector<unsigned char> bytes;
+ CHECK_FALSE(parse_lldp(bytes).has_value());
+}
+
+TEST_CASE("format_lldp_id renders a MAC-address subtype as hex-colon") {
+ std::vector<unsigned char> mac = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
+ CHECK(format_lldp_id(4, mac) == "aa:bb:cc:dd:ee:ff");
+}
+
+TEST_CASE("format_lldp_id renders a non-MAC subtype as plain text") {
+ std::vector<unsigned char> text = {'e', 't', 'h', '0'};
+ CHECK(format_lldp_id(7, text) == "eth0");
+}
+
+TEST_CASE("lldp_summary formats chassis/port/ttl, and name only when present") {
+ LldpInfo info{"aa:bb:cc:dd:ee:ff", "eth0", 120, std::nullopt};
+ CHECK(lldp_summary(info) == "LLDP chassis=aa:bb:cc:dd:ee:ff port=eth0 ttl=120");
+
+ info.system_name = "switch1";
+ CHECK(lldp_summary(info) == "LLDP chassis=aa:bb:cc:dd:ee:ff port=eth0 ttl=120 name=switch1");
+}
diff --git a/tests/test_summarize.cpp b/tests/test_summarize.cpp
index baf4ed4..0cb0f98 100644
--- a/tests/test_summarize.cpp
+++ b/tests/test_summarize.cpp
@@ -166,10 +166,30 @@ TEST_CASE("summarize_packet reports a truncated Ethernet frame without decoding
TEST_CASE("summarize_packet stops after the Ethernet line for an unhandled ethertype") {
std::vector<unsigned char> bytes = {
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
- 0x88, 0xCC, // LLDP, not IPv4/IPv6/ARP
+ 0x80, 0x35, // RARP - real ethertype, just not one this project decodes
};
auto line = packeteer::summarize_packet(bytes, DLT_EN10MB);
- CHECK(line == "ETH aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 ethertype=0x88cc");
+ CHECK(line == "ETH aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 ethertype=0x8035");
+}
+
+TEST_CASE("summarize_packet decodes an LLDP frame end to end") {
+ std::vector<unsigned char> bytes = {
+ 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E, // dst: LLDP multicast
+ 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, // src mac
+ 0x88, 0xCC, // ethertype: LLDP
+ // Chassis ID TLV: subtype=4 (MAC), value=aa:bb:cc:dd:ee:ff
+ 0x02, 0x07, 4, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
+ // Port ID TLV: subtype=7 (locally assigned), value="eth0"
+ 0x04, 0x05, 7, 'e', 't', 'h', '0',
+ // TTL TLV: 120 seconds
+ 0x06, 0x02, 0x00, 0x78,
+ // End of LLDPDU
+ 0x00, 0x00,
+ };
+ auto line = packeteer::summarize_packet(bytes, DLT_EN10MB);
+ CHECK(line ==
+ "ETH aa:bb:cc:dd:ee:ff -> 01:80:c2:00:00:0e ethertype=0x88cc | "
+ "LLDP chassis=aa:bb:cc:dd:ee:ff port=eth0 ttl=120");
}
TEST_CASE("summarize_packet decodes an ARP request end to end") {