#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
#include <netinet/udp.h>
-#include <net/if.h>
-#include <ifaddrs.h>
-#include <sys/ioctl.h>
+#ifdef HAVE_GETIFADDRS
+# include <net/if.h>
+# include <ifaddrs.h>
+# include <sys/ioctl.h>
+#endif
#include <mLib/alloc.h>
#include <mLib/bits.h>
static void f2tv(struct timeval *tv, double t)
{ tv->tv_sec = t; tv->tv_usec = (t - tv->tv_sec)*MILLION; }
+union addr {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+};
+
+/* Check whether an address family is even slightly supported. */
+static int addrfamok(int af)
+{
+ switch (af) {
+ case AF_INET: case AF_INET6: return (1);
+ default: return (0);
+ }
+}
+
+/* Return the size of a socket address. */
+static size_t addrsz(const union addr *a)
+{
+ switch (a->sa.sa_family) {
+ case AF_INET: return (sizeof(a->sin));
+ case AF_INET6: return (sizeof(a->sin6));
+ default: abort();
+ }
+}
+
/*----- Main algorithm skeleton -------------------------------------------*/
struct param {
double timeout; /* Retransmission timeout */
int seqoff; /* Offset to write sequence number */
const struct probe_ops *pops; /* Probe algorithm description */
- struct sockaddr_in sin; /* Destination address */
+ union addr a; /* Destination address */
};
struct probestate {
/* Build and connect a UDP socket. We'll need this to know the local port
* number to use if nothing else. Set other stuff up.
*/
- if ((sk = socket(PF_INET, SOCK_DGRAM, 0)) < 0) goto fail_0;
- if (connect(sk, (struct sockaddr *)&pp->sin, sizeof(pp->sin))) goto fail_1;
+ if ((sk = socket(pp->a.sa.sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ goto fail_0;
+ if (connect(sk, &pp->a.sa, addrsz(&pp->a))) goto fail_1;
st = xmalloc(pp->pops->statesz);
if ((mtu = pp->pops->setup(st, sk, pp)) < 0) goto fail_2;
ps.pp = pp; ps.q = rand() & 0xffff;
- lo = 576; hi = mtu;
+ switch (pp->a.sa.sa_family) {
+ case AF_INET: lo = 576; break;
+ case AF_INET6: lo = 1280; break;
+ default: abort();
+ }
+ hi = mtu;
+ if (hi < lo) { errno = EMSGSIZE; return (-1); }
/* And now we do a thing which is sort of like a binary search, except that
* we also take explicit clues as establishing a new upper bound, and we
/*----- Doing it the hard way ---------------------------------------------*/
+#ifdef HAVE_GETIFADDRS
+
#if defined(linux) || defined(__OpenBSD__)
-#define IPHDR_SANE
+# define IPHDR_SANE
#endif
#ifdef IPHDR_SANE
#endif
static int rawicmp = -1, rawudp = -1, rawerr = 0;
+static int rawicmp6 = -1, rawudp6 = -1, rawerr6 = 0;
#define IPCK_INIT 0xffff
+/* Compare two addresses. Maybe compare the port numbers too. */
+#define AEF_PORT 1u
+static int addreq(const union addr *a, const union addr *b, unsigned f)
+{
+ switch (a->sa.sa_family) {
+ case AF_INET:
+ return (a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr &&
+ (!(f&AEF_PORT) || a->sin.sin_port == b->sin.sin_port));
+ case AF_INET6:
+ return (!memcmp(a->sin6.sin6_addr.s6_addr,
+ b->sin6.sin6_addr.s6_addr, 16) &&
+ (!(f&AEF_PORT) || a->sin6.sin6_port == b->sin6.sin6_port));
+ default:
+ abort();
+ }
+}
+
/* Compute an IP checksum over some data. This is a restartable interface:
* initialize A to `IPCK_INIT' for the first call.
*/
/* TCP/UDP pseudoheader structure. */
struct phdr {
struct in_addr ph_src, ph_dst;
- u_char ph_z, ph_p;
- u_short ph_len;
+ uint8_t ph_z, ph_p;
+ uint16_t ph_len;
+};
+struct phdr6 {
+ struct in6_addr ph6_src, ph6_dst;
+ uint32_t ph6_len;
+ uint8_t ph6_z0, ph6_z1, ph6_z2, ph6_nxt;
};
struct raw_state {
- struct sockaddr_in me, sin;
+ union addr me, a;
int sk, rawicmp, rawudp;
+ uint16_t srcport, dstport;
unsigned q;
};
int i, mtu = -1;
struct ifaddrs *ifa, *ifaa, *ifap;
struct ifreq ifr;
+ struct icmp6_filter f6;
- /* If we couldn't acquire raw sockets, we fail here. */
- if (rawerr) { errno = rawerr; goto fail_0; }
- st->rawicmp = rawicmp; st->rawudp = rawudp; st->sk = sk;
+ /* Check that the address is OK, and that we have the necessary raw
+ * sockets.
+ *
+ * For IPv6, also set the filter so we don't get too many useless wakeups.
+ */
+ switch (pp->a.sa.sa_family) {
+ case AF_INET:
+ if (rawerr) { errno = rawerr; goto fail_0; }
+ st->rawicmp = rawicmp; st->rawudp = rawudp; st->sk = sk;
+ /* IPv4 filtering is available on Linux but isn't portable. */
+ break;
+ case AF_INET6:
+ if (rawerr6) { errno = rawerr6; goto fail_0; }
+ st->rawicmp = rawicmp6; st->rawudp = rawudp6; st->sk = sk;
+ ICMP6_FILTER_SETBLOCKALL(&f6);
+ ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &f6);
+ ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &f6);
+ if (setsockopt(st->rawicmp, IPPROTO_ICMPV6, ICMP6_FILTER,
+ &f6, sizeof(f6))) {
+ die(EXIT_FAILURE, "failed to set icmpv6 filter: %s",
+ strerror(errno));
+ }
+ break;
+ default:
+ errno = EPFNOSUPPORT; goto fail_0;
+ }
/* Initialize the sequence number. */
st->q = rand() & 0xffff;
/* Snaffle the local and remote address and port number. */
- st->sin = pp->sin;
+ st->a = pp->a;
sz = sizeof(st->me);
- if (getsockname(sk, (struct sockaddr *)&st->me, &sz))
+ if (getsockname(sk, &st->me.sa, &sz))
goto fail_0;
+ /* Only now do some fiddling because Linux doesn't like port numbers in
+ * IPv6 raw destination addresses...
+ */
+ switch (pp->a.sa.sa_family) {
+ case AF_INET:
+ st->srcport = st->me.sin.sin_port; st->me.sin.sin_port = 0;
+ st->dstport = st->a.sin.sin_port; st->a.sin.sin_port = 0;
+ break;
+ case AF_INET6:
+ st->srcport = st->me.sin6.sin6_port; st->me.sin6.sin6_port = 0;
+ st->dstport = st->a.sin6.sin6_port; st->a.sin6.sin6_port = 0;
+ break;
+ default:
+ abort();
+ }
+
/* There isn't a portable way to force the DF flag onto a packet through
* UDP, or even through raw IP, unless we write the entire IP header
* ourselves. This is somewhat annoying, especially since we have an
for (i = 0; i < 2; i++) {
for (ifap = 0, ifa = ifaa; ifa; ifa = ifa->ifa_next) {
if (!(ifa->ifa_flags & IFF_UP) || !ifa->ifa_addr ||
- ifa->ifa_addr->sa_family != AF_INET ||
+ ifa->ifa_addr->sa_family != st->me.sa.sa_family ||
(i == 0 &&
- ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr !=
- st->me.sin_addr.s_addr) ||
+ !addreq((union addr *)ifa->ifa_addr, &st->me, 0)) ||
(i == 1 && ifap && strcmp(ifap->ifa_name, ifa->ifa_name) == 0) ||
strlen(ifa->ifa_name) >= sizeof(ifr.ifr_name))
continue;
struct raw_state *st = stv;
unsigned char b[65536], *p;
struct ip *ip;
+ struct ip6_hdr *ip6;
struct udphdr *udp;
struct phdr ph;
+ struct phdr6 ph6;
unsigned ck;
- /* Build the IP header. */
- ip = (struct ip *)b;
- ip->ip_v = 4;
- ip->ip_hl = sizeof(*ip)/4;
- ip->ip_tos = IPTOS_RELIABILITY;
- ip->ip_len = sane_htons(mtu);
- STEP(st->q); ip->ip_id = htons(st->q);
- ip->ip_off = sane_htons(0 | IP_DF);
- ip->ip_ttl = 64;
- ip->ip_p = IPPROTO_UDP;
- ip->ip_sum = 0;
- ip->ip_src = st->me.sin_addr;
- ip->ip_dst = st->sin.sin_addr;
-
- /* Build a UDP packet in the output buffer. */
- udp = (struct udphdr *)(ip + 1);
- udp->uh_sport = st->me.sin_port;
- udp->uh_dport = st->sin.sin_port;
- udp->uh_ulen = htons(mtu - sizeof(*ip));
- udp->uh_sum = 0;
-
- /* Copy the payload. */
- p = (unsigned char *)(udp + 1);
- memcpy(p, buf, mtu - (p - b));
-
- /* Calculate the UDP checksum. */
- ph.ph_src = ip->ip_src;
- ph.ph_dst = ip->ip_dst;
- ph.ph_z = 0;
- ph.ph_p = IPPROTO_UDP;
- ph.ph_len = udp->uh_ulen;
- ck = IPCK_INIT;
- ck = ipcksum(&ph, sizeof(ph), ck);
- ck = ipcksum(udp, mtu - sizeof(*ip), ck);
- udp->uh_sum = htons(ck);
+ switch (st->a.sa.sa_family) {
+
+ case AF_INET:
+
+ /* Build the IP header. */
+ ip = (struct ip *)b;
+ ip->ip_v = 4;
+ ip->ip_hl = sizeof(*ip)/4;
+ ip->ip_tos = IPTOS_RELIABILITY;
+ ip->ip_len = sane_htons(mtu);
+ STEP(st->q); ip->ip_id = htons(st->q);
+ ip->ip_off = sane_htons(0 | IP_DF);
+ ip->ip_ttl = 64;
+ ip->ip_p = IPPROTO_UDP;
+ ip->ip_sum = 0;
+ ip->ip_src = st->me.sin.sin_addr;
+ ip->ip_dst = st->a.sin.sin_addr;
+
+ /* Build a UDP packet in the output buffer. */
+ udp = (struct udphdr *)(ip + 1);
+ udp->uh_sport = st->srcport;
+ udp->uh_dport = st->dstport;
+ udp->uh_ulen = htons(mtu - sizeof(*ip));
+ udp->uh_sum = 0;
+
+ /* Copy the payload. */
+ p = (unsigned char *)(udp + 1);
+ memcpy(p, buf, mtu - (p - b));
+
+ /* Calculate the UDP checksum. */
+ ph.ph_src = ip->ip_src;
+ ph.ph_dst = ip->ip_dst;
+ ph.ph_z = 0;
+ ph.ph_p = IPPROTO_UDP;
+ ph.ph_len = udp->uh_ulen;
+ ck = IPCK_INIT;
+ ck = ipcksum(&ph, sizeof(ph), ck);
+ ck = ipcksum(udp, mtu - sizeof(*ip), ck);
+ udp->uh_sum = htons(ck);
+
+ break;
+
+ case AF_INET6:
+
+ /* Build the IP header. */
+ ip6 = (struct ip6_hdr *)b;
+ STEP(st->q); ip6->ip6_flow = htonl(0x60000000 | st->q);
+ ip6->ip6_plen = htons(mtu - sizeof(*ip6));
+ ip6->ip6_nxt = IPPROTO_UDP;
+ ip6->ip6_hlim = 64;
+ ip6->ip6_src = st->me.sin6.sin6_addr;
+ ip6->ip6_dst = st->a.sin6.sin6_addr;
+
+ /* Build a UDP packet in the output buffer. */
+ udp = (struct udphdr *)(ip6 + 1);
+ udp->uh_sport = st->srcport;
+ udp->uh_dport = st->dstport;
+ udp->uh_ulen = htons(mtu - sizeof(*ip6));
+ udp->uh_sum = 0;
+
+ /* Copy the payload. */
+ p = (unsigned char *)(udp + 1);
+ memcpy(p, buf, mtu - (p - b));
+
+ /* Calculate the UDP checksum. */
+ ph6.ph6_src = ip6->ip6_src;
+ ph6.ph6_dst = ip6->ip6_dst;
+ ph6.ph6_len = udp->uh_ulen;
+ ph6.ph6_z0 = ph6.ph6_z1 = ph6.ph6_z2 = 0;
+ ph6.ph6_nxt = IPPROTO_UDP;
+ ck = IPCK_INIT;
+ ck = ipcksum(&ph6, sizeof(ph6), ck);
+ ck = ipcksum(udp, mtu - sizeof(*ip6), ck);
+ udp->uh_sum = htons(ck);
+
+ break;
+
+ default:
+ abort();
+ }
/* Send the whole thing off. If we're too big for the interface then we
* might need to trim immediately.
*/
- if (sendto(st->rawudp, b, mtu, 0,
- (struct sockaddr *)&st->sin, sizeof(st->sin)) < 0) {
+ if (sendto(st->rawudp, b, mtu, 0, &st->a.sa, addrsz(&st->a)) < 0) {
if (errno == EMSGSIZE) return (RC_LOWER);
else goto fail_0;
}
struct raw_state *st = stv;
unsigned char b[65536];
struct ip *ip;
+ struct ip6_hdr *ip6;
struct icmp *icmp;
+ struct icmp6_hdr *icmp6;
struct udphdr *udp;
+ const unsigned char *payload;
ssize_t n;
/* An ICMP packet: see what's inside. */
if (FD_ISSET(st->rawicmp, fd_in)) {
if ((n = read(st->rawicmp, b, sizeof(b))) < 0) goto fail_0;
- ip = (struct ip *)b;
- if (n < sizeof(*ip) || n < sizeof(4*ip->ip_hl) ||
- ip->ip_v != 4 || ip->ip_p != IPPROTO_ICMP)
- goto skip_icmp;
- n -= sizeof(4*ip->ip_hl);
-
- icmp = (struct icmp *)(b + 4*ip->ip_hl);
- if (n < sizeof(*icmp) || icmp->icmp_type != ICMP_UNREACH)
- goto skip_icmp;
- n -= offsetof(struct icmp, icmp_ip);
-
- ip = &icmp->icmp_ip;
- if (n < sizeof(*ip) ||
- ip->ip_p != IPPROTO_UDP || ip->ip_hl != sizeof(*ip)/4 ||
- ip->ip_id != htons(st->q) ||
- ip->ip_src.s_addr != st->me.sin_addr.s_addr ||
- ip->ip_dst.s_addr != st->sin.sin_addr.s_addr)
- goto skip_icmp;
- n -= sizeof(*ip);
-
- udp = (struct udphdr *)(ip + 1);
- if (n < sizeof(udp) || udp->uh_sport != st->me.sin_port ||
- udp->uh_dport != st->sin.sin_port)
- goto skip_icmp;
- n -= sizeof(*udp);
-
- if (icmp->icmp_code == ICMP_UNREACH_PORT) return (RC_HIGHER);
- else if (icmp->icmp_code != ICMP_UNREACH_NEEDFRAG) goto skip_icmp;
- else if (icmp->icmp_nextmtu) return (htons(icmp->icmp_nextmtu));
- else return (RC_LOWER);
+ switch (st->me.sa.sa_family) {
+
+ case AF_INET:
+
+ ip = (struct ip *)b;
+ if (n < sizeof(*ip) || n < sizeof(4*ip->ip_hl) ||
+ ip->ip_v != 4 || ip->ip_p != IPPROTO_ICMP)
+ goto skip_icmp;
+ n -= sizeof(4*ip->ip_hl);
+
+ icmp = (struct icmp *)(b + 4*ip->ip_hl);
+ if (n < sizeof(*icmp) || icmp->icmp_type != ICMP_UNREACH)
+ goto skip_icmp;
+ n -= offsetof(struct icmp, icmp_ip);
+
+ ip = &icmp->icmp_ip;
+ if (n < sizeof(*ip) ||
+ ip->ip_p != IPPROTO_UDP || ip->ip_hl != sizeof(*ip)/4 ||
+ ip->ip_id != htons(st->q) ||
+ ip->ip_src.s_addr != st->me.sin.sin_addr.s_addr ||
+ ip->ip_dst.s_addr != st->a.sin.sin_addr.s_addr)
+ goto skip_icmp;
+ n -= sizeof(*ip);
+
+ udp = (struct udphdr *)(ip + 1);
+ if (n < sizeof(*udp) || udp->uh_sport != st->srcport ||
+ udp->uh_dport != st->dstport)
+ goto skip_icmp;
+ n -= sizeof(*udp);
+
+ payload = (const unsigned char *)(udp + 1);
+ if (!mypacketp(ps, payload, n)) goto skip_icmp;
+
+ if (icmp->icmp_code == ICMP_UNREACH_PORT) return (RC_HIGHER);
+ else if (icmp->icmp_code != ICMP_UNREACH_NEEDFRAG) goto skip_icmp;
+ else if (icmp->icmp_nextmtu) return (htons(icmp->icmp_nextmtu));
+ else return (RC_LOWER);
+
+ break;
+
+ case AF_INET6:
+ icmp6 = (struct icmp6_hdr *)b;
+ if (n < sizeof(*icmp6) ||
+ (icmp6->icmp6_type != ICMP6_PACKET_TOO_BIG &&
+ icmp6->icmp6_type != ICMP6_DST_UNREACH))
+ goto skip_icmp;
+ n -= sizeof(*icmp6);
+
+ ip6 = (struct ip6_hdr *)(icmp6 + 1);
+ if (n < sizeof(*ip6) || ip6->ip6_nxt != IPPROTO_UDP ||
+ memcmp(ip6->ip6_src.s6_addr,
+ st->me.sin6.sin6_addr.s6_addr, 16) ||
+ memcmp(ip6->ip6_dst.s6_addr,
+ st->a.sin6.sin6_addr.s6_addr, 16) ||
+ (ntohl(ip6->ip6_flow)&0xffff) != st->q)
+ goto skip_icmp;
+ n -= sizeof(*ip6);
+
+ udp = (struct udphdr *)(ip6 + 1);
+ if (n < sizeof(*udp) || udp->uh_sport != st->srcport ||
+ udp->uh_dport != st->dstport)
+ goto skip_icmp;
+ n -= sizeof(*udp);
+
+ payload = (const unsigned char *)(udp + 1);
+ if (!mypacketp(ps, payload, n)) goto skip_icmp;
+
+ if (icmp6->icmp6_type == ICMP6_PACKET_TOO_BIG)
+ return (ntohs(icmp6->icmp6_mtu));
+ else switch (icmp6->icmp6_code) {
+ case ICMP6_DST_UNREACH_ADMIN:
+ case ICMP6_DST_UNREACH_NOPORT:
+ return (RC_HIGHER);
+ default:
+ goto skip_icmp;
+ }
+ break;
+
+ default:
+ abort();
+ }
}
+
skip_icmp:;
/* If we got a reply to the current probe then we're good. If we got an
#undef OPS_CHAIN
#define OPS_CHAIN &raw_ops
+#endif
+
/*----- Doing the job on Linux --------------------------------------------*/
#if defined(linux)
#endif
struct linux_state {
+ int sol, so_mtu_discover, so_mtu;
int sk;
+ size_t hdrlen;
};
static int linux_setup(void *stv, int sk, const struct param *pp)
int i, mtu;
socklen_t sz;
+ /* Check that the address is OK. */
+ switch (pp->a.sa.sa_family) {
+ case AF_INET:
+ st->sol = IPPROTO_IP;
+ st->so_mtu_discover = IP_MTU_DISCOVER;
+ st->so_mtu = IP_MTU;
+ st->hdrlen = 28;
+ break;
+ case AF_INET6:
+ st->sol = IPPROTO_IPV6;
+ st->so_mtu_discover = IPV6_MTU_DISCOVER;
+ st->so_mtu = IPV6_MTU;
+ st->hdrlen = 48;
+ break;
+ default:
+ errno = EPFNOSUPPORT;
+ return (-1);
+ }
+
/* Snaffle the UDP socket. */
st->sk = sk;
/* Turn on kernel path-MTU discovery and force DF on. */
i = IP_PMTUDISC_PROBE;
- if (setsockopt(st->sk, IPPROTO_IP, IP_MTU_DISCOVER, &i, sizeof(i)))
+ if (setsockopt(st->sk, st->sol, st->so_mtu_discover, &i, sizeof(i)))
return (-1);
/* Read the initial MTU guess back and report it. */
sz = sizeof(mtu);
- if (getsockopt(st->sk, IPPROTO_IP, IP_MTU, &mtu, &sz))
+ if (getsockopt(st->sk, st->sol, st->so_mtu, &mtu, &sz))
return (-1);
/* Done. */
struct linux_state *st = stv;
/* Write the packet. */
- if (write(st->sk, buf, mtu - 28) >= 0) return (RC_OK);
+ if (write(st->sk, buf, mtu - st->hdrlen) >= 0) return (RC_OK);
else if (errno == EMSGSIZE) return (RC_LOWER);
else return (RC_FAIL);
}
errno == ECONNREFUSED || errno == EHOSTUNREACH)
return (RC_HIGHER);
sz = sizeof(mtu);
- if (getsockopt(st->sk, IPPROTO_IP, IP_MTU, &mtu, &sz))
+ if (getsockopt(st->sk, st->sol, st->so_mtu, &mtu, &sz))
return (RC_FAIL);
return (mtu);
}
static void usage(FILE *fp)
{
- pquis(fp, "Usage: $ [-H HEADER] [-m METHOD]\n\
+ pquis(fp, "Usage: $ [-46v] [-H HEADER] [-m METHOD]\n\
[-r SECS] [-g FACTOR] [-t SECS] HOST [PORT]\n");
}
Options in full:\n\
\n\
-h, --help Show this help text.\n\
--v, --version Show version number.\n\
+-V, --version Show version number.\n\
-u, --usage Show brief usage message.\n\
\n\
+-4, --ipv4 Restrict to IPv4 only.\n\
+-6, --ipv6 Restrict to IPv6 only.\n\
-g, --growth=FACTOR Growth factor for retransmit interval.\n\
-m, --method=METHOD Use METHOD to probe for MTU.\n\
-r, --retransmit=SECS Retransmit if no reply after SEC.\n\
-t, --timeout=SECS Give up expecting a reply after SECS.\n\
+-v, --verbose Write a running commentary to stderr.\n\
-H, --header=HEX Packet header, in hexadecimal.\n\
\n\
Probe methods:\n\
hex_ctx hc;
dstr d = DSTR_INIT;
size_t sz;
- int i;
- unsigned long u;
- char *q;
- struct hostent *h;
- struct servent *s;
+ int i, err;
+ struct addrinfo aihint = { 0 }, *ailist, *ai;
+ const char *host, *svc = "7";
unsigned f = 0;
#define f_bogus 1u
+#ifdef HAVE_GETIFADDRS
if ((rawicmp = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0 ||
(rawudp = socket(PF_INET, SOCK_RAW, IPPROTO_UDP)) < 0)
rawerr = errno;
+ if ((rawicmp6 = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0 ||
+ (rawudp6 = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW)) < 0)
+ rawerr6 = errno;
+#endif
if (setuid(getuid()))
abort();
ego(argv[0]);
fillbuffer(buf, sizeof(buf));
- pp.sin.sin_port = htons(7);
+
+ aihint.ai_family = AF_UNSPEC;
+ aihint.ai_protocol = IPPROTO_UDP;
+ aihint.ai_socktype = SOCK_DGRAM;
+ aihint.ai_flags = AI_ADDRCONFIG;
for (;;) {
static const struct option opts[] = {
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'V' },
{ "usage", 0, 0, 'u' },
+ { "ipv4", 0, 0, '4' },
+ { "ipv6", 0, 0, '6' },
{ "header", OPTF_ARGREQ, 0, 'H' },
{ "growth", OPTF_ARGREQ, 0, 'g' },
{ "method", OPTF_ARGREQ, 0, 'm' },
{ 0, 0, 0, 0 }
};
- i = mdwopt(argc, argv, "hVu" "H:g:m:r:t:v", opts, 0, 0, 0);
+ i = mdwopt(argc, argv, "hVu" "46H:g:m:r:t:v", opts, 0, 0, 0);
if (i < 0) break;
switch (i) {
case 'h': help(stdout); exit(0);
pp.seqoff = sz;
break;
+ case '4': aihint.ai_family = AF_INET; break;
+ case '6': aihint.ai_family = AF_INET6; break;
case 'g': pp.regr = s2f(optarg, "retransmit growth factor"); break;
case 'r': pp.retx = s2f(optarg, "retransmit interval"); break;
case 't': pp.timeout = s2f(optarg, "timeout"); break;
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(&pp.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
- argv++; argc--;
-
- if (*argv) {
- errno = 0;
- u = strtoul(*argv, &q, 0);
- if (!errno && !*q)
- pp.sin.sin_port = htons(u);
- else if ((s = getservbyname(*argv, "udp")) == 0)
- die(EXIT_FAILURE, "unknown UDP service `%s'", *argv);
- else
- pp.sin.sin_port = s->s_port;
+ host = argv[0];
+ if (argv[1]) svc = argv[1];
+ if ((err = getaddrinfo(host, svc, &aihint, &ailist)) != 0) {
+ die(EXIT_FAILURE, "unknown host `%s' or service `%s': %s",
+ host, svc, gai_strerror(err));
}
+ for (ai = ailist; ai && !addrfamok(ai->ai_family); ai = ai->ai_next);
+ if (!ai) die(EXIT_FAILURE, "no supported address families for `%s'", host);
+ assert(ai->ai_addrlen <= sizeof(pp.a));
+ memcpy(&pp.a, ai->ai_addr, ai->ai_addrlen);
- pp.sin.sin_family = AF_INET;
i = pathmtu(&pp);
if (i < 0)
die(EXIT_FAILURE, "failed to discover MTU: %s", strerror(errno));