diff --git a/contrib/traceroute/traceroute.c b/contrib/traceroute/traceroute.c index c74065a08836..0df5723ef17d 100644 --- a/contrib/traceroute/traceroute.c +++ b/contrib/traceroute/traceroute.c @@ -221,6 +221,7 @@ static const char rcsid[] = #include #include #include +#include #include #include #include @@ -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 diff --git a/usr.sbin/traceroute6/traceroute6.8 b/usr.sbin/traceroute6/traceroute6.8 index 4aa51553b757..84aa263d46af 100644 --- a/usr.sbin/traceroute6/traceroute6.8 +++ b/usr.sbin/traceroute6/traceroute6.8 @@ -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 diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c index 9cdfdcd4e97b..4fe91735fea2 100644 --- a/usr.sbin/traceroute6/traceroute6.c +++ b/usr.sbin/traceroute6/traceroute6.c @@ -274,6 +274,7 @@ static const char rcsid[] = #include #include #include +#include #include #include @@ -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;