1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
#include <doctest/doctest.h>
#include <algorithm>
#include <array>
#include <vector>
#include "packeteer/l7/dhcp.hpp"
using namespace packeteer::net;
namespace {
// Builds a minimal BOOTP fixed header + magic cookie + a message-type
// option (53), optionally with yiaddr set.
std::vector<unsigned char> dhcp_message(std::uint8_t op, std::uint8_t message_type,
std::array<unsigned char, 4> yiaddr = {0, 0, 0, 0}) {
std::vector<unsigned char> bytes(240, 0);
bytes[0] = op;
std::copy(yiaddr.begin(), yiaddr.end(), bytes.begin() + 16);
bytes[236] = 0x63;
bytes[237] = 0x82;
bytes[238] = 0x53;
bytes[239] = 0x63;
// Option 53 (message type), length 1, then End.
bytes.push_back(53);
bytes.push_back(1);
bytes.push_back(message_type);
bytes.push_back(0xFF);
return bytes;
}
} // namespace
TEST_CASE("parse_dhcp decodes a DISCOVER") {
auto msg = parse_dhcp(dhcp_message(1, kDhcpDiscover));
REQUIRE(msg.has_value());
CHECK(msg->op == 1);
REQUIRE(msg->message_type.has_value());
CHECK(*msg->message_type == kDhcpDiscover);
}
TEST_CASE("parse_dhcp decodes an OFFER with yiaddr set") {
auto msg = parse_dhcp(dhcp_message(2, kDhcpOffer, {192, 168, 1, 50}));
REQUIRE(msg.has_value());
REQUIRE(msg->message_type.has_value());
CHECK(*msg->message_type == kDhcpOffer);
CHECK(msg->yiaddr.bytes == std::array<unsigned char, 4>{192, 168, 1, 50});
}
TEST_CASE("parse_dhcp rejects a payload without the magic cookie") {
std::vector<unsigned char> bytes(240, 0); // right size, but cookie bytes are zero
CHECK_FALSE(parse_dhcp(bytes).has_value());
}
TEST_CASE("parse_dhcp rejects a payload shorter than the fixed header + cookie") {
std::vector<unsigned char> bytes(100, 0);
CHECK_FALSE(parse_dhcp(bytes).has_value());
}
TEST_CASE("parse_dhcp handles a message with no options gracefully") {
std::vector<unsigned char> bytes(240, 0);
bytes[0] = 1;
bytes[236] = 0x63; bytes[237] = 0x82; bytes[238] = 0x53; bytes[239] = 0x63;
auto msg = parse_dhcp(bytes);
REQUIRE(msg.has_value());
CHECK_FALSE(msg->message_type.has_value());
}
TEST_CASE("parse_dhcp skips pad options while scanning for message type") {
std::vector<unsigned char> bytes(240, 0);
bytes[236] = 0x63; bytes[237] = 0x82; bytes[238] = 0x53; bytes[239] = 0x63;
bytes.push_back(0x00); // pad
bytes.push_back(0x00); // pad
bytes.push_back(53);
bytes.push_back(1);
bytes.push_back(kDhcpAck);
bytes.push_back(0xFF);
auto msg = parse_dhcp(bytes);
REQUIRE(msg.has_value());
REQUIRE(msg->message_type.has_value());
CHECK(*msg->message_type == kDhcpAck);
}
TEST_CASE("DhcpDissector claims port 67 and names message types") {
DhcpDissector dissector;
CHECK(dissector.port() == kDhcpServerPort);
auto summary = dissector.summarize(dhcp_message(2, kDhcpOffer, {192, 168, 1, 50}));
REQUIRE(summary.has_value());
CHECK(*summary == "DHCP OFFER yiaddr=192.168.1.50");
}
TEST_CASE("DhcpDissector omits yiaddr when it's all-zero") {
DhcpDissector dissector;
auto summary = dissector.summarize(dhcp_message(1, kDhcpDiscover));
REQUIRE(summary.has_value());
CHECK(*summary == "DHCP DISCOVER");
}
TEST_CASE("DhcpDissector falls back to op when message type is absent") {
DhcpDissector dissector;
std::vector<unsigned char> bytes(240, 0);
bytes[0] = 1;
bytes[236] = 0x63; bytes[237] = 0x82; bytes[238] = 0x53; bytes[239] = 0x63;
auto summary = dissector.summarize(bytes);
REQUIRE(summary.has_value());
CHECK(*summary == "DHCP request");
}
|