summaryrefslogtreecommitdiffstats
path: root/bin/tests/system/statschannel
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
commitea648e70a989cca190cd7403fe892fd2dcc290b4 (patch)
treee2b6b1c647da68b0d4d66082835e256eb30970e8 /bin/tests/system/statschannel
parentInitial commit. (diff)
downloadbind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.tar.xz
bind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.zip
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--bin/tests/system/statschannel/clean.sh20
-rw-r--r--bin/tests/system/statschannel/fetch.pl41
-rw-r--r--bin/tests/system/statschannel/ns2/example.db47
-rw-r--r--bin/tests/system/statschannel/ns2/named.conf.in40
-rw-r--r--bin/tests/system/statschannel/prereq.sh25
-rw-r--r--bin/tests/system/statschannel/server-json.pl33
-rw-r--r--bin/tests/system/statschannel/server-xml.pl23
-rw-r--r--bin/tests/system/statschannel/setup.sh17
-rw-r--r--bin/tests/system/statschannel/tests.sh215
-rw-r--r--bin/tests/system/statschannel/traffic-json.pl47
-rw-r--r--bin/tests/system/statschannel/traffic-xml.pl44
-rw-r--r--bin/tests/system/statschannel/traffic.expect.12
-rw-r--r--bin/tests/system/statschannel/traffic.expect.24
-rw-r--r--bin/tests/system/statschannel/traffic.expect.45
-rw-r--r--bin/tests/system/statschannel/traffic.expect.57
-rw-r--r--bin/tests/system/statschannel/traffic.expect.68
16 files changed, 578 insertions, 0 deletions
diff --git a/bin/tests/system/statschannel/clean.sh b/bin/tests/system/statschannel/clean.sh
new file mode 100644
index 0000000..477f209
--- /dev/null
+++ b/bin/tests/system/statschannel/clean.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+rm -f traffic traffic.out.*
+rm -f dig.out*
+rm -f */named.memstats
+rm -f */named.conf
+rm -f */named.run
+rm -f ns*/named.lock
+rm -f ns*/named.stats
+rm -f xml.*stats json.*stats
+rm -f compressed.headers regular.headers compressed.out regular.out
diff --git a/bin/tests/system/statschannel/fetch.pl b/bin/tests/system/statschannel/fetch.pl
new file mode 100644
index 0000000..e589c45
--- /dev/null
+++ b/bin/tests/system/statschannel/fetch.pl
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+# fetch.pl:
+# Simple script to fetch HTTP content from the statistics channel
+# of a BIND server. Fetches the full XML stats from 10.53.0.2 port
+# 8853 by default; these can be overridden by command line arguments.
+
+use File::Fetch;
+use Getopt::Std;
+
+sub usage {
+ print ("Usage: fetch.pl [-s address] [-p port] [path]\n");
+ exit 1;
+}
+
+my %options={};
+getopts("s:p:", \%options);
+
+my $addr = "10.53.0.2";
+$addr = $options{a} if defined $options{a};
+
+my $path = 'xml/v3';
+if (@ARGV >= 1) {
+ $path = shift @ARGV;
+}
+
+my $port = 8853;
+$port = $options{p} if defined $options{p};
+
+my $ff = File::Fetch->new(uri => "http://$addr:$port/$path");
+my $file = $ff->fetch() or die $ff->error;
+print ("$file\n");
diff --git a/bin/tests/system/statschannel/ns2/example.db b/bin/tests/system/statschannel/ns2/example.db
new file mode 100644
index 0000000..b65651a
--- /dev/null
+++ b/bin/tests/system/statschannel/ns2/example.db
@@ -0,0 +1,47 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$ORIGIN .
+$TTL 300 ; 5 minutes
+example IN SOA mname1. . (
+ 1 ; serial
+ 20 ; refresh (20 seconds)
+ 20 ; retry (20 seconds)
+ 1814400 ; expire (3 weeks)
+ 3600 ; minimum (1 hour)
+ )
+example. NS ns2.example.
+ns2.example. A 10.53.0.2
+
+$ORIGIN example.
+a A 10.0.0.1
+ MX 10 mail.example.
+short TXT "short text"
+long TXT (
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ "longlonglonglonglonglonglonglonglonglong"
+ )
+
+mail A 10.0.0.2
diff --git a/bin/tests/system/statschannel/ns2/named.conf.in b/bin/tests/system/statschannel/ns2/named.conf.in
new file mode 100644
index 0000000..bababf6
--- /dev/null
+++ b/bin/tests/system/statschannel/ns2/named.conf.in
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+options {
+ query-source address 10.53.0.2;
+ notify-source 10.53.0.2;
+ transfer-source 10.53.0.2;
+ port @PORT@;
+ pid-file "named.pid";
+ listen-on { 10.53.0.2; };
+ listen-on-v6 { none; };
+ recursion no;
+ notify yes;
+ version none; // make statistics independent of the version number
+};
+
+statistics-channels { inet 10.53.0.2 port @EXTRAPORT1@ allow { localhost; }; };
+
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm hmac-sha256;
+};
+
+controls {
+ inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+zone "example" {
+ type master;
+ file "example.db";
+ allow-transfer { any; };
+};
diff --git a/bin/tests/system/statschannel/prereq.sh b/bin/tests/system/statschannel/prereq.sh
new file mode 100644
index 0000000..f3d4fd1
--- /dev/null
+++ b/bin/tests/system/statschannel/prereq.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+fail=0
+
+if $PERL -e 'use File::Fetch;' 2>/dev/null
+then
+ :
+else
+ echo_i "This test requires the File::Fetch library." >&2
+ fail=1
+fi
+
+exit $fail
diff --git a/bin/tests/system/statschannel/server-json.pl b/bin/tests/system/statschannel/server-json.pl
new file mode 100644
index 0000000..cb1f463
--- /dev/null
+++ b/bin/tests/system/statschannel/server-json.pl
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+# server-json.pl:
+# Parses the JSON version of the server stats into a normalized format.
+
+use JSON;
+
+open(INPUT, "<json.stats");
+my $text = do{local$/;<INPUT>};
+close(INPUT);
+
+my $ref = decode_json($text);
+foreach $key (keys %{$ref->{opcodes}}) {
+ print "opcode " . $key . ": " . $ref->{opcodes}->{$key} . "\n";
+}
+foreach $key (keys %{$ref->{rcodes}}) {
+ print "rcode " . $key . ": " . $ref->{rcodes}->{$key} . "\n";
+}
+foreach $key (keys %{$ref->{qtypes}}) {
+ print "qtype " . $key . ": " . $ref->{qtypes}->{$key} . "\n";
+}
+foreach $key (keys %{$ref->{nsstats}}) {
+ print "nsstat " . $key . ": " . $ref->{nsstats}->{$key} . "\n";
+}
diff --git a/bin/tests/system/statschannel/server-xml.pl b/bin/tests/system/statschannel/server-xml.pl
new file mode 100644
index 0000000..2003024
--- /dev/null
+++ b/bin/tests/system/statschannel/server-xml.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/perl
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+# server-xml.pl:
+# Parses the XML version of the server stats into a normalized format.
+
+use XML::Simple;
+
+my $ref = XMLin("xml.stats");
+my $counters = $ref->{server}->{counters};
+foreach $group (@$counters) {
+ foreach $key (keys %{$group->{counter}}) {
+ print $group->{type} . " " . $key . ": ". $group->{counter}->{$key}->{content} . "\n";
+ }
+}
diff --git a/bin/tests/system/statschannel/setup.sh b/bin/tests/system/statschannel/setup.sh
new file mode 100644
index 0000000..e7ad741
--- /dev/null
+++ b/bin/tests/system/statschannel/setup.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+$SHELL clean.sh
+
+copy_setports ns2/named.conf.in ns2/named.conf
diff --git a/bin/tests/system/statschannel/tests.sh b/bin/tests/system/statschannel/tests.sh
new file mode 100644
index 0000000..780f857
--- /dev/null
+++ b/bin/tests/system/statschannel/tests.sh
@@ -0,0 +1,215 @@
+#!/bin/sh
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+DIGCMD="$DIG @10.53.0.2 -p ${PORT}"
+RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p ${CONTROLPORT} -s"
+
+if [ ! "$HAVEJSONSTATS" ]
+then
+ unset PERL_JSON
+ echo_i "JSON was not configured; skipping" >&2
+elif $PERL -e 'use JSON;' 2>/dev/null
+then
+ PERL_JSON=1
+else
+ unset PERL_JSON
+ echo_i "JSON tests require JSON library; skipping" >&2
+fi
+
+if [ ! "$HAVEXMLSTATS" ]
+then
+ unset PERL_XML
+ echo_i "XML was not configured; skipping" >&2
+elif $PERL -e 'use XML::Simple;' 2>/dev/null
+then
+ PERL_XML=1
+else
+ unset PERL_XML
+ echo_i "XML tests require XML::Simple; skipping" >&2
+fi
+
+if [ ! "$PERL_JSON" -a ! "$PERL_XML" ]; then
+ echo_i "skipping all tests"
+ exit 0
+fi
+
+
+gettraffic() {
+ echo_i "... using $1"
+ case $1 in
+ xml) path='xml/v3/traffic' ;;
+ json) path='json/v1/traffic' ;;
+ *) return 1 ;;
+ esac
+ file=`$PERL fetch.pl -p ${EXTRAPORT1} $path`
+ $PERL traffic-${1}.pl $file 2>/dev/null | sort > traffic.out.$2
+ result=$?
+ rm -f $file
+ return $result
+}
+
+status=0
+n=1
+ret=0
+echo_i "fetching traffic size data ($n)"
+if [ $PERL_XML ]; then
+ gettraffic xml x$n || ret=1
+ cmp traffic.out.x$n traffic.expect.$n || ret=1
+fi
+if [ $PERL_JSON ]; then
+ gettraffic json j$n || ret=1
+ cmp traffic.out.j$n traffic.expect.$n || ret=1
+fi
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+ret=0
+echo_i "fetching traffic size data after small UDP query ($n)"
+$DIGCMD short.example txt > dig.out.$n || ret=1
+if [ $PERL_XML ]; then
+ gettraffic xml x$n || ret=1
+ cmp traffic.out.x$n traffic.expect.$n || ret=1
+fi
+if [ $PERL_JSON ]; then
+ gettraffic json j$n || ret=1
+ cmp traffic.out.j$n traffic.expect.$n || ret=1
+fi
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+ret=0
+n=`expr $n + 1`
+echo_i "fetching traffic size data after large UDP query ($n)"
+$DIGCMD long.example txt > dig.out.$n || ret=1
+if [ $PERL_XML ]; then
+ gettraffic xml x$n || ret=1
+ cmp traffic.out.x$n traffic.expect.$n || ret=1
+fi
+if [ $PERL_JSON ]; then
+ gettraffic json j$n || ret=1
+ cmp traffic.out.j$n traffic.expect.$n || ret=1
+fi
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+ret=0
+echo_i "fetching traffic size data after small TCP query ($n)"
+$DIGCMD +tcp short.example txt > dig.out.$n || ret=1
+if [ $PERL_XML ]; then
+ gettraffic xml x$n || ret=1
+ cmp traffic.out.x$n traffic.expect.$n || ret=1
+fi
+if [ $PERL_JSON ]; then
+ gettraffic json j$n || ret=1
+ cmp traffic.out.j$n traffic.expect.$n || ret=1
+fi
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+ret=0
+echo_i "fetching traffic size data after large TCP query ($n)"
+$DIGCMD +tcp long.example txt > dig.out.$n || ret=1
+if [ $PERL_XML ]; then
+ gettraffic xml x$n || ret=1
+ cmp traffic.out.x$n traffic.expect.$n || ret=1
+fi
+if [ $PERL_JSON ]; then
+ gettraffic json j$n || ret=1
+ cmp traffic.out.j$n traffic.expect.$n || ret=1
+fi
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+ret=0
+echo_i "checking consistency between named.stats and xml/json ($n)"
+rm -f ns2/named.stats
+$DIGCMD +tcp example ns > dig.out.$n || ret=1
+$RNDCCMD 10.53.0.2 stats 2>&1 | sed 's/^/I:ns1 /'
+query_count=`awk '/QUERY/ {print $1}' ns2/named.stats`
+txt_count=`awk '/TXT/ {print $1}' ns2/named.stats`
+noerror_count=`awk '/NOERROR/ {print $1}' ns2/named.stats`
+if [ $PERL_XML ]; then
+ file=`$PERL fetch.pl -p ${EXTRAPORT1} xml/v3/server`
+ mv $file xml.stats
+ $PERL server-xml.pl > xml.fmtstats 2> /dev/null
+ xml_query_count=`awk '/opcode QUERY/ { print $NF }' xml.fmtstats`
+ xml_query_count=${xml_query_count:-0}
+ [ "$query_count" -eq "$xml_query_count" ] || ret=1
+ xml_txt_count=`awk '/qtype TXT/ { print $NF }' xml.fmtstats`
+ xml_txt_count=${xml_txt_count:-0}
+ [ "$txt_count" -eq "$xml_txt_count" ] || ret=1
+ xml_noerror_count=`awk '/rcode NOERROR/ { print $NF }' xml.fmtstats`
+ xml_noerror_count=${xml_noerror_count:-0}
+ [ "$noerror_count" -eq "$xml_noerror_count" ] || ret=1
+fi
+if [ $PERL_JSON ]; then
+ file=`$PERL fetch.pl -p ${EXTRAPORT1} json/v1/server`
+ mv $file json.stats
+ $PERL server-json.pl > json.fmtstats 2> /dev/null
+ json_query_count=`awk '/opcode QUERY/ { print $NF }' json.fmtstats`
+ json_query_count=${json_query_count:-0}
+ [ "$query_count" -eq "$json_query_count" ] || ret=1
+ json_txt_count=`awk '/qtype TXT/ { print $NF }' json.fmtstats`
+ json_txt_count=${json_txt_count:-0}
+ [ "$txt_count" -eq "$json_txt_count" ] || ret=1
+ json_noerror_count=`awk '/rcode NOERROR/ { print $NF }' json.fmtstats`
+ json_noerror_count=${json_noerror_count:-0}
+ [ "$noerror_count" -eq "$json_noerror_count" ] || ret=1
+fi
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+ret=0
+echo_i "checking consistency between regular and compressed output ($n)"
+if [ "$HAVEXMLSTATS" ];
+then
+ URL=http://10.53.0.2:${EXTRAPORT1}/xml/v3/server
+else
+ URL=http://10.53.0.2:${EXTRAPORT1}/json/v1/server
+fi
+$CURL -D regular.headers $URL 2>/dev/null | \
+ sed -e "s#<current-time>.*</current-time>##g" > regular.out
+$CURL -D compressed.headers --compressed $URL 2>/dev/null | \
+ sed -e "s#<current-time>.*</current-time>##g" > compressed.out
+diff regular.out compressed.out >/dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+ret=0
+echo_i "checking if compressed output is really compressed ($n)"
+if [ "$ZLIB" ];
+then
+ REGSIZE=`cat regular.headers | \
+ grep -i Content-Length | sed -e "s/.*: \([0-9]*\).*/\1/"`
+ COMPSIZE=`cat compressed.headers | \
+ grep -i Content-Length | sed -e "s/.*: \([0-9]*\).*/\1/"`
+ if [ ! `expr $REGSIZE / $COMPSIZE` -gt 2 ]; then
+ ret=1
+ fi
+else
+ echo_i "skipped"
+fi
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+n=`expr $n + 1`
+
+echo_i "exit status: $status"
+[ $status -eq 0 ] || exit 1
diff --git a/bin/tests/system/statschannel/traffic-json.pl b/bin/tests/system/statschannel/traffic-json.pl
new file mode 100644
index 0000000..67b53c4
--- /dev/null
+++ b/bin/tests/system/statschannel/traffic-json.pl
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+# traffic-json.pl:
+# Parses the JSON version of the RSSAC002 traffic stats into a
+# normalized format.
+
+use JSON;
+
+my $file = $ARGV[0];
+open(INPUT, "<$file");
+my $text = do{local$/;<INPUT>};
+close(INPUT);
+
+my $ref = decode_json($text);
+
+my $tcprcvd = $ref->{traffic}->{"dns-tcp-requests-sizes-received-ipv4"};
+my $type = "tcp request-size ";
+foreach $key (keys %{$tcprcvd}) {
+ print $type . $key . ": ". $tcprcvd->{$key} ."\n";
+}
+
+my $tcpsent = $ref->{traffic}->{"dns-tcp-responses-sizes-sent-ipv4"};
+my $type = "tcp response-size ";
+foreach $key (keys %{$tcpsent}) {
+ print $type . $key . ": ". $tcpsent->{$key} ."\n";
+}
+
+my $udprcvd = $ref->{traffic}->{"dns-udp-requests-sizes-received-ipv4"};
+my $type = "udp request-size ";
+foreach $key (keys %{$udprcvd}) {
+ print $type . $key . ": ". $udprcvd->{$key} ."\n";
+}
+
+my $udpsent = $ref->{traffic}->{"dns-udp-responses-sizes-sent-ipv4"};
+my $type = "udp response-size ";
+foreach $key (keys %{$udpsent}) {
+ print $type . $key . ": ". $udpsent->{$key} ."\n";
+}
diff --git a/bin/tests/system/statschannel/traffic-xml.pl b/bin/tests/system/statschannel/traffic-xml.pl
new file mode 100644
index 0000000..a866c64
--- /dev/null
+++ b/bin/tests/system/statschannel/traffic-xml.pl
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+# traffic-xml.pl:
+# Parses the XML version of the RSSAC002 traffic stats into a
+# normalized format.
+
+use XML::Simple;
+
+my $file = $ARGV[0];
+
+my $ref = XMLin($file);
+
+my $udp = $ref->{traffic}->{ipv4}->{udp}->{counters};
+foreach $group (@$udp) {
+ my $type = "udp " . $group->{type} . " ";
+ if (exists $group->{counter}->{name}) {
+ print $type . $group->{counter}->{name} . ": " . $group->{counter}->{content} . "\n";
+ } else {
+ foreach $key (keys %{$group->{counter}}) {
+ print $type . $key . ": ". $group->{counter}->{$key}->{content} ."\n";
+ }
+ }
+}
+
+my $tcp = $ref->{traffic}->{ipv4}->{tcp}->{counters};
+foreach $group (@$tcp) {
+ my $type = "tcp " . $group->{type} . " ";
+ if (exists $group->{counter}->{name}) {
+ print $type . $group->{counter}->{name} . ": " . $group->{counter}->{content} . "\n";
+ } else {
+ foreach $key (keys %{$group->{counter}}) {
+ print $type . $key . ": ". $group->{counter}->{$key}->{content} ."\n";
+ }
+ }
+}
diff --git a/bin/tests/system/statschannel/traffic.expect.1 b/bin/tests/system/statschannel/traffic.expect.1
new file mode 100644
index 0000000..5938d5d
--- /dev/null
+++ b/bin/tests/system/statschannel/traffic.expect.1
@@ -0,0 +1,2 @@
+tcp request-size 16-31: 1
+tcp response-size 64-79: 1
diff --git a/bin/tests/system/statschannel/traffic.expect.2 b/bin/tests/system/statschannel/traffic.expect.2
new file mode 100644
index 0000000..6c9e25a
--- /dev/null
+++ b/bin/tests/system/statschannel/traffic.expect.2
@@ -0,0 +1,4 @@
+tcp request-size 16-31: 1
+tcp response-size 64-79: 1
+udp request-size 48-63: 1
+udp response-size 112-127: 1
diff --git a/bin/tests/system/statschannel/traffic.expect.4 b/bin/tests/system/statschannel/traffic.expect.4
new file mode 100644
index 0000000..3f892f5
--- /dev/null
+++ b/bin/tests/system/statschannel/traffic.expect.4
@@ -0,0 +1,5 @@
+tcp request-size 16-31: 1
+tcp response-size 64-79: 1
+udp request-size 48-63: 2
+udp response-size 112-127: 1
+udp response-size 848-863: 1
diff --git a/bin/tests/system/statschannel/traffic.expect.5 b/bin/tests/system/statschannel/traffic.expect.5
new file mode 100644
index 0000000..15911b1
--- /dev/null
+++ b/bin/tests/system/statschannel/traffic.expect.5
@@ -0,0 +1,7 @@
+tcp request-size 16-31: 1
+tcp request-size 48-63: 1
+tcp response-size 112-127: 1
+tcp response-size 64-79: 1
+udp request-size 48-63: 2
+udp response-size 112-127: 1
+udp response-size 848-863: 1
diff --git a/bin/tests/system/statschannel/traffic.expect.6 b/bin/tests/system/statschannel/traffic.expect.6
new file mode 100644
index 0000000..73fc8f1
--- /dev/null
+++ b/bin/tests/system/statschannel/traffic.expect.6
@@ -0,0 +1,8 @@
+tcp request-size 16-31: 1
+tcp request-size 48-63: 2
+tcp response-size 112-127: 1
+tcp response-size 64-79: 1
+tcp response-size 848-863: 1
+udp request-size 48-63: 2
+udp response-size 112-127: 1
+udp response-size 848-863: 1