X-Git-Url: https://git.distorted.org.uk/~mdw/tripe/blobdiff_plain/454f5a1a9cebb400992db85d1ededfc22f254e2b..1d25a3edbe44af1827d434e6864e29befbdf19b7:/pathmtu/pathmtu.c?ds=inline diff --git a/pathmtu/pathmtu.c b/pathmtu/pathmtu.c index dbcba593..55702787 100644 --- a/pathmtu/pathmtu.c +++ b/pathmtu/pathmtu.c @@ -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 @@ -413,8 +434,8 @@ static unsigned ipcksum(const void *buf, size_t n, unsigned a) /* 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 raw_state { @@ -643,7 +664,9 @@ static const struct probe_ops raw_ops = { #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) @@ -654,8 +677,21 @@ static int linux_setup(void *stv, int sk, const struct param *pp) /* Check that the address is OK. */ switch (pp->a.sa.sa_family) { - case AF_INET: break; - default: errno = EPFNOSUPPORT; return (-1); + 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. */ @@ -663,12 +699,12 @@ static int linux_setup(void *stv, int sk, const struct param *pp) /* 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. */ @@ -685,7 +721,7 @@ static int linux_xmit(void *stv, int mtu) 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); } @@ -712,7 +748,7 @@ static int linux_selproc(void *stv, fd_set *fd_in, struct probestate *ps) 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); } @@ -739,7 +775,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 +794,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 +817,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 +832,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 +854,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 +871,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 +897,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));