traceroute6: Implement ECN bleaching detection

Explicit Congestion Notification (ECN) is a mechanism that allows
end-to-end notification of network congestion without dropping packets
by explicitly setting the ECN code point (2 bits).

Per RFC 8087, section 3.5, network devices should not be configured to
change the ECN code point in the packets that they forward, except to
set the CE (Congestion Experienced) code point ('11') to signal
incipient congestion.

The current commit adds an -E flag to traceroute6 that crafts a packet
with an ECT(1) code point ('01').

If the packet is received back with a zero ECN code point ('00'), it
outputs that the hop in question erases or "bleaches" the ECN code point
values.  Bleaching may occur for various reasons (including normalizing
packets to hide which equipment supports ECN).  This policy prevents the
use of ECN by applications.

If the packet is received back with an all-ones ECN code point ('11'),
it outputs that the hop in question is experiencing "congestion".

If the packet is received back with a different ECN code point ('10'),
it outputs that the hop in question changes or "mangles" the ECN code
point values.

If the packet is received with the same ECN code point that was sent
('01'), it outputs that the hop has "passed" the ECN bits appropriately.

Inspired by:	Darwin
Reviewed by:	imp, markj
MFC after:	1 month
Pull Request:	https://github.com/freebsd/freebsd-src/pull/879
This commit is contained in:
Jose Luis Duran 2023-10-27 23:59:28 +00:00 committed by Mark Johnston
parent b0e13f785b
commit 0c2218d1d5
2 changed files with 56 additions and 8 deletions

View file

@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd November 25, 2020
.Dd October 25, 2023
.Dt TRACEROUTE6 8
.Os
.\"
@ -38,7 +38,7 @@
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl adIlnNrSTUv
.Op Fl adEIlnNrSTUv
.Ek
.Bk -words
.Op Fl f Ar firsthop
@ -94,6 +94,22 @@ Turn on AS# lookups for each hop encountered.
Turn on AS# lookups and use the given server instead of the default.
.It Fl d
Debug mode.
.It Fl E
Detect ECN bleaching.
Set the
.Em IPTOS_ECN_ECT1
Explicit Congestion Notification (ECN) bits
.Pq Dv 01 ,
and report if the hop has bleached
.Pq Dv 00
or mangled
.Pq Dv 10
them, or if it is experiencing congestion
.Pq Dv 11 .
Otherwise, report that it passed the bits appropriately.
If
.Fl t
is also specified, the corresponding ECN bits will be replaced.
.It Fl f Ar firsthop
Specify how many hops to skip in trace.
.It Fl g Ar gateway

View file

@ -262,6 +262,7 @@
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/sctp.h>
@ -294,7 +295,7 @@ void capdns_open(void);
int get_hoplim(struct msghdr *);
double deltaT(struct timeval *, struct timeval *);
const char *pr_type(int);
int packet_ok(struct msghdr *, int, int, u_char *, u_char *);
int packet_ok(struct msghdr *, int, int, u_char *, u_char *, u_char *);
void print(struct msghdr *, int);
const char *inetname(struct sockaddr *);
u_int32_t sctp_crc32c(void *, u_int32_t);
@ -340,6 +341,7 @@ static int nflag; /* print addresses numerically */
static int useproto = IPPROTO_UDP; /* protocol to use to send packet */
static int lflag; /* print both numerical address & hostname */
static int as_path; /* print as numbers for each hop */
static int ecnflag; /* ECN bleaching detection flag */
static char *as_server = NULL;
static void *asn;
@ -355,7 +357,7 @@ main(int argc, char *argv[])
struct hostent *hp;
size_t size, minlen;
uid_t uid;
u_char type, code;
u_char type, code, ecn;
#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
char ipsec_inpolicy[] = "in bypass";
char ipsec_outpolicy[] = "out bypass";
@ -401,7 +403,7 @@ main(int argc, char *argv[])
seq = 0;
ident = htons(getpid() & 0xffff); /* same as ping6 */
while ((ch = getopt(argc, argv, "aA:df:g:Ilm:nNp:q:rs:St:TUvw:")) != -1)
while ((ch = getopt(argc, argv, "aA:dEf:g:Ilm:nNp:q:rs:St:TUvw:")) != -1)
switch (ch) {
case 'a':
as_path = 1;
@ -413,6 +415,9 @@ main(int argc, char *argv[])
case 'd':
options |= SO_DEBUG;
break;
case 'E':
ecnflag = 1;
break;
case 'f':
ep = NULL;
errno = 0;
@ -584,6 +589,15 @@ main(int argc, char *argv[])
exit(1);
}
if (ecnflag) {
if (tclass != -1) {
tclass &= ~IPTOS_ECN_MASK;
} else {
tclass = 0;
}
tclass |= IPTOS_ECN_ECT1;
}
/* revoke privs */
uid = getuid();
if (setresuid(uid, uid, uid) == -1) {
@ -948,7 +962,7 @@ main(int argc, char *argv[])
send_probe(++seq, hops);
while ((cc = wait_for_reply(rcvsock, &rcvmhdr))) {
(void) gettimeofday(&t2, NULL);
if (packet_ok(&rcvmhdr, cc, seq, &type, &code)) {
if (packet_ok(&rcvmhdr, cc, seq, &type, &code, &ecn)) {
if (!IN6_ARE_ADDR_EQUAL(&Rcv.sin6_addr,
&lastaddr)) {
if (probe > 0)
@ -957,6 +971,22 @@ main(int argc, char *argv[])
lastaddr = Rcv.sin6_addr;
}
printf(" %.3f ms", deltaT(&t1, &t2));
if (ecnflag) {
switch (ecn) {
case IPTOS_ECN_ECT1:
printf(" (ecn=passed)");
break;
case IPTOS_ECN_NOTECT:
printf(" (ecn=bleached)");
break;
case IPTOS_ECN_CE:
printf(" (ecn=congested)");
break;
default:
printf(" (ecn=mangled)");
break;
}
}
if (type == ICMP6_DST_UNREACH) {
switch (code) {
case ICMP6_DST_UNREACH_NOROUTE:
@ -1290,7 +1320,8 @@ pr_type(int t0)
}
int
packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code)
packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code,
u_char *ecn)
{
struct icmp6_hdr *icp;
struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
@ -1373,6 +1404,7 @@ packet_ok(struct msghdr *mhdr, int cc, int seq, u_char *type, u_char *code)
void *up;
hip = (struct ip6_hdr *)(icp + 1);
*ecn = ntohl(hip->ip6_flow & IPV6_ECN_MASK) >> 20;
if ((up = get_uphdr(hip, (u_char *)(buf + cc))) == NULL) {
if (verbose)
warnx("failed to get upper layer header");
@ -1792,7 +1824,7 @@ usage(void)
{
fprintf(stderr,
"usage: traceroute6 [-adIlnNrSTUv] [-A as_server] [-f firsthop] [-g gateway]\n"
"usage: traceroute6 [-adEIlnNrSTUv] [-A as_server] [-f firsthop] [-g gateway]\n"
" [-m hoplimit] [-p port] [-q probes] [-s src] [-w waittime] target\n"
" [datalen]\n");
exit(1);