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 */