Subject: bin/22523: traceroute icmp extensions patch
To: None <gnats-bugs@gnats.netbsd.org>
From: None <ww@parc.styx.org>
List: netbsd-bugs
Date: 08/11/2003 05:51:45
>Number:         22523
>Category:       bin
>Synopsis:       patch for traceroute to show mpls icmp extensions
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Aug 18 03:29:00 UTC 2003
>Closed-Date:
>Last-Modified:
>Originator:     
>Release:        NetBSD 1.6
>Organization:
>Environment:
System: NetBSD styx.org 1.6 NetBSD 1.6 (STYX) #1: Thu Sep 26 04:10:32 EDT 2002 root@styx.org:/usr/src/sys/arch/macppc/compile/STYX macppc
Architecture: powerpc
Machine: macppc
>Description:
	Patch from Jesper Skriver to make traceroute show mpls labels
	if it recieves icmp packets with extensions defined in 

	http://www.watersprings.org/links/mlr/id/draft-ietf-mpls-icmp-01.txt

	Original patch is http://e.wheel.dk/~jesper/traceroute.diff

 	modified slightly to make it apply cleanly to NetBSD source.
>How-To-Repeat:
>Fix:
Common subdirectories: /usr/src/usr.sbin/traceroute/CVS and traceroute/CVS
diff -u /usr/src/usr.sbin/traceroute/traceroute.c traceroute/traceroute.c
--- /usr/src/usr.sbin/traceroute/traceroute.c	Fri Jan 11 16:42:58 2002
+++ traceroute/traceroute.c	Mon Aug 11 05:41:57 2003
@@ -272,6 +272,55 @@
 	struct timeval tv;	/* time packet left */
 };
 
+/*
+ * Support for ICMP extensions
+ *
+ * http://www.ietf.org/proceedings/01aug/I-D/draft-ietf-mpls-icmp-02.txt
+ */
+#define ICMP_EXT_OFFSET    8 /* ICMP type, code, checksum, unused */ + \
+                         128 /* original datagram */
+#define ICMP_EXT_VERSION 2
+/*
+ * ICMP extensions, common header
+ */
+struct icmp_ext_cmn_hdr {
+#if BYTE_ORDER == BIG_ENDIAN
+       u_char   version:4;
+       u_char   reserved1:4;
+#else
+       u_char   reserved1:4;
+       u_char   version:4;
+#endif
+       u_char   reserved2;
+       u_short  checksum;
+};
+
+/*
+ * ICMP extensions, object header
+ */
+struct icmp_ext_obj_hdr {
+    u_short length;
+    u_char  class_num;
+#define MPLS_STACK_ENTRY_CLASS 1
+    u_char  c_type;
+#define MPLS_STACK_ENTRY_C_TYPE 1
+};
+
+struct mpls_header {
+#if BYTE_ORDER == BIG_ENDIAN
+    u_int32_t label:20;
+    u_char  exp:3;
+    u_char  s:1;
+    u_char  ttl:8;
+#else
+    u_char  ttl:8;
+    u_char  s:1;
+    u_char  exp:3;
+    u_int32_t label:20;
+#endif
+};
+
+
 u_char	packet[512];		/* last inbound (icmp) packet */
 
 struct ip *outip;		/* last output (udp) packet */
@@ -373,6 +422,7 @@
 int	wait_for_reply(int, struct sockaddr_in *, struct timeval *);
 void	frag_err(void);
 int	find_local_ip(struct sockaddr_in *, struct sockaddr_in *);
+void  decode_extensions(u_char *buf, int ip_len);
 #ifdef IPSEC
 #ifdef IPSEC_POLICY_IPSEC
 int	setpolicy(int so, char *policy);
@@ -1014,6 +1064,8 @@
 			}
 			if (cc == 0)
 				Printf(" *");
