Skip to content

Commit 5cd32b6

Browse files
tagrawal03facebook-github-bot
authored andcommitted
"[Katran Test Fixture modernization][2/4] Use PacketBuilder for katran base test cases
Summary: Extending to test cases 12-16 Reviewed By: lima1756 Differential Revision: D82076311 fbshipit-source-id: 360668d8a26a850399b6954713990cd08d8fd914
1 parent ce22e61 commit 5cd32b6

3 files changed

Lines changed: 173 additions & 66 deletions

File tree

katran/lib/testing/fixtures/KatranTestFixtures.h

Lines changed: 71 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ const std::vector<katran::PacketAttributes> testFixtures = {
173173
.Eth("02:00:00:00:00:00", "01:00:00:00:00:00")
174174
.IPv6("fc00:1::1", "fc00:2::1")
175175
.ICMPv6(ICMPv6Header::ECHO_REPLY)},
176-
// 10
176+
// 10
177177
{.description = "v4 ICMP dest-unreachabe fragmentation-needed",
178178
.expectedReturnValue = "XDP_TX",
179179
.inputPacketBuilder =
@@ -212,47 +212,76 @@ const std::vector<katran::PacketAttributes> testFixtures = {
212212
.IPv6("fc00:1::1", "fc00:2::1")
213213
.TCP(80, 31337, 0, 0, 8192, TH_SYN)
214214
.payload("katran test packet")},
215-
//12
216-
{
217-
//Ether(src="0x1", dst="0x2")/IP(src="192.168.1.1", dst="10.200.1.1",ihl=6)/TCP(sport=31337, dport=80,flags="A")/"katran test pkt"
218-
.inputPacket = "AgAAAAAAAQAAAAAACABGAAA3AAEAAEAGrE7AqAEBCsgBAXppAFAAAAAAAAAAAFAQIAAn5AAAa2F0cmFuIHRlc3QgcGt0",
219-
.description = "drop of IPv4 packet w/ options",
220-
.expectedReturnValue = "XDP_DROP",
221-
.expectedOutputPacket = "AgAAAAAAAQAAAAAACABGAAA3AAEAAEAGrE7AqAEBCsgBAXppAFAAAAAAAAAAAFAQIAAn5AAAa2F0cmFuIHRlc3QgcGt0"
222-
},
223-
//13
224-
{
225-
//Ether(src="0x1", dst="0x2")/IP(src="192.168.1.1", dst="10.200.1.1",ihl=5,flags="MF")/TCP(sport=31337, dport=80,flags="A")/"katran test pkt"
226-
.inputPacket = "AgAAAAAAAQAAAAAACABFAAA3AAEgAEAGjU7AqAEBCsgBAXppAFAAAAAAAAAAAFAQIAAn5AAAa2F0cmFuIHRlc3QgcGt0",
227-
.description = "drop of IPv4 fragmented packet",
228-
.expectedReturnValue = "XDP_DROP",
229-
.expectedOutputPacket = "AgAAAAAAAQAAAAAACABFAAA3AAEgAEAGjU7AqAEBCsgBAXppAFAAAAAAAAAAAFAQIAAn5AAAa2F0cmFuIHRlc3QgcGt0"
230-
},
231-
//14
232-
{
233-
//Ether(src="0x1", dst="0x2")/IPv6(src="fc00:2::1", dst="fc00:1::1",nh=44)/TCP(sport=31337, dport=80,flags="A")/"katran test pkt"
234-
.inputPacket = "AgAAAAAAAQAAAAAAht1gAAAAACMsQPwAAAIAAAAAAAAAAAAAAAH8AAABAAAAAAAAAAAAAAABemkAUAAAAAAAAAAAUBAgAP1PAABrYXRyYW4gdGVzdCBwa3Q=",
235-
.description = "drop of IPv6 fragmented packet",
236-
.expectedReturnValue = "XDP_DROP",
237-
.expectedOutputPacket = "AgAAAAAAAQAAAAAAht1gAAAAACMsQPwAAAIAAAAAAAAAAAAAAAH8AAABAAAAAAAAAAAAAAABemkAUAAAAAAAAAAAUBAgAP1PAABrYXRyYW4gdGVzdCBwa3Q="
238-
},
239-
//15
240-
{
241-
//Ether(src="0x1", dst="0x2")/IP(src="192.168.1.1", dst="10.200.1.1",ihl=5)/TCP(sport=31337, dport=82,flags="A")/"katran test pkt"
242-
.inputPacket = "AgAAAAAAAQAAAAAACABFAAA3AAEAAEAGrU7AqAEBCsgBAXppAFIAAAAAAAAAAFAQIAAn4gAAa2F0cmFuIHRlc3QgcGt0",
243-
.description = "pass of v4 packet with dst not equal to any configured VIP",
244-
.expectedReturnValue = "XDP_PASS",
245-
.expectedOutputPacket = "AgAAAAAAAQAAAAAACABFAAA3AAEAAEAGrU7AqAEBCsgBAXppAFIAAAAAAAAAAFAQIAAn4gAAa2F0cmFuIHRlc3QgcGt0"
246-
},
247-
//16
248-
{
249-
//Ether(src="0x1", dst="0x2")/IPv6(src="fc00:2::1", dst="fc00:1::1")/TCP(sport=31337, dport=82,flags="A")/"katran test pkt"
250-
.inputPacket = "AgAAAAAAAQAAAAAAht1gAAAAACMGQPwAAAIAAAAAAAAAAAAAAAH8AAABAAAAAAAAAAAAAAABemkAUgAAAAAAAAAAUBAgAP1NAABrYXRyYW4gdGVzdCBwa3Q=",
251-
.description = "pass of v6 packet with dst not equal to any configured VIP",
252-
.expectedReturnValue = "XDP_PASS",
253-
.expectedOutputPacket = "AgAAAAAAAQAAAAAAht1gAAAAACMGQPwAAAIAAAAAAAAAAAAAAAH8AAABAAAAAAAAAAAAAAABemkAUgAAAAAAAAAAUBAgAP1NAABrYXRyYW4gdGVzdCBwa3Q="
254-
},
255-
//17
215+
// 12
216+
{.description = "drop of IPv4 packet w/ options",
217+
.expectedReturnValue = "XDP_DROP",
218+
.inputPacketBuilder =
219+
katran::testing::PacketBuilder::newPacket()
220+
.Eth("0x1", "0x2")
221+
.IPv4("192.168.1.1", "10.200.1.1", 64, 0, 1, 0, 6)
222+
.TCP(31337, 80, 0, 0, 8192, TH_ACK)
223+
.payload("katran test pkt"),
224+
.expectedOutputPacketBuilder =
225+
katran::testing::PacketBuilder::newPacket()
226+
.Eth("0x1", "0x2")
227+
.IPv4("192.168.1.1", "10.200.1.1", 64, 0, 1, 0, 6)
228+
.TCP(31337, 80, 0, 0, 8192, TH_ACK)
229+
.payload("katran test pkt")},
230+
// 13
231+
{.description = "drop of IPv4 fragmented packet",
232+
.expectedReturnValue = "XDP_DROP",
233+
.inputPacketBuilder = PacketBuilder::newPacket()
234+
.Eth("0x1", "0x2")
235+
.IPv4("192.168.1.1","10.200.1.1",64,0,1,PacketBuilder::IP_FLAG_MF)
236+
.TCP(31337, 80, 0, 0, 8192, TH_ACK)
237+
.payload("katran test pkt"),
238+
.expectedOutputPacketBuilder = PacketBuilder::newPacket()
239+
.Eth("0x1", "0x2")
240+
.IPv4("192.168.1.1","10.200.1.1",64,0,1,PacketBuilder::IP_FLAG_MF)
241+
.TCP(31337, 80, 0, 0, 8192, TH_ACK)
242+
.payload("katran test pkt")},
243+
// 14
244+
{.description = "drop of IPv6 fragmented packet",
245+
.expectedReturnValue = "XDP_DROP",
246+
.inputPacketBuilder = PacketBuilder::newPacket()
247+
.Eth("0x1", "0x2")
248+
.IPv6("fc00:2::1","fc00:1::1",64,0,0,PacketBuilder::IPV6_NH_FRAGMENT)
249+
.TCP(31337, 80, 0, 0, 8192, TH_ACK)
250+
.payload("katran test pkt"),
251+
.expectedOutputPacketBuilder = PacketBuilder::newPacket()
252+
.Eth("0x1", "0x2")
253+
.IPv6("fc00:2::1","fc00:1::1",64,0,0,PacketBuilder::IPV6_NH_FRAGMENT)
254+
.TCP(31337, 80, 0, 0, 8192, TH_ACK)
255+
.payload("katran test pkt")},
256+
// 15
257+
{.description =
258+
"pass of v4 packet with dst not equal to any configured VIP",
259+
.expectedReturnValue = "XDP_PASS",
260+
.inputPacketBuilder = katran::testing::PacketBuilder::newPacket()
261+
.Eth("0x1", "0x2")
262+
.IPv4("192.168.1.1", "10.200.1.1")
263+
.TCP(31337, 82, 0, 0, 8192, TH_ACK)
264+
.payload("katran test pkt"),
265+
.expectedOutputPacketBuilder = katran::testing::PacketBuilder::newPacket()
266+
.Eth("0x1", "0x2")
267+
.IPv4("192.168.1.1", "10.200.1.1")
268+
.TCP(31337, 82, 0, 0, 8192, TH_ACK)
269+
.payload("katran test pkt")},
270+
// 16
271+
{.description =
272+
"pass of v6 packet with dst not equal to any configured VIP",
273+
.expectedReturnValue = "XDP_PASS",
274+
.inputPacketBuilder = katran::testing::PacketBuilder::newPacket()
275+
.Eth("0x1", "0x2")
276+
.IPv6("fc00:2::1", "fc00:1::1")
277+
.TCP(31337, 82, 0, 0, 8192, TH_ACK)
278+
.payload("katran test pkt"),
279+
.expectedOutputPacketBuilder = katran::testing::PacketBuilder::newPacket()
280+
.Eth("0x1", "0x2")
281+
.IPv6("fc00:2::1", "fc00:1::1")
282+
.TCP(31337, 82, 0, 0, 8192, TH_ACK)
283+
.payload("katran test pkt")},
284+
//17
256285
{
257286
//Ether(src="0x1", dst="0x2")/ARP()
258287
.inputPacket = "AgAAAAAAAQAAAAAACAYAAQgABgQAAQAAAAAAAAAAAAAAAAAAAAAAAAAA",

katran/lib/testing/tools/PacketBuilder.cpp

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ PacketBuilder& PacketBuilder::IPv4(
7171
uint8_t ttl,
7272
uint8_t tos,
7373
uint16_t id,
74-
uint16_t flags) {
74+
uint16_t flags,
75+
uint8_t ihl) {
7576
if (!isValidIPv4Address(src)) {
7677
throw std::invalid_argument("Invalid source IPv4 address: " + src);
7778
}
@@ -80,7 +81,7 @@ PacketBuilder& PacketBuilder::IPv4(
8081
}
8182

8283
headerStack_.emplace_back(
83-
std::make_shared<IPv4Header>(src, dst, ttl, tos, id, flags));
84+
std::make_shared<IPv4Header>(src, dst, ttl, tos, id, flags, ihl));
8485
return *this;
8586
}
8687

@@ -89,7 +90,8 @@ PacketBuilder& PacketBuilder::IPv6(
8990
const std::string& dst,
9091
uint8_t hopLimit,
9192
uint8_t trafficClass,
92-
uint32_t flowLabel) {
93+
uint32_t flowLabel,
94+
uint8_t nextHeader) {
9395
if (!isValidIPv6Address(src)) {
9496
throw std::invalid_argument("Invalid source IPv6 address: " + src);
9597
}
@@ -98,7 +100,7 @@ PacketBuilder& PacketBuilder::IPv6(
98100
}
99101

100102
headerStack_.emplace_back(std::make_shared<IPv6Header>(
101-
src, dst, hopLimit, trafficClass, flowLabel));
103+
src, dst, hopLimit, trafficClass, flowLabel, nextHeader));
102104
return *this;
103105
}
104106

