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:
parent
7aee7c6293
commit
51eff8efd9
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user