summaryrefslogtreecommitdiff
path: root/regress/sys
diff options
context:
space:
mode:
Diffstat (limited to 'regress/sys')
-rw-r--r--regress/sys/kern/sosplice/Child.pm34
-rw-r--r--regress/sys/kern/sosplice/Client.pm101
-rw-r--r--regress/sys/kern/sosplice/Makefile8
-rw-r--r--regress/sys/kern/sosplice/Makefile.inc44
-rw-r--r--regress/sys/kern/sosplice/Proc.pm159
-rw-r--r--regress/sys/kern/sosplice/Relay.pm135
-rw-r--r--regress/sys/kern/sosplice/Remote.pm76
-rw-r--r--regress/sys/kern/sosplice/Server.pm93
-rw-r--r--regress/sys/kern/sosplice/error/Makefile15
-rw-r--r--regress/sys/kern/sosplice/error/args-EBUSY.pl41
-rw-r--r--regress/sys/kern/sosplice/error/args-ENOTCONN.pl22
-rw-r--r--regress/sys/kern/sosplice/error/args-EOPNOTSUPP.pl23
-rw-r--r--regress/sys/kern/sosplice/error/args-idle-EINVAL.pl39
-rw-r--r--regress/sys/kern/sosplice/error/args-max-EINVAL.pl39
-rw-r--r--regress/sys/kern/sosplice/error/args-splice-EBADF.pl26
-rw-r--r--regress/sys/kern/sosplice/error/args-splice-EBUSY.pl41
-rw-r--r--regress/sys/kern/sosplice/error/args-splice-EINVAL.pl30
-rw-r--r--regress/sys/kern/sosplice/error/args-splice-ENOTCONN.pl30
-rw-r--r--regress/sys/kern/sosplice/error/args-splice-ENOTSOCK.pl26
-rw-r--r--regress/sys/kern/sosplice/error/args-splice-EOPNOTSUPP.pl31
-rw-r--r--regress/sys/kern/sosplice/error/args-splice-EPROTONOSUPPORT.pl30
-rw-r--r--regress/sys/kern/sosplice/error/args-udp-ENOTCONN.pl27
-rw-r--r--regress/sys/kern/sosplice/error/args-udp-EPROTONOSUPPORT.pl22
-rw-r--r--regress/sys/kern/sosplice/error/args-unix-EPROTONOSUPPORT.pl23
-rw-r--r--regress/sys/kern/sosplice/error/error.pl40
-rw-r--r--regress/sys/kern/sosplice/error/remote.pl38
-rw-r--r--regress/sys/kern/sosplice/funcs.pl653
-rw-r--r--regress/sys/kern/sosplice/tcp/Makefile25
-rw-r--r--regress/sys/kern/sosplice/tcp/args-default.pl9
-rw-r--r--regress/sys/kern/sosplice/tcp/args-idle-reverse.pl25
-rw-r--r--regress/sys/kern/sosplice/tcp/args-idle-timeout.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-idle-trickle.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-inet6-client.pl22
-rw-r--r--regress/sys/kern/sosplice/tcp/args-inet6-server.pl22
-rw-r--r--regress/sys/kern/sosplice/tcp/args-inet6.pl22
-rw-r--r--regress/sys/kern/sosplice/tcp/args-long.pl12
-rw-r--r--regress/sys/kern/sosplice/tcp/args-max-sleep-client-nonblock.pl17
-rw-r--r--regress/sys/kern/sosplice/tcp/args-max-sleep-client-short.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-max-sleep-client.pl17
-rw-r--r--regress/sys/kern/sosplice/tcp/args-max-sleep-relay-short.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-max-sleep-relay.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-max-sleep-server.pl21
-rw-r--r--regress/sys/kern/sosplice/tcp/args-max-time.pl21
-rw-r--r--regress/sys/kern/sosplice/tcp/args-max.pl17
-rw-r--r--regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-client.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-relay.pl13
-rw-r--r--regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-server.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-nonblock.pl15
-rw-r--r--regress/sys/kern/sosplice/tcp/args-null-max-sleep-client-nonblock.pl17
-rw-r--r--regress/sys/kern/sosplice/tcp/args-null-max-sleep-client.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-null-sleep-client.pl13
-rw-r--r--regress/sys/kern/sosplice/tcp/args-null.pl12
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oob-max-sleep-client.pl20
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oob-max-sleep-relay.pl21
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oob-max-sleep-server.pl20
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oob-max.pl21
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oob-nonblock.pl20
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oob-reverse-nonblock.pl21
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oob-reverse.pl20
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oob.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-client.pl21
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-relay.pl23
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-server.pl24
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oobinline-max.pl25
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oobinline-nonblock.pl25
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oobinline-reverse-nonblock.pl26
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oobinline-reverse.pl25
-rw-r--r--regress/sys/kern/sosplice/tcp/args-oobinline.pl24
-rw-r--r--regress/sys/kern/sosplice/tcp/args-relay-close-in.pl29
-rw-r--r--regress/sys/kern/sosplice/tcp/args-relay-read-blocking.pl15
-rw-r--r--regress/sys/kern/sosplice/tcp/args-relay-read-eof.pl25
-rw-r--r--regress/sys/kern/sosplice/tcp/args-relay-read-forked.pl39
-rw-r--r--regress/sys/kern/sosplice/tcp/args-relay-write-forked.pl38
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reset-eof.pl10
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reset-sleep-server.pl26
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reset.pl23
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reverse-long.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reverse-nonblock.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reverse-null.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reverse-sleep-client.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reverse-sleep-relay.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reverse-sleep-server.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-reverse.pl18
-rw-r--r--regress/sys/kern/sosplice/tcp/args-server-abort.pl30
-rw-r--r--regress/sys/kern/sosplice/tcp/args-server-exit.pl31
-rw-r--r--regress/sys/kern/sosplice/tcp/args-short.pl12
-rw-r--r--regress/sys/kern/sosplice/tcp/args-sleep-client.pl13
-rw-r--r--regress/sys/kern/sosplice/tcp/args-sleep-relay.pl15
-rw-r--r--regress/sys/kern/sosplice/tcp/args-sleep-server.pl15
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-client.pl17
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-relay.pl17
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-server.pl19
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallbuf.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-client.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-relay.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-server.pl18
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallrcv.pl15
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-client.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-relay.pl16
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-server.pl18
-rw-r--r--regress/sys/kern/sosplice/tcp/args-smallsnd.pl15
-rw-r--r--regress/sys/kern/sosplice/tcp/args-unsplice-forked.pl51
-rw-r--r--regress/sys/kern/sosplice/tcp/args-write-sleep-server.pl26
-rw-r--r--regress/sys/kern/sosplice/tcp/args-write.pl22
-rw-r--r--regress/sys/kern/sosplice/tcp/direct.pl62
-rwxr-xr-xregress/sys/kern/sosplice/tcp/echo.pl86
-rw-r--r--regress/sys/kern/sosplice/tcp/relay.pl77
-rw-r--r--regress/sys/kern/sosplice/tcp/remote.pl129
-rw-r--r--regress/sys/kern/sosplice/udp/Makefile22
-rw-r--r--regress/sys/kern/sosplice/udp/args-default.pl9
-rw-r--r--regress/sys/kern/sosplice/udp/args-idle-timeout.pl16
-rw-r--r--regress/sys/kern/sosplice/udp/args-idle-trickle.pl24
-rw-r--r--regress/sys/kern/sosplice/udp/args-inet6-client.pl22
-rw-r--r--regress/sys/kern/sosplice/udp/args-inet6-server.pl22
-rw-r--r--regress/sys/kern/sosplice/udp/args-inet6.pl22
-rw-r--r--regress/sys/kern/sosplice/udp/args-long.pl23
-rw-r--r--regress/sys/kern/sosplice/udp/args-max.pl21
-rw-r--r--regress/sys/kern/sosplice/udp/args-multi.pl25
-rw-r--r--regress/sys/kern/sosplice/udp/args-nospace.pl23
-rw-r--r--regress/sys/kern/sosplice/udp/direct.pl64
-rw-r--r--regress/sys/kern/sosplice/udp/relay.pl79
-rw-r--r--regress/sys/kern/sosplice/udp/remote.pl132
122 files changed, 4241 insertions, 0 deletions
diff --git a/regress/sys/kern/sosplice/Child.pm b/regress/sys/kern/sosplice/Child.pm
new file mode 100644
index 00000000000..59ab12c82d2
--- /dev/null
+++ b/regress/sys/kern/sosplice/Child.pm
@@ -0,0 +1,34 @@
+# $OpenBSD: Child.pm,v 1.1.1.1 2013/01/03 17:36:37 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/sosplice/Client.pm b/regress/sys/kern/sosplice/Client.pm
new file mode 100644
index 00000000000..674cdeb7922
--- /dev/null
+++ b/regress/sys/kern/sosplice/Client.pm
@@ -0,0 +1,101 @@
+# $OpenBSD: Client.pm,v 1.1.1.1 2013/01/03 17:36:37 bluhm Exp $
+
+# Copyright (c) 2010-2012 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} ||= $args{alarm} ? "Alarm" :
+ "Shutdown|Broken pipe|Connection reset by peer";
+ my $self = Proc::new($class, %args);
+ $self->{protocol} ||= "tcp";
+ $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";
+
+ if ($self->{bindaddr}) {
+ my $cs = IO::Socket::INET6->new(
+ Proto => $self->{protocol},
+ Domain => $self->{connectdomain},
+ Blocking => ($self->{nonblocking} ? 0 : 1),
+ LocalAddr => $self->{bindaddr},
+ LocalPort => $self->{bindport},
+ ) or die ref($self), " socket connect failed: $!";
+ $self->{bindaddr} = $cs->sockhost();
+ $self->{bindport} = $cs->sockport();
+ $self->{cs} = $cs;
+ }
+
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ my $cs = $self->{cs} || IO::Socket::INET6->new(
+ Proto => $self->{protocol},
+ 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: $!";
+ }
+ if ($self->{protocol} eq "tcp") {
+ 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";
+ $self->{bindaddr} = $cs->sockhost();
+ $self->{bindport} = $cs->sockport();
+
+ 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/sosplice/Makefile b/regress/sys/kern/sosplice/Makefile
new file mode 100644
index 00000000000..ece974717cc
--- /dev/null
+++ b/regress/sys/kern/sosplice/Makefile
@@ -0,0 +1,8 @@
+# $OpenBSD: Makefile,v 1.1.1.1 2013/01/03 17:36:38 bluhm Exp $
+
+SUBDIR =
+SUBDIR += error
+SUBDIR += tcp
+SUBDIR += udp
+
+.include <bsd.subdir.mk>
diff --git a/regress/sys/kern/sosplice/Makefile.inc b/regress/sys/kern/sosplice/Makefile.inc
new file mode 100644
index 00000000000..25d8250e6e8
--- /dev/null
+++ b/regress/sys/kern/sosplice/Makefile.inc
@@ -0,0 +1,44 @@
+# $OpenBSD: Makefile.inc,v 1.1.1.1 2013/01/03 17:36:38 bluhm Exp $
+
+# The following ports must be installed for the regression tests:
+# p5-BSD-Socket-Splice perl interface to OpenBSD socket splicing
+# p5-IO-Socket-INET6 object interface for AF_INET and AF_INET6 domain sockets
+# p5-Socket6 Perl defines relating to AF_INET6 sockets
+
+ARGS != cd ${.CURDIR} && ls args-*.pl
+TARGETS ?= ${ARGS}
+REGRESS_TARGETS = ${TARGETS:S/^/run-regress-/}
+CLEANFILES = *.log ktrace.out stamp-*
+
+# 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 ?=
+
+# Set variables so that make runs with and without obj directory.
+# Only do that if necessary to keep visible output short.
+
+.if ${.CURDIR} == ${.OBJDIR}
+PERLINC = -I..
+PERLPATH =
+.else
+PERLINC = -I${.CURDIR}/..
+PERLPATH = ${.CURDIR}/
+.endif
+
+# make perl syntax check for all args files
+
+.PHONY: syntax
+
+syntax: stamp-syntax
+
+stamp-syntax: ${ARGS}
+.for a in ${ARGS}
+ @perl -c ${PERLPATH}$a
+.endfor
+ @date >$@
diff --git a/regress/sys/kern/sosplice/Proc.pm b/regress/sys/kern/sosplice/Proc.pm
new file mode 100644
index 00000000000..08d3301c6eb
--- /dev/null
+++ b/regress/sys/kern/sosplice/Proc.pm
@@ -0,0 +1,159 @@
+# $OpenBSD: Proc.pm,v 1.1.1.1 2013/01/03 17:36:37 bluhm Exp $
+
+# Copyright (c) 2010-2012 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 {
+ kill TERM => keys %CHILDREN;
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+}
+
+sub new {
+ my $class = shift;
+ my $self = { @_ };
+ $self->{down} ||= $self->{alarm} ? "Alarm" : "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: $!";
+ $fh->autoflush;
+ $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";
+ alarm($self->{alarm}) if $self->{alarm};
+ $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 ($self->{alarm} && $kid > 0 &&
+ WIFSIGNALED($status) && WTERMSIG($status) == 14 ) {
+ # child killed by SIGALRM as expected
+ print {$self->{log}} "Alarm", "\n";
+ } elsif ($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/sosplice/Relay.pm b/regress/sys/kern/sosplice/Relay.pm
new file mode 100644
index 00000000000..4cd9527ed27
--- /dev/null
+++ b/regress/sys/kern/sosplice/Relay.pm
@@ -0,0 +1,135 @@
+# $OpenBSD: Relay.pm,v 1.1.1.1 2013/01/03 17:36:37 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";
+ $args{forward}
+ or croak "$class forward not given";
+ my $self = Proc::new($class, %args);
+ $self->{protocol} ||= "tcp";
+ $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 => $self->{protocol},
+ 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: $!";
+ }
+ if ($self->{protocol} eq "tcp") {
+ 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};
+ if ($self->{protocol} eq "tcp") {
+ $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 => $self->{protocol},
+ 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: $!";
+ }
+ if ($self->{protocol} eq "tcp") {
+ 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";
+ $self->{bindaddr} = $cs->sockhost();
+ $self->{bindport} = $cs->sockport();
+
+ open(STDOUT, '>&', $cs)
+ or die ref($self), " dup STDOUT failed: $!";
+}
+
+1;
diff --git a/regress/sys/kern/sosplice/Remote.pm b/regress/sys/kern/sosplice/Remote.pm
new file mode 100644
index 00000000000..94880e1226f
--- /dev/null
+++ b/regress/sys/kern/sosplice/Remote.pm
@@ -0,0 +1,76 @@
+# $OpenBSD: Remote.pm,v 1.1.1.1 2013/01/03 17:36:38 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 $dir = dirname($0);
+ $dir = getcwd() if ! $dir || $dir eq '.';
+ my @cmd = ('ssh', '-n', @opts, $self->{remotessh}, 'perl',
+ '-I', "$dir/..", "$dir/".basename($0), $self->{forward},
+ $self->{listenaddr}, $self->{connectaddr}, $self->{connectport},
+ ($self->{testfile} ? "$dir/".basename($self->{testfile}) :
+ ()));
+ print STDERR "execute: @cmd\n";
+ exec @cmd;
+ die "Exec @cmd failed: $!";
+}
+
+1;
diff --git a/regress/sys/kern/sosplice/Server.pm b/regress/sys/kern/sosplice/Server.pm
new file mode 100644
index 00000000000..e9b831dd7e0
--- /dev/null
+++ b/regress/sys/kern/sosplice/Server.pm
@@ -0,0 +1,93 @@
+# $OpenBSD: Server.pm,v 1.1.1.1 2013/01/03 17:36:38 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->{protocol} ||= "tcp";
+ $self->{listendomain}
+ or croak "$class listen domain not given";
+ my $ls = IO::Socket::INET6->new(
+ Proto => $self->{protocol},
+ 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: $!";
+ }
+ if ($self->{protocol} eq "tcp") {
+ 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};
+ if ($self->{protocol} eq "tcp") {
+ $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/sosplice/error/Makefile b/regress/sys/kern/sosplice/error/Makefile
new file mode 100644
index 00000000000..6c2dadb7889
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/Makefile
@@ -0,0 +1,15 @@
+# $OpenBSD: Makefile,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+.include <bsd.regress.mk>
+
+# The error tests try to splice unsuitable sockets and check the
+# kernel error code.
+
+.for a in ${ARGS}
+run-regress-$a: $a
+.if empty (REMOTE_SSH)
+ time perl ${PERLINC} ${PERLPATH}error.pl ${PERLPATH}$a
+.else
+ time perl ${PERLINC} ${PERLPATH}remote.pl ${REMOTE_SSH} ${PERLPATH}$a
+.endif
+.endfor
diff --git a/regress/sys/kern/sosplice/error/args-EBUSY.pl b/regress/sys/kern/sosplice/error/args-EBUSY.pl
new file mode 100644
index 00000000000..85375681575
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-EBUSY.pl
@@ -0,0 +1,41 @@
+# test EBUSY for splicing from a spliced socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'EBUSY',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-ENOTCONN.pl b/regress/sys/kern/sosplice/error/args-ENOTCONN.pl
new file mode 100644
index 00000000000..484e1fc5725
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-ENOTCONN.pl
@@ -0,0 +1,22 @@
+# test ENOTCONN for splicing from unconnected socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'ENOTCONN',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-EOPNOTSUPP.pl b/regress/sys/kern/sosplice/error/args-EOPNOTSUPP.pl
new file mode 100644
index 00000000000..790098618ca
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-EOPNOTSUPP.pl
@@ -0,0 +1,23 @@
+# test EOPNOTSUPP for splicing from listen socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'EOPNOTSUPP',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-idle-EINVAL.pl b/regress/sys/kern/sosplice/error/args-idle-EINVAL.pl
new file mode 100644
index 00000000000..fccf11ecf1f
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-idle-EINVAL.pl
@@ -0,0 +1,39 @@
+# test EINVAL for splicing with negative idle timeout
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+use Config;
+
+our %args = (
+ errno => 'EINVAL',
+ func => sub {
+ 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: $!";
+
+ my $packed;
+ if ($Config{longsize} == 8) {
+ $packed = pack('iiiiiiii', $ss->fileno(),0,0,0,-1,-1,-1,-1);
+ } else {
+ $packed = pack('iiiii', $ss->fileno(),0,0,-1,-1);
+ }
+ $s->setsockopt(SOL_SOCKET, SO_SPLICE, $packed)
+ and die "splice to unconnected socket succeeded";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-max-EINVAL.pl b/regress/sys/kern/sosplice/error/args-max-EINVAL.pl
new file mode 100644
index 00000000000..e61d83a81b5
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-max-EINVAL.pl
@@ -0,0 +1,39 @@
+# test EINVAL for splicing with negative maximum
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+use Config;
+
+our %args = (
+ errno => 'EINVAL',
+ func => sub {
+ 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: $!";
+
+ my $packed;
+ if ($Config{longsize} == 8) {
+ $packed = pack('iiiiiiii', $ss->fileno(),0,-1,-1,0,0,0,0);
+ } else {
+ $packed = pack('iiiii', $ss->fileno(),-1,-1,0,0);
+ }
+ $s->setsockopt(SOL_SOCKET, SO_SPLICE, $packed)
+ and die "splice to unconnected socket succeeded";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-splice-EBADF.pl b/regress/sys/kern/sosplice/error/args-splice-EBADF.pl
new file mode 100644
index 00000000000..acc86323cc0
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-splice-EBADF.pl
@@ -0,0 +1,26 @@
+# test EBADF for splicing with non existing fileno
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'EBADF',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-splice-EBUSY.pl b/regress/sys/kern/sosplice/error/args-splice-EBUSY.pl
new file mode 100644
index 00000000000..a5ad9f40116
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-splice-EBUSY.pl
@@ -0,0 +1,41 @@
+# test EBUSY for splicing to a spliced socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'EBUSY',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-splice-EINVAL.pl b/regress/sys/kern/sosplice/error/args-splice-EINVAL.pl
new file mode 100644
index 00000000000..563426c0993
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-splice-EINVAL.pl
@@ -0,0 +1,30 @@
+# test EINVAL for splicing with short fileno size
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'EINVAL',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-splice-ENOTCONN.pl b/regress/sys/kern/sosplice/error/args-splice-ENOTCONN.pl
new file mode 100644
index 00000000000..4a04c16cb76
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-splice-ENOTCONN.pl
@@ -0,0 +1,30 @@
+# test ENOTCONN for splicing to unconnected socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'ENOTCONN',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-splice-ENOTSOCK.pl b/regress/sys/kern/sosplice/error/args-splice-ENOTSOCK.pl
new file mode 100644
index 00000000000..3a240d1ff5c
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-splice-ENOTSOCK.pl
@@ -0,0 +1,26 @@
+# test ENOTSOCK for splicing with non-socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'ENOTSOCK',
+ func => sub {
+ 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', 0))
+ and die "splice with non non-socket fileno succeeded";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-splice-EOPNOTSUPP.pl b/regress/sys/kern/sosplice/error/args-splice-EOPNOTSUPP.pl
new file mode 100644
index 00000000000..d7446435c79
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-splice-EOPNOTSUPP.pl
@@ -0,0 +1,31 @@
+# test EOPNOTSUPP for splicing to listen socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'EOPNOTSUPP',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-splice-EPROTONOSUPPORT.pl b/regress/sys/kern/sosplice/error/args-splice-EPROTONOSUPPORT.pl
new file mode 100644
index 00000000000..018dd0c904b
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-splice-EPROTONOSUPPORT.pl
@@ -0,0 +1,30 @@
+# test EPROTONOSUPPORT for splicing to udp socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'EPROTONOSUPPORT',
+ func => sub {
+ 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";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-udp-ENOTCONN.pl b/regress/sys/kern/sosplice/error/args-udp-ENOTCONN.pl
new file mode 100644
index 00000000000..94a95d4ea9b
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-udp-ENOTCONN.pl
@@ -0,0 +1,27 @@
+# test ENOTCONN for splicing to unconnected udp socket
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'ENOTCONN',
+ func => sub {
+ my $sb = IO::Socket::INET->new(
+ Proto => "udp",
+ LocalAddr => "127.0.0.1",
+ ) or die "bind socket failed: $!";
+
+ my $sc = IO::Socket::INET->new(
+ Proto => "udp",
+ PeerAddr => $sb->sockhost(),
+ PeerPort => $sb->sockport(),
+ ) or die "connect socket failed: $!";
+
+ $sb->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $sc->fileno()))
+ or die "splice from unconnected socket failed: $!";
+ $sc->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $sb->fileno()))
+ and die "splice to unconnected socket succeeded";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-udp-EPROTONOSUPPORT.pl b/regress/sys/kern/sosplice/error/args-udp-EPROTONOSUPPORT.pl
new file mode 100644
index 00000000000..dacfa3bfb58
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-udp-EPROTONOSUPPORT.pl
@@ -0,0 +1,22 @@
+# test EPROTONOSUPPORT for splicing tcp with udp sockets
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+
+our %args = (
+ errno => 'EPROTONOSUPPORT',
+ func => sub {
+ my $s = IO::Socket::INET->new(
+ Proto => "udp",
+ ) 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 udp sockets succeeded";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/args-unix-EPROTONOSUPPORT.pl b/regress/sys/kern/sosplice/error/args-unix-EPROTONOSUPPORT.pl
new file mode 100644
index 00000000000..e664ec8d11f
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/args-unix-EPROTONOSUPPORT.pl
@@ -0,0 +1,23 @@
+# test EPROTONOSUPPORT for splicing unix sockets
+
+use strict;
+use warnings;
+use IO::Socket;
+use BSD::Socket::Splice "SO_SPLICE";
+use IO::Socket::UNIX;
+
+our %args = (
+ errno => 'EPROTONOSUPPORT',
+ func => sub {
+ my $s = IO::Socket::UNIX->new(
+ Type => SOCK_STREAM,
+ ) or die "socket failed: $!";
+
+ my $ss = IO::Socket::UNIX->new(
+ Type => SOCK_STREAM,
+ ) or die "socket splice failed: $!";
+
+ $s->setsockopt(SOL_SOCKET, SO_SPLICE, pack('i', $ss->fileno()))
+ and die "splice udp sockets succeeded";
+ },
+);
diff --git a/regress/sys/kern/sosplice/error/error.pl b/regress/sys/kern/sosplice/error/error.pl
new file mode 100644
index 00000000000..b6a1c583a8b
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/error.pl
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+# $OpenBSD: error.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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 Errno;
+
+sub usage {
+ die "usage: error.pl test-args.pl\n";
+}
+
+@ARGV == 1 or usage();
+
+my $test;
+our %args;
+if (@ARGV and -f $ARGV[-1]) {
+ $test = pop;
+ do $test
+ or die "Do test file $test failed: ", $@ || $!;
+}
+
+@ARGV == 0 or usage();
+
+$args{func}->();
+$!{$args{errno}}
+ or die "error not $args{errno}: $!"
diff --git a/regress/sys/kern/sosplice/error/remote.pl b/regress/sys/kern/sosplice/error/remote.pl
new file mode 100644
index 00000000000..c3cf881544a
--- /dev/null
+++ b/regress/sys/kern/sosplice/error/remote.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+# $OpenBSD: remote.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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 Cwd;
+use File::Basename;
+
+sub usage {
+ die "usage: remote.pl remotessh test-args.pl\n";
+}
+
+@ARGV == 2 or usage();
+
+my($remotessh, $testfile) = @ARGV;
+
+my @opts = split(' ', $ENV{SSH_OPTIONS}) if $ENV{SSH_OPTIONS};
+my $dir = dirname($0);
+$dir = getcwd() if ! $dir || $dir eq '.';
+my @cmd = ('ssh', '-n', @opts, $remotessh, 'perl',
+ '-I', "$dir/..", "$dir/error.pl", "$dir/".basename($testfile));
+#print STDERR "execute: @cmd\n";
+exec @cmd;
+die "Exec @cmd failed: $!";
diff --git a/regress/sys/kern/sosplice/funcs.pl b/regress/sys/kern/sosplice/funcs.pl
new file mode 100644
index 00000000000..8bbf471b40a
--- /dev/null
+++ b/regress/sys/kern/sosplice/funcs.pl
@@ -0,0 +1,653 @@
+# $OpenBSD: funcs.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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 BSD::Socket::Splice qw(setsplice getsplice geterror);
+
+########################################################################
+# Client funcs
+########################################################################
+
+sub write_stream {
+ my $self = shift;
+ my $len = shift // $self->{len} // 251;
+ my $sleep = $self->{sleep};
+
+ 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 ($self->{sleep}) {
+ IO::Handle::flush(\*STDOUT);
+ sleep $self->{sleep};
+ }
+ }
+ if ($len) {
+ $char = "\n";
+ $ctx->add($char);
+ print $char
+ or die ref($self), " print failed: $!";
+ print STDERR ".\n";
+ }
+ IO::Handle::flush(\*STDOUT);
+
+ 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";
+ }
+ IO::Handle::flush(\*STDOUT);
+
+ print STDERR "LEN: $len\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+sub write_datagram {
+ my $self = shift;
+ my @lengths = @{$self->{lengths} || [ shift // $self->{len} // 251 ]};
+ my $sleep = $self->{sleep};
+
+ my $len = 0;
+ my $ctx = Digest::MD5->new();
+ my $char = '0';
+ my @md5s;
+ for (my $num = 0; $num < @lengths; $num++) {
+ my $l = $lengths[$num];
+ my $string = "";
+ for (my $i = 1; $i < $l; $i++) {
+ $ctx->add($char);
+ $string .= $char;
+ given ($char) {
+ when(/9/) { $char = 'A' }
+ when(/Z/) { $char = 'a' }
+ when(/z/) { $char = "\n" }
+ when(/\n/) { $char = '0' }
+ default { $char++ }
+ }
+ }
+ if ($l) {
+ $char = "\n";
+ $ctx->add($char);
+ $string .= $char;
+ }
+ defined(my $write = syswrite(STDOUT, $string))
+ or die ref($self), " syswrite number $num failed: $!";
+ $write == $l
+ or die ref($self), " syswrite length $l did write $write";
+ $len += $write;
+ print STDERR ".";
+ sleep $self->{sleep} if $self->{sleep};
+ }
+ print STDERR "\n";
+
+ print STDERR "LEN: $len\n";
+ print STDERR "LENGTHS: @lengths\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+sub solingerout {
+ my $self = shift;
+
+ setsockopt(STDOUT, SOL_SOCKET, SO_LINGER, pack('ii', 1, 0))
+ or die ref($self), " set linger out failed: $!";
+}
+
+########################################################################
+# Relay funcs
+########################################################################
+
+sub relay_copy_stream {
+ my $self = shift;
+ my $max = $self->{max};
+ my $idle = $self->{idle};
+ 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};
+ defined(my $n = select($rin, undef, $ein, $idle))
+ or die ref($self), " select failed: $!";
+ if ($idle && $n == 0) {
+ print STDERR "\n";
+ print STDERR "Timeout\n";
+ last;
+ }
+ 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;
+ # Unfortunately Perl installs signal handlers without
+ # SA_RESTART. Work around by restarting manually.
+ do {
+ $write = syswrite(STDOUT, $buf, $read - $off,
+ $off);
+ } while (!defined($write) && $!{EINTR});
+ defined($write) || $!{ETIMEDOUT}
+ 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_copy_datagram {
+ my $self = shift;
+ my $max = $self->{max};
+ my $idle = $self->{idle};
+ my $size = $self->{size} || 2**16;
+
+ my $len = 0;
+ for (my $num = 0;; $num++) {
+ my $rin = my $win = '';
+ if ($idle) {
+ vec($rin, fileno(STDIN), 1) = 1;
+ defined(my $n = select($rin, undef, undef, $idle))
+ or die ref($self), " select idle failed: $!";
+ if ($n == 0) {
+ print STDERR "\n";
+ print STDERR "Timeout\n";
+ last;
+ }
+ } elsif ($self->{nonblocking}) {
+ vec($rin, fileno(STDIN), 1) = 1;
+ select($rin, undef, undef, undef)
+ or die ref($self), " select read failed: $!";
+ }
+ defined(my $read = sysread(STDIN, my $buf, $size))
+ or die ref($self), " sysread number $num failed: $!";
+ print STDERR ".";
+
+ if ($max && $len + $read > $max) {
+ print STDERR "\n";
+ print STDERR "Max\n";
+ last;
+ }
+
+ if ($self->{nonblocking}) {
+ vec($win, fileno(STDOUT), 1) = 1;
+ select(undef, $win, undef, undef)
+ or die ref($self), " select write failed: $!";
+ }
+ defined(my $write = syswrite(STDOUT, $buf)) || $!{EMSGSIZE}
+ or die ref($self), " syswrite number $num failed: $!";
+ if (defined($write)) {
+ $read == $write
+ or die ref($self), " syswrite read $read ".
+ "did write $write";
+ $len += $write;
+ }
+
+ if ($max && $len == $max) {
+ print STDERR "\n";
+ print STDERR "Max\n";
+ last;
+ }
+ }
+
+ print STDERR "LEN: $len\n";
+}
+
+sub relay_copy {
+ my $self = shift;
+ my $protocol = $self->{protocol} || "tcp";
+
+ given ($protocol) {
+ when (/tcp/) { relay_copy_stream($self, @_) }
+ when (/udp/) { relay_copy_datagram($self, @_) }
+ default { die ref($self), " unknown protocol name: $protocol" }
+ }
+}
+
+sub relay_splice {
+ my $self = shift;
+ my $max = $self->{max};
+ my $idle = $self->{idle};
+
+ my $len = 0;
+ my $splicelen;
+ my $shortsplice = 0;
+ my $error;
+ do {
+ my $splicemax = $max ? $max - $len : 0;
+ setsplice(\*STDIN, \*STDOUT, $splicemax, $idle)
+ or die ref($self), " splice stdin to stdout failed: $!";
+
+ if ($self->{readblocking}) {
+ my $read;
+ # block by reading from the source socket
+ do {
+ # busy loop to test soreceive
+ $read = sysread(STDIN, my $buf, 2**16);
+ } while ($self->{nonblocking} && !defined($read) &&
+ $!{EAGAIN});
+ defined($read)
+ or die ref($self), " read blocking failed: $!";
+ $read > 0 and die ref($self),
+ " read blocking has data: $read";
+ print STDERR "Read\n";
+ } else {
+ my $rin = '';
+ vec($rin, fileno(STDIN), 1) = 1;
+ select($rin, undef, undef, undef)
+ or die ref($self), " select failed: $!";
+ }
+
+ defined($error = geterror(\*STDIN))
+ or die ref($self), " get error from stdin failed: $!";
+ ($! = $error) && ! $!{ETIMEDOUT} && ! $!{EMSGSIZE}
+ and die ref($self), " splice failed: $!";
+
+ defined($splicelen = getsplice(\*STDIN))
+ or die ref($self), " get splice len from stdin failed: $!";
+ print STDERR "SPLICELEN: $splicelen\n";
+ !$max || $splicelen <= $splicemax
+ or die ref($self), " splice len $splicelen ".
+ "greater than max $splicemax";
+ $len += $splicelen;
+ } while ($max && $max > $len && !$shortsplice++);
+
+ if ($idle && $error == Errno::ETIMEDOUT) {
+ print STDERR "Timeout\n";
+ }
+ 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";
+ } elsif (!$error) {
+ 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 (/copy/) { relay_copy($self, @_) }
+ when (/splice/) { relay_splice($self, @_) }
+ default { die ref($self), " 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_stream {
+ my $self = shift;
+ my $max = $self->{max};
+
+ my $ctx = Digest::MD5->new();
+ my $len = 0;
+ while (<STDIN>) {
+ $len += length($_);
+ $ctx->add($_);
+ print STDERR ".";
+ if ($max && $len >= $max) {
+ print STDERR "\nMax";
+ last;
+ }
+ }
+ 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 read_datagram {
+ my $self = shift;
+ my $num = $self->{num} // 1;
+ my $max = $self->{max};
+ my $size = $self->{size} || 2**16;
+
+ my $ctx = Digest::MD5->new();
+ my $len = 0;
+ my @lengths;
+ for (my $i = 0; $i < $num; $i++) {
+ defined(my $read = sysread(STDIN, my $buf, $size))
+ or die ref($self), " sysread number $i failed: $!";
+ $len += $read;
+ push @lengths, $read;
+ $ctx->add($buf);
+ print STDERR ".";
+ if ($max && $len >= $max) {
+ print STDERR "\nMax";
+ last;
+ }
+ }
+ print STDERR "\n";
+
+ print STDERR "LEN: $len\n";
+ print STDERR "LENGTHS: @lengths\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+sub solingerin {
+ my $self = shift;
+
+ setsockopt(STDIN, SOL_SOCKET, SO_LINGER, pack('ii', 1, 0))
+ or die ref($self), " set linger in failed: $!";
+}
+
+########################################################################
+# Script funcs
+########################################################################
+
+sub check_logs {
+ my ($c, $r, $s, %args) = @_;
+
+ return if $args{nocheck};
+
+ $r->loggrep(qr/^Timeout$/) or die "no relay timeout"
+ if $r && $args{relay}{timeout};
+ $r->loggrep(qr/^Max$/) or die "no relay max"
+ if $r && $args{relay}{max} && $args{len};
+
+ check_len($c, $r, $s, %args);
+ check_lengths($c, $r, $s, %args);
+ check_md5($c, $r, $s, %args);
+ check_error($c, $r, $s, %args);
+}
+
+sub check_len {
+ my ($c, $r, $s, %args) = @_;
+
+ my $clen = $c->loggrep(qr/^LEN: /) // die "no client len"
+ unless $args{client}{nocheck};
+ my $rlen = $r->loggrep(qr/^LEN: /) // die "no relay len"
+ if $r && ! $args{relay}{nocheck};
+ my $slen = $s->loggrep(qr/^LEN: /) // die "no server 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";
+}
+
+sub check_lengths {
+ my ($c, $r, $s, %args) = @_;
+
+ my $clengths = $c->loggrep(qr/^LENGTHS: /)
+ unless $args{client}{nocheck};
+ my $slengths = $s->loggrep(qr/^LENGTHS: /)
+ unless $args{server}{nocheck};
+ !$clengths || !$slengths || $clengths eq $slengths
+ or die "client: $clengths", "server: $slengths", "lengths mismatch";
+ !defined($args{lengths}) || !$clengths ||
+ $clengths eq "LENGTHS: $args{lengths}\n"
+ or die "client: $clengths", "lengths $args{lengths} expected";
+ !defined($args{lengths}) || !$slengths ||
+ $slengths eq "LENGTHS: $args{lengths}\n"
+ or die "server: $slengths", "lengths $args{lengths} expected";
+}
+
+sub check_md5 {
+ my ($c, $r, $s, %args) = @_;
+
+ my $cmd5 = $c->loggrep(qr/^MD5: /) unless $args{client}{nocheck};
+ my $smd5 = $s->loggrep(qr/^MD5: /) unless $args{server}{nocheck};
+ !$cmd5 || !$smd5 || ref($args{md5}) eq 'ARRAY' || $cmd5 eq $smd5
+ or die "client: $cmd5", "server: $smd5", "md5 mismatch";
+ my $md5 = ref($args{md5}) eq 'ARRAY' ?
+ join('|', @{$args{md5}}) : $args{md5};
+ !$md5 || !$cmd5 || $cmd5 =~ /^MD5: ($md5)$/
+ or die "client: $cmd5", "md5 $md5 expected";
+ !$md5 || !$smd5 || $smd5 =~ /^MD5: ($md5)$/
+ or die "server: $smd5", "md5 $md5 expected";
+}
+
+sub check_error {
+ my ($c, $r, $s, %args) = @_;
+
+ $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)) {
+ my $p = $name2proc{$name}
+ or next;
+ $args{$name}{errorin} //= $args{$name}{error};
+ if (defined($args{$name}{errorin})) {
+ my $ein = $p->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 = $p->loggrep(qr/^ERROR OUT: /);
+ defined($eout) &&
+ $eout eq "ERROR OUT: $args{$name}{errorout}\n"
+ or die "$name: $eout",
+ "error out $args{$name}{errorout} expected";
+ }
+ }
+}
+
+1;
diff --git a/regress/sys/kern/sosplice/tcp/Makefile b/regress/sys/kern/sosplice/tcp/Makefile
new file mode 100644
index 00000000000..df5dfe54b65
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/Makefile
@@ -0,0 +1,25 @@
+# $OpenBSD: Makefile,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+.include <bsd.regress.mk>
+
+# 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
+.if empty (REMOTE_SSH)
+ time perl ${PERLINC} ${PERLPATH}relay.pl copy ${PERLPATH}$a
+ time perl ${PERLINC} ${PERLPATH}relay.pl splice ${PERLPATH}$a
+ time perl ${PERLINC} ${PERLPATH}echo.pl copy ${PERLPATH}$a
+ time perl ${PERLINC} ${PERLPATH}echo.pl splice ${PERLPATH}$a
+.else
+ time perl ${PERLINC} ${PERLPATH}remote.pl copy ${LOCAL_ADDR} ${REMOTE_ADDR} ${REMOTE_SSH} ${PERLPATH}$a
+ time perl ${PERLINC} ${PERLPATH}remote.pl splice ${LOCAL_ADDR} ${REMOTE_ADDR} ${REMOTE_SSH} ${PERLPATH}$a
+.endif
+.endfor
diff --git a/regress/sys/kern/sosplice/tcp/args-default.pl b/regress/sys/kern/sosplice/tcp/args-default.pl
new file mode 100644
index 00000000000..1caa30bc13f
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-default.pl
@@ -0,0 +1,9 @@
+# test default values
+
+use strict;
+use warnings;
+
+our %args = (
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-idle-reverse.pl b/regress/sys/kern/sosplice/tcp/args-idle-reverse.pl
new file mode 100644
index 00000000000..8d87c69762b
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-idle-reverse.pl
@@ -0,0 +1,25 @@
+# test non idle connection while reverse splicing gets timeout
+
+use strict;
+use warnings;
+use BSD::Socket::Splice qw(setsplice);
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_stream(@_); },
+ len => 6,
+ sleep => 1,
+ },
+ relay => {
+ func => sub {
+ setsplice(\*STDOUT, \*STDIN, undef, 3)
+ or die "reverse splice failed: $!";
+ relay(@_);
+ },
+ idle => 2,
+ nocheck => 1,
+ },
+ len => 6,
+ md5 => "857f2261690a2305dba03062e778a73b",
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-idle-timeout.pl b/regress/sys/kern/sosplice/tcp/args-idle-timeout.pl
new file mode 100644
index 00000000000..28a1e0e0235
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-idle-timeout.pl
@@ -0,0 +1,16 @@
+# test idle timeout
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 1; write_stream(@_); sleep 3; },
+ },
+ relay => {
+ idle => 2,
+ timeout => 1,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-idle-trickle.pl b/regress/sys/kern/sosplice/tcp/args-idle-trickle.pl
new file mode 100644
index 00000000000..ca744a56ac3
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-idle-trickle.pl
@@ -0,0 +1,16 @@
+# test non idle connection does not timeout by sending a byte every second
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 6,
+ sleep => 1,
+ },
+ relay => {
+ idle => 2,
+ },
+ len => 6,
+ md5 => "857f2261690a2305dba03062e778a73b",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-inet6-client.pl b/regress/sys/kern/sosplice/tcp/args-inet6-client.pl
new file mode 100644
index 00000000000..0a676835700
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-inet6-client.pl
@@ -0,0 +1,22 @@
+# test ipv6 client
+
+use strict;
+use warnings;
+use Socket qw(AF_INET AF_INET6);
+
+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",
+ },
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-inet6-server.pl b/regress/sys/kern/sosplice/tcp/args-inet6-server.pl
new file mode 100644
index 00000000000..66975ce563a
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-inet6-server.pl
@@ -0,0 +1,22 @@
+# test ipv6 server
+
+use strict;
+use warnings;
+use Socket qw(AF_INET AF_INET6);
+
+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",
+ },
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-inet6.pl b/regress/sys/kern/sosplice/tcp/args-inet6.pl
new file mode 100644
index 00000000000..c84a85f61a4
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-inet6.pl
@@ -0,0 +1,22 @@
+# test ipv6 client and server
+
+use strict;
+use warnings;
+use Socket qw(AF_INET AF_INET6);
+
+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",
+ },
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-long.pl b/regress/sys/kern/sosplice/tcp/args-long.pl
new file mode 100644
index 00000000000..6a8da22b184
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-long.pl
@@ -0,0 +1,12 @@
+# test longer data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-max-sleep-client-nonblock.pl b/regress/sys/kern/sosplice/tcp/args-max-sleep-client-nonblock.pl
new file mode 100644
index 00000000000..b47e156d06a
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-max-sleep-client-nonblock.pl
@@ -0,0 +1,17 @@
+# 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_stream(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ max => 113,
+ nonblocking => 1,
+ },
+ len => 113,
+ md5 => "dc099ef642faa02bce71298f11e7d44d",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-max-sleep-client-short.pl b/regress/sys/kern/sosplice/tcp/args-max-sleep-client-short.pl
new file mode 100644
index 00000000000..a9dac043f23
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-max-sleep-client-short.pl
@@ -0,0 +1,16 @@
+# test maximum data length with delay before client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_stream(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ max => 113,
+ },
+ len => 113,
+ md5 => "dc099ef642faa02bce71298f11e7d44d",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-max-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-max-sleep-client.pl
new file mode 100644
index 00000000000..bb5644c608a
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-max-sleep-client.pl
@@ -0,0 +1,17 @@
+# test maximum data length with delay before client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); sleep 3; write_stream(@_); },
+ len => 2**17,
+ nocheck => 1,
+ },
+ relay => {
+ max => 32117,
+ },
+ len => 32117,
+ md5 => "ee338e9693fb2a2ec101bb28935ed123",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-max-sleep-relay-short.pl b/regress/sys/kern/sosplice/tcp/args-max-sleep-relay-short.pl
new file mode 100644
index 00000000000..353aa12d7cd
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-max-sleep-relay-short.pl
@@ -0,0 +1,16 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-max-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-max-sleep-relay.pl
new file mode 100644
index 00000000000..7363ef6fcf8
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-max-sleep-relay.pl
@@ -0,0 +1,19 @@
+# test maximum data length with delay before relay copy
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_stream(@_); },
+ 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-max-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-max-sleep-server.pl
new file mode 100644
index 00000000000..91d2a7af8f7
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-max-sleep-server.pl
@@ -0,0 +1,21 @@
+# test maximum data length with delay before server read
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_stream(@_); },
+ len => 2**17,
+ nocheck => 1,
+ },
+ relay => {
+ max => 32117,
+ },
+ server => {
+ func => sub { sleep 3; read_stream(@_); },
+ },
+ len => 32117,
+ md5 => "ee338e9693fb2a2ec101bb28935ed123",
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-max-time.pl b/regress/sys/kern/sosplice/tcp/args-max-time.pl
new file mode 100644
index 00000000000..abf22c9e53b
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-max-time.pl
@@ -0,0 +1,21 @@
+# test immediate data transfer after maximum data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { $| = 1; errignore(@_); write_stream(@_); sleep(6); },
+ nocheck => 1,
+ },
+ relay => {
+ func => sub { relay(@_); sleep(5); },
+ max => 63,
+ },
+ server => {
+ func => sub { alarm(4); read_stream(@_); },
+ max => 63,
+ },
+ len => 63,
+ md5 => "4a3c3783fc56943715c53e435973b2ee",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-max.pl b/regress/sys/kern/sosplice/tcp/args-max.pl
new file mode 100644
index 00000000000..889927b7a88
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-max.pl
@@ -0,0 +1,17 @@
+# test maximum data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_stream(@_); },
+ len => 2**17,
+ nocheck => 1,
+ },
+ relay => {
+ max => 32117,
+ },
+ len => 32117,
+ md5 => "ee338e9693fb2a2ec101bb28935ed123",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-client.pl
new file mode 100644
index 00000000000..5f40cf86400
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-client.pl
@@ -0,0 +1,16 @@
+# test relay maximum data length then copy with delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_stream(@_); },
+ },
+ relay => {
+ func => sub { relay(@_, 61); relay_copy(@_); },
+ nocheck => 1,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-relay.pl
new file mode 100644
index 00000000000..088250d6fb9
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-relay.pl
@@ -0,0 +1,13 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-server.pl
new file mode 100644
index 00000000000..fee64499e92
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-maxcopy-sleep-server.pl
@@ -0,0 +1,16 @@
+# 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_stream(@_); },
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-nonblock.pl b/regress/sys/kern/sosplice/tcp/args-nonblock.pl
new file mode 100644
index 00000000000..0400c4c8641
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-nonblock.pl
@@ -0,0 +1,15 @@
+# test with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ nonblocking => 1,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-null-max-sleep-client-nonblock.pl b/regress/sys/kern/sosplice/tcp/args-null-max-sleep-client-nonblock.pl
new file mode 100644
index 00000000000..d065c29ed99
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-null-max-sleep-client-nonblock.pl
@@ -0,0 +1,17 @@
+# 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; write_stream(@_); },
+ len => 0,
+ },
+ relay => {
+ max => 113,
+ nonblocking => 1,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-null-max-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-null-max-sleep-client.pl
new file mode 100644
index 00000000000..4b00840c17e
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-null-max-sleep-client.pl
@@ -0,0 +1,16 @@
+# test maximum data length with delay before empty client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_stream(@_); },
+ len => 0,
+ },
+ relay => {
+ max => 113,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-null-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-null-sleep-client.pl
new file mode 100644
index 00000000000..d974ebd00e8
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-null-sleep-client.pl
@@ -0,0 +1,13 @@
+# test with delay before empty client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_stream(@_); },
+ len => 0,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-null.pl b/regress/sys/kern/sosplice/tcp/args-null.pl
new file mode 100644
index 00000000000..bcc768ddcb8
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-null.pl
@@ -0,0 +1,12 @@
+# test emtpy client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 0,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-client.pl
new file mode 100644
index 00000000000..9fbf9c321bf
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-client.pl
@@ -0,0 +1,20 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-relay.pl
new file mode 100644
index 00000000000..21fcf6fb2e6
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-relay.pl
@@ -0,0 +1,21 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-server.pl
new file mode 100644
index 00000000000..89bc64ad986
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oob-max-sleep-server.pl
@@ -0,0 +1,20 @@
+# 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,
+ },
+ server => {
+ func => sub { sleep 3; read_oob(@_); },
+ },
+ len => 61,
+ md5 => "e4282daf8d2ca21cc8b70b1314713314",
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oob-max.pl b/regress/sys/kern/sosplice/tcp/args-oob-max.pl
new file mode 100644
index 00000000000..a46786338ba
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oob-max.pl
@@ -0,0 +1,21 @@
+# 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",
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oob-nonblock.pl b/regress/sys/kern/sosplice/tcp/args-oob-nonblock.pl
new file mode 100644
index 00000000000..69099417482
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oob-nonblock.pl
@@ -0,0 +1,20 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oob-reverse-nonblock.pl b/regress/sys/kern/sosplice/tcp/args-oob-reverse-nonblock.pl
new file mode 100644
index 00000000000..b3d4b1afdab
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oob-reverse-nonblock.pl
@@ -0,0 +1,21 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oob-reverse.pl b/regress/sys/kern/sosplice/tcp/args-oob-reverse.pl
new file mode 100644
index 00000000000..93b31a363c0
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oob-reverse.pl
@@ -0,0 +1,20 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oob.pl b/regress/sys/kern/sosplice/tcp/args-oob.pl
new file mode 100644
index 00000000000..26f5dc388b5
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oob.pl
@@ -0,0 +1,19 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-client.pl
new file mode 100644
index 00000000000..d007443328e
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-client.pl
@@ -0,0 +1,21 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-relay.pl
new file mode 100644
index 00000000000..a468c4be940
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-relay.pl
@@ -0,0 +1,23 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-server.pl
new file mode 100644
index 00000000000..d473fb04a5d
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oobinline-max-sleep-server.pl
@@ -0,0 +1,24 @@
+# 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,
+ },
+ server => {
+ func => sub { sleep 3; read_oob(@_); },
+ },
+ len => 61,
+ md5 => [
+ "c9f459db9b4f369980c79bff17e1c2a0",
+ "4b5efc5f86fa5fc873c82103ceece85d",
+ ],
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oobinline-max.pl b/regress/sys/kern/sosplice/tcp/args-oobinline-max.pl
new file mode 100644
index 00000000000..c43f9038584
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oobinline-max.pl
@@ -0,0 +1,25 @@
+# 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",
+ "4b5efc5f86fa5fc873c82103ceece85d",
+ ],
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oobinline-nonblock.pl b/regress/sys/kern/sosplice/tcp/args-oobinline-nonblock.pl
new file mode 100644
index 00000000000..f63724084c8
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oobinline-nonblock.pl
@@ -0,0 +1,25 @@
+# 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",
+ "5aa8135a1340e173a7d7a5fa048a999e",
+ "e5be513d9d2b877b6841bbb4790c67dc",
+ "5cf8c3fd08f541ae07361a11f17213fe",
+ ],
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oobinline-reverse-nonblock.pl b/regress/sys/kern/sosplice/tcp/args-oobinline-reverse-nonblock.pl
new file mode 100644
index 00000000000..25864520af7
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oobinline-reverse-nonblock.pl
@@ -0,0 +1,26 @@
+# 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",
+ "5aa8135a1340e173a7d7a5fa048a999e",
+ "e5be513d9d2b877b6841bbb4790c67dc",
+ "5cf8c3fd08f541ae07361a11f17213fe",
+ ],
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oobinline-reverse.pl b/regress/sys/kern/sosplice/tcp/args-oobinline-reverse.pl
new file mode 100644
index 00000000000..0f21d6473c8
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oobinline-reverse.pl
@@ -0,0 +1,25 @@
+# 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",
+ "5aa8135a1340e173a7d7a5fa048a999e",
+ "e5be513d9d2b877b6841bbb4790c67dc",
+ "5cf8c3fd08f541ae07361a11f17213fe",
+ ],
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-oobinline.pl b/regress/sys/kern/sosplice/tcp/args-oobinline.pl
new file mode 100644
index 00000000000..decbcc93523
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-oobinline.pl
@@ -0,0 +1,24 @@
+# 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",
+ "5aa8135a1340e173a7d7a5fa048a999e",
+ "e5be513d9d2b877b6841bbb4790c67dc",
+ "5cf8c3fd08f541ae07361a11f17213fe",
+ ],
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-relay-close-in.pl b/regress/sys/kern/sosplice/tcp/args-relay-close-in.pl
new file mode 100644
index 00000000000..e01f96030e6
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/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_stream(@_); },
+ 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)",
+ nocheck => 1,
+ 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,
+ nocheck => 1,
+ },
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-relay-read-blocking.pl b/regress/sys/kern/sosplice/tcp/args-relay-read-blocking.pl
new file mode 100644
index 00000000000..fe67ad42570
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-relay-read-blocking.pl
@@ -0,0 +1,15 @@
+# test waiting for splice finish with blocking read
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ readblocking => 1,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-relay-read-eof.pl b/regress/sys/kern/sosplice/tcp/args-relay-read-eof.pl
new file mode 100644
index 00000000000..4394efca336
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-relay-read-eof.pl
@@ -0,0 +1,25 @@
+# test waiting for splice finish with read and eof has happend
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ # fill server buffer, relay send buffer, half relay recv buffer
+ # then send eof
+ len => 2**13 + 2**10,
+ },
+ relay => {
+ nonblocking => 1,
+ readblocking => 1,
+ sndbuf => 2**12,
+ rcvbuf => 2**12,
+ },
+ server => {
+ # wait until all buffers are filled and client sends eof
+ func => sub { sleep 4; read_stream(@_); },
+ rcvbuf => 2**12,
+ },
+ len => 9216,
+ md5 => "6d263239be35ccf30cb04c5f58a35dbe",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-relay-read-forked.pl b/regress/sys/kern/sosplice/tcp/args-relay-read-forked.pl
new file mode 100644
index 00000000000..374e07c26ee
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-relay-read-forked.pl
@@ -0,0 +1,39 @@
+# test concurrent read and splice
+
+use strict;
+use warnings;
+use POSIX;
+use Time::HiRes;
+
+our %args = (
+ client => {
+ len => 2**20,
+ },
+ relay => {
+ nonblocking => 1,
+ func => sub {
+ defined(my $pid = fork())
+ or die "relay func: fork failed: $!";
+ if ($pid == 0) {
+ my $n;
+ do {
+ $n = sysread(STDIN, my $buf, 10);
+ } while (!defined($n) || $n);
+ POSIX::_exit(0);
+ }
+ # give the userland a moment to read, even if splicing
+ sleep .1;
+ relay(@_);
+ kill 9, $pid;
+ },
+ # As sysread() may extract data from the socket before splicing starts,
+ # the spliced content length is not reliable. Disable the checks.
+ nocheck => 1,
+ },
+ server => {
+ func => sub { sleep 2; read_stream(@_); },
+ nocheck => 1,
+ },
+ len => 1048576,
+ md5 => '6649bbec13f3d7efaedf01c0cfa54f88',
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-relay-write-forked.pl b/regress/sys/kern/sosplice/tcp/args-relay-write-forked.pl
new file mode 100644
index 00000000000..5496bd3291e
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-relay-write-forked.pl
@@ -0,0 +1,38 @@
+# test concurrent write and splice
+
+use strict;
+use warnings;
+use POSIX;
+use Time::HiRes;
+
+our %args = (
+ client => {
+ len => 2**20,
+ },
+ relay => {
+ func => sub {
+ defined(my $pid = fork())
+ or die "relay func: fork failed: $!";
+ if ($pid == 0) {
+ my $n;
+ do {
+ $n = syswrite(STDOUT, "\n foo bar\n");
+ sleep .01;
+ } while (!defined($n) || $n);
+ POSIX::_exit(0);
+ }
+ # give the userland a moment to wite, even if splicing
+ sleep .1;
+ relay(@_);
+ kill 9, $pid;
+ },
+ },
+ server => {
+ func => sub { sleep 2; read_stream(@_); },
+ # As syswrite() adds data to the socket, the content length is not
+ # correct. Disable the checks.
+ nocheck => 1,
+ },
+ len => 1048576,
+ md5 => '6649bbec13f3d7efaedf01c0cfa54f88',
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reset-eof.pl b/regress/sys/kern/sosplice/tcp/args-reset-eof.pl
new file mode 100644
index 00000000000..07b7d1a5e21
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reset-eof.pl
@@ -0,0 +1,10 @@
+# test connection reset by server at eof, after all data has been read
+
+use strict;
+use warnings;
+
+our %args = (
+ server => {
+ func => sub { read_stream(@_); sleep 3; solingerin(@_); },
+ },
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reset-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-reset-sleep-server.pl
new file mode 100644
index 00000000000..68330ce5413
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reset-sleep-server.pl
@@ -0,0 +1,26 @@
+# test delay before server read, client sends reset during splice
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ alarm => 1,
+ func => sub { solingerout(@_); write_stream(@_); },
+ len => 2**19,
+ nocheck => 1,
+ },
+ relay => {
+ func => sub { errignore(@_); relay(@_); },
+ rcvbuf => 2**10,
+ sndbuf => 2**10,
+ down => "Broken pipe|Connection reset by peer",
+ nocheck => 1,
+ },
+ server => {
+ func => sub { sleep 3; read_stream(@_); },
+ nocheck => 1,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reset.pl b/regress/sys/kern/sosplice/tcp/args-reset.pl
new file mode 100644
index 00000000000..2887826468c
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reset.pl
@@ -0,0 +1,23 @@
+# test connection reset by server
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_stream(@_); },
+ 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; solingerin(@_); },
+ rcvbuf => 2**12,
+ },
+ nocheck => 1,
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reverse-long.pl b/regress/sys/kern/sosplice/tcp/args-reverse-long.pl
new file mode 100644
index 00000000000..488e37f34e5
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reverse-long.pl
@@ -0,0 +1,19 @@
+# test longer data lenght when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_stream,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => \&write_stream,
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reverse-nonblock.pl b/regress/sys/kern/sosplice/tcp/args-reverse-nonblock.pl
new file mode 100644
index 00000000000..06591cf6697
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reverse-nonblock.pl
@@ -0,0 +1,19 @@
+# test reverse sending with non-blocking relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_stream,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ nonblocking => 1,
+ },
+ server => {
+ func => \&write_stream,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reverse-null.pl b/regress/sys/kern/sosplice/tcp/args-reverse-null.pl
new file mode 100644
index 00000000000..9751b9fff00
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reverse-null.pl
@@ -0,0 +1,19 @@
+# test emtpy server write when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_stream,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => \&write_stream,
+ len => 0,
+ },
+ len => 0,
+ md5 => "d41d8cd98f00b204e9800998ecf8427e",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reverse-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-reverse-sleep-client.pl
new file mode 100644
index 00000000000..65bd03caf16
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reverse-sleep-client.pl
@@ -0,0 +1,19 @@
+# test delay before client read when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; read_stream(@_); },
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => \&write_stream,
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reverse-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-reverse-sleep-relay.pl
new file mode 100644
index 00000000000..aa454a72df3
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reverse-sleep-relay.pl
@@ -0,0 +1,19 @@
+# test delay before relay copy when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_stream,
+ },
+ relay => {
+ func => sub { ioflip(@_); sleep 3; relay(@_); },
+ },
+ server => {
+ func => \&write_stream,
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reverse-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-reverse-sleep-server.pl
new file mode 100644
index 00000000000..69f2b97885b
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reverse-sleep-server.pl
@@ -0,0 +1,19 @@
+# test delay before server write when reverse sending
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_stream,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => sub { sleep 3; write_stream(@_); },
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-reverse.pl b/regress/sys/kern/sosplice/tcp/args-reverse.pl
new file mode 100644
index 00000000000..d66dcef69cb
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-reverse.pl
@@ -0,0 +1,18 @@
+# test reverse sending from server to client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_stream,
+ },
+ relay => {
+ func => sub { ioflip(@_); relay(@_); },
+ },
+ server => {
+ func => \&write_stream,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-server-abort.pl b/regress/sys/kern/sosplice/tcp/args-server-abort.pl
new file mode 100644
index 00000000000..31cd885d95b
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-server-abort.pl
@@ -0,0 +1,30 @@
+# test server reads and exits after reading some data
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_stream(@_); },
+ 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)",
+ nocheck => 1,
+ 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 => {
+ alarm => 3,
+ nocheck => 1,
+ },
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-server-exit.pl b/regress/sys/kern/sosplice/tcp/args-server-exit.pl
new file mode 100644
index 00000000000..e966492ea52
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-server-exit.pl
@@ -0,0 +1,31 @@
+# test server sleeps and exits without reading data
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { errignore(@_); write_stream(@_); },
+ 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)",
+ nocheck => 1,
+ 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,
+ nocheck => 1,
+ },
+ noecho => 1,
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-short.pl b/regress/sys/kern/sosplice/tcp/args-short.pl
new file mode 100644
index 00000000000..8dee78fc991
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-short.pl
@@ -0,0 +1,12 @@
+# test copy with short block size
+
+use strict;
+use warnings;
+
+our %args = (
+ realy => {
+ size => 17,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-sleep-client.pl
new file mode 100644
index 00000000000..70f7a1f62f9
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-sleep-client.pl
@@ -0,0 +1,13 @@
+# test delay before client write
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_stream(@_); },
+ len => 2**17,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-sleep-relay.pl
new file mode 100644
index 00000000000..8c6341bcaf0
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-sleep-relay.pl
@@ -0,0 +1,15 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-sleep-server.pl
new file mode 100644
index 00000000000..ec027822f17
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-sleep-server.pl
@@ -0,0 +1,15 @@
+# test delay before server read
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ server => {
+ func => sub { sleep 3; read_stream(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-client.pl
new file mode 100644
index 00000000000..88227d33ee3
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-client.pl
@@ -0,0 +1,17 @@
+# test with smaller relay send and receive buffers delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_stream(@_); },
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-relay.pl
new file mode 100644
index 00000000000..e8d430f0ecc
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-relay.pl
@@ -0,0 +1,17 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-server.pl
new file mode 100644
index 00000000000..ee4765ff1b0
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallbuf-sleep-server.pl
@@ -0,0 +1,19 @@
+# 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_stream(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallbuf.pl b/regress/sys/kern/sosplice/tcp/args-smallbuf.pl
new file mode 100644
index 00000000000..5f31d87eccd
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallbuf.pl
@@ -0,0 +1,16 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-client.pl
new file mode 100644
index 00000000000..9a51835e070
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-client.pl
@@ -0,0 +1,16 @@
+# test with smaller relay receive buffer delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_stream(@_); },
+ len => 2**17,
+ },
+ relay => {
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-relay.pl
new file mode 100644
index 00000000000..90629b5e717
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-relay.pl
@@ -0,0 +1,16 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-server.pl
new file mode 100644
index 00000000000..2654c07b343
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallrcv-sleep-server.pl
@@ -0,0 +1,18 @@
+# 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_stream(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallrcv.pl b/regress/sys/kern/sosplice/tcp/args-smallrcv.pl
new file mode 100644
index 00000000000..7d2d1017fe5
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallrcv.pl
@@ -0,0 +1,15 @@
+# test with smaller relay receive buffer
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ rcvbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-client.pl b/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-client.pl
new file mode 100644
index 00000000000..f8b3719fea7
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-client.pl
@@ -0,0 +1,16 @@
+# test with smaller relay send buffer delay before client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 3; write_stream(@_); },
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-relay.pl b/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-relay.pl
new file mode 100644
index 00000000000..6bc7cfc09cf
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-relay.pl
@@ -0,0 +1,16 @@
+# 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",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-server.pl
new file mode 100644
index 00000000000..9ca1e2fa0ca
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallsnd-sleep-server.pl
@@ -0,0 +1,18 @@
+# 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_stream(@_); },
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-smallsnd.pl b/regress/sys/kern/sosplice/tcp/args-smallsnd.pl
new file mode 100644
index 00000000000..968dbe34d81
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-smallsnd.pl
@@ -0,0 +1,15 @@
+# test with smaller relay send buffer
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**17,
+ },
+ relay => {
+ sndbuf => 2**12,
+ },
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-unsplice-forked.pl b/regress/sys/kern/sosplice/tcp/args-unsplice-forked.pl
new file mode 100644
index 00000000000..9b8ae668353
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-unsplice-forked.pl
@@ -0,0 +1,51 @@
+# test delay before server read, unsplice during client write
+
+use strict;
+use warnings;
+use POSIX;
+
+our %args = (
+ client => {
+ len => 2**17,
+ func => sub { errignore(@_); write_stream(@_); },
+ },
+ relay => {
+ func => sub {
+ my $self = shift;
+ defined(my $pid = fork())
+ or die "relay func: fork failed: $!";
+ if ($pid == 0) {
+ sleep 2;
+ setsplice(\*STDIN)
+ or die ref($self), " unsplice stdin failed: $!";
+ POSIX::_exit(0);
+ }
+ sleep 1;
+ eval { relay($self, @_) };
+ if ($self->{forward} =~ /splice/) {
+ $@ =~ /^Relay sysread stdin has data:/
+ or die ref($self), " no data after unsplice: $@";
+ }
+ sleep 2;
+ kill 9, $pid;
+ (my $kid = waitpid($pid, 0)) > 0
+ or die ref($self), " wait unsplice child failed: $!";
+ my $status = $?;
+ my $code;
+ $code = "exit: ". WEXITSTATUS($?) if WIFEXITED($?);
+ $code = "signal: ". WTERMSIG($?) if WIFSIGNALED($?);
+ $code = "stop: ". WSTOPSIG($?) if WIFSTOPPED($?);
+ $status == 0
+ or die ref($self), " unsplice child status: $status $code";
+ },
+ rcvbuf => 2**10,
+ sndbuf => 2**10,
+ },
+ server => {
+ func => sub { sleep 3; read_stream(@_); },
+ },
+ noecho => 1,
+ nocheck => 1,
+ len => 131072,
+ md5 => "31e5ad3d0d2aeb1ad8aaa847dfa665c2",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-write-sleep-server.pl b/regress/sys/kern/sosplice/tcp/args-write-sleep-server.pl
new file mode 100644
index 00000000000..bff55e49836
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-write-sleep-server.pl
@@ -0,0 +1,26 @@
+# test mix write and relay delaying before server read
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 4; write_stream(@_); },
+ len => 65521,
+ nocheck => 1,
+ },
+ relay => {
+ func => sub {
+ write_stream(@_, 32749);
+ IO::Handle::flush(\*STDOUT);
+ relay(@_);
+ write_stream(@_, 2039);
+ },
+ nocheck => 1,
+ },
+ server => {
+ func => sub { sleep 3; read_stream(@_); },
+ },
+ len => 100309,
+ md5 => "0efc7833e5c0652823ca63eaccb9918f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/args-write.pl b/regress/sys/kern/sosplice/tcp/args-write.pl
new file mode 100644
index 00000000000..22fa57eba6f
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/args-write.pl
@@ -0,0 +1,22 @@
+# test mix write and relay
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 65521,
+ nocheck => 1,
+ },
+ relay => {
+ func => sub {
+ write_stream(@_, 32749);
+ IO::Handle::flush(\*STDOUT);
+ relay(@_);
+ write_stream(@_, 2039);
+ },
+ nocheck => 1,
+ },
+ len => 100309,
+ md5 => "0efc7833e5c0652823ca63eaccb9918f",
+);
diff --git a/regress/sys/kern/sosplice/tcp/direct.pl b/regress/sys/kern/sosplice/tcp/direct.pl
new file mode 100644
index 00000000000..e2cc0c8ac33
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/direct.pl
@@ -0,0 +1,62 @@
+#!/usr/bin/perl
+# $OpenBSD: direct.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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';
+
+sub usage {
+ die "usage: direct.pl [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 == 0 or usage();
+
+my $s = Server->new(
+ func => \&read_stream,
+ %{$args{server}},
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+);
+my $c = Client->new(
+ func => \&write_stream,
+ %{$args{client}},
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $s->{listenport},
+);
+
+$s->run;
+$c->run->up;
+$s->up;
+
+$c->down;
+$s->down;
+
+check_logs($c, undef, $s, %args);
diff --git a/regress/sys/kern/sosplice/tcp/echo.pl b/regress/sys/kern/sosplice/tcp/echo.pl
new file mode 100755
index 00000000000..b432bffd37a
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/echo.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+# $OpenBSD: echo.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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} || $args{client}{alarm} || $args{server}{alarm};
+
+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_stream)->(@_);
+ 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;
+ eval { ($args{client}{func} || \&write_stream)->(@_) };
+ warn $@ if $@;
+ eval { shutout(@_) };
+ $s->down;
+ },
+);
+
+$r->run;
+$c->run->up;
+$r->up;
+
+$c->down;
+$r->down;
+$s->{pid} = -1; # XXX hack
+$s->down;
+
+exit if $args{nocheck} || $args{client}{nocheck};
+
+check_logs($c, $r, $s, %args);
diff --git a/regress/sys/kern/sosplice/tcp/relay.pl b/regress/sys/kern/sosplice/tcp/relay.pl
new file mode 100644
index 00000000000..b7d7525f239
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/relay.pl
@@ -0,0 +1,77 @@
+#!/usr/bin/perl
+# $OpenBSD: relay.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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_stream,
+ 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_stream,
+ 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;
+
+check_logs($c, $r, $s, %args);
diff --git a/regress/sys/kern/sosplice/tcp/remote.pl b/regress/sys/kern/sosplice/tcp/remote.pl
new file mode 100644
index 00000000000..1da634617c4
--- /dev/null
+++ b/regress/sys/kern/sosplice/tcp/remote.pl
@@ -0,0 +1,129 @@
+#!/usr/bin/perl
+# $OpenBSD: remote.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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_stream,
+ 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_stream,
+ 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;
+
+check_logs($c, $r, $s, %args);
diff --git a/regress/sys/kern/sosplice/udp/Makefile b/regress/sys/kern/sosplice/udp/Makefile
new file mode 100644
index 00000000000..bd10dd6daf7
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/Makefile
@@ -0,0 +1,22 @@
+# $OpenBSD: Makefile,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+.include <bsd.regress.mk>
+
+# 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. 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
+.if empty (REMOTE_SSH)
+ time perl ${PERLINC} ${PERLPATH}relay.pl copy ${PERLPATH}$a
+ time perl ${PERLINC} ${PERLPATH}relay.pl splice ${PERLPATH}$a
+.else
+ time perl ${PERLINC} ${PERLPATH}remote.pl copy ${LOCAL_ADDR} ${REMOTE_ADDR} ${REMOTE_SSH} ${PERLPATH}$a
+ time perl ${PERLINC} ${PERLPATH}remote.pl splice ${LOCAL_ADDR} ${REMOTE_ADDR} ${REMOTE_SSH} ${PERLPATH}$a
+.endif
+.endfor
diff --git a/regress/sys/kern/sosplice/udp/args-default.pl b/regress/sys/kern/sosplice/udp/args-default.pl
new file mode 100644
index 00000000000..1caa30bc13f
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-default.pl
@@ -0,0 +1,9 @@
+# test default values
+
+use strict;
+use warnings;
+
+our %args = (
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/udp/args-idle-timeout.pl b/regress/sys/kern/sosplice/udp/args-idle-timeout.pl
new file mode 100644
index 00000000000..5e58a1225e4
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-idle-timeout.pl
@@ -0,0 +1,16 @@
+# test idle timeout
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => sub { sleep 1; write_datagram(@_); sleep 3; },
+ },
+ relay => {
+ idle => 2,
+ timeout => 1,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
diff --git a/regress/sys/kern/sosplice/udp/args-idle-trickle.pl b/regress/sys/kern/sosplice/udp/args-idle-trickle.pl
new file mode 100644
index 00000000000..738c0278f09
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-idle-trickle.pl
@@ -0,0 +1,24 @@
+# test idle timeout
+
+use strict;
+use warnings;
+use List::Util qw(sum);
+
+my @lengths = (0, 1, 2, 3, 4, 5);
+
+our %args = (
+ client => {
+ lengths => \@lengths,
+ sleep => 1,
+ },
+ relay => {
+ idle => 2,
+ timeout => 1,
+ },
+ server => {
+ num => scalar @lengths,
+ },
+ len => sum(@lengths),
+ lengths => "@lengths",
+ md5 => "a5612eb5137e859bf52c515a890241a5",
+);
diff --git a/regress/sys/kern/sosplice/udp/args-inet6-client.pl b/regress/sys/kern/sosplice/udp/args-inet6-client.pl
new file mode 100644
index 00000000000..0a676835700
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-inet6-client.pl
@@ -0,0 +1,22 @@
+# test ipv6 client
+
+use strict;
+use warnings;
+use Socket qw(AF_INET AF_INET6);
+
+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",
+ },
+);
diff --git a/regress/sys/kern/sosplice/udp/args-inet6-server.pl b/regress/sys/kern/sosplice/udp/args-inet6-server.pl
new file mode 100644
index 00000000000..66975ce563a
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-inet6-server.pl
@@ -0,0 +1,22 @@
+# test ipv6 server
+
+use strict;
+use warnings;
+use Socket qw(AF_INET AF_INET6);
+
+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",
+ },
+);
diff --git a/regress/sys/kern/sosplice/udp/args-inet6.pl b/regress/sys/kern/sosplice/udp/args-inet6.pl
new file mode 100644
index 00000000000..c84a85f61a4
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-inet6.pl
@@ -0,0 +1,22 @@
+# test ipv6 client and server
+
+use strict;
+use warnings;
+use Socket qw(AF_INET AF_INET6);
+
+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",
+ },
+);
diff --git a/regress/sys/kern/sosplice/udp/args-long.pl b/regress/sys/kern/sosplice/udp/args-long.pl
new file mode 100644
index 00000000000..0323385faf9
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-long.pl
@@ -0,0 +1,23 @@
+# test longer data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ len => 2**16 - 20 - 8 - 1,
+ sndbuf => 2**16,
+ },
+ relay => {
+ timeout => 10,
+ size => 2**16,
+ sndbuf => 2**16,
+ rcvbuf => 2**16,
+ },
+ server => {
+ num => 1,
+ rcvbuf => 2**16,
+ },
+ len => 65507,
+ md5 => "20f83f523b6b48b11d9f8a15e507e16a",
+);
diff --git a/regress/sys/kern/sosplice/udp/args-max.pl b/regress/sys/kern/sosplice/udp/args-max.pl
new file mode 100644
index 00000000000..8d8beac72de
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-max.pl
@@ -0,0 +1,21 @@
+# test maximum data length
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ lengths => [ 1, 3, 1 ],
+ func => sub { errignore(@_); write_datagram(@_); },
+ nocheck => 1,
+ },
+ relay => {
+ max => 4,
+ },
+ server => {
+ num => 2,
+ },
+ len => 4,
+ lengths => "1 3",
+ md5 => "07515c3ca1f21779d63135eb5531eca2",
+);
diff --git a/regress/sys/kern/sosplice/udp/args-multi.pl b/regress/sys/kern/sosplice/udp/args-multi.pl
new file mode 100644
index 00000000000..b61b812c6aa
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-multi.pl
@@ -0,0 +1,25 @@
+# test with mutiple packets
+
+use strict;
+use warnings;
+use List::Util qw(sum);
+
+my @lengths = (251, 16384, 0, 1, 2, 3, 4, 5);
+
+our %args = (
+ client => {
+ lengths => \@lengths,
+ sndbuf => 20000,
+ },
+ relay => {
+ rcvbuf => 20000,
+ sndbuf => 20000,
+ },
+ server => {
+ num => scalar @lengths,
+ rcvbuf => 20000,
+ },
+ len => sum(@lengths),
+ lengths => "@lengths",
+ md5 => "f5b58b46c97b566fc8d34080c475d637",
+);
diff --git a/regress/sys/kern/sosplice/udp/args-nospace.pl b/regress/sys/kern/sosplice/udp/args-nospace.pl
new file mode 100644
index 00000000000..06d517eb191
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/args-nospace.pl
@@ -0,0 +1,23 @@
+# test sending an udp packet that does not fit into splice space
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ lengths => [ 1, 10000, 2, 10001, 3 ],
+ sndbuf => 30000,
+ nocheck => 1,
+ },
+ relay => {
+ rcvbuf => 30000,
+ sndbuf => 10000,
+ },
+ server => {
+ num => 4,
+ rcvbuf => 30000,
+ },
+ len => 10006,
+ lengths => "1 10000 2 3",
+ md5 => "90c347b1853f03d6e73aa88c9d12ce55",
+);
diff --git a/regress/sys/kern/sosplice/udp/direct.pl b/regress/sys/kern/sosplice/udp/direct.pl
new file mode 100644
index 00000000000..12b029c4c86
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/direct.pl
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+# $OpenBSD: direct.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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';
+
+sub usage {
+ die "usage: direct.pl [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 == 0 or usage();
+
+my $s = Server->new(
+ func => \&read_datagram,
+ %{$args{server}},
+ protocol => "udp",
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+);
+my $c = Client->new(
+ func => \&write_datagram,
+ %{$args{client}},
+ protocol => "udp",
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $s->{listenport},
+);
+
+$s->run;
+$c->run->up;
+$s->up;
+
+$c->down;
+$s->down;
+
+check_logs($c, undef, $s, %args);
diff --git a/regress/sys/kern/sosplice/udp/relay.pl b/regress/sys/kern/sosplice/udp/relay.pl
new file mode 100644
index 00000000000..a06fdc2ab14
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/relay.pl
@@ -0,0 +1,79 @@
+#!/usr/bin/perl
+# $OpenBSD: relay.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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_datagram,
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ %{$args{server}},
+ protocol => "udp",
+);
+my $r = Relay->new(
+ forward => $ARGV[0],
+ idle => 5,
+ func => \&relay,
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $s->{listenport},
+ %{$args{relay}},
+ protocol => "udp",
+);
+my $c = Client->new(
+ func => \&write_datagram,
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $r->{listenport},
+ %{$args{client}},
+ protocol => "udp",
+);
+
+$s->run;
+$r->run;
+$c->run->up;
+$r->up;
+$s->up;
+
+$c->down;
+$r->down;
+$s->down;
+
+check_logs($c, $r, $s, %args);
diff --git a/regress/sys/kern/sosplice/udp/remote.pl b/regress/sys/kern/sosplice/udp/remote.pl
new file mode 100644
index 00000000000..8bcc773e86a
--- /dev/null
+++ b/regress/sys/kern/sosplice/udp/remote.pl
@@ -0,0 +1,132 @@
+#!/usr/bin/perl
+# $OpenBSD: remote.pl,v 1.1.1.1 2013/01/03 17:36:39 bluhm Exp $
+
+# Copyright (c) 2010-2013 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",
+ idle => 5,
+ func => \&relay,
+ %{$args{relay}},
+ protocol => "udp",
+ 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_datagram,
+ %{$args{server}},
+ protocol => "udp",
+ 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],
+ protocol => "udp",
+ listenaddr => $ARGV[2],
+ connectaddr => $ARGV[1],
+ connectport => $s->{listenport},
+ );
+ $r->run->up;
+}
+my $c = Client->new(
+ func => \&write_datagram,
+ %{$args{client}},
+ protocol => "udp",
+ 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;
+
+check_logs($c, $r, $s, %args);