When using SCTP for sending probe packets, use INIT chunks for payloads

larger than or equal to 32 bytes. For smaller probe packets, keep using
SHUTDOWN-ACK chunks, possibly bundled with a PAD chunk.
Packets with INIT chunks more likely pass through firewalls. Therefore,
use them when possible.

MFC after:	1 week
This commit is contained in:
Michael Tuexen 2018-01-27 19:23:42 +00:00
parent 7aee7c6293
commit 51eff8efd9
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=328488
3 changed files with 174 additions and 41 deletions

View file

@ -221,6 +221,7 @@ static const char rcsid[] =
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/sctp.h>
#include <netinet/sctp_header.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/tcpip.h>
@ -1570,25 +1571,70 @@ sctp_prep(struct outdata *outdata)
{
struct sctphdr *const sctp = (struct sctphdr *) outp;
struct sctp_chunkhdr *chk;
struct sctp_init_chunk *init;
struct sctp_paramhdr *param;
sctp->src_port = htons(ident);
sctp->dest_port = htons(port + (fixedPort ? 0 : outdata->seq));
sctp->v_tag = (sctp->src_port << 16) | sctp->dest_port;
sctp->checksum = htonl(0);
if (protlen >=
(int)(sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr))) {
chk = (struct sctp_chunkhdr *)(sctp + 1);
chk->chunk_type = SCTP_SHUTDOWN_ACK;
chk->chunk_flags = 0;
chk->chunk_length = htons(4);
if (protlen >= (int)(sizeof(struct sctphdr) +
sizeof(struct sctp_init_chunk))) {
sctp->v_tag = 0;
} else {
sctp->v_tag = (sctp->src_port << 16) | sctp->dest_port;
}
if (protlen >=
(int)(sizeof(struct sctphdr) + 2 * sizeof(struct sctp_chunkhdr))) {
chk = chk + 1;
chk->chunk_type = SCTP_PAD_CHUNK;
chk->chunk_flags = 0;
chk->chunk_length = htons(protlen -
(sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr)));
sctp->checksum = htonl(0);
if (protlen >= (int)(sizeof(struct sctphdr) +
sizeof(struct sctp_init_chunk))) {
/*
* Send a packet containing an INIT chunk. This works
* better in case of firewalls on the path, but
* results in a probe packet containing at least
* 32 bytes of payload. For shorter payloads, use
* SHUTDOWN-ACK chunks.
*/
init = (struct sctp_init_chunk *)(sctp + 1);
init->ch.chunk_type = SCTP_INITIATION;
init->ch.chunk_flags = 0;
init->ch.chunk_length = htons((u_int16_t)(protlen -
sizeof(struct sctphdr)));
init->init.initiate_tag = (sctp->src_port << 16) |
sctp->dest_port;
init->init.a_rwnd = htonl(1500);
init->init.num_outbound_streams = htons(1);
init->init.num_inbound_streams = htons(1);
init->init.initial_tsn = htonl(0);
if (protlen >= (int)(sizeof(struct sctphdr) +
sizeof(struct sctp_init_chunk) +
sizeof(struct sctp_paramhdr))) {
param = (struct sctp_paramhdr *)(init + 1);
param->param_type = htons(SCTP_PAD);
param->param_length =
htons((u_int16_t)(protlen -
sizeof(struct sctphdr) -
sizeof(struct sctp_init_chunk)));
}
} else {
/*
* Send a packet containing a SHUTDOWN-ACK chunk,
* possibly followed by a PAD chunk.
*/
if (protlen >=
(int)(sizeof(struct sctphdr) +
sizeof(struct sctp_chunkhdr))) {
chk = (struct sctp_chunkhdr *)(sctp + 1);
chk->chunk_type = SCTP_SHUTDOWN_ACK;
chk->chunk_flags = 0;
chk->chunk_length = htons(4);
}
if (protlen >=
(int)(sizeof(struct sctphdr) +
2 * sizeof(struct sctp_chunkhdr))) {
chk = chk + 1;
chk->chunk_type = SCTP_PAD_CHUNK;
chk->chunk_flags = 0;
chk->chunk_length = htons(protlen -
(sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr)));
}
}
if (doipcksum) {
sctp->checksum = sctp_crc32c(sctp, protlen);
@ -1600,10 +1646,20 @@ sctp_check(const u_char *data, int seq)
{
struct sctphdr *const sctp = (struct sctphdr *) data;
return (ntohs(sctp->src_port) == ident
&& ntohs(sctp->dest_port) == port + (fixedPort ? 0 : seq)
&& sctp->v_tag ==
(u_int32_t)((sctp->src_port << 16) | sctp->dest_port));
if (ntohs(sctp->src_port) != ident ||
ntohs(sctp->dest_port) != port + (fixedPort ? 0 : seq))
return (0);
if (protlen < (int)(sizeof(struct sctphdr) +
sizeof(struct sctp_init_chunk))) {
return (sctp->v_tag ==
(u_int32_t)((sctp->src_port << 16) | sctp->dest_port));
} else {
/*
* Don't verify the initiate_tag, since it is not available,
* most of the time.
*/
return (sctp->v_tag == 0);
}
}
void

View file

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 30, 2017
.Dd January 27, 2018
.Dt TRACEROUTE6 8
.Os
.\"
@ -140,6 +140,11 @@ that has no route through it
specifies the source IPv6 address to be used.
.It Fl S
Use SCTP packets for the probes.
The size of probe packets must be a multiple of 4.
If
.Ar datalen
is up to 28, probe packets consist of a SHUTDOWN-ACK chunk possibly bundled
with a PAD chunk. For larger probe packets, an INIT chunk is used.
.It Fl T
Use TCP segments for the probes.
.It Fl U

View file

@ -274,6 +274,7 @@ static const char rcsid[] =
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/sctp.h>
#include <netinet/sctp_header.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
@ -678,6 +679,11 @@ main(int argc, char *argv[])
}
if (useproto == IPPROTO_UDP)
datalen -= sizeof(struct udphdr);
if ((useproto == IPPROTO_SCTP) && (datalen & 3)) {
fprintf(stderr,
"traceroute6: packet size must be a multiple of 4.\n");
exit(1);
}
outpacket = malloc(datalen);
if (!outpacket) {
perror("malloc");
@ -1051,6 +1057,8 @@ send_probe(int seq, u_long hops)
struct icmp6_hdr *icp;
struct sctphdr *sctp;
struct sctp_chunkhdr *chk;
struct sctp_init_chunk *init;
struct sctp_paramhdr *param;
struct tcphdr *tcp;
int i;
@ -1082,23 +1090,64 @@ send_probe(int seq, u_long hops)
sctp->src_port = htons(ident);
sctp->dest_port = htons(port + seq);
sctp->v_tag = (sctp->src_port << 16) | sctp->dest_port;
if (datalen >= (u_long)(sizeof(struct sctphdr) +
sizeof(struct sctp_init_chunk))) {
sctp->v_tag = 0;
} else {
sctp->v_tag = (sctp->src_port << 16) | sctp->dest_port;
}
sctp->checksum = htonl(0);
if (datalen >= (u_long)(sizeof(struct sctphdr) +
sizeof(struct sctp_chunkhdr))) {
chk = (struct sctp_chunkhdr *)(sctp + 1);
chk->chunk_type = SCTP_SHUTDOWN_ACK;
chk->chunk_flags = 0;
chk->chunk_length = htons(4);
}
if (datalen >= (u_long)(sizeof(struct sctphdr) +
2 * sizeof(struct sctp_chunkhdr))) {
chk = chk + 1;
chk->chunk_type = SCTP_PAD_CHUNK;
chk->chunk_flags = 0;
chk->chunk_length = htons((u_int16_t)(datalen -
sizeof(struct sctphdr) -
sizeof(struct sctp_chunkhdr)));
sizeof(struct sctp_init_chunk))) {
/*
* Send a packet containing an INIT chunk. This works
* better in case of firewalls on the path, but
* results in a probe packet containing at least
* 32 bytes of payload. For shorter payloads, use
* SHUTDOWN-ACK chunks.
*/
init = (struct sctp_init_chunk *)(sctp + 1);
init->ch.chunk_type = SCTP_INITIATION;
init->ch.chunk_flags = 0;
init->ch.chunk_length = htons((u_int16_t)(datalen -
sizeof(struct sctphdr)));
init->init.initiate_tag = (sctp->src_port << 16) |
sctp->dest_port;
init->init.a_rwnd = htonl(1500);
init->init.num_outbound_streams = htons(1);
init->init.num_inbound_streams = htons(1);
init->init.initial_tsn = htonl(0);
if (datalen >= (u_long)(sizeof(struct sctphdr) +
sizeof(struct sctp_init_chunk) +
sizeof(struct sctp_paramhdr))) {
param = (struct sctp_paramhdr *)(init + 1);
param->param_type = htons(SCTP_PAD);
param->param_length =
htons((u_int16_t)(datalen -
sizeof(struct sctphdr) -
sizeof(struct sctp_init_chunk)));
}
} else {
/*
* Send a packet containing a SHUTDOWN-ACK chunk,
* possibly followed by a PAD chunk.
*/
if (datalen >= (u_long)(sizeof(struct sctphdr) +
sizeof(struct sctp_chunkhdr))) {
chk = (struct sctp_chunkhdr *)(sctp + 1);
chk->chunk_type = SCTP_SHUTDOWN_ACK;
chk->chunk_flags = 0;
chk->chunk_length = htons(4);
}
if (datalen >= (u_long)(sizeof(struct sctphdr) +
2 * sizeof(struct sctp_chunkhdr))) {
chk = chk + 1;
chk->chunk_type = SCTP_PAD_CHUNK;
chk->chunk_flags = 0;
chk->chunk_length = htons((u_int16_t)(datalen -
sizeof(struct sctphdr) -
sizeof(struct sctp_chunkhdr)));
}
}
sctp->checksum = sctp_crc32c(outpacket, datalen);
break;
@ -1291,6 +1340,7 @@ packet_ok(struct msghdr *mhdr, int cc, int seq)
|| type == ICMP6_DST_UNREACH) {
struct ip6_hdr *hip;
struct icmp6_hdr *icmp;
struct sctp_init_chunk *init;
struct sctphdr *sctp;
struct tcphdr *tcp;
struct udphdr *udp;
@ -1319,12 +1369,34 @@ packet_ok(struct msghdr *mhdr, int cc, int seq)
break;
case IPPROTO_SCTP:
sctp = (struct sctphdr *)up;
if (sctp->src_port == htons(ident) &&
sctp->dest_port == htons(port + seq) &&
sctp->v_tag ==
(u_int32_t)((sctp->src_port << 16) | sctp->dest_port))
return (type == ICMP6_TIME_EXCEEDED ?
-1 : code + 1);
if (sctp->src_port != htons(ident) ||
sctp->dest_port != htons(port + seq)) {
break;
}
if (datalen >= (u_long)(sizeof(struct sctphdr) +
sizeof(struct sctp_init_chunk))) {
if (sctp->v_tag != 0) {
break;
}
init = (struct sctp_init_chunk *)(sctp + 1);
/* Check the initiate tag, if available. */
if ((char *)&init->init.a_rwnd > buf + cc) {
return (type == ICMP6_TIME_EXCEEDED ?
-1 : code + 1);
}
if (init->init.initiate_tag == (u_int32_t)
((sctp->src_port << 16) | sctp->dest_port)) {
return (type == ICMP6_TIME_EXCEEDED ?
-1 : code + 1);
}
} else {
if (sctp->v_tag ==
(u_int32_t)((sctp->src_port << 16) |
sctp->dest_port)) {
return (type == ICMP6_TIME_EXCEEDED ?
-1 : code + 1);
}
}
break;
case IPPROTO_TCP:
tcp = (struct tcphdr *)up;