From c64d8cd5d52eeff47dfaab62c3da19ad7b1c9234 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Mon, 29 Dec 2008 19:37:17 +0000 Subject: [PATCH] pathmtu: New program for determining the MTU to a host. Organization: Straylight/Edgeware From: Mark Wooding Eventually, this will be used automatically when configuring network interfaces. --- Makefile.am | 10 +- configure.ac | 14 +++ debian/.gitignore | 2 + debian/control | 8 ++ debian/pathmtu.copyright | 16 +++ debian/pathmtu.install | 2 + pathmtu/Makefile.am | 45 +++++++ pathmtu/pathmtu.1.in | 112 ++++++++++++++++++ pathmtu/pathmtu.c | 247 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 debian/pathmtu.copyright create mode 100644 debian/pathmtu.install create mode 100644 pathmtu/Makefile.am create mode 100644 pathmtu/pathmtu.1.in create mode 100644 pathmtu/pathmtu.c diff --git a/Makefile.am b/Makefile.am index e9d3febb..89262790 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,6 +39,11 @@ SUBDIRS += proxy SUBDIRS += pkstream SUBDIRS += init +## Path MTU discovery. +if PATHMTU +SUBDIRS += pathmtu +endif + ## Wireshark. if HAVE_WIRESHARK SUBDIRS += wireshark @@ -104,6 +109,10 @@ EXTRA_DIST += debian/copyright EXTRA_DIST += debian/pkstream.copyright EXTRA_DIST += debian/pkstream.install +## pathmtu +EXTRA_DIST += debian/pathmtu.copyright +EXTRA_DIST += debian/pathmtu.install + ## server and client EXTRA_DIST += debian/tripe.README EXTRA_DIST += debian/tripe.dirs @@ -123,5 +132,4 @@ EXTRA_DIST += debian/tripemon.install ## wireshark EXTRA_DIST += debian/tripe-wireshark.install - ###----- That's all, folks -------------------------------------------------- diff --git a/configure.ac b/configure.ac index aa775b7b..674116e3 100644 --- a/configure.ac +++ b/configure.ac @@ -106,6 +106,19 @@ AC_ARG_WITH([tracing], AC_DEFINE([NTRACE], [1], [Disable all tracing.])], [:]) +dnl-------------------------------------------------------------------------- +dnl Path MTU discovery. + +case $host_os in + linux*) + pmtu=yes + ;; + *) + pmtu=no + ;; +esac +AM_CONDITIONAL([PATHMTU], [test $pmtu = yes]) + dnl-------------------------------------------------------------------------- dnl Tunnel devices. @@ -290,6 +303,7 @@ AC_CONFIG_FILES( [Makefile] [common/Makefile] [uslip/Makefile] + [pathmtu/Makefile] [client/Makefile] [server/Makefile] [proxy/Makefile] diff --git a/debian/.gitignore b/debian/.gitignore index 87f0ea24..a993e57d 100644 --- a/debian/.gitignore +++ b/debian/.gitignore @@ -10,6 +10,7 @@ compat ## Individual packages pkstream +pathmtu tripe tripe.default tripe.init @@ -18,3 +19,4 @@ tripemon tripe-keys tripe-ethereal tripe-uslip + diff --git a/debian/control b/debian/control index d59c2629..61ed0f6b 100644 --- a/debian/control +++ b/debian/control @@ -29,6 +29,14 @@ Description: Forward UDP packets over a stream packets on standard input and output; it also natively understands TCP sockets. Anything else can probably be fudged up with a port forwarder. +Package: pathmtu +Architecture: any +Depends: ${shlibs:Depends} +Description: Discover the path MTU to a given host + Pathmtu is a simple utility which prints the path MTU to a given host, i.e., + the size of the largest IP packet which can be sent to the host without + needing to be fragmented. + Package: tripe-uslip Architecture: any Depends: ${shlibs:Depends} diff --git a/debian/pathmtu.copyright b/debian/pathmtu.copyright new file mode 100644 index 00000000..dcb9f2a9 --- /dev/null +++ b/debian/pathmtu.copyright @@ -0,0 +1,16 @@ +Pathmtu is copyright (c) 2003 Straylight/Edgeware. + +Pathmtu is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +Pathmtu is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have a copy of the GNU General Public License in +/usr/share/common-licenses/GPL; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. diff --git a/debian/pathmtu.install b/debian/pathmtu.install new file mode 100644 index 00000000..e568fbf3 --- /dev/null +++ b/debian/pathmtu.install @@ -0,0 +1,2 @@ +debian/tmp/usr/bin/pathmtu +debian/tmp/usr/share/man/man1/pathmtu.1 diff --git a/pathmtu/Makefile.am b/pathmtu/Makefile.am new file mode 100644 index 00000000..06af0530 --- /dev/null +++ b/pathmtu/Makefile.am @@ -0,0 +1,45 @@ +### -*-makefile-*- +### +### Build script for pathmtu +### +### (c) 2008 Straylight/Edgeware +### + +###----- Licensing notice --------------------------------------------------- +### +### This file is part of Trivial IP Encryption (TrIPE). +### +### TrIPE is free software; you can redistribute it and/or modify +### it under the terms of the GNU General Public License as published by +### the Free Software Foundation; either version 2 of the License, or +### (at your option) any later version. +### +### TrIPE is distributed in the hope that it will be useful, +### but WITHOUT ANY WARRANTY; without even the implied warranty of +### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +### GNU General Public License for more details. +### +### You should have received a copy of the GNU General Public License +### along with TrIPE; if not, write to the Free Software Foundation, +### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +include $(top_srcdir)/vars.am + +bin_PROGRAMS = +man_MANS = + +LDADD = $(mLib_LIBS) + +###-------------------------------------------------------------------------- +### Path MTU discovery. + +## The program. +bin_PROGRAMS += pathmtu +pathmtu_SOURCES = pathmtu.c + +## Docuemntation. +man_MANS += pathmtu.1 +CLEANFILES += pathmtu.1 +EXTRA_DIST += pathmtu.1.in + +###----- That's all, folks -------------------------------------------------- diff --git a/pathmtu/pathmtu.1.in b/pathmtu/pathmtu.1.in new file mode 100644 index 00000000..364526bc --- /dev/null +++ b/pathmtu/pathmtu.1.in @@ -0,0 +1,112 @@ +.\" -*-nroff-*- +.\" +.\" Documentation for pathmtu +.\" +.\" (c) 2008 Straylight/Edgeware. +.\" + +.\"----- Licensing notice --------------------------------------------------- +.\" +.\" This file is part of Trivial IP Encryption (TrIPE). +.\" +.\" TrIPE is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" TrIPE is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with TrIPE; if not, write to the Free Software Foundation, +.\" Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +. +.\"-------------------------------------------------------------------------- +.so ../defs.man.in \" @@@PRE@@@ +. +.\"-------------------------------------------------------------------------- +.TH pathmtu 1 "29 December 2008" "Straylight/Edgeware" "TrIPE: Trivial IP Encryption" +. +.\"-------------------------------------------------------------------------- +.SH "NAME" +. +pathmtu \- discover path MTU to a given host +. +.\"-------------------------------------------------------------------------- +.SH "SYNOPSIS" +. +.B pathmtu +.RB [ \-t +.IR timeout ] +.RB [ \-H +.IR header ] +.I host +.RI [ port ] +. +.\"-------------------------------------------------------------------------- +.SH "DESCRIPTION" +. +The +.B pathmtu +program discovers the size of the largest IP packet which can be sent to +a given +.I host +(specified as a dotted-quad IP address or host name) without being +fragmented. This is useful information, particularly when setting up +VPN tunnel interfaces. +.PP +The program works by sending UDP packets and finding out whether they +get fragmented. The packets are sent to a specified +.I port +(specified as a number or service name) on the destination host. The +destination does not need to be listening on the given port \(en indeed, +it doesn't matter if the port is firewalled. The default port is 7 +(echo), chosen because if it is active, we'll get an answer. +.PP +If the local host or some intermediate router is configured to drop ICMP +fragmentation-required errors then the discovery attempt will silently +fail. It is likely that TCP connections with the destination host will +fail in unexpected ways if this is the case. Don't drop +fragmentation-required errors! +.PP +Command-line options are as follows. +.TP +.B "\-h, \-\-help" +Writes a brief description of the command-line options available to +standard output and exits with status 0. +.TP +.B "\-v, \-\-version" +Writes tripe's version number to standard output and exits with status +0. +.TP +.B "\-u, \-\-usage" +Writes a brief usage summary to standard output and exits with status 0. +.TP +.BI "\-t, \-\-timeout=" timeout +Sets the time to wait for a reply, in seconds. If no reply or error is +received within the timeout, it is assumed that the attempt to send a +packet was successful. The timeout can be fractional; the default is +five seconds. +.TP +.BI "\-H, \-\-header=" header +Sets the packet header, in hexadecimal. If you set an explicit port +number, it may be worth setting the packet header too, so as not to +alarm anything which might be listening on that port. The default +packet contents are a fixed pseudorandomly-generated block of data. +. +.\"-------------------------------------------------------------------------- +.SH "BUGS" +. +The +.B pathmtu +program currently only works on Linux. Code for other operating systems +is welcome. +. +.\"-------------------------------------------------------------------------- +.SH "AUTHOR" +. +Mark Wooding, +. +.\"----- That's all, folks -------------------------------------------------- diff --git a/pathmtu/pathmtu.c b/pathmtu/pathmtu.c new file mode 100644 index 00000000..acc88378 --- /dev/null +++ b/pathmtu/pathmtu.c @@ -0,0 +1,247 @@ +/* -*-c-*- + * + * Report MTU on path to specified host + * + * (c) 2008 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Trivial IP Encryption (TrIPE). + * + * TrIPE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * TrIPE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with TrIPE; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/*----- Static variables --------------------------------------------------*/ + +static unsigned char buf[65536]; + +/*----- Utility functions -------------------------------------------------*/ + +/* Fill buffer with a constant but pseudorandom string. Uses a simple + * LFSR. + */ +static void fillbuffer(unsigned char *p, size_t sz) +{ + unsigned int y = 0xbc20; + const unsigned char *l = p + sz; + int i; +#define POLY 0x002d + + while (p < l) { + *p++ = y & 0xff; + for (i = 0; i < 8; i++) { + if (!(y & 0x8000)) y <<= 1; + else y = (y << 1) ^ POLY; + } + } +} + +/*----- Doing the actual job ----------------------------------------------*/ + +#if defined(linux) + +#ifndef IP_MTU +# define IP_MTU 14 /* Blech! */ +#endif + +static int pathmtu(struct sockaddr_in *sin, double to) +{ + int sk; + fd_set fd_in; + int mtu; + int i; + size_t sz; + struct timeval tv; + + tv.tv_sec = to; tv.tv_usec = (to - tv.tv_sec) * 1000000; + if ((sk = socket(PF_INET, SOCK_DGRAM, 0)) < 0) goto fail_0; + i = IP_PMTUDISC_DO; + if (setsockopt(sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i))) + goto fail_1; + if (connect(sk, (struct sockaddr *)sin, sizeof(*sin))) goto fail_1; + for (;;) { + sz = sizeof(mtu); + if (getsockopt(sk, SOL_IP, IP_MTU, &mtu, &sz)) goto fail_1; + if (write(sk, buf, mtu - 28) < 0) goto fail_1; + FD_SET(sk, &fd_in); + if (select(sk + 1, &fd_in, 0, 0, &tv) < 0) goto fail_1; + if (!FD_ISSET(sk, &fd_in)) break; + if (read(sk, &i, 1) >= 0 || + errno == ECONNREFUSED || errno == EHOSTUNREACH) + break; + if (errno != EMSGSIZE) goto fail_1; + } + close(sk); + return (mtu); + +fail_1: + close(sk); +fail_0: + return (-1); +} + +#else + +# error "path MTU discovery not implemented" + +#endif + +/*----- Help options ------------------------------------------------------*/ + +static void version(FILE *fp) + { pquis(fp, "$, TrIPE version " VERSION "\n"); } + +static void usage(FILE *fp) + { pquis(fp, "Usage: $ [-t TIMEOUT] [-H HEADER] HOST [PORT]\n"); } + +static void help(FILE *fp) +{ + version(fp); + fputc('\n', fp); + usage(fp); + fputs("\ +\n\ +Options in full:\n\ +\n\ +-h, --help Show this help text.\n\ +-v, --version Show version number.\n\ +-u, --usage Show brief usage message.\n\ +\n\ +-t, --timeout=TIMEOUT Time to wait for reply, in seconds.\n\ +-H, --header=HEX Packet header, in hexadecimal.\n\ +", fp); +} + +/*----- Main code ---------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + hex_ctx hc; + dstr d = DSTR_INIT; + size_t sz; + int i; + unsigned long u; + char *q; + struct hostent *h; + struct servent *s; + double to = 5.0; + unsigned f = 0; + +#define f_bogus 1u + + ego(argv[0]); + fillbuffer(buf, sizeof(buf)); + sin.sin_port = htons(7); + + for (;;) { + static const struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "usage", 0, 0, 'u' }, + { "header", OPTF_ARGREQ, 0, 'H' }, + { "timeout", OPTF_ARGREQ, 0, 't' }, + { 0, 0, 0, 0 } + }; + + i = mdwopt(argc, argv, "hvu" "H:", opts, 0, 0, 0); + if (i < 0) break; + switch (i) { + case 'h': help(stdout); exit(0); + case 'v': version(stdout); exit(0); + case 'u': usage(stdout); exit(0); + + case 'H': + DRESET(&d); + hex_init(&hc); + hex_decode(&hc, optarg, strlen(optarg), &d); + hex_decode(&hc, 0, 0, &d); + sz = d.len < sizeof(buf) ? d.len : sizeof(buf); + memcpy(buf, d.buf, sz); + break; + + case 't': + errno = 0; + to = strtod(optarg, &q); + if (errno || *q) die(EXIT_FAILURE, "bad timeout"); + break; + + default: + f |= f_bogus; + break; + } + } + argv += optind; argc -= optind; + if ((f & f_bogus) || 1 > argc || argc > 2) { + usage(stderr); + exit(EXIT_FAILURE); + } + + if ((h = gethostbyname(*argv)) == 0) + die(EXIT_FAILURE, "unknown host `%s': %s", *argv, hstrerror(h_errno)); + if (h->h_addrtype != AF_INET) + die(EXIT_FAILURE, "unsupported address family for host `%s'", *argv); + memcpy(&sin.sin_addr, h->h_addr, sizeof(struct in_addr)); + argv++; argc--; + + if (*argv) { + errno = 0; + u = strtoul(*argv, &q, 0); + if (!errno && !*q) + sin.sin_port = htons(u); + else if ((s = getservbyname(*argv, "udp")) == 0) + die(EXIT_FAILURE, "unknown UDP service `%s'", *argv); + else + sin.sin_port = s->s_port; + } + + sin.sin_family = AF_INET; + i = pathmtu(&sin, to); + if (i < 0) + die(EXIT_FAILURE, "failed to discover MTU: %s", strerror(errno)); + printf("%d\n", i); + if (ferror(stdout) || fflush(stdout) || fclose(stdout)) + die(EXIT_FAILURE, "failed to write result: %s", strerror(errno)); + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/ -- [mdw]