chiark / gitweb /
svc/conntrack.in: Don't track the local IP address any more.
[tripe] / pathmtu / pathmtu.c
CommitLineData
c64d8cd5
MW
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
55static unsigned char buf[65536];
56
57/*----- Utility functions -------------------------------------------------*/
58
59/* Fill buffer with a constant but pseudorandom string. Uses a simple
60 * LFSR.
61 */
62static 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
86static 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;
664a5c0e 93 struct timeval tv, tvproto;
c64d8cd5 94
664a5c0e 95 tvproto.tv_sec = to; tvproto.tv_usec = (to - tvproto.tv_sec) * 1000000;
c64d8cd5
MW
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;
664a5c0e 105 FD_SET(sk, &fd_in); tv = tvproto;
c64d8cd5
MW
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
116fail_1:
117 close(sk);
118fail_0:
119 return (-1);
120}
121
122#else
123
124# error "path MTU discovery not implemented"
125
126#endif
127
128/*----- Help options ------------------------------------------------------*/
129
130static void version(FILE *fp)
131 { pquis(fp, "$, TrIPE version " VERSION "\n"); }
132
133static void usage(FILE *fp)
134 { pquis(fp, "Usage: $ [-t TIMEOUT] [-H HEADER] HOST [PORT]\n"); }
135
136static void help(FILE *fp)
137{
138 version(fp);
139 fputc('\n', fp);
140 usage(fp);
141 fputs("\
142\n\
143Options 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
156int 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 -------------------------------------------------*/