Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 7 Apr 2018 20:44:30 +0000 (UTC)
From:      Michael Tuexen <tuexen@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r332237 - in stable/11: contrib/traceroute usr.sbin/traceroute6
Message-ID:  <201804072044.w37KiUGm098772@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tuexen
Date: Sat Apr  7 20:44:30 2018
New Revision: 332237
URL: https://svnweb.freebsd.org/changeset/base/332237

Log:
  MFC r328488:
  
  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.

Modified:
  stable/11/contrib/traceroute/traceroute.c
  stable/11/usr.sbin/traceroute6/traceroute6.8
  stable/11/usr.sbin/traceroute6/traceroute6.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/contrib/traceroute/traceroute.c
==============================================================================
--- stable/11/contrib/traceroute/traceroute.c	Sat Apr  7 20:42:06 2018	(r332236)
+++ stable/11/contrib/traceroute/traceroute.c	Sat Apr  7 20:44:30 2018	(r332237)
@@ -220,6 +220,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>
@@ -1489,26 +1490,71 @@ 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;
+	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;
+	}
 	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))) {
+		/*
+		 * 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 (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);
 	}
@@ -1519,10 +1565,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

Modified: stable/11/usr.sbin/traceroute6/traceroute6.8
==============================================================================
--- stable/11/usr.sbin/traceroute6/traceroute6.8	Sat Apr  7 20:42:06 2018	(r332236)
+++ stable/11/usr.sbin/traceroute6/traceroute6.8	Sat Apr  7 20:44:30 2018	(r332237)
@@ -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

Modified: stable/11/usr.sbin/traceroute6/traceroute6.c
==============================================================================
--- stable/11/usr.sbin/traceroute6/traceroute6.c	Sat Apr  7 20:42:06 2018	(r332236)
+++ stable/11/usr.sbin/traceroute6/traceroute6.c	Sat Apr  7 20:44:30 2018	(r332237)
@@ -272,6 +272,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>
 
@@ -676,6 +677,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");
@@ -1049,6 +1055,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;
 
@@ -1080,23 +1088,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;
-		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);
+		    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) +
-		    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;
@@ -1289,6 +1338,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;
@@ -1317,12 +1367,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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201804072044.w37KiUGm098772>