chiark / gitweb /
svc/conntrack: Add magic `down' peer tags.
[tripe] / pathmtu / pathmtu.c
1 /* -*-c-*-
2  *
3  * Report MTU on path to specified host
4  *
5  * (c) 2008 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Trivial IP Encryption (TrIPE).
11  *
12  * TrIPE is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include "config.h"
30
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <unistd.h>
40
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <netdb.h>
45
46 #include <mLib/dstr.h>
47 #include <mLib/hex.h>
48 #include <mLib/mdwopt.h>
49 #include <mLib/quis.h>
50 #include <mLib/report.h>
51 #include <mLib/tv.h>
52
53 /*----- Static variables --------------------------------------------------*/
54
55 static unsigned char buf[65536];
56
57 /*----- Utility functions -------------------------------------------------*/
58
59 /* Fill buffer with a constant but pseudorandom string.  Uses a simple
60  * LFSR.
61  */
62 static void fillbuffer(unsigned char *p, size_t sz)
63 {
64   unsigned int y = 0xbc20;
65   const unsigned char *l = p + sz;
66   int i;
67 #define POLY 0x002d
68
69   while (p < l) {
70     *p++ = y & 0xff;
71     for (i = 0; i < 8; i++) {
72       if (!(y & 0x8000)) y <<= 1;
73       else  y = (y << 1) ^ POLY;
74     }
75   }
76 }
77
78 /*----- Doing the actual job ----------------------------------------------*/
79
80 #if defined(linux)
81
82 #ifndef IP_MTU
83 #  define IP_MTU 14                     /* Blech! */
84 #endif
85
86 static int pathmtu(struct sockaddr_in *sin, double to)
87 {
88   int sk;
89   fd_set fd_in;
90   int mtu;
91   int i;
92   size_t sz;
93   struct timeval tv, tvproto;
94
95   tvproto.tv_sec = to; tvproto.tv_usec = (to - tvproto.tv_sec) * 1000000;
96   if ((sk = socket(PF_INET, SOCK_DGRAM, 0)) < 0) goto fail_0;
97   i = IP_PMTUDISC_DO;
98   if (setsockopt(sk, SOL_IP, IP_MTU_DISCOVER, &i, sizeof(i)))
99     goto fail_1;
100   if (connect(sk, (struct sockaddr *)sin, sizeof(*sin))) goto fail_1;
101   for (;;) {
102     sz = sizeof(mtu);
103     if (getsockopt(sk, SOL_IP, IP_MTU, &mtu, &sz)) goto fail_1;
104     if (write(sk, buf, mtu - 28) < 0) goto fail_1;
105     FD_SET(sk, &fd_in); tv = tvproto;
106     if (select(sk + 1, &fd_in, 0, 0, &tv) < 0) goto fail_1;
107     if (!FD_ISSET(sk, &fd_in)) break;
108     if (read(sk, &i, 1) >= 0 ||
109         errno == ECONNREFUSED || errno == EHOSTUNREACH)
110       break;
111     if (errno != EMSGSIZE) goto fail_1;
112   }
113   close(sk);
114   return (mtu);
115
116 fail_1:
117   close(sk);
118 fail_0:
119   return (-1);
120 }
121
122 #else
123
124 #  error "path MTU discovery not implemented"
125
126 #endif
127
128 /*----- Help options ------------------------------------------------------*/
129
130 static void version(FILE *fp)
131   { pquis(fp, "$, TrIPE version " VERSION "\n"); }
132
133 static void usage(FILE *fp)
134   { pquis(fp, "Usage: $ [-t TIMEOUT] [-H HEADER] HOST [PORT]\n"); }
135
136 static void help(FILE *fp)
137 {
138   version(fp);
139   fputc('\n', fp);
140   usage(fp);
141   fputs("\
142 \n\
143 Options in full:\n\
144 \n\
145 -h, --help              Show this help text.\n\
146 -v, --version           Show version number.\n\
147 -u, --usage             Show brief usage message.\n\
148 \n\
149 -t, --timeout=TIMEOUT   Time to wait for reply, in seconds.\n\
150 -H, --header=HEX        Packet header, in hexadecimal.\n\
151 ", fp);
152 }
153
154 /*----- Main code ---------------------------------------------------------*/
155
156 int main(int argc, char *argv[])
157 {
158   struct sockaddr_in sin;
159   hex_ctx hc;
160   dstr d = DSTR_INIT;
161   size_t sz;
162   int i;
163   unsigned long u;
164   char *q;
165   struct hostent *h;
166   struct servent *s;
167   double to = 5.0;
168   unsigned f = 0;
169
170 #define f_bogus 1u
171
172   ego(argv[0]);
173   fillbuffer(buf, sizeof(buf));
174   sin.sin_port = htons(7);
175
176   for (;;) {
177     static const struct option opts[] = {
178       { "help",         0,              0,      'h' },
179       { "version",      0,              0,      'v' },
180       { "usage",        0,              0,      'u' },
181       { "header",       OPTF_ARGREQ,    0,      'H' },
182       { "timeout",      OPTF_ARGREQ,    0,      't' },
183       { 0,              0,              0,      0 }
184     };
185
186     i = mdwopt(argc, argv, "hvu" "H:", opts, 0, 0, 0);
187     if (i < 0) break;
188     switch (i) {
189       case 'h': help(stdout); exit(0);
190       case 'v': version(stdout); exit(0);
191       case 'u': usage(stdout); exit(0);
192
193       case 'H':
194         DRESET(&d);
195         hex_init(&hc);
196         hex_decode(&hc, optarg, strlen(optarg), &d);
197         hex_decode(&hc, 0, 0, &d);
198         sz = d.len < sizeof(buf) ? d.len : sizeof(buf);
199         memcpy(buf, d.buf, sz);
200         break;
201
202       case 't':
203         errno = 0;
204         to = strtod(optarg, &q);
205         if (errno || *q) die(EXIT_FAILURE, "bad timeout");
206         break;
207
208       default:
209         f |= f_bogus;
210         break;
211     }
212   }
213   argv += optind; argc -= optind;
214   if ((f & f_bogus) || 1 > argc || argc > 2) {
215     usage(stderr);
216     exit(EXIT_FAILURE);
217   }
218
219   if ((h = gethostbyname(*argv)) == 0)
220     die(EXIT_FAILURE, "unknown host `%s': %s", *argv, hstrerror(h_errno));
221   if (h->h_addrtype != AF_INET)
222     die(EXIT_FAILURE, "unsupported address family for host `%s'", *argv);
223   memcpy(&sin.sin_addr, h->h_addr, sizeof(struct in_addr));
224   argv++; argc--;
225
226   if (*argv) {
227     errno = 0;
228     u = strtoul(*argv, &q, 0);
229     if (!errno && !*q)
230       sin.sin_port = htons(u);
231     else if ((s = getservbyname(*argv, "udp")) == 0)
232       die(EXIT_FAILURE, "unknown UDP service `%s'", *argv);
233     else
234       sin.sin_port = s->s_port;
235   }
236
237   sin.sin_family = AF_INET;
238   i = pathmtu(&sin, to);
239   if (i < 0)
240     die(EXIT_FAILURE, "failed to discover MTU: %s", strerror(errno));
241   printf("%d\n", i);
242   if (ferror(stdout) || fflush(stdout) || fclose(stdout))
243     die(EXIT_FAILURE, "failed to write result: %s", strerror(errno));
244   return (0);
245 }
246
247 /*----- That's all, folks -------------------------------------------------*/