diff --git a/src/program/packetblaster/lwaftr/README b/src/program/packetblaster/lwaftr/README index 3ae59a2058..4c340c98c7 100644 --- a/src/program/packetblaster/lwaftr/README +++ b/src/program/packetblaster/lwaftr/README @@ -16,54 +16,69 @@ Usage: packetblaster lwaftr [OPTIONS] --vlan VLANID VLAN tag traffic with VLANID if set - --src_mac SOURCE - Source MAC-Address + --src_mac SOURCE Source MAC-Address Default: 00:00:00:00:00:00 - --dst_mac DESTINATION - Destination MAC-Address + + --dst_mac DESTINATION Destination MAC-Address Default: 00:00:00:00:00:00 - --size SIZES - A comma separated list of numbers. Send packets of - SIZES bytes. The size specifies the lenght of the IPv4 - packet. The actual packet size on the wire is 14 Bytes - longer (Ethernet header). Smallest allowed IPv4 packet - size is 28 (20 Bytes for IPv4 header, 8 Bytes payload - for packet loss detection) - Default: 64,64,64,64,64,64,64,594,594,594,1500 (IMIX) - --b4 IPV6,IPV4,PORT - First B4 Client IPv6 mapped to IPv4 and UDP Port. + + --size SIZES A comma separated list of numbers. Send packets whose + frames are SIZES bytes long. The frame size includes + the size of the packet, including its ethernet + headers, and additionally a 4-byte CRC that is written + and read by the NIC. + + Note that the minimum ethernet frame size is 64 bytes. + While it's technically possible to make smaller frames + and we do allow it, the NIC will pad it up to the + minimum before sending, so it's a bit pointless. + Since Snabb does not see the CRC in the packet, that + means that from Snabb's perspective the minimum useful + packet size is 60 bytes. + + The smallest allowed frame size is 46 bytes, + comprising 14 bytes for the ethernet header, 20 for + the IPv4 header, 8 for the UDP header, and 4 + additional bytes for the ethernet checksum. If the + packet has at least 8 bytes of payload, the generated + packets will include a unique identifier in the + payload as well. + + Default: 64,64,64,64,64,64,64,594,594,594,1500 + + --b4 IPV6,IPV4,PORT First B4 Client IPv6 mapped to IPv4 and UDP Port. B4 IPv6,IPv4 and Port are incremented for every count, then rolled over. Port is incremented by the port number: e.g. 1024 -> 1024, 2048, 3096 .. 64512 (63 in total) Default: 2001:db8::,10.0.0.0,1024 - --aftr IPV6 - IPv6 address of lwaftr server (softwire tunnel endpoint) + + --aftr IPV6 IPv6 address of lwaftr server (softwire tunnel endpoint) Default: 2001:db8:ffff::100 - --ipv4 IPV4 - Public IPv4. Used as source for IPv4 traffic and + + --ipv4 IPV4 Public IPv4. Used as source for IPv4 traffic and as destination in IPv6 packets from B4 Default: 8.8.8.8 - --count COUNT - Number of B4 clients to simulate. + + --count COUNT Number of B4 clients to simulate. Default: 1 - --rate RATE - Rate in MPPS for the generated traffic. Fractions are + + --rate RATE Rate in MPPS for the generated traffic. Fractions are allowed (e.g. 3.148 for IMIX line rate). If set too high, the actual transmitted rate depends on the interfaces capacity. Setting rate to 0 turns it to listening only mode while reporting on incoming packets Default: 1 MPPS - --v4only, -4 - Generate only IPv4 packets from the Internet towards lwaftr - --v6only, -6 - Generate only IPv6 packets from B4 to lwaftr - --duration DURATION - Run for DURATION seconds. + + --v4only, -4 Generate only IPv4 packets from the Internet towards lwaftr + + --v6only, -6 Generate only IPv6 packets from B4 to lwaftr + + --duration DURATION Run for DURATION seconds. Default: unlimited - -V, --verbose - Display verbose link information every second - -h, --help - Print usage information. + + -V, --verbose Display verbose link information every second + + -h, --help Print usage information. This tool generates two types of traffic according to RFC7596: diff --git a/src/program/packetblaster/lwaftr/lib.lua b/src/program/packetblaster/lwaftr/lib.lua index d52039119d..9960fa9783 100644 --- a/src/program/packetblaster/lwaftr/lib.lua +++ b/src/program/packetblaster/lwaftr/lib.lua @@ -33,6 +33,11 @@ local ether_header_ptr_type = ffi.typeof("$*", ether_header_t) local ethernet_header_size = ffi.sizeof(ether_header_t) local OFFSET_ETHERTYPE = 12 +-- The ethernet CRC field is not included in the packet as seen by +-- Snabb, but it is part of the frame and therefore a contributor to the +-- frame size. +local ethernet_crc_size = 4 + local ether_vlan_header_type = ffi.typeof([[ struct { uint16_t tag; @@ -56,6 +61,7 @@ struct { uint8_t dst_ip[4]; } __attribute__((packed)) ]] +local ipv4_header_size = ffi.sizeof(ipv4hdr_t) local ipv4_header_ptr_type = ffi.typeof("$*", ipv4hdr_t) local ipv6_ptr_type = ffi.typeof([[ @@ -367,37 +373,44 @@ function Lwaftrgen:pull () ipv4_udp_hdr.dst_port = C.htons(self.current_port) ipv6_ipv4_udp_hdr.src_port = C.htons(self.current_port) - for _,size in ipairs(self.sizes) do + -- The sizes are frame sizes, including the 4-byte ethernet CRC + -- that we don't see in Snabb. + + local vlan_size = self.vlan and ether_vlan_header_size or 0 + local ethernet_total_size = ethernet_header_size + vlan_size + local minimum_size = ethernet_total_size + ipv4_header_size + + udp_header_size + ethernet_crc_size + for _,size in ipairs(self.sizes) do + assert(size >= minimum_size) + local packet_len = size - ethernet_crc_size + local ipv4_len = packet_len - ethernet_total_size + local udp_len = ipv4_len - ipv4_header_size if not self.ipv6_only then - ipv4_hdr.total_length = C.htons(size) - if self.vlan then - ipv4_udp_hdr.len = C.htons(size - 28 + 4) - self.ipv4_pkt.length = size + ethernet_header_size + 4 - else - ipv4_udp_hdr.len = C.htons(size - 28) - self.ipv4_pkt.length = size + ethernet_header_size - end + ipv4_hdr.total_length = C.htons(ipv4_len) + ipv4_udp_hdr.len = C.htons(udp_len) + self.ipv4_pkt.length = packet_len ipv4_hdr.checksum = 0 - ipv4_hdr.checksum = C.htons(ipsum(self.ipv4_pkt.data + ethernet_header_size, 20, 0)) - self.ipv4_payload.number = self.ipv4_packet_number; - self.ipv4_packet_number = self.ipv4_packet_number + 1 + ipv4_hdr.checksum = C.htons(ipsum(self.ipv4_pkt.data + ethernet_total_size, 20, 0)) + if size >= minimum_size + payload_size then + self.ipv4_payload.number = self.ipv4_packet_number; + self.ipv4_packet_number = self.ipv4_packet_number + 1 + end local ipv4_pkt = packet.clone(self.ipv4_pkt) transmit(output, ipv4_pkt) end if not self.ipv4_only then - ipv6_hdr.payload_length = C.htons(size) - ipv6_ipv4_hdr.total_length = C.htons(size) - if self.vlan then - ipv6_ipv4_udp_hdr.len = C.htons(size - 28 + 4) - self.ipv6_pkt.length = size + 54 + 4 - else - ipv6_ipv4_udp_hdr.len = C.htons(size - 28) - self.ipv6_pkt.length = size + 54 + -- Expectation from callers is to make packets that are SIZE + -- bytes big, *plus* the IPv6 header. + ipv6_hdr.payload_length = C.htons(ipv4_len) + ipv6_ipv4_hdr.total_length = C.htons(ipv4_len) + ipv6_ipv4_udp_hdr.len = C.htons(udp_len) + self.ipv6_pkt.length = packet_len + ipv6_header_size + if size >= minimum_size + payload_size then + self.ipv6_payload.number = self.ipv6_packet_number; + self.ipv6_packet_number = self.ipv6_packet_number + 1 end - self.ipv6_payload.number = self.ipv6_packet_number; - self.ipv6_packet_number = self.ipv6_packet_number + 1 local ipv6_pkt = packet.clone(self.ipv6_pkt) transmit(output, ipv6_pkt) end diff --git a/src/program/packetblaster/lwaftr/lwaftr.lua b/src/program/packetblaster/lwaftr/lwaftr.lua index 2d85469be3..e9c5deeb97 100644 --- a/src/program/packetblaster/lwaftr/lwaftr.lua +++ b/src/program/packetblaster/lwaftr/lwaftr.lua @@ -25,7 +25,7 @@ local long_opts = { duration = "D", -- terminate after n seconds verbose = "V", -- verbose, display stats help = "h", -- display help text - size = "S", -- packet size list (defaults to IMIX) + size = "S", -- frame size list (defaults to IMIX) src_mac = "s", -- source ethernet address dst_mac = "d", -- destination ethernet address vlan = "v", -- VLAN id @@ -64,18 +64,10 @@ function run (args) end local sizes = { 64, 64, 64, 64, 64, 64, 64, 594, 594, 594, 1500 } - local sizes_ipv6 = { 104, 104, 104, 104, 104, 104, 104, 634, 634, 634, 1540 } function opt.S (arg) sizes = {} - sizes_ipv6 = {} for size in string.gmatch(arg, "%d+") do - local s = tonumber(size) - if s < 28 then - s = 28 - print("Warning: Increasing IPv4 packet size to 28") - end - sizes[#sizes+1] = s - sizes_ipv6[#sizes_ipv6+1] = s + 40 + sizes[#sizes + 1] = assert(tonumber(size), "size not a number: "..size) end end @@ -161,6 +153,12 @@ function run (args) args = lib.dogetopt(args, opt, "VD:hS:s:a:d:b:iI:c:r:46p:v:o:t:i:k:", long_opts) + for _,s in ipairs(sizes) do + if s < 18 + (vlan and 4 or 0) + 20 + 8 then + error("Minimum frame size is 46 bytes (18 ethernet+CRC, 20 IPv4, and 8 UDP)") + end + end + if not target then print("either --pci, --tap, --sock, --int or --pcap are required parameters") main.exit(1) @@ -172,14 +170,16 @@ function run (args) if not ipv4_only then print(string.format("IPv6: %s > %s: %s:%d > %s:12345", b4_ipv6, aftr_ipv6, b4_ipv4, b4_port, public_ipv4)) print(" source IPv6 and source IPv4/Port adjusted per client") - print("IPv6 packet sizes: " .. table.concat(sizes_ipv6,",")) + local sizes_ipv6 = {} + for i,size in ipairs(sizes) do sizes_ipv6[i] = size + 40 end + print("IPv6 frame sizes: " .. table.concat(sizes_ipv6,",")) end if not ipv6_only then print() print(string.format("IPv4: %s:12345 > %s:%d", public_ipv4, b4_ipv4, b4_port)) print(" destination IPv4 and Port adjusted per client") - print("IPv4 packet sizes: " .. table.concat(sizes,",")) + print("IPv4 frame sizes: " .. table.concat(sizes,",")) end if ipv4_only and ipv6_only then diff --git a/src/program/packetblaster/lwaftr/test_lwaftr_1.pcap b/src/program/packetblaster/lwaftr/test_lwaftr_1.pcap index 90b0622b61..45f85921c6 100644 Binary files a/src/program/packetblaster/lwaftr/test_lwaftr_1.pcap and b/src/program/packetblaster/lwaftr/test_lwaftr_1.pcap differ diff --git a/src/program/packetblaster/lwaftr/test_lwaftr_2.pcap b/src/program/packetblaster/lwaftr/test_lwaftr_2.pcap index cefd6f4392..287dd1b8fb 100644 Binary files a/src/program/packetblaster/lwaftr/test_lwaftr_2.pcap and b/src/program/packetblaster/lwaftr/test_lwaftr_2.pcap differ diff --git a/src/program/packetblaster/selftest.sh b/src/program/packetblaster/selftest.sh index 1a2ca09f32..aa6a1eaf78 100755 --- a/src/program/packetblaster/selftest.sh +++ b/src/program/packetblaster/selftest.sh @@ -17,6 +17,7 @@ function test_lwaftr_pcap { exit 1 fi cmp $TEMP_PCAP $PCAP + status=$? rm $TEMP_PCAP if [ $status != 0 ]; then echo "Error: lwaftr generated pcap differs from ${PCAP}" @@ -25,7 +26,7 @@ function test_lwaftr_pcap { } test_lwaftr_pcap program/packetblaster/lwaftr/test_lwaftr_1.pcap --count 1 -test_lwaftr_pcap program/packetblaster/lwaftr/test_lwaftr_2.pcap --count 2 --vlan 100 --size 0 +test_lwaftr_pcap program/packetblaster/lwaftr/test_lwaftr_2.pcap --count 2 --vlan 100 --size 50 # lwaftr tap test sudo ip netns add snabbtest || exit $TEST_SKIPPED