summaryrefslogtreecommitdiff
path: root/regress/sys/kern
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2011-01-07 22:06:09 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2011-01-07 22:06:09 +0000
commit288eb9bdabfd29a4643657cee203bd87068d9425 (patch)
tree6e580e504fff76e283741f51e0a6742ef40359c9 /regress/sys/kern
parent3a2bd2908bcd71d2b15fc9ea62ac8a08d5c304db (diff)
Add kernel regression tests for socket splicing.
Diffstat (limited to 'regress/sys/kern')
-rw-r--r--regress/sys/kern/splice/Child.pm34
-rw-r--r--regress/sys/kern/splice/Client.pm81
-rw-r--r--regress/sys/kern/splice/Makefile51
-rw-r--r--regress/sys/kern/splice/Proc.pm153
-rw-r--r--regress/sys/kern/splice/Relay.pm122
-rw-r--r--regress/sys/kern/splice/Remote.pm73
-rw-r--r--regress/sys/kern/splice/Server.pm85
-rw-r--r--regress/sys/kern/splice/args-default.pl11
-rw-r--r--regress/sys/kern/splice/args-inet6-client.pl23
-rw-r--r--regress/sys/kern/splice/args-inet6-server.pl23
-rw-r--r--regress/sys/kern/splice/args-inet6.pl23
-rw-r--r--regress/sys/kern/splice/args-long.pl14
-rw-r--r--regress/sys/kern/splice/args-max-sleep-client-nonblock.pl19
-rw-r--r--regress/sys/kern/splice/args-max-sleep-client-short.pl18
-rw-r--r--regress/sys/kern/splice/args-max-sleep-client.pl19
-rw-r--r--regress/sys/kern/splice/args-max-sleep-relay-short.pl18
-rw-r--r--regress/sys/kern/splice/args-max-sleep-relay.pl21
-rw-r--r--regress/sys/kern/splice/args-max-sleep-server.pl22
-rw-r--r--regress/sys/kern/splice/args-max.pl19
-rw-r--r--regress/sys/kern/splice/args-maxcopy-sleep-client.pl18
-rw-r--r--regress/sys/kern/splice/args-maxcopy-sleep-relay.pl15
-rw-r--r--regress/sys/kern/splice/args-maxcopy-sleep-server.pl18
-rw-r--r--regress/sys/kern/splice/args-nonblock.pl17
-rw-r--r--regress/sys/kern/splice/args-null-max-sleep-client-nonblock.pl19
-rw-r--r--regress/sys/kern/splice/args-null-max-sleep-client.pl18
-rw-r--r--regress/sys/kern/splice/args-null-sleep-client.pl15
-rw-r--r--regress/sys/kern/splice/args-null.pl14
-rw-r--r--regress/sys/kern/splice/args-oob-max-sleep-client.pl22
-rw-r--r--regress/sys/kern/splice/args-oob-max-sleep-relay.pl23
-rw-r--r--regress/sys/kern/splice/args-oob-max-sleep-server.pl22
-rw-r--r--regress/sys/kern/splice/args-oob-max.pl22
-rw-r--r--regress/sys/kern/splice/args-oob-nonblock.pl22
-rw-r--r--regress/sys/kern/splice/args-oob-reverse-nonblock.pl23
-rw-r--r--regress/sys/kern/splice/args-oob-reverse.pl22
-rw-r--r--regress/sys/kern/splice/args-oob.pl21
-rw-r--r--regress/sys/kern/splice/args-oobinline-max-sleep-client.pl23
-rw-r--r--regress/sys/kern/splice/args-oobinline-max-sleep-relay.pl25
-rw-r--r--regress/sys/kern/splice/args-oobinline-max-sleep-server.pl23
-rw-r--r--regress/sys/kern/splice/args-oobinline-max.pl23
-rw-r--r--regress/sys/kern/splice/args-oobinline-nonblock.pl22
-rw-r--r--regress/sys/kern/splice/args-oobinline-reverse-nonblock.pl23
-rw-r--r--regress/sys/kern/splice/args-oobinline-reverse.pl22
-rw-r--r--regress/sys/kern/splice/args-oobinline.pl21
-rw-r--r--regress/sys/kern/splice/args-relay-close-in.pl29
-rw-r--r--regress/sys/kern/splice/args-reset-eof.pl12
-rw-r--r--regress/sys/kern/splice/args-reset.pl24
-rw-r--r--regress/sys/kern/splice/args-reverse-long.pl21
-rw-r--r--regress/sys/kern/splice/args-reverse-nonblock.pl21
-rw-r--r--regress/sys/kern/splice/args-reverse-null.pl21
-rw-r--r--regress/sys/kern/splice/args-reverse-sleep-client.pl21
-rw-r--r--regress/sys/kern/splice/args-reverse-sleep-relay.pl21
-rw-r--r--regress/sys/kern/splice/args-reverse-sleep-server.pl21
-rw-r--r--regress/sys/kern/splice/args-reverse.pl20
-rw-r--r--regress/sys/kern/splice/args-server-abort.pl31
-rw-r--r--regress/sys/kern/splice/args-server-exit.pl33
-rw-r--r--regress/sys/kern/splice/args-short.pl14
-rw-r--r--regress/sys/kern/splice/args-sleep-client.pl15
-rw-r--r--regress/sys/kern/splice/args-sleep-relay.pl17
-rw-r--r--regress/sys/kern/splice/args-sleep-server.pl17
-rw-r--r--regress/sys/kern/splice/args-smallbuf-sleep-client.pl19
-rw-r--r--regress/sys/kern/splice/args-smallbuf-sleep-relay.pl19
-rw-r--r--regress/sys/kern/splice/args-smallbuf-sleep-server.pl21
-rw-r--r--regress/sys/kern/splice/args-smallbuf.pl18
-rw-r--r--regress/sys/kern/splice/args-smallrcv-sleep-client.pl18
-rw-r--r--regress/sys/kern/splice/args-smallrcv-sleep-relay.pl18
-rw-r--r--regress/sys/kern/splice/args-smallrcv-sleep-server.pl20
-rw-r--r--regress/sys/kern/splice/args-smallrcv.pl17
-rw-r--r--regress/sys/kern/splice/args-smallsnd-sleep-client.pl18
-rw-r--r--regress/sys/kern/splice/args-smallsnd-sleep-relay.pl18
-rw-r--r--regress/sys/kern/splice/args-smallsnd-sleep-server.pl20
-rw-r--r--regress/sys/kern/splice/args-smallsnd.pl17
-rw-r--r--regress/sys/kern/splice/args-write-sleep-server.pl28
-rw-r--r--regress/sys/kern/splice/args-write.pl24
-rwxr-xr-xregress/sys/kern/splice/direct.pl91
-rwxr-xr-xregress/sys/kern/splice/echo.pl123
-rwxr-xr-xregress/sys/kern/splice/error-EBUSY.pl38
-rwxr-xr-xregress/sys/kern/splice/error-ENOTCONN.pl19
-rwxr-xr-xregress/sys/kern/splice/error-EOPNOTSUPP.pl20
-rwxr-xr-xregress/sys/kern/splice/error-EPROTONOSUPPORT.pl19
-rwxr-xr-xregress/sys/kern/splice/error-max-EINVAL.pl29
-rwxr-xr-xregress/sys/kern/splice/error-splice-EBADF.pl23
-rwxr-xr-xregress/sys/kern/splice/error-splice-EBUSY.pl38
-rwxr-xr-xregress/sys/kern/splice/error-splice-EINVAL.pl27
-rwxr-xr-xregress/sys/kern/splice/error-splice-ENOTCONN.pl27
-rwxr-xr-xregress/sys/kern/splice/error-splice-EOPNOTSUPP.pl28
-rwxr-xr-xregress/sys/kern/splice/error-splice-EPROTONOSUPPORT.pl27
-rw-r--r--regress/sys/kern/splice/funcs.pl367
-rwxr-xr-xregress/sys/kern/splice/relay.pl121
-rwxr-xr-xregress/sys/kern/splice/remote.pl166
89 files changed, 3098 insertions, 0 deletions
diff --git a/regress/sys/kern/splice/Child.pm b/regress/sys/kern/splice/Child.pm
new file mode 100644
index 00000000000..091c2c9e448
--- /dev/null
+++ b/regress/sys/kern/splice/Child.pm
@@ -0,0 +1,34 @@
+# $OpenBSD: Child.pm,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Child;
+use parent 'Proc';
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{up} ||= "Forked";
+ my $self = Proc::new($class, %args);
+ return $self;
+}
+
+sub child {
+}
+
+1;
diff --git a/regress/sys/kern/splice/Client.pm b/regress/sys/kern/splice/Client.pm
new file mode 100644
index 00000000000..74f3b866e93
--- /dev/null
+++ b/regress/sys/kern/splice/Client.pm
@@ -0,0 +1,81 @@
+# $OpenBSD: Client.pm,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Client;
+use parent 'Proc';
+use Carp;
+use Socket qw(IPPROTO_TCP TCP_NODELAY);
+use Socket6;
+use IO::Socket;
+use IO::Socket::INET6;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{logfile} ||= "client.log";
+ $args{up} ||= "Connected";
+ $args{down} ||= "Shutdown|Broken pipe|Connection reset by peer";
+ my $self = Proc::new($class, %args);
+ $self->{connectdomain}
+ or croak "$class connect domain not given";
+ $self->{connectaddr}
+ or croak "$class connect addr not given";
+ $self->{connectport}
+ or croak "$class connect port not given";
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ my $cs = IO::Socket::INET6->new(
+ Proto => "tcp",
+ Domain => $self->{connectdomain},
+ Blocking => ($self->{nonblocking} ? 0 : 1),
+ ) or die ref($self), " socket connect failed: $!";
+ if ($self->{oobinline}) {
+ setsockopt($cs, SOL_SOCKET, SO_OOBINLINE, pack('i', 1))
+ or die ref($self), " set oobinline connect failed: $!";
+ }
+ if ($self->{sndbuf}) {
+ setsockopt($cs, SOL_SOCKET, SO_SNDBUF,
+ pack('i', $self->{sndbuf}))
+ or die ref($self), " set sndbuf connect failed: $!";
+ }
+ if ($self->{rcvbuf}) {
+ setsockopt($cs, SOL_SOCKET, SO_RCVBUF,
+ pack('i', $self->{rcvbuf}))
+ or die ref($self), " set rcvbuf connect failed: $!";
+ }
+ setsockopt($cs, IPPROTO_TCP, TCP_NODELAY, pack('i', 1))
+ or die ref($self), " set nodelay connect failed: $!";
+ my @rres = getaddrinfo($self->{connectaddr}, $self->{connectport},
+ $self->{connectdomain}, SOCK_STREAM);
+ $cs->connect($rres[3])
+ or die ref($self), " connect failed: $!";
+ print STDERR "connect sock: ",$cs->sockhost()," ",$cs->sockport(),"\n";
+ print STDERR "connect peer: ",$cs->peerhost()," ",$cs->peerport(),"\n";
+
+ open(STDOUT, '>&', $cs)
+ or die ref($self), " dup STDOUT failed: $!";
+ open(STDIN, '<&', $cs)
+ or die ref($self), " dup STDIN failed: $!";
+}
+
+1;
diff --git a/regress/sys/kern/splice/Makefile b/regress/sys/kern/splice/Makefile
new file mode 100644
index 00000000000..390da442f89
--- /dev/null
+++ b/regress/sys/kern/splice/Makefile
@@ -0,0 +1,51 @@
+# $OpenBSD: Makefile,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# The following ports must be installed for the args-* regression test:
+# p5-IO-Socket-INET6 object interface for AF_INET and AF_INET6 domain sockets
+# p5-Socket6 Perl defines relating to AF_INET6 sockets
+
+ERRS != ls error-*.pl
+ARGS != ls args-*.pl
+TARGETS ?= ${ERRS} ${ARGS}
+REGRESS_TARGETS = ${TARGETS:S/^/run-regress-/}
+CLEANFILES = *.log ktrace.out
+
+# Fill out these variables if you want to test socket splicing with
+# the relay process running on a remote machine. You have to specify
+# a local and remote ip address for the tcp connections. To control
+# the remote machine you need a hostname for ssh to log in. All the
+# test files must be in the same directory local and remote.
+#LOCAL_ADDR ?=
+#REMOTE_ADDR ?=
+#REMOTE_SSH ?=
+
+# The error tests try to splice unsuitable sockets and check the
+# kernel error code.
+
+.for e in ${ERRS}
+run-regress-$e: $e
+ time perl $e
+.endfor
+
+# The arg tests take a perl hash with arguments controlling the
+# test parameters. Generally they consist of client, relay, server.
+# The relay.pl test fork these three processes locally. The remote.pl
+# test has local client and server but the relay process is running
+# on a remote machine reachable with ssh. For echo.pl test the
+# relay is an echo process and the client and server process share
+# the same tcp connection. All tests can run with a regular userland
+# copy relay or with a kernel socket splicing relay.
+
+.for a in ${ARGS}
+run-regress-$a: $a
+ time perl relay.pl copy $a
+ time perl relay.pl splice $a
+.if REMOTE_SSH
+ time perl remote.pl copy ${LOCAL_ADDR} ${REMOTE_ADDR} ${REMOTE_SSH} $a
+ time perl remote.pl splice ${LOCAL_ADDR} ${REMOTE_ADDR} ${REMOTE_SSH} $a
+.endif
+ time perl echo.pl copy $a
+ time perl echo.pl splice $a
+.endfor
+
+.include <bsd.regress.mk>
diff --git a/regress/sys/kern/splice/Proc.pm b/regress/sys/kern/splice/Proc.pm
new file mode 100644
index 00000000000..ac04baa9938
--- /dev/null
+++ b/regress/sys/kern/splice/Proc.pm
@@ -0,0 +1,153 @@
+# $OpenBSD: Proc.pm,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Proc;
+use Carp;
+use List::Util qw(first);
+use POSIX;
+use Time::HiRes qw(time alarm sleep);
+
+my %CHILDREN;
+
+BEGIN {
+ $SIG{TERM} = $SIG{INT} = sub {
+ my $sig = shift;
+ kill TERM => keys %CHILDREN;
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+ POSIX::raise($sig);
+ };
+}
+
+END {
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+ kill TERM => keys %CHILDREN;
+}
+
+sub new {
+ my $class = shift;
+ my $self = { @_ };
+ $self->{down} ||= "Shutdown";
+ $self->{func} && ref($self->{func}) eq 'CODE'
+ or croak "$class func not given";
+ $self->{logfile}
+ or croak "$class log file not given";
+ open(my $fh, '>', $self->{logfile})
+ or die "$class log file $self->{logfile} create failed: $!";
+ $self->{log} = $fh;
+ return bless $self, $class;
+}
+
+sub run {
+ my $self = shift;
+
+ defined(my $pid = fork())
+ or die ref($self), " fork child failed";
+ if ($pid) {
+ $CHILDREN{$pid} = 1;
+ $self->{pid} = $pid;
+ return $self;
+ }
+ %CHILDREN = ();
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+ $SIG{__DIE__} = sub {
+ die @_ if $^S;
+ warn @_;
+ IO::Handle::flush(\*STDERR);
+ POSIX::_exit(255);
+ };
+ open(STDERR, '>&', $self->{log})
+ or die ref($self), " dup STDERR failed: $!";
+
+ $self->child();
+ print STDERR $self->{up}, "\n";
+ $self->{func}->($self);
+ print STDERR "Shutdown", "\n";
+ IO::Handle::flush(\*STDOUT);
+ IO::Handle::flush(\*STDERR);
+
+ POSIX::_exit(0);
+}
+
+sub wait {
+ my $self = shift;
+ my $flags = shift;
+
+ my $pid = $self->{pid}
+ or croak ref($self), " no child pid";
+ my $kid = waitpid($pid, $flags);
+ if ($kid > 0) {
+ my $status = $?;
+ my $code;
+ $code = "exit: ". WEXITSTATUS($?) if WIFEXITED($?);
+ $code = "signal: ". WTERMSIG($?) if WIFSIGNALED($?);
+ $code = "stop: ". WSTOPSIG($?) if WIFSTOPPED($?);
+ return wantarray ? ($kid, $status, $code) : $kid;
+ }
+ return $kid;
+}
+
+sub loggrep {
+ my $self = shift;
+ my($regex, $timeout) = @_;
+
+ my $end = time() + $timeout if $timeout;
+
+ do {
+ my($kid, $status, $code) = $self->wait(WNOHANG);
+ if ($kid > 0 && $status != 0) {
+ # child terminated with failure
+ die ref($self), " child status: $status $code";
+ }
+ open(my $fh, '<', $self->{logfile})
+ or die ref($self), " log file open failed: $!";
+ my $match = first { /$regex/ } <$fh>;
+ return $match if $match;
+ close($fh);
+ # pattern not found
+ if ($kid == 0) {
+ # child still running, wait for log data
+ sleep .1;
+ } else {
+ # child terminated, no new log data possible
+ return;
+ }
+ } while ($timeout and time() < $end);
+
+ return;
+}
+
+sub up {
+ my $self = shift;
+ my $timeout = shift || 10;
+ $self->loggrep(qr/$self->{up}/, $timeout)
+ or croak ref($self), " no $self->{up} in $self->{logfile} ".
+ "after $timeout seconds";
+ return $self;
+}
+
+sub down {
+ my $self = shift;
+ my $timeout = shift || 30;
+ $self->loggrep(qr/$self->{down}/, $timeout)
+ or croak ref($self), " no $self->{down} in $self->{logfile} ".
+ "after $timeout seconds";
+ return $self;
+}
+
+1;
diff --git a/regress/sys/kern/splice/Relay.pm b/regress/sys/kern/splice/Relay.pm
new file mode 100644
index 00000000000..c2ac5240cd5
--- /dev/null
+++ b/regress/sys/kern/splice/Relay.pm
@@ -0,0 +1,122 @@
+# $OpenBSD: Relay.pm,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Relay;
+use parent 'Proc';
+use Carp;
+use Socket qw(IPPROTO_TCP TCP_NODELAY);
+use Socket6;
+use IO::Socket;
+use IO::Socket::INET6;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{logfile} ||= "relay.log";
+ $args{up} ||= "Connected";
+ my $self = Proc::new($class, %args);
+ $self->{listendomain}
+ or croak "$class listen domain not given";
+ $self->{connectdomain}
+ or croak "$class connect domain not given";
+ $self->{connectaddr}
+ or croak "$class connect addr not given";
+ $self->{connectport}
+ or croak "$class connect port not given";
+ my $ls = IO::Socket::INET6->new(
+ Proto => "tcp",
+ ReuseAddr => 1,
+ Domain => $self->{listendomain},
+ $self->{listenaddr} ? (LocalAddr => $self->{listenaddr}) : (),
+ $self->{listenport} ? (LocalPort => $self->{listenport}) : (),
+ ) or die ref($self), " socket failed: $!";
+ if ($self->{oobinline}) {
+ setsockopt($ls, SOL_SOCKET, SO_OOBINLINE, pack('i', 1))
+ or die ref($self), " set oobinline listen failed: $!";
+ }
+ if ($self->{sndbuf}) {
+ setsockopt($ls, SOL_SOCKET, SO_SNDBUF,
+ pack('i', $self->{sndbuf}))
+ or die ref($self), " set sndbuf listen failed: $!";
+ }
+ if ($self->{rcvbuf}) {
+ setsockopt($ls, SOL_SOCKET, SO_RCVBUF,
+ pack('i', $self->{rcvbuf}))
+ or die ref($self), " set rcvbuf listen failed: $!";
+ }
+ setsockopt($ls, IPPROTO_TCP, TCP_NODELAY, pack('i', 1))
+ or die ref($self), " set nodelay listen failed: $!";
+ listen($ls, 1)
+ or die ref($self), " listen failed: $!";
+ my $log = $self->{log};
+ print $log "listen sock: ",$ls->sockhost()," ",$ls->sockport(),"\n";
+ $self->{listenaddr} = $ls->sockhost() unless $self->{listenaddr};
+ $self->{listenport} = $ls->sockport() unless $self->{listenport};
+ $self->{ls} = $ls;
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ my $as = $self->{ls}->accept()
+ or die ref($self), " socket accept failed: $!";
+ print STDERR "accept sock: ",$as->sockhost()," ",$as->sockport(),"\n";
+ print STDERR "accept peer: ",$as->peerhost()," ",$as->peerport(),"\n";
+ $as->blocking($self->{nonblocking} ? 0 : 1)
+ or die ref($self), " non-blocking accept failed: $!";
+
+ open(STDIN, '<&', $as)
+ or die ref($self), " dup STDIN failed: $!";
+ print STDERR "Accepted\n";
+
+ my $cs = IO::Socket::INET6->new(
+ Proto => "tcp",
+ Domain => $self->{connectdomain},
+ Blocking => ($self->{nonblocking} ? 0 : 1),
+ ) or die ref($self), " socket connect failed: $!";
+ if ($self->{oobinline}) {
+ setsockopt($cs, SOL_SOCKET, SO_OOBINLINE, pack('i', 1))
+ or die ref($self), " set oobinline connect failed: $!";
+ }
+ if ($self->{sndbuf}) {
+ setsockopt($cs, SOL_SOCKET, SO_SNDBUF,
+ pack('i', $self->{sndbuf}))
+ or die ref($self), " set sndbuf connect failed: $!";
+ }
+ if ($self->{rcvbuf}) {
+ setsockopt($cs, SOL_SOCKET, SO_RCVBUF,
+ pack('i', $self->{rcvbuf}))
+ or die ref($self), " set rcvbuf connect failed: $!";
+ }
+ setsockopt($cs, IPPROTO_TCP, TCP_NODELAY, pack('i', 1))
+ or die ref($self), " set nodelay connect failed: $!";
+ my @rres = getaddrinfo($self->{connectaddr}, $self->{connectport},
+ $self->{connectdomain}, SOCK_STREAM);
+ $cs->connect($rres[3])
+ or die ref($self), " connect failed: $!";
+ print STDERR "connect sock: ",$cs->sockhost()," ",$cs->sockport(),"\n";
+ print STDERR "connect peer: ",$cs->peerhost()," ",$cs->peerport(),"\n"
+ unless $self->{nonblocking};
+
+ open(STDOUT, '>&', $cs)
+ or die ref($self), " dup STDOUT failed: $!";
+}
+
+1;
diff --git a/regress/sys/kern/splice/Remote.pm b/regress/sys/kern/splice/Remote.pm
new file mode 100644
index 00000000000..2f68c09d0ff
--- /dev/null
+++ b/regress/sys/kern/splice/Remote.pm
@@ -0,0 +1,73 @@
+# $OpenBSD: Remote.pm,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Remote;
+use parent 'Proc';
+use Carp;
+use Cwd;
+use File::Basename;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{logfile} ||= "remote.log";
+ $args{up} ||= "Started";
+ $args{func} = sub { Carp::confess "$class func may not be called" };
+ $args{remotessh}
+ or croak "$class remote ssh host not given";
+ $args{forward}
+ or croak "$class forward not given";
+ my $self = Proc::new($class, %args);
+ $self->{listenaddr}
+ or croak "$class listen addr not given";
+ $self->{connectaddr}
+ or croak "$class connect addr not given";
+ $self->{connectport}
+ or croak "$class connect port not given";
+ return $self;
+}
+
+sub up {
+ my $self = Proc::up(shift, @_);
+ my $timeout = shift || 10;
+ my $lsock = $self->loggrep(qr/^listen sock: /, $timeout)
+ or croak ref($self), " no listen sock in $self->{logfile} ".
+ "after $timeout seconds";
+ my($addr, $port) = $lsock =~ /: (\S+) (\S+)$/
+ or croak ref($self), " no listen addr and port in $self->{logfile}";
+ $self->{listenaddr} = $addr;
+ $self->{listenport} = $port;
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ print STDERR $self->{up}, "\n";
+ my @opts = split(' ', $ENV{SSH_OPTIONS}) if $ENV{SSH_OPTIONS};
+ my @cmd = ('ssh', '-n', @opts, $self->{remotessh}, 'perl',
+ '-I', getcwd(), getcwd().'/'.basename($0), $self->{forward},
+ $self->{listenaddr}, $self->{connectaddr}, $self->{connectport},
+ ($self->{testfile} ? getcwd().'/'.basename($self->{testfile}) :
+ ()));
+ exec @cmd;
+ die "Exec @cmd failed: $!";
+}
+
+1;
diff --git a/regress/sys/kern/splice/Server.pm b/regress/sys/kern/splice/Server.pm
new file mode 100644
index 00000000000..32168d820a5
--- /dev/null
+++ b/regress/sys/kern/splice/Server.pm
@@ -0,0 +1,85 @@
+# $OpenBSD: Server.pm,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Server;
+use parent 'Proc';
+use Carp;
+use Socket qw(IPPROTO_TCP TCP_NODELAY);
+use Socket6;
+use IO::Socket;
+use IO::Socket::INET6;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{logfile} ||= "server.log";
+ $args{up} ||= "Accepted";
+ my $self = Proc::new($class, %args);
+ $self->{listendomain}
+ or croak "$class listen domain not given";
+ my $ls = IO::Socket::INET6->new(
+ Proto => "tcp",
+ ReuseAddr => 1,
+ Domain => $self->{listendomain},
+ $self->{listenaddr} ? (LocalAddr => $self->{listenaddr}) : (),
+ $self->{listenport} ? (LocalPort => $self->{listenport}) : (),
+ ) or die ref($self), " socket failed: $!";
+ if ($self->{oobinline}) {
+ setsockopt($ls, SOL_SOCKET, SO_OOBINLINE, pack('i', 1))
+ or die ref($self), " set oobinline listen failed: $!";
+ }
+ if ($self->{sndbuf}) {
+ setsockopt($ls, SOL_SOCKET, SO_SNDBUF,
+ pack('i', $self->{sndbuf}))
+ or die ref($self), " set sndbuf listen failed: $!";
+ }
+ if ($self->{rcvbuf}) {
+ setsockopt($ls, SOL_SOCKET, SO_RCVBUF,
+ pack('i', $self->{rcvbuf}))
+ or die ref($self), " set rcvbuf listen failed: $!";
+ }
+ setsockopt($ls, IPPROTO_TCP, TCP_NODELAY, pack('i', 1))
+ or die ref($self), " set nodelay listen failed: $!";
+ listen($ls, 1)
+ or die ref($self), " socket failed: $!";
+ my $log = $self->{log};
+ print $log "listen sock: ",$ls->sockhost()," ",$ls->sockport(),"\n";
+ $self->{listenaddr} = $ls->sockhost() unless $self->{listenaddr};
+ $self->{listenport} = $ls->sockport() unless $self->{listenport};
+ $self->{ls} = $ls;
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ my $as = $self->{ls}->accept()
+ or die ref($self), " socket accept failed: $!";
+ print STDERR "accept sock: ",$as->sockhost()," ",$as->sockport(),"\n";
+ print STDERR "accept peer: ",$as->peerhost()," ",$as->peerport(),"\n";
+ $as->blocking($self->{nonblocking} ? 0 : 1)
+ or die ref($self), " non-blocking accept failed: $!";
+
+ open(STDIN, '<&', $as)
+ or die ref($self), " dup STDIN failed: $!";
+ open(STDOUT, '>&', $as)
+ or die ref($self), " dup STDOUT failed: $!";
+}
+
+1;
diff --git a/regress/sys/kern/splice/args-default.pl b/regress/sys/kern/splice/args-default.pl
new file mode 100644
index 00000000000..28e53a92126
--- /dev/null
+++ b/regress/sys/kern/splice/args-default.pl
@@ -0,0 +1,11 @@
+# test default values
+
+use strict;
+use warnings;
+
+our %args = (
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-inet6-client.pl b/regress/sys/kern/splice/args-inet6-client.pl
new file mode 100644
index 00000000000..a542fd12f6d
--- /dev/null
+++ b/regress/sys/kern/splice/args-inet6-client.pl
@@ -0,0 +1,23 @@
+# test ipv6 client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ connectdomain => AF_INET6,
+ connectaddr => "::1",
+ },
+ relay => {
+ listendomain => AF_INET6,
+ listenaddr => "::1",
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ },
+ server => {
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ },
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-inet6-server.pl b/regress/sys/kern/splice/args-inet6-server.pl
new file mode 100644
index 00000000000..fa560098f5b
--- /dev/null
+++ b/regress/sys/kern/splice/args-inet6-server.pl
@@ -0,0 +1,23 @@
+# test ipv6 server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ },
+ relay => {
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ connectdomain => AF_INET6,
+ connectaddr => "::1",
+ },
+ server => {
+ listendomain => AF_INET6,
+ listenaddr => "::1",
+ },
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-inet6.pl b/regress/sys/kern/splice/args-inet6.pl
new file mode 100644
index 00000000000..31b481ca796
--- /dev/null
+++ b/regress/sys/kern/splice/args-inet6.pl
@@ -0,0 +1,23 @@
+# test ipv6 client and server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ connectdomain => AF_INET6,
+ connectaddr => "::1",
+ },
+ relay => {
+ listendomain => AF_INET6,
+ listenaddr => "::1",
+ connectdomain => AF_INET6,
+ connectaddr => "::1",
+ },
+ server => {
+ listendomain => AF_INET6,
+ listenaddr => "::1",
+ },
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-long.pl b/regress/sys/kern/splice/args-long.pl
new file mode 100644
index 00000000000..e88d6f40c0d
--- /dev/null
+++ b/regress/sys/kern/splice/args-long.pl
@@ -0,0 +1,14 @@
+# test longer data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-max-sleep-client-nonblock.pl b/regress/sys/kern/splice/args-max-sleep-client-nonblock.pl
new file mode 100644
index 00000000000..6bf962dcca3
--- /dev/null
+++ b/regress/sys/kern/splice/args-max-sleep-client-nonblock.pl
@@ -0,0 +1,19 @@
+# test maximum data length with delay before client write with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_char(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ max => 113,
+ nonblocking => 1,
+ },
+ len => 113,
+ md5 => "dc099ef642faa02bce71298f11e7d44d",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-max-sleep-client-short.pl b/regress/sys/kern/splice/args-max-sleep-client-short.pl
new file mode 100644
index 00000000000..79d21aea536
--- /dev/null
+++ b/regress/sys/kern/splice/args-max-sleep-client-short.pl
@@ -0,0 +1,18 @@
+# test maximum data length with delay before client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_char(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ max => 113,
+ },
+ len => 113,
+ md5 => "dc099ef642faa02bce71298f11e7d44d",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-max-sleep-client.pl b/regress/sys/kern/splice/args-max-sleep-client.pl
new file mode 100644
index 00000000000..e46f44e68ee
--- /dev/null
+++ b/regress/sys/kern/splice/args-max-sleep-client.pl
@@ -0,0 +1,19 @@
+# test maximum data length with delay before client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); sleep 3; write_char(@_); },
+ len => 2**17,
+ nocheck => 1,
+ },
+ relay => {
+ max => 32117,
+ },
+ len => 32117,
+ md5 => "ee338e9693fb2a2ec101bb28935ed123",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-max-sleep-relay-short.pl b/regress/sys/kern/splice/args-max-sleep-relay-short.pl
new file mode 100644
index 00000000000..f8d38b64a3f
--- /dev/null
+++ b/regress/sys/kern/splice/args-max-sleep-relay-short.pl
@@ -0,0 +1,18 @@
+# test maximum data length with delay before relay copy and short len
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ nocheck => 1,
+ },
+ relay => {
+ func => sub { sleep 3; relay(@_); },
+ max => 113,
+ },
+ len => 113,
+ md5 => "dc099ef642faa02bce71298f11e7d44d",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-max-sleep-relay.pl b/regress/sys/kern/splice/args-max-sleep-relay.pl
new file mode 100644
index 00000000000..a921608e704
--- /dev/null
+++ b/regress/sys/kern/splice/args-max-sleep-relay.pl
@@ -0,0 +1,21 @@
+# test maximum data length with delay before relay copy
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_char(@_); },
+ len => 2**17,
+ down => "Client print failed: Broken pipe",
+ nocheck => 1,
+ },
+ relay => {
+ func => sub { sleep 3; relay(@_); shutin(@_); sleep 1; },
+ max => 32117,
+ },
+ len => 32117,
+ md5 => "ee338e9693fb2a2ec101bb28935ed123",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-max-sleep-server.pl b/regress/sys/kern/splice/args-max-sleep-server.pl
new file mode 100644
index 00000000000..13f01f4e8a5
--- /dev/null
+++ b/regress/sys/kern/splice/args-max-sleep-server.pl
@@ -0,0 +1,22 @@
+# test maximum data length with delay before server read
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_char(@_); },
+ len => 2**17,
+ nocheck => 1,
+ },
+ relay => {
+ max => 32117,
+ },
+ server => {
+ func => sub { sleep 3; read_char(@_); },
+ },
+ len => 32117,
+ md5 => "ee338e9693fb2a2ec101bb28935ed123",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-max.pl b/regress/sys/kern/splice/args-max.pl
new file mode 100644
index 00000000000..33d98d02130
--- /dev/null
+++ b/regress/sys/kern/splice/args-max.pl
@@ -0,0 +1,19 @@
+# test maximum data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_char(@_); },
+ len => 2**17,
+ nocheck => 1,
+ },
+ relay => {
+ max => 32117,
+ },
+ len => 32117,
+ md5 => "ee338e9693fb2a2ec101bb28935ed123",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-maxcopy-sleep-client.pl b/regress/sys/kern/splice/args-maxcopy-sleep-client.pl
new file mode 100644
index 00000000000..adaed950640
--- /dev/null
+++ b/regress/sys/kern/splice/args-maxcopy-sleep-client.pl
@@ -0,0 +1,18 @@
+# test relay maximum data length then copy with delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_char(@_); },
+ },
+ relay => {
+ func => sub { relay(@_, 61); relay_copy(@_); },
+ nocheck => 1,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-maxcopy-sleep-relay.pl b/regress/sys/kern/splice/args-maxcopy-sleep-relay.pl
new file mode 100644
index 00000000000..36e0832cb0e
--- /dev/null
+++ b/regress/sys/kern/splice/args-maxcopy-sleep-relay.pl
@@ -0,0 +1,15 @@
+# test relay maximum data length then copy with delay before relay
+
+use strict;
+use warnings;
+
+our %args = (
+ relay => {
+ func => sub { sleep 3; relay(@_, 61); relay_copy(@_); },
+ nocheck => 1,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-maxcopy-sleep-server.pl b/regress/sys/kern/splice/args-maxcopy-sleep-server.pl
new file mode 100644
index 00000000000..672c90db1ef
--- /dev/null
+++ b/regress/sys/kern/splice/args-maxcopy-sleep-server.pl
@@ -0,0 +1,18 @@
+# test relay maximum data length then copy with delay before server
+
+use strict;
+use warnings;
+
+our %args = (
+ relay => {
+ func => sub { relay(@_, 61); relay_copy(@_); },
+ nocheck => 1,
+ },
+ server => {
+ func => sub { sleep 3; read_char(@_); },
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-nonblock.pl b/regress/sys/kern/splice/args-nonblock.pl
new file mode 100644
index 00000000000..4f3ae6a9737
--- /dev/null
+++ b/regress/sys/kern/splice/args-nonblock.pl
@@ -0,0 +1,17 @@
+# test with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ nonblocking => 1,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-null-max-sleep-client-nonblock.pl b/regress/sys/kern/splice/args-null-max-sleep-client-nonblock.pl
new file mode 100644
index 00000000000..801a9584d7e
--- /dev/null
+++ b/regress/sys/kern/splice/args-null-max-sleep-client-nonblock.pl
@@ -0,0 +1,19 @@
+# test maximum data length with delay before empty client write with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3 },
+ len => 0,
+ },
+ relay => {
+ max => 113,
+ nonblocking => 1,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-null-max-sleep-client.pl b/regress/sys/kern/splice/args-null-max-sleep-client.pl
new file mode 100644
index 00000000000..cbf89b46a49
--- /dev/null
+++ b/regress/sys/kern/splice/args-null-max-sleep-client.pl
@@ -0,0 +1,18 @@
+# test maximum data length with delay before empty client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3 },
+ len => 0,
+ },
+ relay => {
+ max => 113,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-null-sleep-client.pl b/regress/sys/kern/splice/args-null-sleep-client.pl
new file mode 100644
index 00000000000..5a02ceb43f4
--- /dev/null
+++ b/regress/sys/kern/splice/args-null-sleep-client.pl
@@ -0,0 +1,15 @@
+# test with delay before empty client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3 },
+ len => 0,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-null.pl b/regress/sys/kern/splice/args-null.pl
new file mode 100644
index 00000000000..fcb0c70826f
--- /dev/null
+++ b/regress/sys/kern/splice/args-null.pl
@@ -0,0 +1,14 @@
+# test emtpy client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 0,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oob-max-sleep-client.pl b/regress/sys/kern/splice/args-oob-max-sleep-client.pl
new file mode 100644
index 00000000000..3fa0302317a
--- /dev/null
+++ b/regress/sys/kern/splice/args-oob-max-sleep-client.pl
@@ -0,0 +1,22 @@
+# test out-of-band data with maximum data length delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); sleep 3; write_oob(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ max => 61,
+ nocheck => 1,
+ },
+ server => {
+ func => \&read_oob,
+ },
+ len => 61,
+ md5 => "e4282daf8d2ca21cc8b70b1314713314",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oob-max-sleep-relay.pl b/regress/sys/kern/splice/args-oob-max-sleep-relay.pl
new file mode 100644
index 00000000000..72e0d2d6aa6
--- /dev/null
+++ b/regress/sys/kern/splice/args-oob-max-sleep-relay.pl
@@ -0,0 +1,23 @@
+# test out-of-band data with maximum data length delay before relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_oob(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ func => sub { sleep 3; relay(@_); },
+ max => 61,
+ nocheck => 1,
+ },
+ server => {
+ func => \&read_oob,
+ },
+ len => 61,
+ md5 => "e4282daf8d2ca21cc8b70b1314713314",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oob-max-sleep-server.pl b/regress/sys/kern/splice/args-oob-max-sleep-server.pl
new file mode 100644
index 00000000000..39193a63585
--- /dev/null
+++ b/regress/sys/kern/splice/args-oob-max-sleep-server.pl
@@ -0,0 +1,22 @@
+# test out-of-band data with maximum data length delay before server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_oob(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ max => 61,
+ nocheck => 1,
+ },
+ server => {
+ func => sub { sleep 3; read_oob(@_); },
+ },
+ len => 61,
+ md5 => "e4282daf8d2ca21cc8b70b1314713314",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oob-max.pl b/regress/sys/kern/splice/args-oob-max.pl
new file mode 100644
index 00000000000..a60cfed6d48
--- /dev/null
+++ b/regress/sys/kern/splice/args-oob-max.pl
@@ -0,0 +1,22 @@
+# test out-of-band data with maximum data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_oob(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ max => 61,
+ nocheck => 1,
+ },
+ server => {
+ func => \&read_oob,
+ },
+ len => 61,
+ md5 => "e4282daf8d2ca21cc8b70b1314713314",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oob-nonblock.pl b/regress/sys/kern/splice/args-oob-nonblock.pl
new file mode 100644
index 00000000000..63ca0c55471
--- /dev/null
+++ b/regress/sys/kern/splice/args-oob-nonblock.pl
@@ -0,0 +1,22 @@
+# test out-of-band data with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&write_oob,
+ nocheck => 1,
+ },
+ relay => {
+ nonblocking => 1,
+ nocheck => 1,
+ },
+ server => {
+ func => \&read_oob,
+ },
+ len => 247,
+ md5 => "f76df02a35322058b8c29002aaea944f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oob-reverse-nonblock.pl b/regress/sys/kern/splice/args-oob-reverse-nonblock.pl
new file mode 100644
index 00000000000..b9b8f3bb712
--- /dev/null
+++ b/regress/sys/kern/splice/args-oob-reverse-nonblock.pl
@@ -0,0 +1,23 @@
+# test out-of-band data when reverse sending with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_oob,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ nonblocking => 1,
+ nocheck => 1,
+ },
+ server => {
+ func => \&write_oob,
+ nocheck => 1,
+ },
+ len => 247,
+ md5 => "f76df02a35322058b8c29002aaea944f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oob-reverse.pl b/regress/sys/kern/splice/args-oob-reverse.pl
new file mode 100644
index 00000000000..fb83bb3b645
--- /dev/null
+++ b/regress/sys/kern/splice/args-oob-reverse.pl
@@ -0,0 +1,22 @@
+# test out-of-band data when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_oob,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ nocheck => 1,
+ },
+ server => {
+ func => \&write_oob,
+ nocheck => 1,
+ },
+ len => 247,
+ md5 => "f76df02a35322058b8c29002aaea944f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oob.pl b/regress/sys/kern/splice/args-oob.pl
new file mode 100644
index 00000000000..b630a0702e7
--- /dev/null
+++ b/regress/sys/kern/splice/args-oob.pl
@@ -0,0 +1,21 @@
+# test out-of-band data
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&write_oob,
+ nocheck => 1,
+ },
+ relay => {
+ nocheck => 1,
+ },
+ server => {
+ func => \&read_oob,
+ },
+ len => 247,
+ md5 => "f76df02a35322058b8c29002aaea944f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oobinline-max-sleep-client.pl b/regress/sys/kern/splice/args-oobinline-max-sleep-client.pl
new file mode 100644
index 00000000000..9e48feb9cfe
--- /dev/null
+++ b/regress/sys/kern/splice/args-oobinline-max-sleep-client.pl
@@ -0,0 +1,23 @@
+# test inline out-of-band data with maximum data length delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); sleep 3; write_oob(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ oobinline => 1,
+ max => 61,
+ nocheck => 1,
+ },
+ server => {
+ func => \&read_oob,
+ },
+ len => 61,
+ md5 => "c9f459db9b4f369980c79bff17e1c2a0",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oobinline-max-sleep-relay.pl b/regress/sys/kern/splice/args-oobinline-max-sleep-relay.pl
new file mode 100644
index 00000000000..d778af41778
--- /dev/null
+++ b/regress/sys/kern/splice/args-oobinline-max-sleep-relay.pl
@@ -0,0 +1,25 @@
+# test inline out-of-band data with maximum data length delay before relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_oob(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ func => sub { sleep 3; relay(@_); },
+ oobinline => 1,
+ max => 61,
+ nocheck => 1,
+ },
+ server => {
+ func => \&read_oob,
+ },
+ len => 61,
+ # the oob data is converted to non-oob
+ md5 => "4b5efc5f86fa5fc873c82103ceece85d",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oobinline-max-sleep-server.pl b/regress/sys/kern/splice/args-oobinline-max-sleep-server.pl
new file mode 100644
index 00000000000..7f35a2cb405
--- /dev/null
+++ b/regress/sys/kern/splice/args-oobinline-max-sleep-server.pl
@@ -0,0 +1,23 @@
+# test inline out-of-band data with maximum data length delay before server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_oob(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ oobinline => 1,
+ max => 61,
+ nocheck => 1,
+ },
+ server => {
+ func => sub { sleep 3; read_oob(@_); },
+ },
+ len => 61,
+ md5 => "c9f459db9b4f369980c79bff17e1c2a0",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oobinline-max.pl b/regress/sys/kern/splice/args-oobinline-max.pl
new file mode 100644
index 00000000000..c6801ddfb5f
--- /dev/null
+++ b/regress/sys/kern/splice/args-oobinline-max.pl
@@ -0,0 +1,23 @@
+# test inline out-of-band data with maximum data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_oob(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ oobinline => 1,
+ max => 61,
+ nocheck => 1,
+ },
+ server => {
+ func => \&read_oob,
+ },
+ len => 61,
+ md5 => "c9f459db9b4f369980c79bff17e1c2a0",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oobinline-nonblock.pl b/regress/sys/kern/splice/args-oobinline-nonblock.pl
new file mode 100644
index 00000000000..31e3f69d92a
--- /dev/null
+++ b/regress/sys/kern/splice/args-oobinline-nonblock.pl
@@ -0,0 +1,22 @@
+# test inline out-of-band data with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&write_oob,
+ },
+ relay => {
+ oobinline => 1,
+ nonblocking => 1,
+ },
+ server => {
+ func => \&read_oob,
+ oobinline => 1,
+ },
+ len => 251,
+ md5 => "24b69642243fee9834bceee5b47078ae",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oobinline-reverse-nonblock.pl b/regress/sys/kern/splice/args-oobinline-reverse-nonblock.pl
new file mode 100644
index 00000000000..e9a784433c1
--- /dev/null
+++ b/regress/sys/kern/splice/args-oobinline-reverse-nonblock.pl
@@ -0,0 +1,23 @@
+# test inline out-of-band data when reverse sending with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_oob,
+ oobinline => 1,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ oobinline => 1,
+ nonblocking => 1,
+ },
+ server => {
+ func => \&write_oob,
+ },
+ len => 251,
+ md5 => "24b69642243fee9834bceee5b47078ae",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oobinline-reverse.pl b/regress/sys/kern/splice/args-oobinline-reverse.pl
new file mode 100644
index 00000000000..4a570b3e5bf
--- /dev/null
+++ b/regress/sys/kern/splice/args-oobinline-reverse.pl
@@ -0,0 +1,22 @@
+# test inline out-of-band data when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_oob,
+ oobinline => 1,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ oobinline => 1,
+ },
+ server => {
+ func => \&write_oob,
+ },
+ len => 251,
+ md5 => "24b69642243fee9834bceee5b47078ae",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-oobinline.pl b/regress/sys/kern/splice/args-oobinline.pl
new file mode 100644
index 00000000000..7de0bcb55a0
--- /dev/null
+++ b/regress/sys/kern/splice/args-oobinline.pl
@@ -0,0 +1,21 @@
+# test inline out-of-band data
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&write_oob,
+ },
+ relay => {
+ oobinline => 1,
+ },
+ server => {
+ func => \&read_oob,
+ oobinline => 1,
+ },
+ len => 251,
+ md5 => "24b69642243fee9834bceee5b47078ae",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-relay-close-in.pl b/regress/sys/kern/splice/args-relay-close-in.pl
new file mode 100644
index 00000000000..06180bc910c
--- /dev/null
+++ b/regress/sys/kern/splice/args-relay-close-in.pl
@@ -0,0 +1,29 @@
+# test relay closes stdin after relaying some data
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_char(@_); },
+ len => 2**30, # not reached
+ sndbuf => 2**10, # small buffer triggers error during write
+ # the error message seems to be timing dependent
+ down => "Client print failed: (Broken pipe|Connection reset by peer)",
+ error => 54,
+ },
+ relay => {
+ func => sub { errignore(@_); $SIG{ALRM} = sub { close STDIN };
+ alarm(3); relay(@_); },
+ rcvbuf => 2**10,
+ sndbuf => 2**10,
+ down => "Bad file descriptor",
+ nocheck => 1,
+ errorin => "",
+ },
+ server => {
+ rcvbuf => 2**10,
+ },
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reset-eof.pl b/regress/sys/kern/splice/args-reset-eof.pl
new file mode 100644
index 00000000000..11665fc628e
--- /dev/null
+++ b/regress/sys/kern/splice/args-reset-eof.pl
@@ -0,0 +1,12 @@
+# test connection reset by server at eof, after all data has been read
+
+use strict;
+use warnings;
+
+our %args = (
+ server => {
+ func => sub { read_char(@_); sleep 3; solinger(@_); },
+ },
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reset.pl b/regress/sys/kern/splice/args-reset.pl
new file mode 100644
index 00000000000..319425c5053
--- /dev/null
+++ b/regress/sys/kern/splice/args-reset.pl
@@ -0,0 +1,24 @@
+# test connection reset by server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_char(@_); },
+ len => 2**17,
+ },
+ relay => {
+ func => sub { errignore(@_); relay(@_); },
+ sndbuf => 2**12,
+ rcvbuf => 2**12,
+ down => "Broken pipe|Connection reset by peer",
+ },
+ server => {
+ func => sub { sleep 3; solinger(@_); },
+ rcvbuf => 2**12,
+ },
+ nocheck => 1,
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reverse-long.pl b/regress/sys/kern/splice/args-reverse-long.pl
new file mode 100644
index 00000000000..27d5f0e4ae8
--- /dev/null
+++ b/regress/sys/kern/splice/args-reverse-long.pl
@@ -0,0 +1,21 @@
+# test longer data lenght when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_char,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => \&write_char,
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reverse-nonblock.pl b/regress/sys/kern/splice/args-reverse-nonblock.pl
new file mode 100644
index 00000000000..48146c8b354
--- /dev/null
+++ b/regress/sys/kern/splice/args-reverse-nonblock.pl
@@ -0,0 +1,21 @@
+# test reverse sending with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_char,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ nonblocking => 1,
+ },
+ server => {
+ func => \&write_char,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reverse-null.pl b/regress/sys/kern/splice/args-reverse-null.pl
new file mode 100644
index 00000000000..65c54a93b0a
--- /dev/null
+++ b/regress/sys/kern/splice/args-reverse-null.pl
@@ -0,0 +1,21 @@
+# test emtpy server write when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_char,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => \&write_char,
+ len => 0,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reverse-sleep-client.pl b/regress/sys/kern/splice/args-reverse-sleep-client.pl
new file mode 100644
index 00000000000..244e65dc85b
--- /dev/null
+++ b/regress/sys/kern/splice/args-reverse-sleep-client.pl
@@ -0,0 +1,21 @@
+# test delay before client read when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; read_char(@_); },
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => \&write_char,
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reverse-sleep-relay.pl b/regress/sys/kern/splice/args-reverse-sleep-relay.pl
new file mode 100644
index 00000000000..fbc218f82cf
--- /dev/null
+++ b/regress/sys/kern/splice/args-reverse-sleep-relay.pl
@@ -0,0 +1,21 @@
+# test delay before relay copy when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_char,
+ },
+ relay => {
+ func => sub { ioflip(@_); sleep 3; relay(@_); },
+ },
+ server => {
+ func => \&write_char,
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reverse-sleep-server.pl b/regress/sys/kern/splice/args-reverse-sleep-server.pl
new file mode 100644
index 00000000000..ceff0eac461
--- /dev/null
+++ b/regress/sys/kern/splice/args-reverse-sleep-server.pl
@@ -0,0 +1,21 @@
+# test delay before server write when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_char,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => sub { sleep 3; write_char(@_); },
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-reverse.pl b/regress/sys/kern/splice/args-reverse.pl
new file mode 100644
index 00000000000..33ef06466a7
--- /dev/null
+++ b/regress/sys/kern/splice/args-reverse.pl
@@ -0,0 +1,20 @@
+# test reverse sending from server to client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_char,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => \&write_char,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-server-abort.pl b/regress/sys/kern/splice/args-server-abort.pl
new file mode 100644
index 00000000000..b0b64874e6d
--- /dev/null
+++ b/regress/sys/kern/splice/args-server-abort.pl
@@ -0,0 +1,31 @@
+# test server reads and exits after reading some data
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_char(@_); },
+ len => 2**30, # not reached
+ sndbuf => 2**10, # small buffer triggers error during write
+ # the error message seems to be timing dependent
+ down => "Client print failed: (Broken pipe|Connection reset by peer)",
+ error => 54,
+ },
+ relay => {
+ func => sub { errignore(@_); relay(@_); },
+ rcvbuf => 2**10,
+ sndbuf => 2**10,
+ down => "Broken pipe|Connection reset by peer",
+ nocheck => 1,
+ errorin => 0, # syscall has read the error and resetted it
+ errorout => 54,
+ },
+ server => {
+ func => sub { $SIG{ALRM} = sub { print STDERR "\nShutdown\n"; exit 0 };
+ alarm(3); read_char(@_); },
+ },
+ noecho => 1,
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-server-exit.pl b/regress/sys/kern/splice/args-server-exit.pl
new file mode 100644
index 00000000000..99cc94b0bb6
--- /dev/null
+++ b/regress/sys/kern/splice/args-server-exit.pl
@@ -0,0 +1,33 @@
+# test server sleeps and exits without reading data
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_char(@_); },
+ len => 2**17,
+ sndbuf => 2**10, # small buffer triggers error during write
+ # the error message seems to be timing dependent
+ down => "Client print failed: (Broken pipe|Connection reset by peer)",
+ error => 54,
+ },
+ relay => {
+ func => sub { errignore(@_); relay(@_); },
+ rcvbuf => 2**10,
+ sndbuf => 2**10,
+ down => "Broken pipe",
+ nocheck => 1,
+ errorin => 0, # syscall has read the error and resetted it
+ errorout => 54,
+ },
+ server => {
+ func => sub { sleep 3; },
+ rcvbuf => 2**10,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+ noecho => 1,
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-short.pl b/regress/sys/kern/splice/args-short.pl
new file mode 100644
index 00000000000..39c238c9c31
--- /dev/null
+++ b/regress/sys/kern/splice/args-short.pl
@@ -0,0 +1,14 @@
+# test copy with short block size
+
+use strict;
+use warnings;
+
+our %args = (
+ realy => {
+ size => 17,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-sleep-client.pl b/regress/sys/kern/splice/args-sleep-client.pl
new file mode 100644
index 00000000000..9070a18c691
--- /dev/null
+++ b/regress/sys/kern/splice/args-sleep-client.pl
@@ -0,0 +1,15 @@
+# test delay before client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_char(@_); },
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-sleep-relay.pl b/regress/sys/kern/splice/args-sleep-relay.pl
new file mode 100644
index 00000000000..3b3b9b1651c
--- /dev/null
+++ b/regress/sys/kern/splice/args-sleep-relay.pl
@@ -0,0 +1,17 @@
+# test delay before relay copy
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ func => sub { sleep 3; relay(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-sleep-server.pl b/regress/sys/kern/splice/args-sleep-server.pl
new file mode 100644
index 00000000000..50920d1a091
--- /dev/null
+++ b/regress/sys/kern/splice/args-sleep-server.pl
@@ -0,0 +1,17 @@
+# test delay before server read
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ server => {
+ func => sub { sleep 3; read_char(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallbuf-sleep-client.pl b/regress/sys/kern/splice/args-smallbuf-sleep-client.pl
new file mode 100644
index 00000000000..e3f01629433
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallbuf-sleep-client.pl
@@ -0,0 +1,19 @@
+# test with smaller relay send and receive buffers delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_char(@_); },
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallbuf-sleep-relay.pl b/regress/sys/kern/splice/args-smallbuf-sleep-relay.pl
new file mode 100644
index 00000000000..3a116bfeafe
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallbuf-sleep-relay.pl
@@ -0,0 +1,19 @@
+# test with smaller relay send and receive buffers delay before relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ func => sub { sleep 3; relay(@_); },
+ sndbuf => 2**12,
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallbuf-sleep-server.pl b/regress/sys/kern/splice/args-smallbuf-sleep-server.pl
new file mode 100644
index 00000000000..0ffbf9bae0f
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallbuf-sleep-server.pl
@@ -0,0 +1,21 @@
+# test with smaller relay send and receive buffers delay before server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ rcvbuf => 2**12,
+ },
+ server => {
+ func => sub { sleep 3; read_char(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallbuf.pl b/regress/sys/kern/splice/args-smallbuf.pl
new file mode 100644
index 00000000000..a554b849c4f
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallbuf.pl
@@ -0,0 +1,18 @@
+# test with smaller relay send and receive buffers
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallrcv-sleep-client.pl b/regress/sys/kern/splice/args-smallrcv-sleep-client.pl
new file mode 100644
index 00000000000..154e7ee0c0b
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallrcv-sleep-client.pl
@@ -0,0 +1,18 @@
+# test with smaller relay receive buffer delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_char(@_); },
+ len => 2**17,
+ },
+ relay => {
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallrcv-sleep-relay.pl b/regress/sys/kern/splice/args-smallrcv-sleep-relay.pl
new file mode 100644
index 00000000000..f1845c55d36
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallrcv-sleep-relay.pl
@@ -0,0 +1,18 @@
+# test with smaller relay receive buffer delay before relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ func => sub { sleep 3; relay(@_); },
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallrcv-sleep-server.pl b/regress/sys/kern/splice/args-smallrcv-sleep-server.pl
new file mode 100644
index 00000000000..6f30c7ee903
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallrcv-sleep-server.pl
@@ -0,0 +1,20 @@
+# test with smaller relay receive buffer delay before server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ rcvbuf => 2**12,
+ },
+ server => {
+ func => sub { sleep 3; read_char(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallrcv.pl b/regress/sys/kern/splice/args-smallrcv.pl
new file mode 100644
index 00000000000..f13f4e09d32
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallrcv.pl
@@ -0,0 +1,17 @@
+# test with smaller relay receive buffer
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallsnd-sleep-client.pl b/regress/sys/kern/splice/args-smallsnd-sleep-client.pl
new file mode 100644
index 00000000000..f2c2715023c
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallsnd-sleep-client.pl
@@ -0,0 +1,18 @@
+# test with smaller relay send buffer delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_char(@_); },
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallsnd-sleep-relay.pl b/regress/sys/kern/splice/args-smallsnd-sleep-relay.pl
new file mode 100644
index 00000000000..0d79e6bc0cf
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallsnd-sleep-relay.pl
@@ -0,0 +1,18 @@
+# test with smaller relay send buffer delay before relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ func => sub { sleep 3; relay(@_); },
+ sndbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallsnd-sleep-server.pl b/regress/sys/kern/splice/args-smallsnd-sleep-server.pl
new file mode 100644
index 00000000000..62270e60d2b
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallsnd-sleep-server.pl
@@ -0,0 +1,20 @@
+# test with smaller relay send buffer delay before server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ },
+ server => {
+ func => sub { sleep 3; read_char(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-smallsnd.pl b/regress/sys/kern/splice/args-smallsnd.pl
new file mode 100644
index 00000000000..3193d69830e
--- /dev/null
+++ b/regress/sys/kern/splice/args-smallsnd.pl
@@ -0,0 +1,17 @@
+# test with smaller relay send buffer
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-write-sleep-server.pl b/regress/sys/kern/splice/args-write-sleep-server.pl
new file mode 100644
index 00000000000..a67ba013adb
--- /dev/null
+++ b/regress/sys/kern/splice/args-write-sleep-server.pl
@@ -0,0 +1,28 @@
+# test mix write and relay delaying before server read
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 4; write_char(@_); },
+ len => 65521,
+ nocheck => 1,
+ },
+ relay => {
+ func => sub {
+ write_char(@_, 32749);
+ IO::Handle::flush(\*STDOUT);
+ relay(@_);
+ write_char(@_, 2039);
+ },
+ nocheck => 1,
+ },
+ server => {
+ func => sub { sleep 3; read_char(@_); },
+ },
+ len => 100309,
+ md5 => "0efc7833e5c0652823ca63eaccb9918f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/args-write.pl b/regress/sys/kern/splice/args-write.pl
new file mode 100644
index 00000000000..055475f7dc9
--- /dev/null
+++ b/regress/sys/kern/splice/args-write.pl
@@ -0,0 +1,24 @@
+# test mix write and relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 65521,
+ nocheck => 1,
+ },
+ relay => {
+ func => sub {
+ write_char(@_, 32749);
+ IO::Handle::flush(\*STDOUT);
+ relay(@_);
+ write_char(@_, 2039);
+ },
+ nocheck => 1,
+ },
+ len => 100309,
+ md5 => "0efc7833e5c0652823ca63eaccb9918f",
+);
+
+1;
diff --git a/regress/sys/kern/splice/direct.pl b/regress/sys/kern/splice/direct.pl
new file mode 100755
index 00000000000..f7823132fd9
--- /dev/null
+++ b/regress/sys/kern/splice/direct.pl
@@ -0,0 +1,91 @@
+#!/usr/bin/perl
+# $OpenBSD: direct.pl,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+use Socket;
+use Socket6;
+
+use Client;
+use Server;
+require 'funcs.pl';
+
+our %args;
+if (my $test = pop) {
+ do $test
+ or die "Do test file $test failed: ", $@ || $!;
+}
+
+@ARGV == 0 or die "usage: direct.pl [test-args.pl]\n";
+
+my $s = Server->new(
+ func => \&read_char,
+ %{$args{server}},
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+);
+my $c = Client->new(
+ func => \&write_char,
+ %{$args{client}},
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $s->{listenport},
+);
+
+$s->run;
+$c->run->up;
+$s->up;
+
+$c->down;
+$s->down;
+
+exit if $args{nocheck} || $args{client}{nocheck};
+
+my $clen = $c->loggrep(qr/^LEN: /) unless $args{client}{nocheck};
+my $slen = $s->loggrep(qr/^LEN: /) unless $args{server}{nocheck};
+!$clen || !$slen || $clen eq $slen
+ or die "client: $clen", "server: $slen", "len mismatch";
+!defined($args{len}) || !$clen || $clen eq "LEN: $args{len}\n"
+ or die "client: $clen", "len $args{len} expected";
+!defined($args{len}) || !$slen || $slen eq "LEN: $args{len}\n"
+ or die "server: $slen", "len $args{len} expected";
+
+my $cmd5 = $c->loggrep(qr/^MD5: /) unless $args{client}{nocheck};
+my $smd5 = $s->loggrep(qr/^MD5: /) unless $args{server}{nocheck};
+!$cmd5 || !$smd5 || $cmd5 eq $smd5
+ or die "client: $cmd5", "server: $smd5", "md5 mismatch";
+!defined($args{md5}) || !$cmd5 || $cmd5 eq "MD5: $args{md5}\n"
+ or die "client: $cmd5", "md5 $args{md5} expected";
+!defined($args{md5}) || !$smd5 || $smd5 eq "MD5: $args{md5}\n"
+ or die "server: $smd5", "md5 $args{md5} expected";
+
+my %name2proc = (client => $c, server => $s);
+foreach my $name (qw(client server)) {
+ $args{$name}{errorin} //= $args{$name}{error};
+ if (defined($args{$name}{errorin})) {
+ my $ein = $name2proc{$name}->loggrep(qr/^ERROR IN: /);
+ defined($ein) && $ein eq "ERROR IN: $args{$name}{errorin}\n"
+ or die "$name: $ein",
+ "error in $args{$name}{errorin} expected";
+ }
+ if (defined($args{$name}{errorout})) {
+ my $eout = $name2proc{$name}->loggrep(qr/^ERROR OUT: /);
+ defined($eout) && $eout eq "ERROR OUT: $args{$name}{errorout}\n"
+ or die "$name: $eout",
+ "error out $args{$name}{errorout} expected";
+ }
+}
diff --git a/regress/sys/kern/splice/echo.pl b/regress/sys/kern/splice/echo.pl
new file mode 100755
index 00000000000..4cf12252fa2
--- /dev/null
+++ b/regress/sys/kern/splice/echo.pl
@@ -0,0 +1,123 @@
+#!/usr/bin/perl
+# $OpenBSD: echo.pl,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+use Socket;
+use Socket6;
+
+use Child;
+use Client;
+use Server;
+require 'funcs.pl';
+
+sub usage {
+ die "usage: echo.pl copy|splice [test-args.pl]\n";
+}
+
+my $test;
+our %args;
+if (@ARGV and -f $ARGV[-1]) {
+ $test = pop;
+ do $test
+ or die "Do test file $test failed: ", $@ || $!;
+}
+@ARGV == 1 or usage();
+
+exit 0 if $args{noecho};
+
+my $r = Server->new(
+ forward => $ARGV[0],
+ func => \&relay,
+ logfile => "relay.log",
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ %{$args{relay}},
+);
+my $s = Child->new(
+ logfile => "server.log",
+ oobinline => 1,
+ %{$args{server}},
+ func => sub {
+ ($args{server}{func} || \&read_char)->(@_);
+ eval { shutout(@_) };
+ },
+);
+my $c = Client->new(
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $r->{listenport},
+ oobinline => 1,
+ %{$args{client}},
+ func => sub {
+ $s->run->up;
+ ($args{client}{func} || \&write_char)->(@_);
+ eval { shutout(@_) };
+ $s->down;
+ },
+);
+
+$r->run;
+$c->run->up;
+$r->up;
+
+$c->down;
+$r->down;
+$s->{pid} = -1; # XXX hack
+
+exit if $args{nocheck};
+
+my $clen = $c->loggrep(qr/^LEN: /) unless $args{client}{nocheck};
+my $rlen = $r->loggrep(qr/^LEN: /) unless $args{relay}{nocheck};
+my $slen = $s->loggrep(qr/^LEN: /) unless $args{server}{nocheck};
+!$clen || !$rlen || $clen eq $rlen
+ or die "client: $clen", "relay: $rlen", "len mismatch";
+!$rlen || !$slen || $rlen eq $slen
+ or die "relay: $rlen", "server: $slen", "len mismatch";
+!$clen || !$slen || $clen eq $slen
+ or die "client: $clen", "server: $slen", "len mismatch";
+!defined($args{len}) || !$clen || $clen eq "LEN: $args{len}\n"
+ or die "client: $clen", "len $args{len} expected";
+!defined($args{len}) || !$rlen || $rlen eq "LEN: $args{len}\n"
+ or die "relay: $rlen", "len $args{len} expected";
+!defined($args{len}) || !$slen || $slen eq "LEN: $args{len}\n"
+ or die "server: $slen", "len $args{len} expected";
+
+my $cmd5 = $c->loggrep(qr/^MD5: /) unless $args{client}{nocheck};
+my $smd5 = $s->loggrep(qr/^MD5: /) unless $args{server}{nocheck};
+!$cmd5 || !$smd5 || $cmd5 eq $smd5
+ or die "client: $cmd5", "server: $smd5", "md5 mismatch";
+!defined($args{md5}) || !$cmd5 || $cmd5 eq "MD5: $args{md5}\n"
+ or die "client: $cmd5", "md5 $args{md5} expected";
+!defined($args{md5}) || !$smd5 || $smd5 eq "MD5: $args{md5}\n"
+ or die "server: $smd5", "md5 $args{md5} expected";
+
+$args{relay}{errorin} = 0 unless $args{relay}{nocheck};
+$args{relay}{errorout} = 0 unless $args{relay}{nocheck};
+$args{relay}{errorin} //= $args{relay}{error};
+if (defined($args{relay}{errorin})) {
+ my $ein = $r->loggrep(qr/^ERROR IN: /);
+ defined($ein) && $ein eq "ERROR IN: $args{relay}{errorin}\n"
+ or die "relay: $ein",
+ "error in $args{relay}{errorin} expected";
+}
+if (defined($args{relay}{errorout})) {
+ my $eout = $r->loggrep(qr/^ERROR OUT: /);
+ defined($eout) && $eout eq "ERROR OUT: $args{relay}{errorout}\n"
+ or die "relay: $eout",
+ "error out $args{relay}{errorout} expected";
+}
diff --git a/regress/sys/kern/splice/error-EBUSY.pl b/regress/sys/kern/splice/error-EBUSY.pl
new file mode 100755
index 00000000000..e75431413ea
--- /dev/null
+++ b/regress/sys/kern/splice/error-EBUSY.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+# test EBUSY for splicing from a spliced socket
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $sl = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 5,
+ LocalAddr => "127.0.0.1",
+) or die "socket listen failed: $!";
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket connect failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket splice connect failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ or die "splice failed: $!";
+
+my $so = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket other failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $so->fileno()))
+ and die "splice from spliced socket succeeded";
+$!{EBUSY}
+ or die "error not EBUSY: $!"
diff --git a/regress/sys/kern/splice/error-ENOTCONN.pl b/regress/sys/kern/splice/error-ENOTCONN.pl
new file mode 100755
index 00000000000..b08fb9cecb8
--- /dev/null
+++ b/regress/sys/kern/splice/error-ENOTCONN.pl
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+# test ENOTCONN for splicing from unconnected socket
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+) or die "socket failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "tcp",
+) or die "socket splice failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ and die "splice from unconnected socket succeeded";
+$!{ENOTCONN}
+ or die "error not ENOTCONN: $!"
diff --git a/regress/sys/kern/splice/error-EOPNOTSUPP.pl b/regress/sys/kern/splice/error-EOPNOTSUPP.pl
new file mode 100755
index 00000000000..916f92d96b9
--- /dev/null
+++ b/regress/sys/kern/splice/error-EOPNOTSUPP.pl
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+# test EOPNOTSUPP for splicing from listen socket
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 1,
+) or die "socket failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "tcp",
+) or die "socket splice failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ and die "splice from listen socket succeeded";
+$!{EOPNOTSUPP}
+ or die "error not EOPNOTSUPP: $!"
diff --git a/regress/sys/kern/splice/error-EPROTONOSUPPORT.pl b/regress/sys/kern/splice/error-EPROTONOSUPPORT.pl
new file mode 100755
index 00000000000..612f81a64fe
--- /dev/null
+++ b/regress/sys/kern/splice/error-EPROTONOSUPPORT.pl
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+# test EPROTONOSUPPORT for splicing udp sockets
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $s = IO::Socket::INET->new(
+ Proto => "udp",
+) or die "socket failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "udp",
+) or die "socket splice failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ and die "splice udp sockets succeeded";
+$!{EPROTONOSUPPORT}
+ or die "error not EPROTONOSUPPORT: $!"
diff --git a/regress/sys/kern/splice/error-max-EINVAL.pl b/regress/sys/kern/splice/error-max-EINVAL.pl
new file mode 100755
index 00000000000..d26525f17c5
--- /dev/null
+++ b/regress/sys/kern/splice/error-max-EINVAL.pl
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+# test EINVAL for splicing with negative maximum
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $sl = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 5,
+ LocalAddr => "127.0.0.1",
+) or die "socket listen failed: $!";
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket splice failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('iiii', $ss->fileno(),-1,-1,-1))
+ and die "splice to unconnected socket succeeded";
+$!{EINVAL}
+ or die "error not EINVAL: $!"
diff --git a/regress/sys/kern/splice/error-splice-EBADF.pl b/regress/sys/kern/splice/error-splice-EBADF.pl
new file mode 100755
index 00000000000..53b30ecfc84
--- /dev/null
+++ b/regress/sys/kern/splice/error-splice-EBADF.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/perl
+# test EBADF for splicing with non existing fileno
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $sl = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 5,
+ LocalAddr => "127.0.0.1",
+) or die "socket listen failed: $!";
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', 23))
+ and die "splice with non existing fileno succeeded";
+$!{EBADF}
+ or die "error not EBADF: $!"
diff --git a/regress/sys/kern/splice/error-splice-EBUSY.pl b/regress/sys/kern/splice/error-splice-EBUSY.pl
new file mode 100755
index 00000000000..dd0f169d6f1
--- /dev/null
+++ b/regress/sys/kern/splice/error-splice-EBUSY.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+# test EBUSY for splicing to a spliced socket
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $sl = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 5,
+ LocalAddr => "127.0.0.1",
+) or die "socket listen failed: $!";
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket connect failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket splice connect failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ or die "splice failed: $!";
+
+my $so = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket other failed: $!";
+
+$so->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ and die "splice to spliced socket succeeded";
+$!{EBUSY}
+ or die "error not EBUSY: $!"
diff --git a/regress/sys/kern/splice/error-splice-EINVAL.pl b/regress/sys/kern/splice/error-splice-EINVAL.pl
new file mode 100755
index 00000000000..c36b2f80e3e
--- /dev/null
+++ b/regress/sys/kern/splice/error-splice-EINVAL.pl
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+# test EINVAL for splicing with short fileno size
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $sl = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 5,
+ LocalAddr => "127.0.0.1",
+) or die "socket listen failed: $!";
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "tcp",
+) or die "socket splice failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('s', $ss->fileno()))
+ and die "splice with short fileno size succeeded";
+$!{EINVAL}
+ or die "error not EINVAL: $!"
diff --git a/regress/sys/kern/splice/error-splice-ENOTCONN.pl b/regress/sys/kern/splice/error-splice-ENOTCONN.pl
new file mode 100755
index 00000000000..a20a8171ac8
--- /dev/null
+++ b/regress/sys/kern/splice/error-splice-ENOTCONN.pl
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+# test ENOTCONN for splicing to unconnected socket
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $sl = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 5,
+ LocalAddr => "127.0.0.1",
+) or die "socket listen failed: $!";
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "tcp",
+) or die "socket splice failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ and die "splice to unconnected socket succeeded";
+$!{ENOTCONN}
+ or die "error not ENOTCONN: $!"
diff --git a/regress/sys/kern/splice/error-splice-EOPNOTSUPP.pl b/regress/sys/kern/splice/error-splice-EOPNOTSUPP.pl
new file mode 100755
index 00000000000..843da7b37fc
--- /dev/null
+++ b/regress/sys/kern/splice/error-splice-EOPNOTSUPP.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+# test EOPNOTSUPP for splicing to listen socket
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $sl = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 5,
+ LocalAddr => "127.0.0.1",
+) or die "socket listen failed: $!";
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 1,
+) or die "socket splice failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ and die "splice to listen socket succeeded";
+$!{EOPNOTSUPP}
+ or die "error not EOPNOTSUPP: $!"
diff --git a/regress/sys/kern/splice/error-splice-EPROTONOSUPPORT.pl b/regress/sys/kern/splice/error-splice-EPROTONOSUPPORT.pl
new file mode 100755
index 00000000000..20fb388c9e3
--- /dev/null
+++ b/regress/sys/kern/splice/error-splice-EPROTONOSUPPORT.pl
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+# test EPROTONOSUPPORT for splicing to udp socket
+
+use Errno;
+use IO::Socket;
+use constant SO_SPLICE => 0x1023;
+
+my $sl = IO::Socket::INET->new(
+ Proto => "tcp",
+ Listen => 5,
+ LocalAddr => "127.0.0.1",
+) or die "socket listen failed: $!";
+
+my $s = IO::Socket::INET->new(
+ Proto => "tcp",
+ PeerAddr => $sl->sockhost(),
+ PeerPort => $sl->sockport(),
+) or die "socket failed: $!";
+
+my $ss = IO::Socket::INET->new(
+ Proto => "udp",
+) or die "socket splice failed: $!";
+
+$s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ and die "splice to udp socket succeeded";
+$!{EPROTONOSUPPORT}
+ or die "error not EPROTONOSUPPORT: $!"
diff --git a/regress/sys/kern/splice/funcs.pl b/regress/sys/kern/splice/funcs.pl
new file mode 100644
index 00000000000..a5507b22c8d
--- /dev/null
+++ b/regress/sys/kern/splice/funcs.pl
@@ -0,0 +1,367 @@
+# $OpenBSD: funcs.pl,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+use feature 'switch';
+use Errno;
+use Digest::MD5;
+use IO::Socket qw(sockatmark);
+use Socket;
+use Time::HiRes qw(time alarm sleep);
+use constant SO_SPLICE => 0x1023;
+
+########################################################################
+# Client funcs
+########################################################################
+
+sub write_char {
+ my $self = shift;
+ my $len = shift // $self->{len} // 251;
+
+ my $ctx = Digest::MD5->new();
+ my $char = '0';
+ for (my $i = 1; $i < $len; $i++) {
+ $ctx->add($char);
+ print $char
+ or die ref($self), " print failed: $!";
+ given ($char) {
+ when(/9/) { $char = 'A' }
+ when(/Z/) { $char = 'a' }
+ when(/z/) { $char = "\n" }
+ when(/\n/) { print STDERR "."; $char = '0' }
+ default { $char++ }
+ }
+ }
+ if ($len) {
+ $char = "\n";
+ $ctx->add($char);
+ print $char
+ or die ref($self), " print failed: $!";
+ print STDERR ".\n";
+ }
+
+ print STDERR "LEN: ", $len, "\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+sub write_oob {
+ my $self = shift;
+ my $len = shift // $self->{len} // 251;
+
+ my $ctx = Digest::MD5->new();
+ my $msg = "";
+ my $char = '0';
+ for (my $i = 1; $i < $len; $i++) {
+ $msg .= $char;
+ given ($char) {
+ when(/9/) {
+ $ctx->add("[$char]");
+ defined(send(STDOUT, $msg, MSG_OOB))
+ or die ref($self), " send OOB failed: $!";
+ # If tcp urgent data is sent too fast,
+ # it may get overwritten and lost.
+ sleep .1;
+ $msg = "";
+ $char = 'A';
+ }
+ when(/Z/) { $ctx->add($char); $char = 'a' }
+ when(/z/) { $ctx->add($char); $char = "\n" }
+ when(/\n/) {
+ $ctx->add($char);
+ defined(send(STDOUT, $msg, 0))
+ or die ref($self), " send failed: $!";
+ print STDERR ".";
+ $msg = "";
+ $char = '0';
+ }
+ default { $ctx->add($char); $char++ }
+ }
+ }
+ if ($len) {
+ $char = "\n";
+ $msg .= $char;
+ $ctx->add($char);
+ send(STDOUT, $msg, 0)
+ or die ref($self), " send failed: $!";
+ print STDERR ".\n";
+ }
+
+ print STDERR "LEN: ", $len, "\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+########################################################################
+# Relay funcs
+########################################################################
+
+sub relay_copy {
+ my $self = shift;
+ my $max = shift // $self->{max};
+ my $size = $self->{size} || 8093;
+
+ my $len = 0;
+ while (1) {
+ my $rin = my $win = my $ein = '';
+ vec($rin, fileno(STDIN), 1) = 1;
+ vec($ein, fileno(STDIN), 1) = 1 unless $self->{oobinline};
+ select($rin, undef, $ein, undef)
+ or die ref($self), " select failed: $!";
+ my $buf;
+ my $atmark = sockatmark(\*STDIN)
+ or die ref($self), " sockatmark failed: $!";
+ if ($atmark == 1) {
+ if ($self->{oobinline}) {
+ defined(recv(STDIN, $buf, 1, 0))
+ or die ref($self), " recv OOB failed: $!";
+ $len += length($buf);
+ defined(send(STDOUT, $buf, MSG_OOB))
+ or die ref($self), " send OOB failed: $!";
+ } else {
+ defined(recv(STDIN, $buf, 1, MSG_OOB)) ||
+ $!{EINVAL}
+ or die ref($self), " recv OOB failed: $!";
+ print STDERR "OOB: $buf\n" if length($buf);
+ }
+ }
+ if ($self->{nonblocking}) {
+ vec($rin, fileno(STDIN), 1) = 1;
+ select($rin, undef, undef, undef)
+ or die ref($self), " select read failed: $!";
+ }
+ my $read = sysread(STDIN, $buf,
+ $max && $max < $size ? $max : $size);
+ defined $read
+ or die ref($self), " sysread at $len failed: $!";
+ if ($read == 0) {
+ print STDERR "\n";
+ print STDERR "End\n";
+ last;
+ }
+ print STDERR ".";
+ if ($max && $len + $read > $max) {
+ $read = $max - $len;
+ }
+ my $off = 0;
+ while ($off < $read) {
+ if ($self->{nonblocking}) {
+ vec($win, fileno(STDOUT), 1) = 1;
+ select(undef, $win, undef, undef)
+ or die ref($self),
+ " select write failed: $!";
+ }
+ my $write = syswrite(STDOUT, $buf, $read - $off, $off);
+ defined $write
+ or die ref($self), " syswrite at $len failed: $!";
+ defined $write or next;
+ $off += $write;
+ $len += $write;
+ }
+ if ($max && $len == $max) {
+ print STDERR "\n";
+ print STDERR "Max\n";
+ last;
+ }
+ }
+
+ print STDERR "LEN: ", $len, "\n";
+}
+
+sub relay_splice {
+ my $self = shift;
+ my $max = shift // $self->{max};
+
+ my $len = 0;
+ my $splicelen;
+ my $shortsplice = 0;
+ do {
+ $shortsplice++ > 1
+ and die ref($self), " more than one short splice";
+
+ my $splicemax = $max ? $max - $len : 0; # XXX should be quad
+ # XXX this works for i386 only
+ my $sosplice = pack('iii', fileno(STDOUT), $splicemax, 0);
+ setsockopt(STDIN, SOL_SOCKET, SO_SPLICE, $sosplice)
+ or die ref($self), " splice stdin to stdout failed: $!";
+
+ my $rin = '';
+ vec($rin, fileno(STDIN), 1) = 1;
+ select($rin, undef, undef, undef)
+ or die ref($self), " select failed: $!";
+
+ my $error = getsockopt(STDIN, SOL_SOCKET, SO_ERROR)
+ or die ref($self), " get error from stdin failed: $!";
+ $! = unpack('i', $error)
+ and die ref($self), " splice failed: $!";
+
+ $sosplice = getsockopt(STDIN, SOL_SOCKET, SO_SPLICE)
+ or die ref($self), " get splice len from stdin failed: $!";
+ $splicelen = unpack('ii', $sosplice); # XXX should be quad
+ print STDERR "SPLICELEN: ", $splicelen, "\n";
+ !$max || $splicelen <= $splicemax
+ or die ref($self), " splice len $splicelen ".
+ "greater than max $splicemax";
+ $len += $splicelen;
+ } while ($splicelen and !$max || $max > $len);
+
+ if ($max && $max == $len) {
+ print STDERR "Max\n";
+ } elsif ($max && $max < $len) {
+ die ref($self), " max $max less than len $len";
+ } elsif ($max && $max > $len && $splicelen) {
+ die ref($self), " max $max greater than len $len";
+ } else {
+ defined(my $read = sysread(STDIN, my $buf, 2**16))
+ or die ref($self), " sysread stdin failed: $!";
+ $read > 0
+ and die ref($self), " sysread stdin has data: $read";
+ print STDERR "End\n";
+ }
+ print STDERR "LEN: ", $len, "\n";
+}
+
+sub relay {
+ my $self = shift;
+ my $forward = $self->{forward};
+
+ given ($forward) {
+ when (/splice/) { relay_splice($self, @_) }
+ when (/copy/) { relay_copy($self, @_) }
+ default { die "Unknown forward name: $forward" }
+ }
+
+ my $soerror;
+ $soerror = getsockopt(STDIN, SOL_SOCKET, SO_ERROR)
+ or die ref($self), " get error from stdin failed: $!";
+ print STDERR "ERROR IN: ", unpack('i', $soerror), "\n";
+ $soerror = getsockopt(STDOUT, SOL_SOCKET, SO_ERROR)
+ or die ref($self), " get error from stdout failed: $!";
+ print STDERR "ERROR OUT: ", unpack('i', $soerror), "\n";
+}
+
+sub ioflip {
+ my $self = shift;
+
+ open(my $fh, '<&', \*STDIN)
+ or die ref($self), " ioflip dup failed: $!";
+ open(STDIN, '<&', \*STDOUT)
+ or die ref($self), " ioflip dup STDIN failed: $!";
+ open(STDOUT, '>&', $fh)
+ or die ref($self), " ioflip dup STDOUT failed: $!";
+ close($fh)
+ or die ref($self), " ioflip close failed: $!";
+}
+
+sub errignore {
+ $SIG{PIPE} = 'IGNORE';
+ $SIG{__DIE__} = sub {
+ die @_ if $^S;
+ warn @_;
+ my $soerror;
+ $soerror = getsockopt(STDIN, SOL_SOCKET, SO_ERROR);
+ print STDERR "ERROR IN: ", unpack('i', $soerror), "\n";
+ $soerror = getsockopt(STDOUT, SOL_SOCKET, SO_ERROR);
+ print STDERR "ERROR OUT: ", unpack('i', $soerror), "\n";
+ IO::Handle::flush(\*STDERR);
+ POSIX::_exit(0);
+ };
+}
+
+sub shutin {
+ my $self = shift;
+ shutdown(\*STDIN, SHUT_RD)
+ or die ref($self), " shutdown read failed: $!";
+}
+
+sub shutout {
+ my $self = shift;
+ IO::Handle::flush(\*STDOUT)
+ or die ref($self), " flush stdout failed: $!";
+ shutdown(\*STDOUT, SHUT_WR)
+ or die ref($self), " shutdown write failed: $!";
+}
+
+########################################################################
+# Server funcs
+########################################################################
+
+sub read_char {
+ my $self = shift;
+
+ my $ctx = Digest::MD5->new();
+ my $len = 0;
+ while (<STDIN>) {
+ $len += length($_);
+ $ctx->add($_);
+ print STDERR ".";
+ }
+ print STDERR "\n";
+
+ print STDERR "LEN: ", $len, "\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+sub read_oob {
+ my $self = shift;
+ my $size = $self->{size} || 4091;
+
+ my $ctx = Digest::MD5->new();
+ my $len = 0;
+ while (1) {
+ my $rin = my $ein = '';
+ vec($rin, fileno(STDIN), 1) = 1;
+ vec($ein, fileno(STDIN), 1) = 1 unless $self->{oobinline};
+ select($rin, undef, $ein, undef)
+ or die ref($self), " select failed: $!";
+ my $buf;
+ my $atmark = sockatmark(\*STDIN)
+ or die ref($self), " sockatmark failed: $!";
+ if ($atmark == 1) {
+ if ($self->{oobinline}) {
+ defined(recv(STDIN, $buf, 1, 0))
+ or die ref($self), " recv OOB failed: $!";
+ print STDERR "[$buf]";
+ $ctx->add("[$buf]");
+ $len += length($buf);
+ } else {
+ defined(recv(STDIN, $buf, 1, MSG_OOB)) ||
+ $!{EINVAL}
+ or die ref($self), " recv OOB failed: $!";
+ print STDERR "OOB: $buf\n" if length($buf);
+ }
+ }
+ defined(recv(STDIN, $buf, $size, 0))
+ or die ref($self), " recv failed: $!";
+ last unless length($buf);
+ print STDERR $buf;
+ $ctx->add($buf);
+ $len += length($buf);
+ print STDERR ".";
+ }
+ print STDERR "\n";
+
+ print STDERR "LEN: ", $len, "\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+sub solinger {
+ my $self = shift;
+
+ setsockopt(STDIN, SOL_SOCKET, SO_LINGER, pack('ii', 1, 0))
+ or die ref($self), " set linger failed: $!";
+}
+
+1;
diff --git a/regress/sys/kern/splice/relay.pl b/regress/sys/kern/splice/relay.pl
new file mode 100755
index 00000000000..d7740cd1e41
--- /dev/null
+++ b/regress/sys/kern/splice/relay.pl
@@ -0,0 +1,121 @@
+#!/usr/bin/perl
+# $OpenBSD: relay.pl,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+use Socket;
+use Socket6;
+
+use Client;
+use Relay;
+use Server;
+require 'funcs.pl';
+
+sub usage {
+ die "usage: relay.pl copy|splice [test-args.pl]\n";
+}
+
+my $test;
+our %args;
+if (@ARGV and -f $ARGV[-1]) {
+ $test = pop;
+ do $test
+ or die "Do test file $test failed: ", $@ || $!;
+}
+@ARGV == 1 or usage();
+
+my $s = Server->new(
+ func => \&read_char,
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ oobinline => 1,
+ %{$args{server}},
+);
+my $r = Relay->new(
+ forward => $ARGV[0],
+ func => \&relay,
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $s->{listenport},
+ %{$args{relay}},
+);
+my $c = Client->new(
+ func => \&write_char,
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $r->{listenport},
+ oobinline => 1,
+ %{$args{client}},
+);
+
+$s->run;
+$r->run;
+$c->run->up;
+$r->up;
+$s->up;
+
+$c->down;
+$r->down;
+$s->down;
+
+exit if $args{nocheck};
+
+my $clen = $c->loggrep(qr/^LEN: /) unless $args{client}{nocheck};
+my $rlen = $r->loggrep(qr/^LEN: /) unless $args{relay}{nocheck};
+my $slen = $s->loggrep(qr/^LEN: /) unless $args{server}{nocheck};
+!$clen || !$rlen || $clen eq $rlen
+ or die "client: $clen", "relay: $rlen", "len mismatch";
+!$rlen || !$slen || $rlen eq $slen
+ or die "relay: $rlen", "server: $slen", "len mismatch";
+!$clen || !$slen || $clen eq $slen
+ or die "client: $clen", "server: $slen", "len mismatch";
+!defined($args{len}) || !$clen || $clen eq "LEN: $args{len}\n"
+ or die "client: $clen", "len $args{len} expected";
+!defined($args{len}) || !$rlen || $rlen eq "LEN: $args{len}\n"
+ or die "relay: $rlen", "len $args{len} expected";
+!defined($args{len}) || !$slen || $slen eq "LEN: $args{len}\n"
+ or die "server: $slen", "len $args{len} expected";
+
+my $cmd5 = $c->loggrep(qr/^MD5: /) unless $args{client}{nocheck};
+my $smd5 = $s->loggrep(qr/^MD5: /) unless $args{server}{nocheck};
+!$cmd5 || !$smd5 || $cmd5 eq $smd5
+ or die "client: $cmd5", "server: $smd5", "md5 mismatch";
+!defined($args{md5}) || !$cmd5 || $cmd5 eq "MD5: $args{md5}\n"
+ or die "client: $cmd5", "md5 $args{md5} expected";
+!defined($args{md5}) || !$smd5 || $smd5 eq "MD5: $args{md5}\n"
+ or die "server: $smd5", "md5 $args{md5} expected";
+
+$args{relay}{errorin} = 0 unless $args{relay}{nocheck};
+$args{relay}{errorout} = 0 unless $args{relay}{nocheck};
+my %name2proc = (client => $c, relay => $r, server => $s);
+foreach my $name (qw(client relay server)) {
+ $args{$name}{errorin} //= $args{$name}{error};
+ if (defined($args{$name}{errorin})) {
+ my $ein = $name2proc{$name}->loggrep(qr/^ERROR IN: /);
+ defined($ein) && $ein eq "ERROR IN: $args{$name}{errorin}\n"
+ or die "$name: $ein",
+ "error in $args{$name}{errorin} expected";
+ }
+ if (defined($args{$name}{errorout})) {
+ my $eout = $name2proc{$name}->loggrep(qr/^ERROR OUT: /);
+ defined($eout) && $eout eq "ERROR OUT: $args{$name}{errorout}\n"
+ or die "$name: $eout",
+ "error out $args{$name}{errorout} expected";
+ }
+}
diff --git a/regress/sys/kern/splice/remote.pl b/regress/sys/kern/splice/remote.pl
new file mode 100755
index 00000000000..4d08e54657f
--- /dev/null
+++ b/regress/sys/kern/splice/remote.pl
@@ -0,0 +1,166 @@
+#!/usr/bin/perl
+# $OpenBSD: remote.pl,v 1.1 2011/01/07 22:06:08 bluhm Exp $
+
+# Copyright (c) 2010 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+use File::Basename;
+use File::Copy;
+use Socket;
+use Socket6;
+
+use Client;
+use Relay;
+use Server;
+use Remote;
+require 'funcs.pl';
+
+sub usage {
+ die <<"EOF";
+usage:
+ remote.pl localport remoteaddr remoteport [test-args.pl]
+ Run test with local client and server. Remote relay
+ forwarding from remoteaddr remoteport to server localport
+ has to be started manually.
+ remote.pl copy|splice listenaddr connectaddr connectport [test-args.pl]
+ Only start remote relay.
+ remote.pl copy|splice localaddr remoteaddr remotessh [test-args.pl]
+ Run test with local client and server. Remote relay is
+ started automatically with ssh on remotessh.
+EOF
+}
+
+my $test;
+our %args;
+if (@ARGV and -f $ARGV[-1]) {
+ $test = pop;
+ do $test
+ or die "Do test file $test failed: ", $@ || $!;
+}
+my $mode =
+ @ARGV == 3 && $ARGV[0] =~ /^\d+$/ && $ARGV[2] =~ /^\d+$/ ? "manual" :
+ @ARGV == 4 && $ARGV[1] !~ /^\d+$/ && $ARGV[3] =~ /^\d+$/ ? "relay" :
+ @ARGV == 4 && $ARGV[1] !~ /^\d+$/ && $ARGV[3] !~ /^\d+$/ ? "auto" :
+ usage();
+
+my $r;
+if ($mode eq "relay") {
+ $r = Relay->new(
+ forward => $ARGV[0],
+ logfile => dirname($0)."/remote.log",
+ func => \&relay,
+ %{$args{relay}},
+ listendomain => AF_INET,
+ listenaddr => $ARGV[1],
+ connectdomain => AF_INET,
+ connectaddr => $ARGV[2],
+ connectport => $ARGV[3],
+ );
+ open(my $log, '<', $r->{logfile})
+ or die "Remote log file open failed: $!";
+ $SIG{__DIE__} = sub {
+ die @_ if $^S;
+ copy($log, \*STDERR);
+ warn @_;
+ exit 255;
+ };
+ copy($log, \*STDERR);
+ $r->run;
+ copy($log, \*STDERR);
+ $r->up;
+ copy($log, \*STDERR);
+ $r->down;
+ copy($log, \*STDERR);
+
+ exit;
+}
+
+my $s = Server->new(
+ func => \&read_char,
+ oobinline => 1,
+ %{$args{server}},
+ listendomain => AF_INET,
+ listenaddr => ($mode eq "auto" ? $ARGV[1] : undef),
+ listenport => ($mode eq "manual" ? $ARGV[0] : undef),
+);
+if ($mode eq "auto") {
+ $r = Remote->new(
+ forward => $ARGV[0],
+ logfile => "relay.log",
+ testfile => $test,
+ %{$args{relay}},
+ remotessh => $ARGV[3],
+ listenaddr => $ARGV[2],
+ connectaddr => $ARGV[1],
+ connectport => $s->{listenport},
+ );
+ $r->run->up;
+}
+my $c = Client->new(
+ func => \&write_char,
+ oobinline => 1,
+ %{$args{client}},
+ connectdomain => AF_INET,
+ connectaddr => ($mode eq "manual" ? $ARGV[1] : $r->{listenaddr}),
+ connectport => ($mode eq "manual" ? $ARGV[2] : $r->{listenport}),
+);
+
+$s->run;
+$c->run->up;
+$s->up;
+
+$c->down;
+$r->down if $r;
+$s->down;
+
+exit if $args{nocheck};
+
+my $clen = $c->loggrep(qr/^LEN: /) unless $args{client}{nocheck};
+my $slen = $s->loggrep(qr/^LEN: /) unless $args{server}{nocheck};
+!$clen || !$slen || $clen eq $slen
+ or die "client: $clen", "server: $slen", "len mismatch";
+!defined($args{len}) || !$clen || $clen eq "LEN: $args{len}\n"
+ or die "client: $clen", "len $args{len} expected";
+!defined($args{len}) || !$slen || $slen eq "LEN: $args{len}\n"
+ or die "server: $slen", "len $args{len} expected";
+
+my $cmd5 = $c->loggrep(qr/^MD5: /) unless $args{client}{nocheck};
+my $smd5 = $s->loggrep(qr/^MD5: /) unless $args{server}{nocheck};
+!$cmd5 || !$smd5 || $cmd5 eq $smd5
+ or die "client: $cmd5", "server: $smd5", "md5 mismatch";
+!defined($args{md5}) || !$cmd5 || $cmd5 eq "MD5: $args{md5}\n"
+ or die "client: $cmd5", "md5 $args{md5} expected";
+!defined($args{md5}) || !$smd5 || $smd5 eq "MD5: $args{md5}\n"
+ or die "server: $smd5", "md5 $args{md5} expected";
+
+$args{relay}{errorin} = 0 unless $args{relay}{nocheck};
+$args{relay}{errorout} = 0 unless $args{relay}{nocheck};
+my %name2proc = (client => $c, relay => $r, server => $s);
+foreach my $name (qw(client relay server)) {
+ $args{$name}{errorin} //= $args{$name}{error};
+ if (defined($args{$name}{errorin})) {
+ my $ein = $name2proc{$name}->loggrep(qr/^ERROR IN: /);
+ defined($ein) && $ein eq "ERROR IN: $args{$name}{errorin}\n"
+ or die "$name: $ein",
+ "error in $args{$name}{errorin} expected";
+ }
+ if (defined($args{$name}{errorout})) {
+ my $eout = $name2proc{$name}->loggrep(qr/^ERROR OUT: /);
+ defined($eout) && $eout eq "ERROR OUT: $args{$name}{errorout}\n"
+ or die "$name: $eout",
+ "error out $args{$name}{errorout} expected";
+ }
+}