From e49644924e6009555cfd45cf72f1a93e037b23dd Mon Sep 17 00:00:00 2001 From: Heikki Hannikainen Date: Mon, 24 Oct 2022 00:59:58 +0300 Subject: [PATCH] sctp: Add a test for SCTP uplink between servers --- tests/cfg-aprsc/sctp-hub | 42 +++++++++ tests/cfg-aprsc/sctp-leaf | 45 +++++++++ tests/libperl/runproduct.pm | 21 +++-- tests/t/69sctp.t | 181 ++++++++++++++++++++++++++++++++++++ 4 files changed, 282 insertions(+), 7 deletions(-) create mode 100644 tests/cfg-aprsc/sctp-hub create mode 100644 tests/cfg-aprsc/sctp-leaf create mode 100644 tests/t/69sctp.t diff --git a/tests/cfg-aprsc/sctp-hub b/tests/cfg-aprsc/sctp-hub new file mode 100644 index 0000000..c61d5e5 --- /dev/null +++ b/tests/cfg-aprsc/sctp-hub @@ -0,0 +1,42 @@ + +# sctp hub + +ServerId TESTING-1 +PassCode 31421 +MyEmail email@example.com +MyAdmin "Admin, N0CALL" + +### Directories ######### +# Data directory (for database files) +RunDir data + +### Intervals ######### +# Interval specification format examples: +# 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... + +# When no data is received from an upstream server in N seconds, switch to +# another server +UpstreamTimeout 90s + +# When no data is received from a downstream server in N seconds, disconnect +ClientTimeout 48h + +### TCP listener ########## +# Listen tcp
+# socketname: any name you wish to show up in logs and statistics +# porttype: one of: +# fullfeed - everything, after dupe filtering +# dupefeed - everything that comes in - with dupes! +# msgonly - messages only +# userfilter - user-specified filters +# +Listen "Full feed" fullfeed tcp ::0 55152 acl "cfg-aprsc/acl-all.acl" +Listen "Full feed SCTP" fullfeed sctp ::0 55152 acl "cfg-aprsc/acl-all.acl" +Listen "Igate port" igate tcp 0.0.0.0 55582 acl "cfg-aprsc/acl-all.acl" + +### HTTP listener ########## +# Status port provides a status view to web browsers. +# It starts up by default on 0.0.0.0:14501. +HTTPStatus 127.0.0.1 55501 + + diff --git a/tests/cfg-aprsc/sctp-leaf b/tests/cfg-aprsc/sctp-leaf new file mode 100644 index 0000000..aacf1c7 --- /dev/null +++ b/tests/cfg-aprsc/sctp-leaf @@ -0,0 +1,45 @@ + +# sctp hub + +ServerId TESTING-2 +PassCode 31421 +MyEmail email@example.com +MyAdmin "Admin, N0CALL" + +### Directories ######### +# Data directory (for database files) +RunDir data + +### Intervals ######### +# Interval specification format examples: +# 600 (600 seconds), 5m, 2h, 1h30m, 1d3h15m24s, etc... + +# When no data is received from an upstream server in N seconds, switch to +# another server +UpstreamTimeout 90s + +# When no data is received from a downstream server in N seconds, disconnect +ClientTimeout 48h + +### TCP/SCTP listener ########## +# Listen tcp
+# socketname: any name you wish to show up in logs and statistics +# porttype: one of: +# fullfeed - everything, after dupe filtering +# dupefeed - everything that comes in - with dupes! +# msgonly - messages only +# userfilter - user-specified filters +# +Listen "Full feed" fullfeed tcp ::0 55153 acl "cfg-aprsc/acl-all.acl" +Listen "Full feed SCTP" fullfeed sctp ::0 55153 acl "cfg-aprsc/acl-all.acl" +Listen "Igate port" igate tcp 0.0.0.0 55583 acl "cfg-aprsc/acl-all.acl" + +### SCTP uplink +Uplink fullsctp full sctp 127.0.0.1 55152 + +### HTTP listener ########## +# Status port provides a status view to web browsers. +# It starts up by default on 0.0.0.0:14501. +HTTPStatus 127.0.0.1 55502 + + diff --git a/tests/libperl/runproduct.pm b/tests/libperl/runproduct.pm index f8e33c5..71d02bb 100644 --- a/tests/libperl/runproduct.pm +++ b/tests/libperl/runproduct.pm @@ -27,7 +27,6 @@ my %products = ( 'cfgdir' => 'cfg-aprsc', 'pidfile' => 'logs/aprsc.pid', 'env' => { 'APRSC_NO_VERSION_REPORT' => '1' } - }, 'javap' => { 'binary' => './javaprssrvr/java', @@ -48,11 +47,11 @@ my %products = ( } ); -sub new($$) +sub new($$;$) { - my($class, $config) = @_; - my $self = bless { @_ }, $class; - + my($class, $config, $instance) = @_; + my $self = bless { }, $class; + if (defined $ENV{'TEST_PRODUCT'}) { $self->{'prod_name'} = $ENV{'TEST_PRODUCT'}; } else { @@ -78,6 +77,14 @@ sub new($$) $self->{'cmdline'} = $prod->{'binary'} . ' ' . $prod->{'stdargs'} . ' ' . $prod->{'cfgfileargs'} . ' ' . $cfgfile; + + if (defined $prod->{'pidfile'}) { + $self->{'pidfile'} = $prod->{'pidfile'}; + } + if (defined $instance) { + $self->{'pidfile'} = $instance . '.pid'; + $self->{'cmdline'} .= " -p " . $self->{'pidfile'}; + } $self->{'error'} = 'No errors yet'; @@ -101,8 +108,8 @@ sub start($) return "Product already running."; } - if (defined $self->{'prod'}->{'pidfile'}) { - my $pf = $self->{'prod'}->{'pidfile'}; + if (defined $self->{'pidfile'}) { + my $pf = $self->{'pidfile'}; if (open(PF, $pf)) { my $pl = ; close(PF); diff --git a/tests/t/69sctp.t b/tests/t/69sctp.t new file mode 100644 index 0000000..f2f32c4 --- /dev/null +++ b/tests/t/69sctp.t @@ -0,0 +1,181 @@ + +# +# Test SCTP, only on aprsc +# + +use Test; + +BEGIN { + plan tests => (!defined $ENV{'TEST_PRODUCT'} || $ENV{'TEST_PRODUCT'} =~ /aprsc/) ? 4 + 1 + 4 + 3 + 2 : 0; +}; + +if (defined $ENV{'TEST_PRODUCT'} && $ENV{'TEST_PRODUCT'} !~ /aprsc/) { + exit(0); +} + +use runproduct; +use LWP; +use LWP::UserAgent; +use HTTP::Request::Common; +use JSON::XS; +use Ham::APRS::IS; +use istest; +use Data::Dumper; + +# set up the JSON module +my $json = new JSON::XS; + +if (!$json) { + die "JSON loading failed"; +} + +$json->latin1(0); +$json->ascii(1); +$json->utf8(0); + +# set up http client ############ + +my $ua = LWP::UserAgent->new; + +$ua->agent( + agent => "httpaprstester/1.0", + timeout => 10, + max_redirect => 0, +); + +# set up two aprsc processes, the leaf connects to the hub with SCTP +my $phub = new runproduct('sctp-hub', 'hub'); +my $pleaf = new runproduct('sctp-leaf', 'leaf'); + +ok(defined $phub, 1, "Failed to initialize product runner"); +ok(defined $pleaf, 1, "Failed to initialize product runner"); +ok($phub->start(), 1, "Failed to start hub"); +ok($pleaf->start(), 1, "Failed to start leaf"); + +# wait for the leaf to connect to the hub +my $timeout_at = time() + 10; + +my $uplink; + +while (time() < $timeout_at) { + # first get the status page without any traffic + $res = $ua->simple_request(HTTP::Request::Common::GET("http://127.0.0.1:55502/status.json")); + if ($res->code ne 200) { + continue; + } + my $j1 = $json->decode($res->decoded_content(charset => 'none')); + if (!defined $j1) { + continue; + } + if (!defined $j1->{'uplinks'} || length($j1->{'uplinks'}) < 1) { + continue; + } + $uplink = $j1->{'uplinks'}->[0]; + if ($uplink->{'username'} eq 'TESTING-1') { + last; + } + undef $uplink; + sleep(1); +} + +ok(defined $uplink, 1, "Leaf server failed to establish an SCTP uplink with hub"); + +# establish connections + +my $login_tx = "N0GAT"; +my $i_tx = new Ham::APRS::IS("localhost:55583", $login_tx); +ok(defined $i_tx, 1, "Failed to initialize Ham::APRS::IS"); + +my $login_rx = "N1GAT"; +my $i_rx = new Ham::APRS::IS("localhost:55152", $login_rx); +ok(defined $i_rx, 1, "Failed to initialize Ham::APRS::IS"); + +$ret = $i_rx->connect('retryuntil' => 8); +ok($ret, 1, "Failed to connect to the server: " . $i_rx->{'error'}); + +my $ret; +$ret = $i_tx->connect('retryuntil' => 8); +ok($ret, 1, "Failed to connect to the server: " . $i_tx->{'error'}); + +############################################ + +my $flush_interval = 300; +my $bytelimit = 4*1024*1024; +my $window = 64*1024; +#my $window = 2*1024; +my $outstanding = 0; +my $txn = 0; +my $rxn = 0; +my $txl = 0; +my $rxl = 0; +my @l = (); +my $txq = ''; +my $txq_l = 0; + +my $start_t = time(); + +while ($txl < $bytelimit) { + $s = "M" . ($txn % 10000 + 10) . ">APRS,qAR,$login_tx:!6028.51N/02505.68E# packet $txn blaa blaa blaa blaa END"; + push @l, $s; + $s .= "\r\n"; + my $sl = length($s); + $txl += $sl; + $txq_l += $sl; + $txq .= $s; + $txn++; + + if ($txq_l >= $flush_interval) { + $i_tx->sendline($txq, 1); + $outstanding += $txq_l; + $txq_l = 0; + $txq = ''; + } + + while (($outstanding > $window) && (my $rx = $i_rx->getline_noncomment(1))) { + my $exp = shift @l; + if ($exp ne $rx) { + warn "Ouch, received wrong packet: $rx\nExpected: $exp"; + } + my $rx_l = length($rx) + 2; + $outstanding -= $rx_l; + $rxn++; + $rxl += $rx_l; + } +} + +if ($txq_l > 0) { + warn "flushing the rest\n"; + $i_tx->sendline($txq, 1); + $outstanding += $txq_l; + $txq_l = 0; + $txq = 0; +} + +warn "reading the rest, have received $rxn packets, sent $txn\n"; +while (($outstanding > 0) && (my $rx = $i_rx->getline_noncomment(2))) { + my $exp = shift @l; + if ($exp ne $rx) { + warn "Ouch, received wrong packet: $rx\n"; + } + my $rx_l = length($rx) + 2; + $outstanding -= $rx_l; + $rxn++; + $rxl += $rx_l; +} +warn "after reading the rest, have received $rxn packets, sent $txn, outstanding $outstanding bytes\n"; + +$end_t = time(); +$dur_t = $end_t - $start_t; + +warn sprintf("took %.3f seconds, %.0f packets/sec\n", $dur_t, $rxn / $dur_t); + +ok($rxn, $txn, "Received wrong number of lines from blob"); +ok($rxl, $txl, "Received wrong number of bytes from blob"); +ok($outstanding, 0, "There are outstanding bytes in the server after timeout"); + +# stop + +ok($phub->stop(), 1, "Failed to stop hub"); +ok($pleaf->stop(), 1, "Failed to stop leaf"); + +