chiark / gitweb /
pathmtu/pathmtu.c: Support IPv6 addresses in the generic code.
[tripe] / pathmtu / pathmtu.c
index dbcba593ca9e5b967118badbf019301da2d8660d..8cf7d18a8f21e7ff0898665c08c6f7a2be10b133 100644 (file)
@@ -108,13 +108,24 @@ static void f2tv(struct timeval *tv, double t)
 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();
   }
 }
@@ -127,6 +138,10 @@ static int addreq(const union addr *a, const union addr *b, unsigned f)
     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();
   }
@@ -255,7 +270,13 @@ static int pathmtu(const struct param *pp)
   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
@@ -739,7 +760,7 @@ static void version(FILE *fp)
 
 static void usage(FILE *fp)
 {
-  pquis(fp, "Usage: $ [-v] [-H HEADER] [-m METHOD]\n\
+  pquis(fp, "Usage: $ [-46v] [-H HEADER] [-m METHOD]\n\
         [-r SECS] [-g FACTOR] [-t SECS] HOST [PORT]\n");
 }
 
@@ -758,6 +779,8 @@ Options in full:\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\
@@ -779,11 +802,9 @@ int main(int argc, char *argv[])
   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
@@ -796,13 +817,19 @@ int main(int argc, char *argv[])
 
   ego(argv[0]);
   fillbuffer(buf, sizeof(buf));
-  pp.a.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' },
@@ -812,7 +839,7 @@ int main(int argc, char *argv[])
       { 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);
@@ -829,6 +856,8 @@ int main(int argc, char *argv[])
        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;
@@ -853,25 +882,17 @@ int main(int argc, char *argv[])
     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.a.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.a.sin.sin_port = htons(u);
-    else if ((s = getservbyname(*argv, "udp")) == 0)
-      die(EXIT_FAILURE, "unknown UDP service `%s'", *argv);
-    else
-      pp.a.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.a.sin.sin_family = AF_INET;
   i = pathmtu(&pp);
   if (i < 0)
     die(EXIT_FAILURE, "failed to discover MTU: %s", strerror(errno));