@@ -348,12 +350,13 @@ std::vector<uint8_t> PacketBuilder::buildAsBytes() const {
348350
return const_cast<PacketBuilder*>(this)->buildBinaryPacket();
349351
}
350352

351-
PacketBuilder::PacketResult PacketBuilder::build() {
352-
auto binaryPacket = buildBinaryPacket();
353+
PacketBuilder::PacketResult PacketBuilder::build() const {
354+
auto binaryPacket = const_cast<PacketBuilder*>(this)->buildBinaryPacket();
353355

354356
PacketResult result;
355357
result.base64Packet = bytesToBase64(binaryPacket);
356-
result.scapyCommand = generateScapyCommand();
358+
result.scapyCommand =
359+
const_cast<PacketBuilder*>(this)->generateScapyCommand();
357360
result.packetSize = binaryPacket.size();
358361

359362
return result;
@@ -398,7 +401,8 @@ std::string PacketBuilder::generateScapyCommand() {
398401
return command;
399402
}
400403

401-
std::string PacketBuilder::bytesToBase64(const std::vector<uint8_t>& bytes) {
404+
std::string PacketBuilder::bytesToBase64(
405+
const std::vector<uint8_t>& bytes) const {
402406
return folly::base64Encode(folly::StringPiece(
403407
reinterpret_cast<const char*>(bytes.data()), bytes.size()));
404408
}
@@ -570,7 +574,8 @@ IPv4Header::IPv4Header(
570574
uint8_t ttl,
571575
uint8_t tos,
572576
uint16_t id,
573-
uint16_t flags) {
577+
uint16_t flags,
578+
uint8_t ihl) {
574579
static_assert(
575580
sizeof(ip_.saddr) == 4, "IPv4 source address field size mismatch");
576581
static_assert(
@@ -588,7 +593,7 @@ IPv4Header::IPv4Header(
588593
}
589594

590595
ip_.version = 4;
591-
ip_.ihl = IPV4_MIN_HEADER_LENGTH; // 4*5=20 bytes IP header length
596+
ip_.ihl = ihl; // Set IHL as specified, default is 5 (20 bytes)
592597
ip_.tos = tos;
593598
ip_.tot_len = 0; // Will be calculated in serialize()
594599
ip_.id = htons(id);
@@ -609,17 +614,39 @@ void IPv4Header::serialize(
609614
// Set total length based on current packet size (which contains the
610615
// payload)
611616
ip.tot_len = htons(sizeof(struct iphdr) + packet.size());
617+
// Calculate actual header length including options
618+
size_t actualHeaderLength = ip_.ihl * 4;
612619

613-
// Calculate checksum
620+
// Set total length based on current packet size plus actual header length
621+
ip.tot_len = htons(actualHeaderLength + packet.size());
622+
623+
// Build header with options (if any)
624+
std::vector<uint8_t> ipHeaderWithOptions;
625+
ipHeaderWithOptions.resize(actualHeaderLength, 0);
626+
627+
// Copy the basic IPv4 header
628+
std::memcpy(ipHeaderWithOptions.data(), &ip, sizeof(struct iphdr));
629+
630+
// If IHL > 5, fill options area with zeros (dummy options for testing)
631+
// This creates the required space but doesn't implement specific options
632+
if (actualHeaderLength > sizeof(struct iphdr)) {
633+
// Options area is already zero-filled from resize() above
634+
// In a real implementation, actual IPv4 options would be added here
635+
}
636+
637+
// Calculate checksum over the complete header (including options)
614638
ip.check = 0;
615-
const uint8_t* ipBytes = reinterpret_cast<const uint8_t*>(&ip);
616-
std::vector<uint8_t> ipForChecksum(ipBytes, ipBytes + sizeof(struct iphdr));
617-
uint16_t checksum = calculateChecksum(ipForChecksum);
639+
std::memcpy(ipHeaderWithOptions.data(), &ip, sizeof(struct iphdr));
640+
uint16_t checksum =
641+
calculateChecksum(ipHeaderWithOptions, 0, actualHeaderLength);
618642
ip.check = htons(checksum);
619643

620-
const uint8_t* finalIpBytes = reinterpret_cast<const uint8_t*>(&ip);
644+
// Update the header with the correct checksum
645+
std::memcpy(ipHeaderWithOptions.data(), &ip, sizeof(struct iphdr));
646+
647+
// Insert the complete header (with options) at the beginning of the packet
621648
packet.insert(
622-
packet.begin(), finalIpBytes, finalIpBytes + sizeof(struct iphdr));
649+
packet.begin(), ipHeaderWithOptions.begin(), ipHeaderWithOptions.end());
623650
}
624651

625652
std::string IPv4Header::generateScapyCommand() const {
@@ -638,6 +665,36 @@ std::string IPv4Header::generateScapyCommand() const {
638665
command += ", tos=" + std::to_string(ip_.tos);
639666
}
640667

668+
// Add IHL if not the default value of 5
669+
if (ip_.ihl != IPV4_MIN_HEADER_LENGTH) {
670+
command += ", ihl=" + std::to_string(ip_.ihl);
671+
}
672+
673+
// Add flags if non-zero
674+
uint16_t flags =
675+
ntohs(ip_.frag_off) & 0xE000; // Extract flag bits (bits 13-15)
676+
if (flags != 0) {
677+
std::string flagsStr;
678+
if (flags & 0x2000) { // More Fragments (MF) - bit 13
679+
flagsStr += "MF";
680+
}
681+
if (flags & 0x4000) { // Don't Fragment (DF) - bit 14
682+
if (!flagsStr.empty()) {
683+
flagsStr += "+";
684+
}
685+
flagsStr += "DF";
686+
}
687+
if (flags & 0x8000) { // Reserved flag - bit 15
688+
if (!flagsStr.empty()) {
689+
flagsStr += "+";
690+
}
691+
flagsStr += "RF";
692+
}
693+
if (!flagsStr.empty()) {
694+
command += ", flags='" + flagsStr + "'";
695+
}
696+
}
697+
641698
command += ")";
642699
return command;
643700
}
@@ -702,7 +759,8 @@ IPv6Header::IPv6Header(
702759
const std::string& dst,
703760
uint8_t hopLimit,
704761
uint8_t trafficClass,
705-
uint32_t flowLabel) {
762+
uint32_t flowLabel,
763+
uint8_t nextHeader) {
706764
static_assert(
707765
sizeof(ip6_.ip6_src) == 16, "IPv6 source address field size mismatch");
708766
static_assert(
@@ -722,7 +780,8 @@ IPv6Header::IPv6Header(
722780
(flowLabel & IPV6_FLOW_LABEL_MASK);
723781
ip6_.ip6_flow = htonl(version_tc_fl);
724782
ip6_.ip6_plen = 0; // Will be calculated in serialize()
725-
ip6_.ip6_nxt = 0; // Will be updated based on next header
783+
ip6_.ip6_nxt = nextHeader; // Set custom next header or will be updated based
784+
// on next header
726785
ip6_.ip6_hlim = hopLimit;
727786

728787
struct in6_addr src_addr {
@@ -762,11 +821,19 @@ std::string IPv6Header::generateScapyCommand() const {
762821
if (ip6_.ip6_hlim != DEFAULT_IPV6_HOP_LIMIT) {
763822
command += ", hlim=" + std::to_string(ip6_.ip6_hlim);
764823
}
824+
// Add next header if it's set to a specific value (non-zero)
825+
if (ip6_.ip6_nxt != 0) {
826+
command += ", nh=" + std::to_string(ip6_.ip6_nxt);
827+
}
765828
command += ")";
766829
return command;
767830
}
768831

769832
void IPv6Header::updateForNextHeader(Type nextHeaderType) {
833+
if (ip6_.ip6_nxt != 0) {
834+
return;
835+
}
836+
770837
switch (nextHeaderType) {
771838
case UDP_HEADER:
772839
ip6_.ip6_nxt = IPPROTO_UDP;

0 commit comments

Comments
 (0)