+			if (cc && probe == nprobes-1)
+			    decode_extensions(packet, cc);
 			(void)fflush(stdout);
 		}
 		putchar('\n');
@@ -1026,6 +1078,118 @@
 		as_shutdown(asn);
 
 	exit(0);
+}
+
+void
+decode_extensions(u_char *buf, int ip_len)
+{
+	struct icmp_ext_cmn_hdr *cmn_hdr;
+	struct icmp_ext_obj_hdr *obj_hdr;
+	struct mpls_header *mpls;
+	int datalen, obj_len;
+	u_int32_t mpls_h;
+	struct ip *ip;
+
+	ip = (struct ip *)buf;
+
+	if (ip_len <= sizeof(struct ip) + ICMP_EXT_OFFSET) {
+	    /*
+	     * No support for ICMP extensions on this host
+	     */
+	    return;
+	}
+
+	/*
+	 * Move forward to the start of the ICMP extensions, if present
+	 */
+	buf += (ip->ip_hl << 2) + ICMP_EXT_OFFSET;
+	cmn_hdr = (struct icmp_ext_cmn_hdr *)buf;
+
+	if (cmn_hdr->version != ICMP_EXT_VERSION) {
+	    /*
+	     * Unknown version
+	     */
+	    return;
+	}
+
+	datalen = ip_len - ((u_char *)cmn_hdr - (u_char *)ip);
+
+	/*
+	 * Check the checksum, cmn_hdr->checksum == 0 means no checksum'ing
+	 * done by sender.
+	 *
+	 * If the checksum is ok, we'll get 0, as the checksum is calculated
+	 * with	the checksum field being 0'd.
+	 */
+	if (ntohs(cmn_hdr->checksum) &&
+	    in_cksum((u_short *)cmn_hdr, datalen)) {
+
+	    return;
+	}
+
+	buf += sizeof(*cmn_hdr);
+	datalen -= sizeof(*cmn_hdr);
+
+	while (datalen > 0) {
+	    obj_hdr = (struct icmp_ext_obj_hdr *)buf;
+	    obj_len = ntohs(obj_hdr->length);
+
+	    /*
+	     * Sanity check the length field
+	     */
+	    if (obj_len > datalen) {
+		return;
+	    }
+
+	    datalen -= obj_len;
+
+	    /*
+	     * Move past the object header
+	     */
+	    buf += sizeof(struct icmp_ext_obj_hdr);
+	    obj_len -= sizeof(struct icmp_ext_obj_hdr);
+
+	    switch (obj_hdr->class_num) {
+	    case MPLS_STACK_ENTRY_CLASS:
+		switch (obj_hdr->c_type) {
+		case MPLS_STACK_ENTRY_C_TYPE:
+		    while (obj_len >= sizeof(u_int32_t)) {
+			mpls_h = ntohl(*(u_int32_t *)buf);
+
+			buf += sizeof(u_int32_t);
+			obj_len -= sizeof(u_int32_t);
+
+			mpls = (struct mpls_header *) &mpls_h;
+			printf("\n     MPLS Label=%d CoS=%d TTL=%d S=%d",
+			       mpls->label, mpls->exp, mpls->ttl, mpls->s);
+		    }
+		    if (obj_len > 0) {
+			/*
+			 * Something went wrong, and we're at a unknown offset
+			 * into the packet, ditch the rest of it.
+			 */
+			return;
+		    }
+		    break;
+		default:
+		    /*
+		     * Unknown object, skip past it
+		     */
+		    buf += ntohs(obj_hdr->length) -
+			sizeof(struct icmp_ext_obj_hdr);
+		    break;
+		}
+		break;
+
+	    default:
+		/*
+		 * Unknown object, skip past it
+		 */
+		buf += ntohs(obj_hdr->length) -
+		    sizeof(struct icmp_ext_obj_hdr);
+		break;
+	    }
+	}
 }
 
 int
>Release-Note:
>Audit-Trail:
>Unformatted: