Subject: mtr: UDP patch
Package: mtr
Version: 0.72
Severity: wishlist
Tags: patch

Hi,

I wrote a patch for Mtr 0.72 to implement UDP support. You can find it
attached. UDP mode is enabled using the "-u" commandline switch, or by
typing u in the GUI. The patch has been tested on Debian
testing/unstable, both on IPv4 and IPv6.

-- System Information:
Debian Release: lenny/sid
  APT prefers unstable
  APT policy: (600, 'unstable'), (500, 'oldstable'), (500, 'stable')
Architecture: i386 (i686)

Kernel: Linux 2.6.18-4-486
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)
Shell: /bin/sh linked to /bin/bash

Versions of packages mtr depends on:
ii  libc6                     2.7-9          GNU C Library: Shared
libraries ii  libglib1.2                1.2.10-17      The GLib library
of C routines ii  libgtk1.2                 1.2.10-18      The GIMP
Toolkit set of widgets fo ii  libncurses5               5.6+20080308-1
Shared libraries for terminal hand pn  xlibs
<none>         (no description available)

mtr recommends no packages.
diff -Naur mtr-0.72.orig/curses.c mtr-0.72/curses.c
--- mtr-0.72.orig/curses.c	2006-09-29 21:40:09.000000000 +0200
+++ mtr-0.72/curses.c	2008-03-24 17:00:52.000000000 +0100
@@ -75,6 +75,7 @@
 extern int tos;
 extern float WaitTime;
 extern int af;
+extern int mtrtype;
 
 void pwcenter(char *str) 
 {
@@ -242,6 +243,17 @@
     }
     return ActionNone;
   }
