From 9ee53e869876be6a6d019c35178223271edf7c80 Mon Sep 17 00:00:00 2001 From: Alexander Bluhm Date: Tue, 28 Jul 2015 12:31:30 +0000 Subject: Add more and deeper tests for pf divert-reply rules. Especially the combination of sending and receiving multiple packets over one socket is tested for UDP, raw IP and ICMP. --- regress/sys/net/pf_divert/LICENSE | 2 +- regress/sys/net/pf_divert/Makefile | 6 +- regress/sys/net/pf_divert/README | 48 ++++++++++ regress/sys/net/pf_divert/args-icmp-reply-reuse.pl | 23 +++++ regress/sys/net/pf_divert/args-icmp-reply-to.pl | 21 +++++ regress/sys/net/pf_divert/args-icmp-to.pl | 10 +-- regress/sys/net/pf_divert/args-rip-reply-to.pl | 27 ++++++ regress/sys/net/pf_divert/args-rip-reply.pl | 11 ++- regress/sys/net/pf_divert/args-rip-to.pl | 11 ++- regress/sys/net/pf_divert/args-tcp-reply.pl | 6 +- regress/sys/net/pf_divert/args-tcp-to.pl | 6 +- regress/sys/net/pf_divert/args-udp-reply-to.pl | 26 ++++++ regress/sys/net/pf_divert/args-udp-reply.pl | 8 +- regress/sys/net/pf_divert/args-udp-to.pl | 8 +- regress/sys/net/pf_divert/funcs.pl | 100 +++++++++++++++------ regress/sys/net/pf_divert/remote.pl | 13 ++- 16 files changed, 258 insertions(+), 68 deletions(-) create mode 100644 regress/sys/net/pf_divert/README create mode 100644 regress/sys/net/pf_divert/args-icmp-reply-reuse.pl create mode 100644 regress/sys/net/pf_divert/args-icmp-reply-to.pl create mode 100644 regress/sys/net/pf_divert/args-rip-reply-to.pl create mode 100644 regress/sys/net/pf_divert/args-udp-reply-to.pl diff --git a/regress/sys/net/pf_divert/LICENSE b/regress/sys/net/pf_divert/LICENSE index a6e1dd38fcf..407681ad0a6 100644 --- a/regress/sys/net/pf_divert/LICENSE +++ b/regress/sys/net/pf_divert/LICENSE @@ -1,4 +1,4 @@ -# Copyright (c) 2010-2014 Alexander Bluhm +# Copyright (c) 2010-2015 Alexander Bluhm # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above diff --git a/regress/sys/net/pf_divert/Makefile b/regress/sys/net/pf_divert/Makefile index b2917173084..f2798df2c68 100644 --- a/regress/sys/net/pf_divert/Makefile +++ b/regress/sys/net/pf_divert/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.11 2014/08/18 22:58:19 bluhm Exp $ +# $OpenBSD: Makefile,v 1.12 2015/07/28 12:31:29 bluhm Exp $ # The following ports must be installed for the regression tests: # p5-IO-Socket-INET6 object interface for AF_INET and AF_INET6 domain sockets @@ -54,9 +54,13 @@ TARGETS ?= inet-args-tcp-to inet6-args-tcp-to \ inet-args-tcp-reply inet6-args-tcp-reply \ inet-args-udp-to inet6-args-udp-to \ inet-args-udp-reply inet6-args-udp-reply \ + inet-args-udp-reply-to inet6-args-udp-reply-to \ inet-args-rip-to inet6-args-rip-to \ inet-args-rip-reply inet6-args-rip-reply \ + inet-args-rip-reply-to inet6-args-rip-reply-to \ inet-args-icmp-to inet6-args-icmp-to \ + inet-args-icmp-reply-to inet6-args-icmp-reply-to \ + inet-args-icmp-reply-reuse inet6-args-icmp-reply-reuse \ inet-reuse-tcp inet6-reuse-tcp \ inet-reuse-udp inet6-reuse-udp \ inet-reuse-rip inet6-reuse-rip diff --git a/regress/sys/net/pf_divert/README b/regress/sys/net/pf_divert/README new file mode 100644 index 00000000000..ffa90c0a0b5 --- /dev/null +++ b/regress/sys/net/pf_divert/README @@ -0,0 +1,48 @@ +Run pf divert-to and divert-reply regression test. The framework +runs both a client and a server process. One process is started +on the local and the other one on the remote machine. The kernel's +pf of the remote machine gets tested. The remote machine's pf.conf +must contain an anchor named "regress" where the test places its +divert rules automatically. All tests are done with IPv4 and IPv6. + +The protocols TCP, UDP, Raw IP, ICMP get tested. TCP uses a listen +and a connected stream socket, the others use bound and possibly +connected datagram sockets. Over the TCP connection messages are +sent on both directions. The datagram sockets deal with single +packets. The remote machine can be reached over a non existing +address which is diverted to the stack. + +The args-...-to tests install an incoming divert-to pf rule on the +remote machine and run a server there. The server binds to the +localhost address, for TCP it also listens and accepts. The client +is started on the local machine and connects to the non existing +address of the remote machine. For TCP the bidirectional connection, +for the others a singe packet from the client has to reach the +server. + +The args-...-reply tests install an outgoing divert-reply pf rule +on the remote machine and start a client there. The client binds +with bindany to the non existing address and connects to the local +machine's address. The server is run on the local machine, for TCP +it also listens and accepts. For TCP the bidirectional connection, +for the others a singe packet from the client has to reach the +server. + +The args-...-reply-to tests use the same setup as the args-...-reply +tests. But addtitionally to the packet from the client to the +server, the server sends a packet back which has to be received by +the client. To figure out the client's address and port, the server +receives with recvfrom and sends back with sendto. The args-icmp-reply-to +test does not use a server as the kernel of the local machine +automatically reflects the ICMP echo request packet with an reply. + +The args-icmp-reply-reuse test is similar to the args-icmp-reply-to +test, but it sends two ICMP echo requests and expects two ICMP echo +replies. All four packets use the same socket. The second echo +has a different ID, so it cannot use the same pf state. Check that +the second reply reaches the client. This can only work, if pf +creates a second outgoing state although all packet use one socket. + +The reuse-... tests run the corresponding args-...-reply and +args-...-to tests consecutively to check that the pf states to not +interfere. diff --git a/regress/sys/net/pf_divert/args-icmp-reply-reuse.pl b/regress/sys/net/pf_divert/args-icmp-reply-reuse.pl new file mode 100644 index 00000000000..f8cd4553ee8 --- /dev/null +++ b/regress/sys/net/pf_divert/args-icmp-reply-reuse.pl @@ -0,0 +1,23 @@ +# test divert-reply with icmp and socket reuse + +use strict; +use warnings; +use Socket; + +our %args = ( + socktype => Socket::SOCK_RAW, + protocol => sub { shift->{af} eq "inet" ? "icmp" : "icmp6" }, + client => { + func => sub { + my $self = shift; + write_icmp_echo($self, $$); + read_icmp_echo($self, "reply"); + write_icmp_echo($self, $$+1); + read_icmp_echo($self, "reply"); + }, + out => "ICMP6?", + in => "ICMP6? reply", + }, + # no server as our kernel does the icmp reply automatically + divert => "reply", +); diff --git a/regress/sys/net/pf_divert/args-icmp-reply-to.pl b/regress/sys/net/pf_divert/args-icmp-reply-to.pl new file mode 100644 index 00000000000..29667a153b1 --- /dev/null +++ b/regress/sys/net/pf_divert/args-icmp-reply-to.pl @@ -0,0 +1,21 @@ +# test divert-reply with icmp with out and in packet + +use strict; +use warnings; +use Socket; + +our %args = ( + socktype => Socket::SOCK_RAW, + protocol => sub { shift->{af} eq "inet" ? "icmp" : "icmp6" }, + client => { + func => sub { + my $self = shift; + write_icmp_echo($self); + read_icmp_echo($self, "reply"); + }, + out => "ICMP6?", + in => "ICMP6? reply", + }, + # no server as our kernel does the icmp reply automatically + divert => "reply", +); diff --git a/regress/sys/net/pf_divert/args-icmp-to.pl b/regress/sys/net/pf_divert/args-icmp-to.pl index 172dea9322a..45d3a74ae17 100644 --- a/regress/sys/net/pf_divert/args-icmp-to.pl +++ b/regress/sys/net/pf_divert/args-icmp-to.pl @@ -5,9 +5,9 @@ use warnings; use Socket; our %args = ( - socktype => Socket::SOCK_RAW, - protocol => sub { shift->{af} eq "inet" ? "icmp" : "icmp6" }, - client => { func => \&write_icmp_echo, out => "ICMP", noin => 1, }, - server => { func => \&read_icmp_echo, in => "ICMP", noout => 1, }, - divert => "to", + socktype => Socket::SOCK_RAW, + protocol => sub { shift->{af} eq "inet" ? "icmp" : "icmp6" }, + client => { func => \&write_icmp_echo, out => "ICMP6?", noin => 1, }, + server => { func => \&read_icmp_echo, in => "ICMP6?", noout => 1, }, + divert => "to", ); diff --git a/regress/sys/net/pf_divert/args-rip-reply-to.pl b/regress/sys/net/pf_divert/args-rip-reply-to.pl new file mode 100644 index 00000000000..b8e3a0aaaff --- /dev/null +++ b/regress/sys/net/pf_divert/args-rip-reply-to.pl @@ -0,0 +1,27 @@ +# test divert-reply with raw ip with out and in packet + +use strict; +use warnings; +use Socket; + +our %args = ( + socktype => Socket::SOCK_RAW, + protocol => 254, + client => { + func => sub { + my $self = shift; + write_datagram($self); + read_datagram($self); + }, + }, + server => { + func => sub { + my $self = shift; + read_datagram($self); + $self->{toaddr} = $self->{fromaddr}; + $self->{toport} = $self->{fromport}; + write_datagram($self); + }, + }, + divert => "reply", +); diff --git a/regress/sys/net/pf_divert/args-rip-reply.pl b/regress/sys/net/pf_divert/args-rip-reply.pl index 1c71ecddb20..0744f48fe76 100644 --- a/regress/sys/net/pf_divert/args-rip-reply.pl +++ b/regress/sys/net/pf_divert/args-rip-reply.pl @@ -5,10 +5,9 @@ use warnings; use Socket; our %args = ( - socktype => Socket::SOCK_RAW, - protocol => 254, - skip => sub { shift->{af} eq "inet" ? 20 : 0 }, - client => { func => \&write_datagram, noin => 1, }, - server => { func => \&read_datagram, noout => 1, }, - divert => "reply", + socktype => Socket::SOCK_RAW, + protocol => 254, + client => { func => \&write_datagram, noin => 1, }, + server => { func => \&read_datagram, noout => 1, }, + divert => "reply", ); diff --git a/regress/sys/net/pf_divert/args-rip-to.pl b/regress/sys/net/pf_divert/args-rip-to.pl index 2fd414eb33c..19ba8fa6100 100644 --- a/regress/sys/net/pf_divert/args-rip-to.pl +++ b/regress/sys/net/pf_divert/args-rip-to.pl @@ -5,10 +5,9 @@ use warnings; use Socket; our %args = ( - socktype => Socket::SOCK_RAW, - protocol => 254, - skip => sub { shift->{af} eq "inet" ? 20 : 0 }, - client => { func => \&write_datagram, noin => 1, }, - server => { func => \&read_datagram, noout => 1, }, - divert => "to", + socktype => Socket::SOCK_RAW, + protocol => 254, + client => { func => \&write_datagram, noin => 1, }, + server => { func => \&read_datagram, noout => 1, }, + divert => "to", ); diff --git a/regress/sys/net/pf_divert/args-tcp-reply.pl b/regress/sys/net/pf_divert/args-tcp-reply.pl index 82d435632d9..8bf599cb01a 100644 --- a/regress/sys/net/pf_divert/args-tcp-reply.pl +++ b/regress/sys/net/pf_divert/args-tcp-reply.pl @@ -4,6 +4,8 @@ use strict; use warnings; our %args = ( - protocol => "tcp", - divert => "reply", + protocol => "tcp", + client => { func => \&write_read_stream }, + server => { func => \&write_read_stream }, + divert => "reply", ); diff --git a/regress/sys/net/pf_divert/args-tcp-to.pl b/regress/sys/net/pf_divert/args-tcp-to.pl index 0e67e4c873e..b99b0772760 100644 --- a/regress/sys/net/pf_divert/args-tcp-to.pl +++ b/regress/sys/net/pf_divert/args-tcp-to.pl @@ -4,6 +4,8 @@ use strict; use warnings; our %args = ( - protocol => "tcp", - divert => "to", + protocol => "tcp", + client => { func => \&write_read_stream }, + server => { func => \&write_read_stream }, + divert => "to", ); diff --git a/regress/sys/net/pf_divert/args-udp-reply-to.pl b/regress/sys/net/pf_divert/args-udp-reply-to.pl new file mode 100644 index 00000000000..c77088c9557 --- /dev/null +++ b/regress/sys/net/pf_divert/args-udp-reply-to.pl @@ -0,0 +1,26 @@ +# test divert-reply with udp with out and in packet + +use strict; +use warnings; +use Socket; + +our %args = ( + protocol => "udp", + client => { + func => sub { + my $self = shift; + write_datagram($self); + read_datagram($self); + }, + }, + server => { + func => sub { + my $self = shift; + read_datagram($self); + $self->{toaddr} = $self->{fromaddr}; + $self->{toport} = $self->{fromport}; + write_datagram($self); + }, + }, + divert => "reply", +); diff --git a/regress/sys/net/pf_divert/args-udp-reply.pl b/regress/sys/net/pf_divert/args-udp-reply.pl index 7e2c5a77f37..e78d572cb17 100644 --- a/regress/sys/net/pf_divert/args-udp-reply.pl +++ b/regress/sys/net/pf_divert/args-udp-reply.pl @@ -4,8 +4,8 @@ use strict; use warnings; our %args = ( - protocol => "udp", - client => { func => \&write_datagram, noin => 1, }, - server => { func => \&read_datagram, noout => 1, }, - divert => "reply", + protocol => "udp", + client => { func => \&write_datagram, noin => 1, }, + server => { func => \&read_datagram, noout => 1, }, + divert => "reply", ); diff --git a/regress/sys/net/pf_divert/args-udp-to.pl b/regress/sys/net/pf_divert/args-udp-to.pl index 673f7233705..2469dc42de5 100644 --- a/regress/sys/net/pf_divert/args-udp-to.pl +++ b/regress/sys/net/pf_divert/args-udp-to.pl @@ -4,8 +4,8 @@ use strict; use warnings; our %args = ( - protocol => "udp", - client => { func => \&write_datagram, noin => 1, }, - server => { func => \&read_datagram, noout => 1, }, - divert => "to", + protocol => "udp", + client => { func => \&write_datagram, noin => 1, }, + server => { func => \&read_datagram, noout => 1, }, + divert => "to", ); diff --git a/regress/sys/net/pf_divert/funcs.pl b/regress/sys/net/pf_divert/funcs.pl index 91987d156ef..a47b7c4a409 100644 --- a/regress/sys/net/pf_divert/funcs.pl +++ b/regress/sys/net/pf_divert/funcs.pl @@ -1,6 +1,6 @@ -# $OpenBSD: funcs.pl,v 1.4 2013/06/05 04:34:27 bluhm Exp $ +# $OpenBSD: funcs.pl,v 1.5 2015/07/28 12:31:29 bluhm Exp $ -# Copyright (c) 2010-2013 Alexander Bluhm +# Copyright (c) 2010-2015 Alexander Bluhm # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -16,6 +16,8 @@ use strict; use warnings; +use Socket; +use Socket6; ######################################################################## # Client and Server funcs @@ -35,28 +37,65 @@ sub write_read_stream { sub write_datagram { my $self = shift; + my $dgram = shift; + + my $out = $dgram || ref($self). "\n"; + my $addr = $self->{toaddr}; + my $port = $self->{toport}; + if ($addr) { + my ($to, $netaddr); + if ($self->{af} eq "inet") { + $netaddr = inet_pton(AF_INET, $addr); + $to = pack_sockaddr_in($port, $netaddr); + } else { + $netaddr = inet_pton(AF_INET6, $addr); + $to = pack_sockaddr_in6($port, $netaddr); + } + $self->{toaddr} = $addr; + $self->{toport} = $port; + print STDERR "send to: $addr $port\n"; - my $out = ref($self). "\n"; - print $out; - IO::Handle::flush(\*STDOUT); - print STDERR ">>> $out"; + send(STDIN, $out, 0, $to) + or die ref($self), " send to failed: $!"; + } else { + send(STDIN, $out, 0) + or die ref($self), " send failed: $!"; + } + + unless ($dgram) { + print STDERR ">>> $out"; + } } sub read_datagram { my $self = shift; - my $skip = $self->{skip}; - $skip = $skip->($self) if ref $skip eq 'CODE'; - - my $in; - if ($skip) { - # Raw sockets include the IPv4 header. - sysread(STDIN, $in, 70000); - # Cut the header off. - substr($in, 0, $skip, ""); + my $dgram = shift; + + my $from = recv(STDIN, my $in, 70000, 0) + or die ref($self), " recv from failed: $!"; + # Raw sockets include the IPv4 header. + if ($self->{socktype} && $self->{socktype} == Socket::SOCK_RAW && + $self->{af} eq "inet") { + substr($in, 0, 20, ""); + } + + my ($port, $netaddr, $addr); + if ($self->{af} eq "inet") { + ($port, $netaddr) = unpack_sockaddr_in($from); + $addr = inet_ntop(AF_INET, $netaddr); } else { - $in = ; + ($port, $netaddr) = unpack_sockaddr_in6($from); + $addr = inet_ntop(AF_INET6, $netaddr); + } + $self->{fromaddr} = $addr; + $self->{fromport} = $port; + print STDERR "recv from: $addr $port\n"; + + if ($dgram) { + $$dgram = $in; + } else { + print STDERR "<<< $in"; } - print STDERR "<<< $in"; } sub in_cksum { @@ -73,16 +112,19 @@ sub in_cksum { use constant IPPROTO_ICMPV6 => 58; use constant ICMP_ECHO => 8; +use constant ICMP_ECHOREPLY => 0; use constant ICMP6_ECHO_REQUEST => 128; +use constant ICMP6_ECHO_REPLY => 129; my $seq = 0; sub write_icmp_echo { my $self = shift; + my $pid = shift || $$; my $af = $self->{af}; my $type = $af eq "inet" ? ICMP_ECHO : ICMP6_ECHO_REQUEST; # type, code, cksum, id, seq - my $icmp = pack("CCnnn", $type, 0, 0, $$, ++$seq); + my $icmp = pack("CCnnn", $type, 0, 0, $pid, ++$seq); if ($af eq "inet") { substr($icmp, 2, 2, pack("n", in_cksum($icmp))); } else { @@ -95,24 +137,21 @@ sub write_icmp_echo { substr($icmp, 2, 2, pack("n", in_cksum($phdr. $icmp))); } - print $icmp; - IO::Handle::flush(\*STDOUT); + write_datagram($self, $icmp); my $text = $af eq "inet" ? "ICMP" : "ICMP6"; print STDERR ">>> $text ", unpack("H*", $icmp), "\n"; } sub read_icmp_echo { my $self = shift; + my $reply = shift; my $af = $self->{af}; - # Raw sockets include the IPv4 header. - sysread(STDIN, my $icmp, 70000); - # Cut the header off. - if ($af eq "inet") { - substr($icmp, 0, 20, ""); - } + my $icmp; + read_datagram($self, \$icmp); my $text = $af eq "inet" ? "ICMP" : "ICMP6"; + $text .= " reply" if $reply; my $phdr = ""; if ($af eq "inet6") { # src, dst, plen, pad, next @@ -127,7 +166,10 @@ sub read_icmp_echo { $text = "BAD $text CHECKSUM"; } else { my($type, $code, $cksum, $id, $seq) = unpack("CCnnn", $icmp); - if ($type != ($af eq "inet" ? ICMP_ECHO : ICMP6_ECHO_REQUEST)) { + my $t = $reply ? + ($af eq "inet" ? ICMP_ECHOREPLY : ICMP6_ECHO_REPLY) : + ($af eq "inet" ? ICMP_ECHO : ICMP6_ECHO_REQUEST); + if ($type != $t) { $text = "BAD $text TYPE"; } elsif ($code != 0) { $text = "BAD $text CODE"; @@ -152,7 +194,7 @@ sub check_logs { sub check_inout { my ($c, $s, %args) = @_; - if ($c && !$args{client}{nocheck}) { + if ($args{client} && !$args{client}{nocheck}) { my $out = $args{client}{out} || "Client"; $c->loggrep(qr/^>>> $out/) or die "no client output" unless $args{client}{noout}; @@ -160,7 +202,7 @@ sub check_inout { $c->loggrep(qr/^<<< $in/) or die "no client input" unless $args{client}{noin}; } - if ($s && !$args{server}{nocheck}) { + if ($args{server} && !$args{server}{nocheck}) { my $out = $args{server}{out} || "Server"; $s->loggrep(qr/^>>> $out/) or die "no server output" unless $args{server}{noout}; diff --git a/regress/sys/net/pf_divert/remote.pl b/regress/sys/net/pf_divert/remote.pl index 977e7c40388..2cddecd7b0d 100644 --- a/regress/sys/net/pf_divert/remote.pl +++ b/regress/sys/net/pf_divert/remote.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl -# $OpenBSD: remote.pl,v 1.5 2013/11/03 00:32:36 bluhm Exp $ +# $OpenBSD: remote.pl,v 1.6 2015/07/28 12:31:29 bluhm Exp $ -# Copyright (c) 2010-2013 Alexander Bluhm +# Copyright (c) 2010-2015 Alexander Bluhm # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -82,7 +82,6 @@ if (@ARGV == 5 && $mode eq "auto") { } my($c, $l, $r, $s, $logfile); -my $func = \&write_read_stream; my $divert = $args{divert} || "to"; my $local = $divert eq "to" ? "client" : "server"; my $remote = $divert eq "to" ? "server" : "client"; @@ -99,7 +98,6 @@ if ($mode eq "divert" xor $divert eq "reply") { if ($local eq "server") { $l = $s = Server->new( - func => $func, %args, %{$args{server}}, logfile => $logfile, @@ -111,7 +109,7 @@ if ($local eq "server") { listenport => $serverport || $bindport, srcaddr => $srcaddr, dstaddr => $dstaddr, - ); + ) if $args{server}; } if ($mode eq "auto") { $r = Remote->new( @@ -133,7 +131,6 @@ if ($mode eq "auto") { } if ($local eq "client") { $l = $c = Client->new( - func => $func, %args, %{$args{client}}, logfile => $logfile, @@ -147,9 +144,9 @@ if ($local eq "client") { bindport => $clientport || $bindport, srcaddr => $srcaddr, dstaddr => $dstaddr, - ); + ) if $args{client}; } -$l->{log}->print("local command: $command\n"); +$l->{log}->print("local command: $command\n") if $l; if ($mode eq "divert") { open(my $log, '<', $l->{logfile}) -- cgit v1.2.3