mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
internet_checksum.run: test net checksum alorithms
See repos/os/src/test/internet_checksum/README for more detail. Ref #4636
This commit is contained in:
parent
3a0ded3bdd
commit
e350dc27e9
92
repos/os/run/internet_checksum.run
Normal file
92
repos/os/run/internet_checksum.run
Normal file
@ -0,0 +1,92 @@
|
||||
assert_spec linux
|
||||
|
||||
set tshark [installed_command tshark]
|
||||
set trafgen [installed_command trafgen]
|
||||
set lx_fs_root "internet_checksum_dir"
|
||||
set lx_fs_dir "bin/$lx_fs_root"
|
||||
set input_file_name "input.pcap"
|
||||
set input_file "bin/$input_file_name"
|
||||
|
||||
rename exit run_tool_exit
|
||||
proc exit {{code 0}} {
|
||||
global lx_fs_dir
|
||||
global input_file
|
||||
if {[get_cmd_switch --autopilot]} { exec rm -rf $input_file $lx_fs_dir }
|
||||
run_tool_exit $code
|
||||
}
|
||||
build { core init lib/ld lib/vfs test/internet_checksum server/lx_fs }
|
||||
create_boot_directory
|
||||
|
||||
set seed ""
|
||||
if {[info exists ::env(SEED)]} {
|
||||
set seed $::env(SEED)
|
||||
} else {
|
||||
set min_seed 0
|
||||
set max_seed [expr 2**32]
|
||||
set seed [expr int($min_seed + floor(rand() * $max_seed))]
|
||||
}
|
||||
puts "\nUse script with SEED=$seed in order to get reproducible results\n"
|
||||
|
||||
install_config {
|
||||
<config verbose="yes">
|
||||
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="LOG"/>
|
||||
<service name="CPU"/>
|
||||
<service name="PD"/>
|
||||
</parent-provides>
|
||||
|
||||
<start name="lx_fs" ld="no" caps="100">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides> <service name="File_system"/> </provides>
|
||||
<config> <policy label="test-internet_checksum -> " root="/} $lx_fs_root {" writeable="yes"/> </config>
|
||||
<route> <any-service> <parent/> </any-service> </route>
|
||||
</start>
|
||||
|
||||
<start name="test-internet_checksum" caps="100">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config seed="} $seed {"> <vfs> <fs/> </vfs> </config>
|
||||
<route>
|
||||
<service name="File_system"> <child name="lx_fs"/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
</config> }
|
||||
|
||||
exec rm -rf $lx_fs_dir
|
||||
exec mkdir -p $lx_fs_dir
|
||||
exec $trafgen --rand --seed $seed --in [genode_dir]/repos/os/src/test/internet_checksum/trafgen.cfg --num 10000 --out $input_file
|
||||
|
||||
proc assert_no_bad_checksums_in {pcap_file} {
|
||||
global tshark
|
||||
set view_filter "ip.checksum_bad.expert || tcp.checksum_bad.expert || udp.checksum.bad || icmp.checksum_bad"
|
||||
set out "<invalid>"
|
||||
set out [exec $tshark -o ip.check_checksum:TRUE -o tcp.check_checksum:TRUE -o udp.check_checksum:TRUE -Y "$view_filter" -r $pcap_file]
|
||||
if {$out != ""} {
|
||||
puts "\nError: bad checksums found in $pcap_file\n"
|
||||
exit -1
|
||||
}
|
||||
}
|
||||
assert_no_bad_checksums_in $input_file
|
||||
build_boot_image [list {*}[build_artifacts] $lx_fs_root $input_file_name]
|
||||
append qemu_args " -nographic "
|
||||
run_genode_until {\[init\] child "test-internet_checksum" exited.*?\n} 30
|
||||
|
||||
set output_file "$lx_fs_dir/output.pcap"
|
||||
assert_no_bad_checksums_in $output_file
|
||||
set num_checked_checksums ""
|
||||
set string_buf ""
|
||||
regexp {checked [0-9]+ checksums} $output string_buf
|
||||
regexp {[0-9]+} $string_buf num_checked_checksums
|
||||
|
||||
set tmp_file "$lx_fs_dir/tmp"
|
||||
exec $tshark -o ip.check_checksum:TRUE -o tcp.check_checksum:TRUE -o udp.check_checksum:TRUE -V -r $output_file | grep -e "Checksum Status:" -e "Header checksum status:" > $tmp_file
|
||||
set num_output_checksums [exec wc -l < $tmp_file]
|
||||
if {$num_checked_checksums != $num_output_checksums} {
|
||||
puts "\nError: number of checksums in $output_file differs from number of checked checksums\n"
|
||||
exit -1
|
||||
}
|
||||
grep_output {\[init\] child "test-internet_checksum" exited}
|
||||
compare_output_to {[init] child "test-internet_checksum" exited with exit value 0}
|
19
repos/os/src/test/internet_checksum/README
Normal file
19
repos/os/src/test/internet_checksum/README
Normal file
@ -0,0 +1,19 @@
|
||||
This test aims for testing the typical use of the internet checksum algorithms
|
||||
provided with the net library. It is accompanied by a similarly named test
|
||||
script.
|
||||
|
||||
The test script feeds the test component with a sufficiently big input.pcap
|
||||
file that is randomly generated on each run using trafgen. The test component
|
||||
then iterates over all packets in the file. For each found IPv4, UDP, TCP or
|
||||
ICMP header (except the embedded ones in ICMP errors), it typically does the
|
||||
following:
|
||||
|
||||
1. try to validate the initial checksum
|
||||
2. try to re-calculate the initial checksum and see if it stas the same
|
||||
3. modify the header randomly, update the checksum incrementally, and write
|
||||
out the result to a file output.pcap
|
||||
|
||||
The checksums in the resulting output.pcap file are then checked by the test
|
||||
script using tshark. On each run, the test script prints the seed used for
|
||||
randomization in both, the test component and trafgen. In order to reproduce a
|
||||
given test result, one can simply run the test script with SEED=<seed>.
|
275
repos/os/src/test/internet_checksum/main.cc
Normal file
275
repos/os/src/test/internet_checksum/main.cc
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* \brief Test the reachability of a host on an IP network
|
||||
* \author Martin Stein
|
||||
* \date 2018-03-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <net/ipv4.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/icmp.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/udp.h>
|
||||
#include <net/internet_checksum.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/sleep.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <os/vfs.h>
|
||||
|
||||
using namespace Net;
|
||||
using namespace Genode;
|
||||
|
||||
#define ASSERT(condition) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
Genode::error(__FILE__, ":", __LINE__, ": ", " assertion \"", #condition, "\" failed "); \
|
||||
Genode::sleep_forever(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define ASSERT_NEVER_REACHED \
|
||||
do { \
|
||||
Genode::error(__FILE__, ":", __LINE__, ": ", " should have never been reached"); \
|
||||
Genode::sleep_forever(); \
|
||||
} while (false)
|
||||
|
||||
|
||||
struct Parser
|
||||
{
|
||||
char *start;
|
||||
size_t num_bytes;
|
||||
|
||||
void advance_by(size_t num_bytes)
|
||||
{
|
||||
ASSERT(num_bytes >= num_bytes);
|
||||
start += num_bytes;
|
||||
num_bytes -= num_bytes;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T const &fetch()
|
||||
{
|
||||
ASSERT(num_bytes >= sizeof(T));
|
||||
T const &obj = *(T const *)start;
|
||||
advance_by(sizeof(T));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void fetch(Byte_range_ptr const &dst)
|
||||
{
|
||||
ASSERT(num_bytes >= dst.num_bytes);
|
||||
memcpy(dst.start, start, dst.num_bytes);
|
||||
advance_by(dst.num_bytes);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Pseudo_random_number_generator
|
||||
{
|
||||
uint64_t seed;
|
||||
|
||||
uint8_t random_byte()
|
||||
{
|
||||
seed = (16807 * seed) % 2147483647;
|
||||
return (uint8_t)seed;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Pcap_file_header
|
||||
{
|
||||
static constexpr uint32_t MAGIC_NUMBER = 0xA1B2C3D4;
|
||||
|
||||
uint32_t magic_number;
|
||||
uint32_t unused[5];
|
||||
|
||||
bool valid() const { return magic_number == MAGIC_NUMBER; }
|
||||
};
|
||||
|
||||
|
||||
struct Pcap_packet_record
|
||||
{
|
||||
uint32_t unused_0[2];
|
||||
uint32_t captured_pkt_len;
|
||||
uint32_t original_pkt_len;
|
||||
};
|
||||
|
||||
|
||||
struct Main
|
||||
{
|
||||
Env &env;
|
||||
Heap heap { env.ram(), env.rm() };
|
||||
Attached_rom_dataspace config_rom { env, "config" };
|
||||
Root_directory root { env, heap, config_rom.xml().sub_node("vfs") };
|
||||
Reconstructible<Append_file> pcap_file { root, "/output.pcap" };
|
||||
Attached_rom_dataspace pcap_rom { env, "input.pcap" };
|
||||
Parser pcap_parser { pcap_rom.local_addr<char>(), pcap_rom.size() };
|
||||
unsigned long num_errors = 0;
|
||||
unsigned long num_packets = 0;
|
||||
unsigned long num_ip4_checksums = 0;
|
||||
unsigned long num_udp_checksums = 0;
|
||||
unsigned long num_tcp_checksums = 0;
|
||||
unsigned long num_icmp_checksums = 0;
|
||||
Pseudo_random_number_generator prng { config_rom.xml().attribute_value("seed", 0ULL) };
|
||||
|
||||
Main(Env &env);
|
||||
|
||||
void check_tcp(Tcp_packet &tcp, Ipv4_packet &ip, size_t tcp_size);
|
||||
|
||||
void check_udp(Udp_packet &udp, Ipv4_packet &ip);
|
||||
|
||||
void check_icmp(Icmp_packet &icmp, size_t icmp_size);
|
||||
|
||||
void check_ip4(Ipv4_packet &ip);
|
||||
|
||||
void modify_ip4(Ipv4_packet &ip, Internet_checksum_diff &ip_icd);
|
||||
|
||||
void check_recalculated_checksum(char const *prot, uint16_t got_checksum, uint16_t expect_checksum)
|
||||
{
|
||||
if (got_checksum != expect_checksum) {
|
||||
error("frame ", num_packets + 1, ": ", prot, ": re-calculating initial checksum failed (got ",
|
||||
Hex(got_checksum)," expected ", Hex(expect_checksum), " diff ", expect_checksum - got_checksum, ")");
|
||||
num_errors++;
|
||||
}
|
||||
}
|
||||
|
||||
void validate_initial_checksum_error(char const *prot)
|
||||
{
|
||||
error("frame ", num_packets + 1, ": ", prot, ": validating initial checksum failed");
|
||||
num_errors++;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Main::check_icmp(Icmp_packet &icmp, size_t icmp_size)
|
||||
{
|
||||
uint16_t initial_checksum = icmp.checksum();
|
||||
size_t l5_size = icmp_size - sizeof(Icmp_packet);
|
||||
if (icmp.checksum_error(l5_size))
|
||||
validate_initial_checksum_error("icmp");
|
||||
icmp.update_checksum(l5_size);
|
||||
check_recalculated_checksum("icmp", icmp.checksum(), initial_checksum);
|
||||
num_icmp_checksums++;
|
||||
}
|
||||
|
||||
|
||||
void Main::check_udp(Udp_packet &udp, Ipv4_packet &ip)
|
||||
{
|
||||
uint16_t initial_checksum = udp.checksum();
|
||||
if (udp.checksum_error(ip.src(), ip.dst()))
|
||||
validate_initial_checksum_error("udp");
|
||||
udp.update_checksum(ip.src(), ip.dst());
|
||||
check_recalculated_checksum("udp", udp.checksum(), initial_checksum);
|
||||
num_udp_checksums++;
|
||||
}
|
||||
|
||||
|
||||
void Main::check_tcp(Tcp_packet &tcp, Ipv4_packet &ip, size_t tcp_size)
|
||||
{
|
||||
uint16_t initial_checksum = tcp.checksum();
|
||||
tcp.update_checksum(ip.src(), ip.dst(), tcp_size);
|
||||
check_recalculated_checksum("tcp", tcp.checksum(), initial_checksum);
|
||||
num_tcp_checksums++;
|
||||
}
|
||||
|
||||
|
||||
void Main::check_ip4(Ipv4_packet &ip)
|
||||
{
|
||||
uint16_t initial_checksum = ip.checksum();
|
||||
if (ip.checksum_error())
|
||||
validate_initial_checksum_error("ip");
|
||||
ip.update_checksum();
|
||||
check_recalculated_checksum("ip", ip.checksum(), initial_checksum);
|
||||
num_ip4_checksums++;
|
||||
}
|
||||
|
||||
|
||||
void Main::modify_ip4(Ipv4_packet &ip, Internet_checksum_diff &ip_icd)
|
||||
{
|
||||
Ipv4_address ip_src = ip.src();
|
||||
ip_src.addr[0] &= prng.random_byte();
|
||||
ip_src.addr[1] |= prng.random_byte();
|
||||
ip_src.addr[2] *= prng.random_byte();
|
||||
ip_src.addr[3] += prng.random_byte();
|
||||
ip.src(ip_src, ip_icd);
|
||||
|
||||
if (prng.random_byte() & 0b11)
|
||||
return;
|
||||
|
||||
Ipv4_address ip_dst = ip.dst();
|
||||
ip_dst.addr[0] |= prng.random_byte();
|
||||
ip_dst.addr[1] += prng.random_byte();
|
||||
ip_dst.addr[2] &= prng.random_byte();
|
||||
ip_dst.addr[3] *= prng.random_byte();
|
||||
ip.dst(ip_dst, ip_icd);
|
||||
}
|
||||
|
||||
|
||||
Main::Main(Env &env) : env(env)
|
||||
{
|
||||
using Append_result = Append_file::Append_result;
|
||||
static constexpr size_t BUF_SIZE = 1024;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
Pcap_file_header const &header = pcap_parser.fetch<Pcap_file_header>();
|
||||
ASSERT(header.valid());
|
||||
ASSERT(pcap_file->append((char *)&header, sizeof(header)) == Append_result::OK);
|
||||
|
||||
while(1) {
|
||||
if (pcap_parser.num_bytes < sizeof(Pcap_packet_record))
|
||||
break;
|
||||
|
||||
Pcap_packet_record const &record = pcap_parser.fetch<Pcap_packet_record>();
|
||||
if (!record.captured_pkt_len)
|
||||
break;
|
||||
|
||||
ASSERT(pcap_file->append((char *)&record, sizeof(record)) == Append_result::OK);
|
||||
ASSERT(record.captured_pkt_len == record.original_pkt_len);
|
||||
ASSERT(record.captured_pkt_len <= BUF_SIZE);
|
||||
pcap_parser.fetch({buf, record.captured_pkt_len});
|
||||
|
||||
Size_guard size_guard(record.captured_pkt_len);
|
||||
Ethernet_frame ð = Ethernet_frame::cast_from(buf, size_guard);
|
||||
ASSERT(eth.type() == Ethernet_frame::Type::IPV4);
|
||||
Ipv4_packet &ip = eth.data<Ipv4_packet>(size_guard);
|
||||
check_ip4(ip);
|
||||
size_t l4_size = ip.total_length() - sizeof(Ipv4_packet);
|
||||
switch (ip.protocol()) {
|
||||
case Ipv4_packet::Protocol::TCP: check_tcp(ip.data<Tcp_packet>(size_guard), ip, l4_size); break;
|
||||
case Ipv4_packet::Protocol::UDP: check_udp(ip.data<Udp_packet>(size_guard), ip); break;
|
||||
case Ipv4_packet::Protocol::ICMP: check_icmp(ip.data<Icmp_packet>(size_guard), l4_size); break;
|
||||
default: break;
|
||||
}
|
||||
Internet_checksum_diff ip_icd { };
|
||||
modify_ip4(ip, ip_icd);
|
||||
switch (ip.protocol()) {
|
||||
case Ipv4_packet::Protocol::TCP: ip.data<Tcp_packet>(size_guard).update_checksum(ip.src(), ip.dst(), l4_size); break;
|
||||
case Ipv4_packet::Protocol::UDP: ip.data<Udp_packet>(size_guard).update_checksum(ip.src(), ip.dst()); break;
|
||||
case Ipv4_packet::Protocol::ICMP: ip.data<Icmp_packet>(size_guard).update_checksum(l4_size - sizeof(Icmp_packet)); break;
|
||||
default: break;
|
||||
}
|
||||
ip.update_checksum(ip_icd);
|
||||
ip.checksum(ip.checksum());
|
||||
ASSERT(pcap_file->append((char *)ð, record.captured_pkt_len) == Append_result::OK);
|
||||
num_packets++;
|
||||
}
|
||||
unsigned long num_checksums = num_ip4_checksums + num_udp_checksums + num_tcp_checksums + num_icmp_checksums;
|
||||
log("checked ", num_checksums, " checksum", num_checksums == 1 ? "" : "s",
|
||||
" (ip4 ", num_ip4_checksums, " tcp ", num_tcp_checksums," udp ", num_udp_checksums, " icmp ", num_icmp_checksums,
|
||||
") in ", num_packets, " packet", num_packets == 1 ? "" : "s", " with ", num_errors, " error", num_errors == 1 ? "" : "s");
|
||||
|
||||
pcap_file.destruct();
|
||||
env.parent().exit(num_errors ? -1 : 0);
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Env &env) { static Main main(env); }
|
7
repos/os/src/test/internet_checksum/target.mk
Normal file
7
repos/os/src/test/internet_checksum/target.mk
Normal file
@ -0,0 +1,7 @@
|
||||
TARGET = test-internet_checksum
|
||||
|
||||
LIBS += base net vfs
|
||||
|
||||
SRC_CC += main.cc
|
||||
|
||||
INC_DIR += $(PRG_DIR)
|
61
repos/os/src/test/internet_checksum/trafgen.cfg
Normal file
61
repos/os/src/test/internet_checksum/trafgen.cfg
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* FIXME: Currently, we skip testing odd ICMP packet lengths because trafgen
|
||||
* generates bad checksums for such packets.
|
||||
*/
|
||||
|
||||
/* UDP */
|
||||
{
|
||||
eth(saddr=drnd(), daddr=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
udp(sport=drnd(), dport=drnd()),
|
||||
drnd(801)
|
||||
}
|
||||
{
|
||||
eth(saddr=drnd(), daddr=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
udp(sport=drnd(), dport=drnd()),
|
||||
drnd(80)
|
||||
}
|
||||
/* TCP */
|
||||
{
|
||||
eth(saddr=drnd(), daddr=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
tcp(sport=drnd(), dport=drnd(), seq=drnd(), ackseq=drnd()),
|
||||
drnd(701)
|
||||
}
|
||||
{
|
||||
eth(saddr=drnd(), daddr=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
tcp(sport=drnd(), dport=drnd(), seq=drnd(), ackseq=drnd()),
|
||||
drnd(70)
|
||||
}
|
||||
/* ICMP echo */
|
||||
{
|
||||
eth(saddr=drnd(), daddr=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
icmp4(echorequest, addr=drnd(), id=drnd(), seq=drnd()),
|
||||
drnd(600)
|
||||
}
|
||||
{
|
||||
eth(saddr=drnd(), daddr=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
icmp4(echoreply, addr=drnd(), id=drnd(), seq=drnd()),
|
||||
drnd(60)
|
||||
}
|
||||
/* ICMP error + embedded packet */
|
||||
{
|
||||
eth(saddr=drnd(), daddr=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
icmp4(type=1, code=drnd(0,16), addr=drnd(), id=drnd(), seq=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
udp(sport=drnd(), dport=drnd()),
|
||||
drnd(500)
|
||||
}
|
||||
{
|
||||
eth(saddr=drnd(), daddr=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
icmp4(type=1, code=drnd(0,16), addr=drnd(), id=drnd(), seq=drnd()),
|
||||
ip4(saddr=drnd(), daddr=drnd(), ttl=drnd(), id=drnd()),
|
||||
tcp(sport=drnd(), dport=drnd(), seq=drnd(), ackseq=drnd()),
|
||||
drnd(50)
|
||||
}
|
@ -22,6 +22,7 @@ hello
|
||||
ieee754
|
||||
init_smp
|
||||
intel_fb
|
||||
internet_checksum
|
||||
libc_integration
|
||||
libc_vfs_fs_ext2
|
||||
libc_vfs_fs_fat
|
||||
|
Loading…
Reference in New Issue
Block a user