+  if (tolower(c) == 'u') {
+    switch ( mtrtype ) {
+    case IPPROTO_ICMP:
+      mtrtype = IPPROTO_UDP;
+      break;
+    case IPPROTO_UDP:
+      mtrtype = IPPROTO_ICMP;
+      break;
+    }
+    return ActionNone;
+  }
   /* reserve to display help message -Min */
   if (tolower(c) == '?'|| tolower(c) == 'h') {
     mvprintw(2, 0, "Command:\n" );
@@ -256,7 +268,8 @@
     printw("  m <n>   set the max time-to-live, default n= # of hops\n" );
     printw("  s <n>   set the packet size to n or random(n<0)\n" );
     printw("  b <c>   set ping bit pattern to c(0..255) or random(c<0)\n" );
-    printw("  Q <t>   set ping packet's TOS to t\n\n\n" );
+    printw("  Q <t>   set ping packet's TOS to t\n" );
+    printw("  u       switch between ICMP ECHO and UDP datagrams\n\n" );
     mvprintw(16, 0, " press any key to go back..." );
 
     getch();                  /* get any key */
diff -Naur mtr-0.72.orig/mtr.8 mtr-0.72/mtr.8
--- mtr-0.72.orig/mtr.8	2006-09-29 21:33:06.000000000 +0200
+++ mtr-0.72/mtr.8	2008-03-24 17:00:52.000000000 +0100
@@ -8,7 +8,7 @@
 .SH SYNOPSIS
 .B mtr 
 [\c
-.B \-hvrctglspni46\c
+.B \-hvrctglspniu46\c
 ]
 [\c
 .B \-\-help\c
@@ -208,6 +208,11 @@
 ECHO requests.  The default value for this parameter is one second.
 
 .TP
+.B \-u
+.br
+Use UDP datagrams instead of ICMP ECHO.
+
+.TP
 .B \-4
 .br
 Use IPv4 only.
diff -Naur mtr-0.72.orig/mtr.c mtr-0.72/mtr.c
--- mtr-0.72.orig/mtr.c	2006-09-29 21:38:49.000000000 +0200
+++ mtr-0.72/mtr.c	2008-03-24 17:00:52.000000000 +0100
@@ -65,6 +65,7 @@
 int   bitpattern = 0;
 int   tos = 0;
 int af = DEFAULT_AF;
+int mtrtype = IPPROTO_ICMP;     /* Use ICMP as default packet type */
 
                                 /* begin ttl windows addByMin */
 int  fstTTL = 1;                /* default start at first hop */
@@ -143,6 +144,7 @@
     { "address", 1, 0, 'a' },
     { "first-ttl", 1, 0, 'f' },	/* -f & -m are borrowed from traceroute */
     { "max-ttl", 1, 0, 'm' },
+    { "udp", 0, 0, 'u' },	/* UDP (default is ICMP) */
     { "inet", 0, 0, '4' },	/* IPv4 only */
     { "inet6", 0, 0, '6' },	/* IPv6 only */
     { 0, 0, 0, 0 }
@@ -152,7 +154,7 @@
   while(1) {
     /* added f:m:o: byMin */
     opt = getopt_long(argc, argv,
-		      "vhrxtglpo:i:c:s:b:Q:na:f:m:46", long_options, NULL);
+		      "vhrxtglpo:i:c:s:b:Q:na:f:m:u46", long_options, NULL);
     if(opt == -1)
       break;
 
@@ -253,6 +255,9 @@
 	tos = 0;
       }
       break;
+    case 'u':
+      mtrtype = IPPROTO_UDP;
+      break;
     case '4':
       af = AF_INET;
       break;
@@ -354,13 +359,19 @@
 
   parse_arg (argc, argv);
 
+  /* Now that we know mtrtype we can select which socket to use */
+  if (net_selectsocket() != 0) {
+    fprintf( stderr, "mtr: Couldn't determine raw socket type.\n" );
+    exit( EXIT_FAILURE );
+  }
+
   if (PrintVersion) {
     printf ("mtr " VERSION "\n");
     exit(0);
   }
 
   if (PrintHelp) {
-    printf("usage: %s [-hvrctglspni46] [--help] [--version] [--report]\n"
+    printf("usage: %s [-hvrctglspniu46] [--help] [--version] [--report]\n"
 	   "\t\t[--report-cycles=COUNT] [--curses] [--gtk]\n"
            "\t\t[--raw] [--split] [--no-dns] [--address interface]\n" /* BL */
            "\t\t[--psize=bytes/-s bytes]\n"            /* ok */
diff -Naur mtr-0.72.orig/net.c mtr-0.72/net.c
--- mtr-0.72.orig/net.c	2006-09-29 21:31:01.000000000 +0200
+++ mtr-0.72/net.c	2008-03-24 17:05:10.000000000 +0100
@@ -54,6 +54,22 @@
   uint16 sequence;
 };
 
+/* Structure of an UDP header.  */
+struct UDPHeader {
+  uint16 srcport;
+  uint16 dstport;
+  uint16 length;
+  uint16 checksum;
+};
+
+/* Structure of an IPv4 UDP pseudoheader.  */
+struct UDPv4PHeader {
+  uint32 saddr;
+  uint32 daddr;
+  uint8 zero;
+  uint8 protocol;
+  uint16 len;
+};
 
 /*  Structure of an IP header.  */
 struct IPHeader {
@@ -77,6 +93,7 @@
 #define ICMP_TSTAMPREPLY	14
 
 #define ICMP_TIME_EXCEEDED	11
+#define ICMP_UNREACHABLE        3
 
 #ifndef SOL_IP
 #define SOL_IP 0
@@ -131,8 +148,12 @@
 
 int    timestamp;
 int    sendsock4;
+int    sendsock4_icmp;
+int    sendsock4_udp;
 int    recvsock4;
 int    sendsock6;
+int    sendsock6_icmp;
+int    sendsock6_udp;
 int    recvsock6;
 int    sendsock;
 int    recvsock;
@@ -175,7 +196,7 @@
 extern int bitpattern;		/* packet bit pattern used by ping */
 extern int tos;			/* type of service set in ping packet*/
 extern int af;			/* address family of remote target */
-
+extern int mtrtype;		/* type of query packet used */
 
 /* return the number of microseconds to wait before sending the next
    ping */
@@ -206,14 +227,40 @@
 }
 
 
+/* Prepend pseudoheader to the udp datagram and calculate checksum */
+int udp_checksum(void *pheader, void *udata, int psize, int dsize)
+{
+  unsigned int tsize = psize + dsize;
+  char csumpacket[tsize];
+  memset(csumpacket, (unsigned char) abs(bitpattern), abs(tsize));
+
+  struct UDPv4PHeader *prepend = (struct UDPv4PHeader *) csumpacket;
+  struct UDPv4PHeader *udppheader = (struct UDPv4PHeader *) pheader;
+  prepend->saddr = udppheader->saddr;
+  prepend->daddr = udppheader->daddr;
+  prepend->zero = 0;
+  prepend->protocol = udppheader->protocol;
+  prepend->len = udppheader->len;
+
+  struct UDPHeader *content = (struct UDPHeader *)(csumpacket + psize);
+  struct UDPHeader *udpdata = (struct UDPHeader *) udata;
+  content->srcport = udpdata->srcport;
+  content->dstport = udpdata->dstport;
+  content->length = udpdata->length;
+  content->checksum = udpdata->checksum;
+
+  return checksum(csumpacket,tsize);
+}
+
+
 int new_sequence(int index) 
 {
-  static int next_sequence = 0;
+  static int next_sequence = MinSequence;
   int seq;
 
   seq = next_sequence++;
   if (next_sequence >= MaxSequence)
-    next_sequence = 0;
+    next_sequence = MinSequence;
 
   sequence[seq].index = index;
   sequence[seq].transit = 1;
@@ -236,15 +283,21 @@
   /*ok  char packet[sizeof(struct IPHeader) + sizeof(struct ICMPHeader)];*/
   char packet[MAXPACKET];
   struct IPHeader *ip = (struct IPHeader *) packet;
-  struct ICMPHeader *icmp;
+  struct ICMPHeader *icmp = NULL;
+  struct UDPHeader *udp = NULL;
+  struct UDPv4PHeader *udpp = NULL;
+  uint16 mypid;
 
   /*ok  int packetsize = sizeof(struct IPHeader) + sizeof(struct ICMPHeader) + datasize;*/
   int rv;
   static int first=1;
-  int ttl, iphsize = 0, echotype = 0, salen = 0;
+  int ttl, iphsize = 0, echotype = 0, salen = 0, udphsize = 0;
 
   ttl = index + 1;
 
+  /* offset for ipv6 checksum calculation */
+  int offset = 6;
+
   if ( packetsize < MINPACKET ) packetsize = MINPACKET;
   if ( packetsize > MAXPACKET ) packetsize = MAXPACKET;
 
@@ -271,7 +324,7 @@
   ip->id = 0;
   ip->frag = 0;    /* 1, if want to find mtu size? Min */
     ip->ttl = ttl;
-  ip->protocol = IPPROTO_ICMP;
+  ip->protocol = mtrtype;
   ip->check = 0;
 
   /* BSD needs the source address here, Linux & others do not... */
@@ -295,22 +348,71 @@
 #endif
   }
 
-  icmp = (struct ICMPHeader *)(packet + iphsize);
-  icmp->type     = echotype;
-  icmp->code     = 0;
-  icmp->checksum = 0;
-  icmp->id       = getpid();
-  icmp->sequence = new_sequence(index);
-  icmp->checksum = checksum(icmp, abs(packetsize) - iphsize);
+  switch ( mtrtype ) {
+  case IPPROTO_ICMP:
+    icmp = (struct ICMPHeader *)(packet + iphsize);
+    icmp->type     = echotype;
+    icmp->code     = 0;
+    icmp->checksum = 0;
+    icmp->id       = getpid();
+    icmp->sequence = new_sequence(index);
+    icmp->checksum = checksum(icmp, abs(packetsize) - iphsize);
+    
+    gettimeofday(&sequence[icmp->sequence].time, NULL);
+    break;
+
+  case IPPROTO_UDP:
+    udp = (struct UDPHeader *)(packet + iphsize);
+    udphsize = sizeof (struct UDPHeader);
+    udp->checksum  = 0;
+    mypid = (uint16)getpid();
+    if (mypid < MinPort)
+      mypid += MinPort;
+
+    udp->srcport = htons(mypid);
+    udp->length = abs(packetsize) - iphsize;
+    if(!BSDfix)
+      udp->length = htons(udp->length);
+ 
+    udp->dstport = new_sequence(index);
+    gettimeofday(&sequence[udp->dstport].time, NULL);
+    udp->dstport = htons(udp->dstport);
+    break;
+  }
 
   switch ( af ) {
   case AF_INET:
+    switch ( mtrtype ) {
+    case IPPROTO_UDP:
+      /* checksum is not mandatory. only calculate if we know ip->saddr */
+      if (ip->saddr) {
+        udpp = (struct UDPv4PHeader *)(malloc(sizeof(struct UDPv4PHeader)));
+        udpp->saddr = ip->saddr;
+        udpp->daddr = ip->daddr;
+        udpp->protocol = ip->protocol;
+        udpp->len = udp->length;
+        udp->checksum = udp_checksum(udpp, udp, sizeof(struct UDPv4PHeader), abs(packetsize) - iphsize);
+      }
+      break;
+    }
+
     ip->check = checksum(packet, abs(packetsize));
     break;
+#ifdef ENABLE_IPV6
+  case AF_INET6:
+    switch ( mtrtype ) {
+    case IPPROTO_UDP:
+      /* kernel checksum calculation */
+      if ( setsockopt(sendsock, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset)) ) {
+        perror( "setsockopt IPV6_CHECKSUM" );
+        exit( EXIT_FAILURE);
+      }
+      break;
+    }
+    break;
+#endif
   }
 
-  gettimeofday(&sequence[icmp->sequence].time, NULL);
-
   rv = sendto(sendsock, packet, abs(packetsize), 0, 
 	      remotesockaddr, salen);
   if (first && (rv < 0) && ((errno == EINVAL) || (errno == EMSGSIZE))) {
@@ -450,9 +552,11 @@
   socklen_t fromsockaddrsize;
   int num;
   struct ICMPHeader *header = NULL;
+  struct UDPHeader *udpheader = NULL;
   struct timeval now;
   ip_t * fromaddress = NULL;
-  int echoreplytype = 0, timeexceededtype = 0;
+  int echoreplytype = 0, timeexceededtype = 0, unreachabletype = 0;
+  int sequence = 0;
 
   gettimeofday(&now, NULL);
   switch ( af ) {
@@ -461,6 +565,7 @@
     fromaddress = (ip_t *) &(fsa4->sin_addr);
     echoreplytype = ICMP_ECHOREPLY;
     timeexceededtype = ICMP_TIME_EXCEEDED;
+    unreachabletype = ICMP_UNREACHABLE;
     break;
 #ifdef ENABLE_IPV6
   case AF_INET6:
@@ -468,6 +573,7 @@
     fromaddress = (ip_t *) &(fsa6->sin6_addr);
     echoreplytype = ICMP6_ECHO_REPLY;
     timeexceededtype = ICMP6_TIME_EXCEEDED;
+    unreachabletype = ICMP6_DST_UNREACH;
     break;
 #endif
   }
@@ -490,41 +596,78 @@
     break;
 #endif
   }
-  if (header->type == echoreplytype) {
-    if(header->id != (uint16)getpid())
-      return;
 
-    net_process_ping (header->sequence, (void *) fromaddress, now);
-  } else if (header->type == timeexceededtype) {
-    switch ( af ) {
-    case AF_INET:
-
-      if ((size_t) num < sizeof(struct IPHeader) + 
-                         sizeof(struct ICMPHeader) + 
-                         sizeof (struct IPHeader) + 
-                         sizeof (struct ICMPHeader))
+  switch ( mtrtype ) {
+  case IPPROTO_ICMP:
+    if (header->type == echoreplytype) {
+      if(header->id != (uint16)getpid())
         return;
-      header = (struct ICMPHeader *)(packet + sizeof (struct IPHeader) + 
-                                              sizeof (struct ICMPHeader) + 
-                                              sizeof (struct IPHeader));
-    break;
+
+      sequence = header->sequence;
+    } else if (header->type == timeexceededtype) {
+      switch ( af ) {
+      case AF_INET:
+
+        if ((size_t) num < sizeof(struct IPHeader) + 
+                           sizeof(struct ICMPHeader) + 
+                           sizeof (struct IPHeader) + 
+                           sizeof (struct ICMPHeader))
+          return;
+        header = (struct ICMPHeader *)(packet + sizeof (struct IPHeader) + 
+                                                sizeof (struct ICMPHeader) + 
+                                                sizeof (struct IPHeader));
+      break;
 #ifdef ENABLE_IPV6
-    case AF_INET6:
-      if ( num < sizeof (struct ICMPHeader) + 
-                 sizeof (struct ip6_hdr) + sizeof (struct ICMPHeader) )
+      case AF_INET6:
+        if ( num < sizeof (struct ICMPHeader) + 
+                   sizeof (struct ip6_hdr) + sizeof (struct ICMPHeader) )
+          return;
+        header = (struct ICMPHeader *) ( packet + 
+                                         sizeof (struct ICMPHeader) +
+                                         sizeof (struct ip6_hdr) );
+        break;
+#endif
+      }
+  
+      if (header->id != (uint16)getpid())
         return;
-      header = (struct ICMPHeader *) ( packet + 
-                                       sizeof (struct ICMPHeader) +
-                                       sizeof (struct ip6_hdr) );
+  
+      sequence = header->sequence;
+    }
+    break;
+  
+  case IPPROTO_UDP:
+    if (header->type == timeexceededtype || header->type == unreachabletype) {
+      switch ( af ) {
+      case AF_INET:
+
+        if ((size_t) num < sizeof(struct IPHeader) +
+                           sizeof(struct ICMPHeader) +
+                           sizeof (struct IPHeader) +
+                           sizeof (struct UDPHeader))
+          return;
+        udpheader = (struct UDPHeader *)(packet + sizeof (struct IPHeader) +
+                                                  sizeof (struct ICMPHeader) +
+                                                  sizeof (struct IPHeader));
       break;
+#ifdef ENABLE_IPV6
+      case AF_INET6:
+        if ( num < sizeof (struct ICMPHeader) +
+                   sizeof (struct ip6_hdr) + sizeof (struct UDPHeader) )
+          return;
+        udpheader = (struct UDPHeader *) ( packet +
+                                           sizeof (struct ICMPHeader) +
+                                           sizeof (struct ip6_hdr) );
+        break;
 #endif
+      }
+      sequence = ntohs(udpheader->dstport);
     }
-
-    if (header->id != (uint16)getpid())
-      return;
-
-    net_process_ping(header->sequence, (void *)fromaddress, now);
+    break;
   }
+
+  if (sequence)
+    net_process_ping(sequence, (void *)fromaddress, now);
 }
 
 
@@ -758,14 +901,16 @@
   int trueopt = 1;
 
 #if !defined(IP_HDRINCL) && defined(IP_TOS) && defined(IP_TTL)
-  sendsock4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+  sendsock4_icmp = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+  sendsock4_udp = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
 #else
   sendsock4 = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
 #endif
   if (sendsock4 < 0) 
     return -1;
 #ifdef ENABLE_IPV6
-  sendsock6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+  sendsock6_icmp = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+  sendsock6_udp = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
 #endif
 
 #ifdef IP_HDRINCL
@@ -787,7 +932,38 @@
   return 0;
 }
 
- 
+
+int net_selectsocket(void)
+{
+#if !defined(IP_HDRINCL) && defined(IP_TOS) && defined(IP_TTL)
+  switch ( mtrtype ) {
+  case IPPROTO_ICMP:
+    sendsock4 = sendsock4_icmp;
+    break;
+  case IPPROTO_UDP:
+    sendsock4 = sendsock4_udp;
+    break;
+  }
+#endif
+  if (sendsock4 < 0)
+    return -1;
+#ifdef ENABLE_IPV6
+  switch ( mtrtype ) {
+  case IPPROTO_ICMP:
+    sendsock6 = sendsock6_icmp;
+    break;
+  case IPPROTO_UDP:
+    sendsock6 = sendsock6_udp;
+    break;
+  }
+  if (sendsock6 < 0)
+    return -1;
+#endif
+
+ return 0;
+}
+
+
 int net_open(struct hostent * host) 
 {
 #ifdef ENABLE_IPV6
@@ -946,9 +1122,15 @@
 
 void net_close(void)
 {
-  if (sendsock4 >= 0) close(sendsock4);
+  if (sendsock4 >= 0) {
+    close(sendsock4_icmp);
+    close(sendsock4_udp);
+  }
   if (recvsock4 >= 0) close(recvsock4);
-  if (sendsock6 >= 0) close(sendsock6);
+  if (sendsock6 >= 0) {
+    close(sendsock6_icmp);
+    close(sendsock6_udp);
+  }
   if (recvsock6 >= 0) close(recvsock6);
 }
 
diff -Naur mtr-0.72.orig/net.h mtr-0.72/net.h
--- mtr-0.72.orig/net.h	2006-03-23 06:59:27.000000000 +0100
+++ mtr-0.72/net.h	2008-03-24 17:00:52.000000000 +0100
@@ -28,6 +28,7 @@
 #endif
 
 int net_preopen(void);
+int net_selectsocket(void);
 int net_open(struct hostent *host);
 void net_reopen(struct hostent *address);
 int net_set_interfaceaddress (char *InterfaceAddress); 
@@ -80,10 +81,12 @@
 
 #define MAXPATH 8
 #define MaxHost 256
+#define MinSequence 33000
 #define MaxSequence 65536
+#define MinPort 1024
 
-#define MAXPACKET 4470		/* largest test ICMP packet size */
-#define MINPACKET 28		/* 20 bytes IP header and 8 bytes ICMP */
+#define MAXPACKET 4470		/* largest test packet size */
+#define MINPACKET 28		/* 20 bytes IP header and 8 bytes ICMP or UDP */
 
 /* stuff used by display such as report, curses... --Min */
 #define MAXFLD 20		/* max stats fields to display */

Reply via email to