3 * Report MTU on path to specified host
5 * (c) 2008 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Trivial IP Encryption (TrIPE).
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.
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.
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.
27 /*----- Header files ------------------------------------------------------*/
39 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
48 #include <netinet/in_systm.h>
49 #include <netinet/ip.h>
50 #include <netinet/ip_icmp.h>
51 #include <netinet/udp.h>
55 #include <sys/ioctl.h>
57 #include <mLib/alloc.h>
58 #include <mLib/bits.h>
59 #include <mLib/dstr.h>
61 #include <mLib/mdwopt.h>
62 #include <mLib/quis.h>
63 #include <mLib/report.h>
66 /*----- Static variables --------------------------------------------------*/
68 static unsigned char buf
[65536];
72 /*----- Utility functions -------------------------------------------------*/
74 /* Step a value according to a simple LFSR. */
76 do (q) = ((q) & 0x8000) ? ((q) << 1) ^ POLY : ((q) << 1); while (0)
78 /* Fill buffer with a constant but pseudorandom string. Uses a simple
81 static void fillbuffer(unsigned char *p
, size_t sz
)
83 unsigned int y
= 0xbc20;
84 const unsigned char *l
= p
+ sz
;
89 for (i
= 0; i
< 8; i
++) STEP(y
);
93 /* Convert a string to floating point. */
94 static double s2f(const char *s
, const char *what
)
101 if (errno
|| *q
) die(EXIT_FAILURE
, "bad %s", what
);
105 /* Convert a floating-point value into a struct timeval. */
106 static void f2tv(struct timeval
*tv
, double t
)
107 { tv
->tv_sec
= t
; tv
->tv_usec
= (t
- tv
->tv_sec
)*MILLION
; }
109 /*----- Main algorithm skeleton -------------------------------------------*/
112 unsigned f
; /* Various flags */
113 #define F_VERBOSE 1u /* Give a running commentary */
114 double retx
; /* Initial retransmit interval */
115 double regr
; /* Retransmit growth factor */
116 double timeout
; /* Retransmission timeout */
117 int seqoff
; /* Offset to write sequence number */
118 const struct probe_ops
*pops
; /* Probe algorithm description */
119 struct sockaddr_in sin
; /* Destination address */
123 const struct param
*pp
;
129 const struct probe_ops
*next
;
131 int (*setup
)(void *, int, const struct param
*);
132 void (*finish
)(void *);
133 void (*selprep
)(void *, int *, fd_set
*);
134 int (*xmit
)(void *, int);
135 int (*selproc
)(void *, fd_set
*, struct probestate
*);
146 /* or a positive MTU upper-bound */
149 /* Add a file descriptor FD to the set `fd_in', updating `*maxfd'. */
151 do { FD_SET(fd, fd_in); if (*maxfd < fd) *maxfd = fd; } while (0)
153 /* Check whether a buffer contains a packet from our current probe. */
154 static int mypacketp(struct probestate
*ps
,
155 const unsigned char *p
, size_t sz
)
157 const struct param
*pp
= ps
->pp
;
159 return (sz
>= pp
->seqoff
+ 2 && LOAD16(p
+ pp
->seqoff
) == ps
->q
);
162 /* See whether MTU is an acceptable MTU value. Return an appropriate
163 * RC_... code or a new suggested MTU.
165 static int probe(struct probestate
*ps
, void *st
, int mtu
)
167 const struct param
*pp
= ps
->pp
;
169 struct timeval tv
, now
, when
, done
;
170 double timer
= pp
->retx
;
173 /* Set up the first retransmit and give-up timers. */
174 gettimeofday(&now
, 0);
175 f2tv(&tv
, pp
->timeout
); TV_ADD(&done
, &now
, &tv
);
176 f2tv(&tv
, timer
); TV_ADD(&when
, &now
, &tv
);
177 if (TV_CMP(&when
, >, &done
)) when
= done
;
179 /* Send the initial probe. */
180 if (pp
->f
& F_VERBOSE
)
181 moan("sending probe of size %d (seq = %04x)", mtu
, ps
->q
);
183 STORE16(buf
+ pp
->seqoff
, ps
->q
);
184 if ((rc
= pp
->pops
->xmit(st
, mtu
)) != RC_OK
) return (rc
);
188 /* Wait for something interesting to happen. */
189 maxfd
= 0; FD_ZERO(&fd_in
);
190 pp
->pops
->selprep(st
, &maxfd
, &fd_in
);
191 TV_SUB(&tv
, &when
, &now
);
192 if (select(maxfd
+ 1, &fd_in
, 0, 0, &tv
) < 0) return (RC_FAIL
);
193 gettimeofday(&now
, 0);
195 /* See whether the probe method has any answers for us. */
196 if ((rc
= pp
->pops
->selproc(st
, &fd_in
, ps
)) != RC_OK
) return (rc
);
198 /* If we've waited too long, give up. If we should retransmit, do
201 if (TV_CMP(&now
, >, &done
))
203 else if (TV_CMP(&now
, >, &when
)) {
204 if (pp
->f
& F_VERBOSE
) moan("re-sending probe of size %d", mtu
);
205 if ((rc
= pp
->pops
->xmit(st
, mtu
)) != RC_OK
) return (rc
);
207 timer
*= pp
->regr
; f2tv(&tv
, timer
); TV_ADD(&when
, &when
, &tv
);
208 } while (TV_CMP(&when
, <, &now
));
209 if (TV_CMP(&when
, >, &done
)) when
= done
;
214 /* Discover the path MTU to the destination address. */
215 static int pathmtu(const struct param
*pp
)
221 struct probestate ps
;
223 /* Build and connect a UDP socket. We'll need this to know the local port
224 * number to use if nothing else. Set other stuff up.
226 if ((sk
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0) goto fail_0
;
227 if (connect(sk
, (struct sockaddr
*)&pp
->sin
, sizeof(pp
->sin
))) goto fail_1
;
228 st
= xmalloc(pp
->pops
->statesz
);
229 if ((mtu
= pp
->pops
->setup(st
, sk
, pp
)) < 0) goto fail_2
;
230 ps
.pp
= pp
; ps
.q
= rand() & 0xffff;
233 /* And now we do a thing which is sort of like a binary search, except that
234 * we also take explicit clues as establishing a new upper bound, and we
235 * try to hug that initially.
238 assert(lo
<= mtu
&& mtu
<= hi
);
239 if (pp
->f
& F_VERBOSE
) moan("probe: %d <= %d <= %d", lo
, mtu
, hi
);
240 rc
= probe(&ps
, st
, mtu
);
244 if (pp
->f
& F_VERBOSE
) moan("probe failed");
248 /* If we've not seen a dropped packet before then we don't know what
249 * this means yet -- in particular, we don't know which bit of the
250 * network is swallowing packets. Send a minimum-size probe. If
251 * that doesn't come back then assume that the remote host is
252 * swallowing our packets. If it does, then we assume that dropped
253 * packets are a result of ICMP fragmentation-needed reports being
254 * lost or suppressed.
256 if (pp
->f
& F_VERBOSE
) moan("gave up: black hole detected");
258 if (pp
->f
& F_VERBOSE
) moan("sending minimum-size probe");
259 switch (probe(&ps
, st
, lo
)) {
263 if (pp
->f
& F_VERBOSE
) {
264 moan("no reply from min-size probe: "
265 "assume black hole at target");
270 if (pp
->f
& F_VERBOSE
) {
271 moan("reply from min-size probe OK: "
272 "assume black hole in network");
277 if (pp
->f
& F_VERBOSE
)
278 moan("unexpected return code from probe");
284 if (droppy
) goto higher
; else goto lower
;
289 if (pp
->f
& F_VERBOSE
)
290 moan("probe returned: remote host is not a black hole");
294 if (pp
->f
& F_VERBOSE
) moan("probe returned: found correct MTU");
299 /* Now we must make a new guess, between lo and hi. We know that lo
300 * is good; but we're not so sure about hi here. We know that hi >
301 * lo, so this will find an approximate midpoint, greater than lo and
304 if (pp
->f
& F_VERBOSE
) moan("probe returned: guessing higher");
305 mtu
+= (hi
- lo
+ 1)/2;
310 /* If this didn't work, and we're already at the bottom of our
311 * possible range, then something has gone horribly wrong.
316 if (pp
->f
& F_VERBOSE
) moan("error returned: found correct MTU");
321 /* We must make a new guess, between lo and hi. We're probably
322 * fairly sure that lo will succeed, since either it's the minimum
323 * MTU or we've tested it already; but we're not quite sure about hi,
324 * so we want to aim high.
326 if (pp
->f
& F_VERBOSE
) moan("error returned: guessing lower");
327 mtu
-= (hi
- lo
+ 1)/2;
331 if (pp
->f
& F_VERBOSE
) moan("error returned with new MTU estimate");
338 /* Clean up and return our result. */
339 pp
->pops
->finish(st
);
345 pp
->pops
->finish(st
);
354 /*----- Doing it the hard way ---------------------------------------------*/
356 #if defined(linux) || defined(__OpenBSD__)
361 # define sane_htons htons
362 # define sane_htonl htonl
368 static int rawicmp
= -1, rawudp
= -1, rawerr
= 0;
370 #define IPCK_INIT 0xffff
372 /* Compute an IP checksum over some data. This is a restartable interface:
373 * initialize A to `IPCK_INIT' for the first call.
375 static unsigned ipcksum(const void *buf
, size_t n
, unsigned a
)
377 unsigned long aa
= a
^ 0xffff;
378 const unsigned char *p
= buf
, *l
= p
+ n
;
380 while (p
< l
- 1) { aa
+= LOAD16_B(p
); p
+= 2; }
381 if (p
< l
) { aa
+= (unsigned)(*p
) << 8; }
382 do aa
= (aa
& 0xffff) + (aa
>> 16); while (aa
>= 0x10000);
383 return (aa
== 0xffff ? aa
: aa
^ 0xffff);
386 /* TCP/UDP pseudoheader structure. */
388 struct in_addr ph_src
, ph_dst
;
394 struct sockaddr_in me
, sin
;
395 int sk
, rawicmp
, rawudp
;
399 static int raw_setup(void *stv
, int sk
, const struct param
*pp
)
401 struct raw_state
*st
= stv
;
404 struct ifaddrs
*ifa
, *ifaa
, *ifap
;
407 /* If we couldn't acquire raw sockets, we fail here. */
408 if (rawerr
) { errno
= rawerr
; goto fail_0
; }
409 st
->rawicmp
= rawicmp
; st
->rawudp
= rawudp
; st
->sk
= sk
;
411 /* Initialize the sequence number. */
412 st
->q
= rand() & 0xffff;
414 /* Snaffle the local and remote address and port number. */
417 if (getsockname(sk
, (struct sockaddr
*)&st
->me
, &sz
))
420 /* There isn't a portable way to force the DF flag onto a packet through
421 * UDP, or even through raw IP, unless we write the entire IP header
422 * ourselves. This is somewhat annoying, especially since we have an
423 * uphill struggle keeping track of which systems randomly expect which
424 * header fields to be presented in host byte order. Oh, well.
427 if (setsockopt(rawudp
, IPPROTO_IP
, IP_HDRINCL
, &i
, sizeof(i
))) goto fail_0
;
429 /* Find an upper bound on the MTU. Do two passes over the interface
430 * list. If we can find matches for our local address then use the
431 * highest one of those; otherwise do a second pass and simply take the
432 * highest MTU of any network interface.
434 if (getifaddrs(&ifaa
)) goto fail_0
;
435 for (i
= 0; i
< 2; i
++) {
436 for (ifap
= 0, ifa
= ifaa
; ifa
; ifa
= ifa
->ifa_next
) {
437 if (!(ifa
->ifa_flags
& IFF_UP
) || !ifa
->ifa_addr
||
438 ifa
->ifa_addr
->sa_family
!= AF_INET
||
440 ((struct sockaddr_in
*)ifa
->ifa_addr
)->sin_addr
.s_addr
!=
441 st
->me
.sin_addr
.s_addr
) ||
442 (i
== 1 && ifap
&& strcmp(ifap
->ifa_name
, ifa
->ifa_name
) == 0) ||
443 strlen(ifa
->ifa_name
) >= sizeof(ifr
.ifr_name
))
446 strcpy(ifr
.ifr_name
, ifa
->ifa_name
);
447 if (ioctl(sk
, SIOCGIFMTU
, &ifr
)) goto fail_1
;
448 if (mtu
< ifr
.ifr_mtu
) mtu
= ifr
.ifr_mtu
;
452 if (mtu
< 0) { errno
= ENOTCONN
; goto fail_1
; }
464 static void raw_finish(void *stv
) { ; }
466 static void raw_selprep(void *stv
, int *maxfd
, fd_set
*fd_in
)
467 { struct raw_state
*st
= stv
; ADDFD(st
->sk
); ADDFD(st
->rawicmp
); }
469 static int raw_xmit(void *stv
, int mtu
)
471 struct raw_state
*st
= stv
;
472 unsigned char b
[65536], *p
;
478 /* Build the IP header. */
481 ip
->ip_hl
= sizeof(*ip
)/4;
482 ip
->ip_tos
= IPTOS_RELIABILITY
;
483 ip
->ip_len
= sane_htons(mtu
);
484 STEP(st
->q
); ip
->ip_id
= htons(st
->q
);
485 ip
->ip_off
= sane_htons(0 | IP_DF
);
487 ip
->ip_p
= IPPROTO_UDP
;
489 ip
->ip_src
= st
->me
.sin_addr
;
490 ip
->ip_dst
= st
->sin
.sin_addr
;
492 /* Build a UDP packet in the output buffer. */
493 udp
= (struct udphdr
*)(ip
+ 1);
494 udp
->uh_sport
= st
->me
.sin_port
;
495 udp
->uh_dport
= st
->sin
.sin_port
;
496 udp
->uh_ulen
= htons(mtu
- sizeof(*ip
));
499 /* Copy the payload. */
500 p
= (unsigned char *)(udp
+ 1);
501 memcpy(p
, buf
, mtu
- (p
- b
));
503 /* Calculate the UDP checksum. */
504 ph
.ph_src
= ip
->ip_src
;
505 ph
.ph_dst
= ip
->ip_dst
;
507 ph
.ph_p
= IPPROTO_UDP
;
508 ph
.ph_len
= udp
->uh_ulen
;
510 ck
= ipcksum(&ph
, sizeof(ph
), ck
);
511 ck
= ipcksum(udp
, mtu
- sizeof(*ip
), ck
);
512 udp
->uh_sum
= htons(ck
);
514 /* Send the whole thing off. If we're too big for the interface then we
515 * might need to trim immediately.
517 if (sendto(st
->rawudp
, b
, mtu
, 0,
518 (struct sockaddr
*)&st
->sin
, sizeof(st
->sin
)) < 0) {
519 if (errno
== EMSGSIZE
) return (RC_LOWER
);
530 static int raw_selproc(void *stv
, fd_set
*fd_in
, struct probestate
*ps
)
532 struct raw_state
*st
= stv
;
533 unsigned char b
[65536];
539 /* An ICMP packet: see what's inside. */
540 if (FD_ISSET(st
->rawicmp
, fd_in
)) {
541 if ((n
= read(st
->rawicmp
, b
, sizeof(b
))) < 0) goto fail_0
;
544 if (n
< sizeof(*ip
) || n
< sizeof(4*ip
->ip_hl
) ||
545 ip
->ip_v
!= 4 || ip
->ip_p
!= IPPROTO_ICMP
)
547 n
-= sizeof(4*ip
->ip_hl
);
549 icmp
= (struct icmp
*)(b
+ 4*ip
->ip_hl
);
550 if (n
< sizeof(*icmp
) || icmp
->icmp_type
!= ICMP_UNREACH
)
552 n
-= offsetof(struct icmp
, icmp_ip
);
555 if (n
< sizeof(*ip
) ||
556 ip
->ip_p
!= IPPROTO_UDP
|| ip
->ip_hl
!= sizeof(*ip
)/4 ||
557 ip
->ip_id
!= htons(st
->q
) ||
558 ip
->ip_src
.s_addr
!= st
->me
.sin_addr
.s_addr
||
559 ip
->ip_dst
.s_addr
!= st
->sin
.sin_addr
.s_addr
)
563 udp
= (struct udphdr
*)(ip
+ 1);
564 if (n
< sizeof(udp
) || udp
->uh_sport
!= st
->me
.sin_port
||
565 udp
->uh_dport
!= st
->sin
.sin_port
)
569 if (icmp
->icmp_code
== ICMP_UNREACH_PORT
) return (RC_HIGHER
);
570 else if (icmp
->icmp_code
!= ICMP_UNREACH_NEEDFRAG
) goto skip_icmp
;
571 else if (icmp
->icmp_nextmtu
) return (htons(icmp
->icmp_nextmtu
));
572 else return (RC_LOWER
);
576 /* If we got a reply to the current probe then we're good. If we got an
577 * error, or the packet's sequence number is wrong, then ignore it.
579 if (FD_ISSET(st
->sk
, fd_in
)) {
580 if ((n
= read(st
->sk
, b
, sizeof(b
))) < 0) return (RC_OK
);
581 else if (mypacketp(ps
, b
, n
)) return (RC_HIGHER
);
591 static const struct probe_ops raw_ops
= {
592 "raw", OPS_CHAIN
, sizeof(struct raw_state
),
593 raw_setup
, raw_finish
,
594 raw_selprep
, raw_xmit
, raw_selproc
598 #define OPS_CHAIN &raw_ops
600 /*----- Doing the job on Linux --------------------------------------------*/
605 # define IP_MTU 14 /* Blech! */
612 static int linux_setup(void *stv
, int sk
, const struct param
*pp
)
614 struct linux_state
*st
= stv
;
618 /* Snaffle the UDP socket. */
621 /* Turn on kernel path-MTU discovery and force DF on. */
622 i
= IP_PMTUDISC_PROBE
;
623 if (setsockopt(st
->sk
, IPPROTO_IP
, IP_MTU_DISCOVER
, &i
, sizeof(i
)))
626 /* Read the initial MTU guess back and report it. */
628 if (getsockopt(st
->sk
, IPPROTO_IP
, IP_MTU
, &mtu
, &sz
))
635 static void linux_finish(void *stv
) { ; }
637 static void linux_selprep(void *stv
, int *maxfd
, fd_set
*fd_in
)
638 { struct linux_state
*st
= stv
; ADDFD(st
->sk
); }
640 static int linux_xmit(void *stv
, int mtu
)
642 struct linux_state
*st
= stv
;
644 /* Write the packet. */
645 if (write(st
->sk
, buf
, mtu
- 28) >= 0) return (RC_OK
);
646 else if (errno
== EMSGSIZE
) return (RC_LOWER
);
647 else return (RC_FAIL
);
650 static int linux_selproc(void *stv
, fd_set
*fd_in
, struct probestate
*ps
)
652 struct linux_state
*st
= stv
;
656 unsigned char b
[65536];
658 /* Read an answer. If it looks like the right kind of error then report a
659 * success. This is potentially wrong, since we can't tell whether an
660 * error was delayed from an earlier probe. However, we never return
661 * RC_LOWER from this method, so the packet sizes ought to be monotonically
662 * decreasing and this won't cause trouble. Otherwise update from the
663 * kernel's idea of the right MTU.
665 if (FD_ISSET(st
->sk
, fd_in
)) {
666 n
= read(st
->sk
, &buf
, sizeof(buf
));
668 mypacketp(ps
, b
, n
) :
669 errno
== ECONNREFUSED
|| errno
== EHOSTUNREACH
)
672 if (getsockopt(st
->sk
, IPPROTO_IP
, IP_MTU
, &mtu
, &sz
))
679 static const struct probe_ops linux_ops
= {
680 "linux", OPS_CHAIN
, sizeof(struct linux_state
),
681 linux_setup
, linux_finish
,
682 linux_selprep
, linux_xmit
, linux_selproc
686 #define OPS_CHAIN &linux_ops
690 /*----- Help options ------------------------------------------------------*/
692 static const struct probe_ops
*probe_ops
= OPS_CHAIN
;
694 static void version(FILE *fp
)
695 { pquis(fp
, "$, TrIPE version " VERSION
"\n"); }
697 static void usage(FILE *fp
)
699 pquis(fp
, "Usage: $ [-H HEADER] [-m METHOD]\n\
700 [-r SECS] [-g FACTOR] [-t SECS] HOST [PORT]\n");
703 static void help(FILE *fp
)
705 const struct probe_ops
*ops
;
714 -h, --help Show this help text.\n\
715 -v, --version Show version number.\n\
716 -u, --usage Show brief usage message.\n\
718 -g, --growth=FACTOR Growth factor for retransmit interval.\n\
719 -m, --method=METHOD Use METHOD to probe for MTU.\n\
720 -r, --retransmit=SECS Retransmit if no reply after SEC.\n\
721 -t, --timeout=SECS Give up expecting a reply after SECS.\n\
722 -H, --header=HEX Packet header, in hexadecimal.\n\
726 for (ops
= probe_ops
; ops
; ops
= ops
->next
)
727 printf("\t%s\n", ops
->name
);
730 /*----- Main code ---------------------------------------------------------*/
732 int main(int argc
, char *argv
[])
734 struct param pp
= { 0, 0.333, 3.0, 8.0, 0, OPS_CHAIN
};
747 if ((rawicmp
= socket(PF_INET
, SOCK_RAW
, IPPROTO_ICMP
)) < 0 ||
748 (rawudp
= socket(PF_INET
, SOCK_RAW
, IPPROTO_UDP
)) < 0)
750 if (setuid(getuid()))
754 fillbuffer(buf
, sizeof(buf
));
755 pp
.sin
.sin_port
= htons(7);
758 static const struct option opts
[] = {
759 { "help", 0, 0, 'h' },
760 { "version", 0, 0, 'V' },
761 { "usage", 0, 0, 'u' },
762 { "header", OPTF_ARGREQ
, 0, 'H' },
763 { "growth", OPTF_ARGREQ
, 0, 'g' },
764 { "method", OPTF_ARGREQ
, 0, 'm' },
765 { "retransmit", OPTF_ARGREQ
, 0, 'r' },
766 { "timeout", OPTF_ARGREQ
, 0, 't' },
767 { "verbose", 0, 0, 'v' },
771 i
= mdwopt(argc
, argv
, "hVu" "H:g:m:r:t:v", opts
, 0, 0, 0);
774 case 'h': help(stdout
); exit(0);
775 case 'V': version(stdout
); exit(0);
776 case 'u': usage(stdout
); exit(0);
781 hex_decode(&hc
, optarg
, strlen(optarg
), &d
);
782 hex_decode(&hc
, 0, 0, &d
);
783 sz
= d
.len
< 532 ? d
.len
: 532;
784 memcpy(buf
, d
.buf
, sz
);
788 case 'g': pp
.regr
= s2f(optarg
, "retransmit growth factor"); break;
789 case 'r': pp
.retx
= s2f(optarg
, "retransmit interval"); break;
790 case 't': pp
.timeout
= s2f(optarg
, "timeout"); break;
793 for (pp
.pops
= OPS_CHAIN
; pp
.pops
; pp
.pops
= pp
.pops
->next
)
794 if (strcmp(pp
.pops
->name
, optarg
) == 0) goto found_alg
;
795 die(EXIT_FAILURE
, "unknown probe algorithm `%s'", optarg
);
799 case 'v': pp
.f
|= F_VERBOSE
; break;
806 argv
+= optind
; argc
-= optind
;
807 if ((f
& f_bogus
) || 1 > argc
|| argc
> 2) {
812 if ((h
= gethostbyname(*argv
)) == 0)
813 die(EXIT_FAILURE
, "unknown host `%s': %s", *argv
, hstrerror(h_errno
));
814 if (h
->h_addrtype
!= AF_INET
)
815 die(EXIT_FAILURE
, "unsupported address family for host `%s'", *argv
);
816 memcpy(&pp
.sin
.sin_addr
, h
->h_addr
, sizeof(struct in_addr
));
821 u
= strtoul(*argv
, &q
, 0);
823 pp
.sin
.sin_port
= htons(u
);
824 else if ((s
= getservbyname(*argv
, "udp")) == 0)
825 die(EXIT_FAILURE
, "unknown UDP service `%s'", *argv
);
827 pp
.sin
.sin_port
= s
->s_port
;
830 pp
.sin
.sin_family
= AF_INET
;
833 die(EXIT_FAILURE
, "failed to discover MTU: %s", strerror(errno
));
835 if (ferror(stdout
) || fflush(stdout
) || fclose(stdout
))
836 die(EXIT_FAILURE
, "failed to write result: %s", strerror(errno
));
840 /*----- That's all, folks -------------------------------------------------*/