Add support for send, receive and state-change DTrace providers for

SCTP. They are based on what is specified in the Solaris DTrace manual
for Solaris 11.4.

Reviewed by:		0mp, dteske, markj
Relnotes:		yes
Differential Revision:	https://reviews.freebsd.org/D16839
This commit is contained in:
Michael Tuexen 2018-08-22 21:23:32 +00:00
parent 376a4e3255
commit 1e88cc8b59
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=338213
20 changed files with 988 additions and 16 deletions

View file

@ -27,11 +27,12 @@
#pragma ident "%Z%%M% %I% %E% SMI"
#
# get.ipv4remote.pl [tcpport]
# get.ipv4remote.pl [port] [proto]
#
# Find an IPv4 reachable remote host using both ifconfig(1M) and ping(1M).
# If a tcpport is specified, return a host that is also listening on this
# TCP port. Print the local address and the remote address, or an
# If a port is specified, return a host that is also listening on this
# port. If the port is specified, the protocol can also be specified and
# defaults to tcp. Print the local address and the remote address, or an
# error message if no suitable remote host was found. Exit status is 0 if
# a host was found.
#
@ -41,7 +42,8 @@ use IO::Socket;
my $MAXHOSTS = 32; # max hosts to port scan
my $TIMEOUT = 3; # connection timeout
my $tcpport = @ARGV == 1 ? $ARGV[0] : 0;
my $port = @ARGV >= 1 ? $ARGV[0] : 0;
my $proto = @ARGV == 2 ? $ARGV[1] : "tcp";
#
# Determine local IP address
@ -79,14 +81,15 @@ while (<PING>) {
if (/bytes from (.*): / and not defined $Broadcast{$1}) {
my $addr = $1;
if ($tcpport != 0) {
if ($port != 0) {
#
# Test TCP
#
my $socket = IO::Socket::INET->new(
Proto => "tcp",
Type => SOCK_STREAM,
Proto => $proto,
PeerAddr => $addr,
PeerPort => $tcpport,
PeerPort => $port,
Timeout => $TIMEOUT,
);
next unless $socket;

View file

@ -0,0 +1,137 @@
#!/usr/bin/env ksh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
#
#
# Test {ip,sctp}:::{send,receive} of IPv4 SCTP to local host.
#
# This may fail due to:
#
# 1. A change to the ip stack breaking expected probe behavior,
# which is the reason we are testing.
# 2. The lo0 interface missing or not up.
# 3. An unlikely race causes the unlocked global send/receive
# variables to be corrupted.
#
# This test performs a SCTP association and checks that at least the
# following packet counts were traced:
#
# 7 x ip:::send (4 during the setup, 3 during the teardown)
# 7 x sctp:::send (4 during the setup, 3 during the teardown)
# 7 x ip:::receive (4 during the setup, 3 during the teardown)
# 7 x sctp:::receive (4 during the setup, 3 during the teardown)
# The actual count tested is 7 each way, since we are tracing both
# source and destination events.
#
if (( $# != 1 )); then
print -u2 "expected one argument: <dtrace-path>"
exit 2
fi
dtrace=$1
local=127.0.0.1
DIR=/var/tmp/dtest.$$
sctpport=1024
bound=5000
while [ $sctpport -lt $bound ]; do
ncat --sctp -z $local $sctpport > /dev/null || break
sctpport=$(($sctpport + 1))
done
if [ $sctpport -eq $bound ]; then
echo "couldn't find an available SCTP port"
exit 1
fi
mkdir $DIR
cd $DIR
# ncat will exit when the association is closed.
ncat --sctp --listen $local $sctpport &
cat > test.pl <<-EOPERL
use IO::Socket;
my \$s = IO::Socket::INET->new(
Type => SOCK_STREAM,
Proto => "sctp",
LocalAddr => "$local",
PeerAddr => "$local",
PeerPort => $sctpport,
Timeout => 3);
die "Could not connect to host $local port $sctpport \$@" unless \$s;
close \$s;
sleep(2);
EOPERL
$dtrace -c 'perl test.pl' -qs /dev/stdin <<EODTRACE
BEGIN
{
ipsend = sctpsend = ipreceive = sctpreceive = 0;
}
ip:::send
/args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local" &&
args[4]->ipv4_protocol == IPPROTO_SCTP/
{
ipsend++;
}
sctp:::send
/args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local"/
{
sctpsend++;
}
ip:::receive
/args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local" &&
args[4]->ipv4_protocol == IPPROTO_SCTP/
{
ipreceive++;
}
sctp:::receive
/args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local"/
{
sctpreceive++;
}
END
{
printf("Minimum SCTP events seen\n\n");
printf("ip:::send (%d) - %s\n", ipsend, ipsend >= 7 ? "yes" : "no");
printf("ip:::receive (%d) - %s\n", ipreceive, ipreceive >= 7 ? "yes" : "no");
printf("sctp:::send (%d) - %s\n", sctpsend, sctpsend >= 7 ? "yes" : "no");
printf("sctp:::receive (%d) - %s\n", sctpreceive, sctpreceive >= 7 ? "yes" : "no");
}
EODTRACE
status=$?
cd /
/bin/rm -rf $DIR
exit $status

View file

@ -0,0 +1,7 @@
Minimum SCTP events seen
ip:::send - yes
ip:::receive - yes
sctp:::send - yes
sctp:::receive - yes

View file

@ -0,0 +1,130 @@
#!/usr/bin/env ksh93
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
#
#
# Test {sctp,ip}:::{send,receive} of IPv4 SCTP to a remote host.
#
# This may fail due to:
#
# 1. A change to the ip stack breaking expected probe behavior,
# which is the reason we are testing.
# 2. No physical network interface is plumbed and up.
# 3. No other hosts on this subnet are reachable and listening on ssh.
# 4. An unlikely race causes the unlocked global send/receive
# variables to be corrupted.
#
# This test performs an SCTP association and checks that at least the
# following packet counts were traced:
#
# 4 x ip:::send (2 during setup, 2 during teardown)
# 4 x sctp:::send (2 during connection setup, 2 during connection teardown)
# 3 x ip:::receive (2 during setup, 1 during teardown)
# 3 x sctp:::receive (2 during setup, 1 during teardown)
if (( $# != 1 )); then
print -u2 "expected one argument: <dtrace-path>"
exit 2
fi
dtrace=$1
getaddr=./get.ipv4remote.pl
sctpport=80
DIR=/var/tmp/dtest.$$
if [[ ! -x $getaddr ]]; then
print -u2 "could not find or execute sub program: $getaddr"
exit 3
fi
$getaddr $sctpport sctp | read source dest
if (( $? != 0 )); then
exit 4
fi
mkdir $DIR
cd $DIR
cat > test.pl <<-EOPERL
use IO::Socket;
my \$s = IO::Socket::INET->new(
Type => SOCK_STREAM,
Proto => "sctp",
LocalAddr => "$source",
PeerAddr => "$dest",
PeerPort => $sctpport,
Timeout => 3);
die "Could not connect to host $dest port $sctpport \$@" unless \$s;
close \$s;
sleep(2);
EOPERL
$dtrace -c 'perl test.pl' -qs /dev/stdin <<EODTRACE
BEGIN
{
ipsend = sctpsend = ipreceive = sctpreceive = 0;
}
ip:::send
/args[2]->ip_saddr == "$source" && args[2]->ip_daddr == "$dest" &&
args[4]->ipv4_protocol == IPPROTO_SCTP/
{
ipsend++;
}
sctp:::send
/args[2]->ip_saddr == "$source" && args[2]->ip_daddr == "$dest"/
{
sctpsend++;
}
ip:::receive
/args[2]->ip_saddr == "$dest" && args[2]->ip_daddr == "$source" &&
args[4]->ipv4_protocol == IPPROTO_SCTP/
{
ipreceive++;
}
sctp:::receive
/args[2]->ip_saddr == "$dest" && args[2]->ip_daddr == "$source"/
{
sctpreceive++;
}
END
{
printf("Minimum SCTP events seen\n\n");
printf("ip:::send - %s\n", ipsend >= 4 ? "yes" : "no");
printf("ip:::receive - %s\n", ipreceive >= 3 ? "yes" : "no");
printf("sctp:::send - %s\n", sctpsend >= 4 ? "yes" : "no");
printf("sctp:::receive - %s\n", sctpreceive >= 3 ? "yes" : "no");
}
EODTRACE
status=$?
cd /
/bin/rm -rf $DIR
exit $status

View file

@ -0,0 +1,7 @@
Minimum SCTP events seen
ip:::send - yes
ip:::receive - yes
sctp:::send - yes
sctp:::receive - yes

View file

@ -0,0 +1,159 @@
#!/usr/bin/env ksh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
#
#
# Test sctp:::state-change and sctp:::{send,receive} by connecting to
# the local discard service.
# A number of state transition events along with SCTP send and
# receive events for the message should result.
#
# This may fail due to:
#
# 1. A change to the ip stack breaking expected probe behavior,
# which is the reason we are testing.
# 2. The lo0 interface missing or not up.
# 3. An unlikely race causes the unlocked global send/receive
# variables to be corrupted.
#
# This test performs a SCTP connection and checks that at least the
# following packet counts were traced:
#
# 7 x ip:::send (4 during the setup, 3 during the teardown)
# 7 x sctp:::send (4 during the setup, 3 during the teardown)
# 7 x ip:::receive (4 during the setup, 3 during the teardown)
# 7 x sctp:::receive (4 during the setup, 3 during the teardown)
#
# The actual count tested is 7 each way, since we are tracing both
# source and destination events.
#
if (( $# != 1 )); then
print -u2 "expected one argument: <dtrace-path>"
exit 2
fi
dtrace=$1
local=127.0.0.1
DIR=/var/tmp/dtest.$$
sctpport=1024
bound=5000
while [ $sctpport -lt $bound ]; do
ncat --sctp -z $local $sctpport > /dev/null || break
sctpport=$(($sctpport + 1))
done
if [ $sctpport -eq $bound ]; then
echo "couldn't find an available SCTP port"
exit 1
fi
mkdir $DIR
cd $DIR
# ncat will exit when the association is closed.
ncat --sctp --listen $local $sctpport &
cat > test.pl <<-EOPERL
use IO::Socket;
my \$s = IO::Socket::INET->new(
Type => SOCK_STREAM,
Proto => "sctp",
LocalAddr => "$local",
PeerAddr => "$local",
PeerPort => $sctpport,
Timeout => 3);
die "Could not connect to host $local port $sctpport \$@" unless \$s;
close \$s;
sleep(2);
EOPERL
$dtrace -c 'perl test.pl' -qs /dev/stdin <<EODTRACE
BEGIN
{
ipsend = sctpsend = ipreceive = sctpreceive = 0;
}
ip:::send
/args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local" &&
args[4]->ipv4_protocol == IPPROTO_SCTP/
{
ipsend++;
}
sctp:::send
/args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local" &&
(args[4]->sctp_sport == $sctpport || args[4]->sctp_dport == $sctpport)/
{
sctpsend++;
}
ip:::receive
/args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local" &&
args[4]->ipv4_protocol == IPPROTO_SCTP/
{
ipreceive++;
}
sctp:::receive
/args[2]->ip_saddr == "$local" && args[2]->ip_daddr == "$local" &&
(args[4]->sctp_sport == $sctpport || args[4]->sctp_dport == $sctpport)/
{
sctpreceive++;
}
sctp:::state-change
{
state_event[args[3]->sctps_state]++;
}
END
{
printf("Minimum SCTP events seen\n\n");
printf("ip:::send - %s\n", ipsend >= 7 ? "yes" : "no");
printf("ip:::receive - %s\n", ipreceive >= 7 ? "yes" : "no");
printf("sctp:::send - %s\n", sctpsend >= 7 ? "yes" : "no");
printf("sctp:::receive - %s\n", sctpreceive >= 7 ? "yes" : "no");
printf("sctp:::state-change to cookie-wait - %s\n",
state_event[SCTP_STATE_COOKIE_WAIT] >=1 ? "yes" : "no");
printf("sctp:::state-change to cookie-echoed - %s\n",
state_event[SCTP_STATE_COOKIE_ECHOED] >=1 ? "yes" : "no");
printf("sctp:::state-change to established - %s\n",
state_event[SCTP_STATE_ESTABLISHED] >= 2 ? "yes" : "no");
printf("sctp:::state-change to shutdown-sent - %s\n",
state_event[SCTP_STATE_SHUTDOWN_SENT] >= 1 ? "yes" : "no");
printf("sctp:::state-change to shutdown-received - %s\n",
state_event[SCTP_STATE_SHUTDOWN_RECEIVED] >= 1 ? "yes" : "no");
printf("sctp:::state-change to shutdown-ack-sent - %s\n",
state_event[SCTP_STATE_SHUTDOWN_ACK_SENT] >= 1 ? "yes" : "no");
}
EODTRACE
status=$?
cd /
/bin/rm -rf $DIR
exit $status

View file

@ -0,0 +1,12 @@
Minimum SCTP events seen
ip:::send - yes
ip:::receive - yes
sctp:::send - yes
sctp:::receive - yes
sctp:::state-change to cookie-wait - yes
sctp:::state-change to cookie-echoed - yes
sctp:::state-change to established - yes
sctp:::state-change to shutdown-sent - yes
sctp:::state-change to shutdown-received - yes
sctp:::state-change to shutdown-ack-sent - yes

View file

@ -0,0 +1,149 @@
#!/usr/bin/env ksh93
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
#
#
# Test sctp:::state-change and sctp:::{send,receive} by connecting to
# the remote http service.
# A number of state transition events along with sctp send and receive
# events for the message should result.
#
# This may fail due to:
#
# 1. A change to the ip stack breaking expected probe behavior,
# which is the reason we are testing.
# 2. The lo0 interface missing or not up.
# 3. The remote ssh service is not online.
# 4. An unlikely race causes the unlocked global send/receive
# variables to be corrupted.
#
# This test performs a SCTP association to the http service (port 80) and
# checks that at least the following packet counts were traced:
#
# 4 x ip:::send (2 during setup, 2 during teardown)
# 4 x sctp:::send (2 during setup, 2 during teardown)
# 3 x ip:::receive (2 during setup, 1 during teardown)
# 3 x sctp:::receive (2 during setup, 1 during teardown)
#
if (( $# != 1 )); then
print -u2 "expected one argument: <dtrace-path>"
exit 2
fi
dtrace=$1
getaddr=./get.ipv4remote.pl
sctpport=80
DIR=/var/tmp/dtest.$$
if [[ ! -x $getaddr ]]; then
print -u2 "could not find or execute sub program: $getaddr"
exit 3
fi
$getaddr $sctpport sctp | read source dest
if (( $? != 0 )); then
exit 4
fi
mkdir $DIR
cd $DIR
cat > test.pl <<-EOPERL
use IO::Socket;
my \$s = IO::Socket::INET->new(
Type => SOCK_STREAM,
Proto => "sctp",
LocalAddr => "$source",
PeerAddr => "$dest",
PeerPort => $sctpport,
Timeout => 3);
die "Could not connect to host $dest port $sctpport \$@" unless \$s;
close \$s;
sleep(2);
EOPERL
$dtrace -c 'perl test.pl' -qs /dev/stdin <<EODTRACE
BEGIN
{
ipsend = sctpsend = ipreceive = sctpreceive = 0;
}
ip:::send
/args[2]->ip_saddr == "$source" && args[2]->ip_daddr == "$dest" &&
args[4]->ipv4_protocol == IPPROTO_SCTP/
{
ipsend++;
}
sctp:::send
/args[2]->ip_saddr == "$source" && args[2]->ip_daddr == "$dest" &&
args[4]->sctp_dport == $sctpport/
{
sctpsend++;
}
ip:::receive
/args[2]->ip_saddr == "$dest" && args[2]->ip_daddr == "$source" &&
args[4]->ipv4_protocol == IPPROTO_SCTP/
{
ipreceive++;
}
sctp:::receive
/args[2]->ip_saddr == "$dest" && args[2]->ip_daddr == "$source" &&
args[4]->sctp_sport == $sctpport/
{
sctpreceive++;
}
sctp:::state-change
{
state_event[args[3]->sctps_state]++;
}
END
{
printf("Minimum SCTP events seen\n\n");
printf("ip:::send - %s\n", ipsend >= 4 ? "yes" : "no");
printf("ip:::receive - %s\n", ipreceive >= 3 ? "yes" : "no");
printf("sctp:::send - %s\n", sctpsend >= 4 ? "yes" : "no");
printf("sctp:::receive - %s\n", sctpreceive >= 3 ? "yes" : "no");
printf("sctp:::state-change to cookie-wait - %s\n",
state_event[SCTP_STATE_COOKIE_WAIT] >=1 ? "yes" : "no");
printf("sctp:::state-change to cookie-echoed - %s\n",
state_event[SCTP_STATE_COOKIE_ECHOED] >= 1 ? "yes" : "no");
printf("sctp:::state-change to established - %s\n",
state_event[SCTP_STATE_ESTABLISHED] >= 1 ? "yes" : "no");
printf("sctp:::state-change to shutdown-sent - %s\n",
state_event[SCTP_STATE_SHUTDOWN-SENT] >= 1 ? "yes" : "no");
}
EODTRACE
status=$?
cd /
/bin/rm -rf $DIR
exit $status

View file

@ -0,0 +1,12 @@
Minimum SCTP events seen
ip:::send - yes
ip:::receive - yes
SCTP:::send - yes
sctp:::receive - yes
sctp:::state-change to cookie-wait - yes
sctp:::state-change to cookie-echoed - yes
sctp:::state-change to established - yes
sctp:::state-change to shutdown-sent - yes
sctp:::state-change to closed - yes

View file

@ -51,6 +51,7 @@ DSRCS= errno.d \
io.d \
ip.d \
psinfo.d \
sctp.d \
siftr.d \
signal.d \
tcp.d \

171
cddl/lib/libdtrace/sctp.d Normal file
View file

@ -0,0 +1,171 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* $FreeBSD$
*/
/*
* Copyright (c) 2018 Michael Tuexen <tuexen@FreeBSD.org>
*/
#pragma D depends_on library ip.d
#pragma D depends_on library socket.d
#pragma D depends_on module kernel
#pragma D depends_on provider sctp
#pragma D binding "1.13" SCTP_STATE_MASK
inline int32_t SCTP_STATE_MASK = 0x0000007f;
#pragma D binding "1.13" SCTP_STATE_SHUTDOWN_PENDING
inline int32_t SCTP_STATE_SHUTDOWN_PENDING = 0x00000080;
#pragma D binding "1.13" SCTP_STATE_CLOSED_SOCKET
inline int32_t SCTP_STATE_CLOSED_SOCKET = 0x00000100;
#pragma D binding "1.13" SCTP_STATE_ABOUT_TO_BE_FREED
inline int32_t SCTP_STATE_ABOUT_TO_BE_FREED = 0x00000200;
#pragma D binding "1.13" SCTP_STATE_ABOUT_TO_BE_FREED
inline int32_t SCTP_STATE_PARTIAL_MSG_LEFT = 0x00000400;
#pragma D binding "1.13" SCTP_STATE_PARTIAL_MSG_LEFT
inline int32_t SCTP_STATE_WAS_ABORTED = 0x00000800;
#pragma D binding "1.13" SCTP_STATE_IN_ACCEPT_QUEUE
inline int32_t SCTP_STATE_IN_ACCEPT_QUEUE = 0x00001000;
#pragma D binding "1.13" SCTP_STATE_BOUND
inline int32_t SCTP_STATE_BOUND = 0x00001000;
#pragma D binding "1.13" SCTP_STATE_EMPTY
inline int32_t SCTP_STATE_EMPTY = 0x00000000;
#pragma D binding "1.13" SCTP_STATE_CLOSED
inline int32_t SCTP_STATE_CLOSED = 0x00000000;
#pragma D binding "1.13" SCTP_STATE_INUSE
inline int32_t SCTP_STATE_INUSE = 0x00000001;
#pragma D binding "1.13" SCTP_STATE_COOKIE_WAIT
inline int32_t SCTP_STATE_COOKIE_WAIT = 0x00000002;
#pragma D binding "1.13" SCTP_STATE_COOKIE_ECHOED
inline int32_t SCTP_STATE_COOKIE_ECHOED = 0x00000004;
#pragma D binding "1.13" SCTP_STATE_ESTABLISHED
inline int32_t SCTP_STATE_ESTABLISHED = 0x00000008;
#pragma D binding "1.13" SCTP_STATE_OPEN
inline int32_t SCTP_STATE_OPEN = 0x00000008;
#pragma D binding "1.13" SCTP_STATE_SHUTDOWN_SENT
inline int32_t SCTP_STATE_SHUTDOWN_SENT = 0x00000010;
#pragma D binding "1.13" SCTP_STATE_SHUTDOWN_RECEIVED
inline int32_t SCTP_STATE_SHUTDOWN_RECEIVED = 0x00000020;
#pragma D binding "1.13" SCTP_STATE_SHUTDOWN_ACK_SENT
inline int32_t SCTP_STATE_SHUTDOWN_ACK_SENT = 0x00000040;
/* SCTP association state strings. */
#pragma D binding "1.13" sctp_state_string
inline string sctp_state_string[int32_t state] =
state & SCTP_STATE_ABOUT_TO_BE_FREED ? "state-closed" :
state & SCTP_STATE_SHUTDOWN_PENDING ? "state-shutdown-pending" :
(state & SCTP_STATE_MASK) == SCTP_STATE_EMPTY ? "state-closed" :
(state & SCTP_STATE_MASK) == SCTP_STATE_INUSE ? "state-closed" :
(state & SCTP_STATE_MASK) == SCTP_STATE_COOKIE_WAIT ? "state-cookie-wait" :
(state & SCTP_STATE_MASK) == SCTP_STATE_COOKIE_ECHOED ? "state-cookie-echoed" :
(state & SCTP_STATE_MASK) == SCTP_STATE_OPEN ? "state-established" :
(state & SCTP_STATE_MASK) == SCTP_STATE_SHUTDOWN_SENT ? "state-shutdown-sent" :
(state & SCTP_STATE_MASK) == SCTP_STATE_SHUTDOWN_RECEIVED ? "state-shutdown-received" :
(state & SCTP_STATE_MASK) == SCTP_STATE_SHUTDOWN_ACK_SENT ? "state-shutdown-ack-sent" :
"<unknown>";
/*
* sctpsinfo contains stable SCTP details.
*/
typedef struct sctpsinfo {
uintptr_t sctps_addr; /* pointer to struct sctp_tcb */
int sctps_num_raddrs; /* number of remote addresses */
uintptr_t sctps_raddrs; /* pointer to struct sctp_nets */
int sctps_num_laddrs; /* number of local addresses */
uintptr_t sctps_laddrs; /* pointer to struct sctp_laddr */
uint16_t sctps_lport; /* local port */
uint16_t sctps_rport; /* remote port */
string sctps_laddr; /* local address, as a string */
string sctps_raddr; /* remote address, as a string */
int32_t sctps_state;
} sctpsinfo_t;
/*
* sctplsinfo provides the old SCTP state for state changes.
*/
typedef struct sctplsinfo {
int32_t sctps_state; /* previous SCTP state */
} sctplsinfo_t;
/*
* sctpinfo is the SCTP header fields.
*/
typedef struct sctpinfo {
uint16_t sctp_sport; /* source port */
uint16_t sctp_dport; /* destination port */
uint32_t sctp_verify; /* verification tag */
uint32_t sctp_checksum; /* CRC32C of the SCTP packet */
struct sctphdr *sctp_hdr; /* raw SCTP header */
} sctpinfo_t;
#pragma D binding "1.13" translator
translator csinfo_t < struct sctp_tcb *p > {
cs_addr = NULL;
cs_cid = (uint64_t)p;
cs_pid = 0;
cs_zoneid = 0;
};
#pragma D binding "1.13" translator
translator sctpsinfo_t < struct sctp_tcb *p > {
sctps_addr = (uintptr_t)p;
sctps_num_raddrs = p == NULL ? -1 : p->asoc.numnets;
sctps_raddrs = p == NULL ? NULL : (uintptr_t)(p->asoc.nets.tqh_first);
sctps_num_laddrs = p == NULL ? -1 :
p->sctp_ep == NULL ? -1 :
p->sctp_ep->laddr_count;
sctps_laddrs = p == NULL ? NULL :
p->sctp_ep == NULL ? NULL :
(uintptr_t)(p->sctp_ep->sctp_addr_list.lh_first);
sctps_lport = p == NULL ? 0 :
p->sctp_ep == NULL ? 0 :
ntohs(p->sctp_ep->ip_inp.inp.inp_inc.inc_ie.ie_lport);
sctps_rport = p == NULL ? 0 : ntohs(p->rport);
sctps_laddr = p == NULL ? "<unknown>" :
p->asoc.primary_destination == NULL ? "<unknown>" :
p->asoc.primary_destination->ro._s_addr == NULL ? "<unknown>" :
p->asoc.primary_destination->ro._s_addr->address.sa.sa_family == AF_INET ?
inet_ntoa(&p->asoc.primary_destination->ro._s_addr->address.sin.sin_addr.s_addr) :
p->asoc.primary_destination->ro._s_addr->address.sa.sa_family == AF_INET6 ?
inet_ntoa6(&p->asoc.primary_destination->ro._s_addr->address.sin6.sin6_addr) :
"<unknown>";
sctps_raddr = p == NULL ? "<unknown>" :
p->asoc.primary_destination == NULL ? "<unknown>" :
p->asoc.primary_destination->ro._l_addr.sa.sa_family == AF_INET ?
inet_ntoa(&p->asoc.primary_destination->ro._l_addr.sin.sin_addr.s_addr) :
p->asoc.primary_destination->ro._l_addr.sa.sa_family == AF_INET6 ?
inet_ntoa6(&p->asoc.primary_destination->ro._l_addr.sin6.sin6_addr) :
"<unknown>";
sctps_state = p == NULL ? SCTP_STATE_CLOSED : p->asoc.state;
};
#pragma D binding "1.13" translator
translator sctpinfo_t < struct sctphdr *p > {
sctp_sport = p == NULL ? 0 : ntohs(p->src_port);
sctp_dport = p == NULL ? 0 : ntohs(p->dest_port);
sctp_verify = p == NULL ? 0 : ntohl(p->v_tag);
sctp_checksum = p == NULL ? 0 : ntohl(p->checksum);
sctp_hdr = p;
};
#pragma D binding "1.13" translator
translator sctplsinfo_t < int state > {
sctps_state = state;
};

View file

@ -9,6 +9,8 @@ PACKAGE= tests
${PACKAGE}FILES= \
tst.ipv4localicmp.ksh \
tst.ipv4localicmp.ksh.out \
tst.ipv4localsctp.ksh \
tst.ipv4localsctp.ksh.out \
tst.ipv4localtcp.ksh \
tst.ipv4localtcp.ksh.out \
tst.ipv4localudp.ksh \
@ -17,6 +19,8 @@ ${PACKAGE}FILES= \
tst.ipv4localudplite.ksh.out \
tst.ipv4remoteicmp.ksh \
tst.ipv4remoteicmp.ksh.out \
tst.ipv4remotesctp.ksh \
tst.ipv4remotesctp.ksh.out \
tst.ipv4remotetcp.ksh \
tst.ipv4remotetcp.ksh.out \
tst.ipv4remoteudp.ksh \
@ -27,8 +31,12 @@ ${PACKAGE}FILES= \
tst.ipv6localicmp.ksh.out \
tst.ipv6remoteicmp.ksh \
tst.ipv6remoteicmp.ksh.out \
tst.localsctpstate.ksh \
tst.localsctpstate.ksh.out \
tst.localtcpstate.ksh \
tst.localtcpstate.ksh.out \
tst.remotesctpstate.ksh \
tst.remotesctpstate.ksh.out \
tst.remotetcpstate.ksh \
tst.remotetcpstate.ksh.out \

View file

@ -117,11 +117,13 @@ exclude SKIP common/builtinvar/tst.ipl.d
exclude SKIP common/builtinvar/tst.ipl1.d
# These tests rely on being able to find a host via broadcast pings.
exclude EXFAIL common/ip/tst.ipv4remotesctp.ksh
exclude EXFAIL common/ip/tst.ipv4remotetcp.ksh
exclude EXFAIL common/ip/tst.ipv4remoteudp.ksh
exclude EXFAIL common/ip/tst.ipv4remoteudplite.ksh
exclude EXFAIL common/ip/tst.ipv6remoteicmp.ksh
exclude EXFAIL common/ip/tst.ipv4remoteicmp.ksh
exclude EXFAIL common/ip/tst.remotesctpstate.ksh
exclude EXFAIL common/ip/tst.remotetcpstate.ksh
# Tries to enable pid$target:libc::entry, though there's no "libc" module.

View file

@ -23,7 +23,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd August 1, 2018
.Dd August 22, 2018
.Dt DTRACE_SCTP 4
.Os
.Sh NAME
@ -44,6 +44,12 @@ protocol
.Fn sctp:rwnd:assoc:val uint32_t uint32_t int int
.Fn sctp:flightsize:net:val uint32_t uint32_t uintptr_t int int
.Fn sctp:flightsize:assoc:val uint32_t uint32_t int int
.Fn sctp:::receive "pktinfo_t *" "csinfo_t *" "ipinfo_t *" "sctpsinfo_t *" \
"sctpinfo_t *"
.Fn sctp:::send "pktinfo_t *" "csinfo_t *" "ipinfo_t *" "sctpsinfo_t *" \
"sctpinfo_t *"
.Fn sctp:::state-change "void *" "csinfo_t *" "void *" "sctpsinfo_t *" \
"void *" "sctplsinfo_t *"
.Sh DESCRIPTION
The DTrace
.Nm sctp
@ -105,14 +111,105 @@ probe fires when a remotely-initiated active SCTP open succeeds.
At this point the new connection is in the ESTABLISHED state, and the probe
arguments expose the headers associated with the final ACK of the four-way
handshake.
.Pp
The
.Fn sctp:::send
and
.Fn sctp:::receive
probes fire when the host sends or receives an SCTP packet, respectively.
As with the
.Xr dtrace_udp 4
provider,
.Nm sctp
probes fire only for packets sent by or to the local host; forwarded packets are
handled in the IP layer and are only visible to the
.Xr dtrace_ip 4
provider.
.Pp
The
.Fn sctp:::state-change
probe fires upon local SCTP association state transitions.
Its first, third and fifth arguments are currently always
.Dv NULL .
Its last argument describes the from-state in the transition, and the to-state
can be obtained from
.Dv args[3]->sctps_state .
.\" .Sh ARGUMENTS
.\" .Sh FILES
.\" .Sh EXAMPLES
.\" .Sh COMPATIBILITY
.\" This provider has not been tested for compatiblity with the
.\" .Nm sctp
.\" provider in Solaris
.\" .Pq if one exists .
.Sh FILES
.Bl -tag -width "/usr/lib/dtrace/sctp.d" -compact
.It Pa /usr/lib/dtrace/sctp.d
DTrace type and translator definitions for the
.Nm sctp
provider.
.El
.Sh EXAMPLES
A script that logs SCTP packets in real time:
.Bd -literal -offset indent
#pragma D option quiet
#pragma D option switchrate=10hz
dtrace:::BEGIN
{
printf(" %3s %15s:%-5s %15s:%-5s\n", "CPU",
"LADDR", "LPORT", "RADDR", "RPORT");
}
sctp:::send
{
printf(" %3d %16s:%-5d -> %16s:%-5d\n", cpu,
args[2]->ip_saddr, args[4]->sctp_sport,
args[2]->ip_daddr, args[4]->sctp_dport);
}
sctp:::receive
{
printf(" %3d %16s:%-5d <- %16s:%-5d\n", cpu,
args[2]->ip_daddr, args[4]->sctp_dport,
args[2]->ip_saddr, args[4]->sctp_sport);
}
.Ed
A script that logs SCTP association state changes as they occur:
.Bd -literal -offset indent
#pragma D option quiet
#pragma D option switchrate=10
int last[int];
dtrace:::BEGIN
{
printf(" %3s %12s %-25s %-25s\n",
"CPU", "DELTA(us)", "OLD", "NEW");
}
sctp:::state-change
/ last[args[1]->cs_cid] /
{
this->elapsed = (timestamp - last[args[1]->cs_cid]) / 1000;
printf(" %3d %12d %-25s -> %-25s\n", cpu, this->elapsed,
sctp_state_string[args[5]->sctps_state],
sctp_state_string[args[3]->sctps_state]);
last[args[1]->cs_cid] = timestamp;
}
sctp:::state-change
/ last[args[1]->cs_cid] == 0 /
{
printf(" %3d %12s %-25s -> %-25s\n", cpu, "-",
sctp_state_string[args[5]->sctps_state],
sctp_state_string[args[3]->sctps_state]);
last[args[1]->cs_cid] = timestamp;
}
.Ed
.Sh COMPATIBILITY
The
.Fn sctp:::send ,
.Fn sctp:::receive ,
and
.Fn sctp:::state-change
probes are compatible with the
.Nm sctp
provider in Solaris.
All other probes are only available in FreeBSD.
.Sh SEE ALSO
.Xr dtrace 1 ,
.Xr dtrace_ip 4 ,

View file

@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sdt.h>
SDT_PROVIDER_DEFINE(ip);
SDT_PROVIDER_DEFINE(sctp);
SDT_PROVIDER_DEFINE(tcp);
SDT_PROVIDER_DEFINE(udp);
SDT_PROVIDER_DEFINE(udplite);
@ -56,6 +57,28 @@ SDT_PROBE_DEFINE6_XLATE(ip, , , send,
"struct ip *", "ipv4info_t *",
"struct ip6_hdr *", "ipv6info_t *");
SDT_PROBE_DEFINE5_XLATE(sctp, , , receive,
"void *", "pktinfo_t *",
"struct sctp_tcb *", "csinfo_t *",
"struct mbuf *", "ipinfo_t *",
"struct sctp_tcb *", "sctpsinfo_t *" ,
"struct sctphdr *", "sctpinfo_t *");
SDT_PROBE_DEFINE5_XLATE(sctp, , , send,
"void *", "pktinfo_t *",
"struct sctp_tcb *", "csinfo_t *",
"uint8_t *", "ipinfo_t *",
"struct sctp_tcb *", "sctpsinfo_t *" ,
"struct sctphdr *", "sctpinfo_t *");
SDT_PROBE_DEFINE6_XLATE(sctp, , , state__change,
"void *", "void *",
"struct sctp_tcb *", "csinfo_t *",
"void *", "void *",
"struct sctp_tcb *", "sctpsinfo_t *",
"void *", "void *",
"int", "sctplsinfo_t *");
SDT_PROBE_DEFINE5_XLATE(tcp, , , accept__established,
"void *", "pktinfo_t *",
"struct tcpcb *", "csinfo_t *",

View file

@ -48,8 +48,21 @@
SDT_PROBE5(tcp, , , probe, arg0, arg1, arg2, arg3, arg4)
#define TCP_PROBE6(probe, arg0, arg1, arg2, arg3, arg4, arg5) \
SDT_PROBE6(tcp, , , probe, arg0, arg1, arg2, arg3, arg4, arg5)
#define SCTP_PROBE1(probe, arg0) \
SDT_PROBE1(sctp, , , probe, arg0)
#define SCTP_PROBE2(probe, arg0, arg1) \
SDT_PROBE2(sctp, , , probe, arg0, arg1)
#define SCTP_PROBE3(probe, arg0, arg1, arg2) \
SDT_PROBE3(sctp, , , probe, arg0, arg1, arg2)
#define SCTP_PROBE4(probe, arg0, arg1, arg2, arg3) \
SDT_PROBE4(sctp, , , probe, arg0, arg1, arg2, arg3)
#define SCTP_PROBE5(probe, arg0, arg1, arg2, arg3, arg4) \
SDT_PROBE5(sctp, , , probe, arg0, arg1, arg2, arg3, arg4)
#define SCTP_PROBE6(probe, arg0, arg1, arg2, arg3, arg4, arg5) \
SDT_PROBE6(sctp, , , probe, arg0, arg1, arg2, arg3, arg4, arg5)
SDT_PROVIDER_DECLARE(ip);
SDT_PROVIDER_DECLARE(sctp);
SDT_PROVIDER_DECLARE(tcp);
SDT_PROVIDER_DECLARE(udp);
SDT_PROVIDER_DECLARE(udplite);
@ -57,6 +70,10 @@ SDT_PROVIDER_DECLARE(udplite);
SDT_PROBE_DECLARE(ip, , , receive);
SDT_PROBE_DECLARE(ip, , , send);
SDT_PROBE_DECLARE(sctp, , , receive);
SDT_PROBE_DECLARE(sctp, , , send);
SDT_PROBE_DECLARE(sctp, , , state__change);
SDT_PROBE_DECLARE(tcp, , , accept__established);
SDT_PROBE_DECLARE(tcp, , , accept__refused);
SDT_PROBE_DECLARE(tcp, , , connect__established);

View file

@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/sdt.h>
SDT_PROVIDER_DEFINE(sctp);
SDT_PROVIDER_DECLARE(sctp);
/********************************************************/
/* Cwnd probe - tracks changes in the congestion window on a netp */

View file

@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#if defined(INET) || defined(INET6)
#include <netinet/udp.h>
#endif
#include <netinet/in_kdtrace.h>
#include <sys/smp.h>
@ -5569,6 +5570,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
net->flowtype = mflowtype;
net->flowid = mflowid;
}
SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh);
if ((inp != NULL) && (stcb != NULL)) {
sctp_send_packet_dropped(stcb, net, m, length, iphlen, 1);
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR, SCTP_SO_NOT_LOCKED);
@ -5609,6 +5611,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
net->flowid = mflowid;
}
if (inp == NULL) {
SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh);
SCTP_STAT_INCR(sctps_noport);
if (badport_bandlim(BANDLIM_SCTP_OOTB) < 0) {
goto out;
@ -5657,6 +5660,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
*/
SCTP_TCB_UNLOCK(stcb);
stcb = NULL;
SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh);
snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
msg);
@ -5713,11 +5717,13 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
if ((stcb != NULL) &&
sctp_auth_is_required_chunk(SCTP_DATA, stcb->asoc.local_auth_chunks)) {
/* "silently" ignore */
SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh);
SCTP_STAT_INCR(sctps_recvauthmissing);
goto out;
}
if (stcb == NULL) {
/* out of the blue DATA chunk */
SCTP_PROBE5(receive, NULL, NULL, m, NULL, sh);
snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
msg);
@ -5728,11 +5734,13 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
}
if (stcb->asoc.my_vtag != ntohl(sh->v_tag)) {
/* v_tag mismatch! */
SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh);
SCTP_STAT_INCR(sctps_badvtag);
goto out;
}
}
SCTP_PROBE5(receive, NULL, stcb, m, stcb, sh);
if (stcb == NULL) {
/*
* no valid TCB for this packet, or we found it's a bad

View file

@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#endif
#include <netinet/udp_var.h>
#include <machine/in_cksum.h>
#include <netinet/in_kdtrace.h>
@ -4251,6 +4252,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
SCTP_SOCKET_UNLOCK(so, 0);
}
#endif
SCTP_PROBE5(send, NULL, stcb, ip, stcb, sctphdr);
SCTP_IP_OUTPUT(ret, o_pak, ro, stcb, vrf_id);
#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
if ((SCTP_BASE_SYSCTL(sctp_output_unlocked)) && (so_locked)) {
@ -4584,6 +4586,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING)
sctp_packet_log(o_pak);
#endif
SCTP_PROBE5(send, NULL, stcb, ip6h, stcb, sctphdr);
SCTP_IP6_OUTPUT(ret, o_pak, (struct route_in6 *)ro, &ifp, stcb, vrf_id);
#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
if ((SCTP_BASE_SYSCTL(sctp_output_unlocked)) && (so_locked)) {
@ -11252,6 +11255,7 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst,
sctp_packet_log(o_pak);
}
#endif
SCTP_PROBE5(send, NULL, NULL, ip, NULL, shout);
SCTP_IP_OUTPUT(ret, o_pak, NULL, NULL, vrf_id);
break;
#endif
@ -11274,6 +11278,7 @@ sctp_send_resp_msg(struct sockaddr *src, struct sockaddr *dst,
sctp_packet_log(o_pak);
}
#endif
SCTP_PROBE5(send, NULL, NULL, ip6, NULL, shout);
SCTP_IP6_OUTPUT(ret, o_pak, NULL, NULL, NULL, vrf_id);
break;
#endif

View file

@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$");
#endif
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/in_kdtrace.h>
#include <sys/proc.h>
#ifdef INET6
#include <netinet/icmp6.h>
@ -7385,6 +7386,10 @@ sctp_hc_get_mtu(union sctp_sockstore *addr, uint16_t fibnum)
void
sctp_set_state(struct sctp_tcb *stcb, int new_state)
{
#if defined(KDTRACE_HOOKS)
int old_state = stcb->asoc.state;
#endif
KASSERT((new_state & ~SCTP_STATE_MASK) == 0,
("sctp_set_state: Can't set substate (new_state = %x)",
new_state));
@ -7394,13 +7399,32 @@ sctp_set_state(struct sctp_tcb *stcb, int new_state)
(new_state == SCTP_STATE_SHUTDOWN_ACK_SENT)) {
SCTP_CLEAR_SUBSTATE(stcb, SCTP_STATE_SHUTDOWN_PENDING);
}
#if defined(KDTRACE_HOOKS)
if (((old_state & SCTP_STATE_MASK) != new_state) &&
!(((old_state & SCTP_STATE_MASK) == SCTP_STATE_EMPTY) &&
(new_state == SCTP_STATE_INUSE))) {
SCTP_PROBE6(state__change, NULL, stcb, NULL, stcb, NULL, old_state);
}
#endif
}
void
sctp_add_substate(struct sctp_tcb *stcb, int substate)
{
#if defined(KDTRACE_HOOKS)
int old_state = stcb->asoc.state;
#endif
KASSERT((substate & SCTP_STATE_MASK) == 0,
("sctp_add_substate: Can't set state (substate = %x)",
substate));
stcb->asoc.state |= substate;
#if defined(KDTRACE_HOOKS)
if (((substate & SCTP_STATE_ABOUT_TO_BE_FREED) &&
((old_state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0)) ||
((substate & SCTP_STATE_SHUTDOWN_PENDING) &&
((old_state & SCTP_STATE_SHUTDOWN_PENDING) == 0))) {
SCTP_PROBE6(state__change, NULL, stcb, NULL, stcb, NULL, old_state);
}
#endif
}