Index: sbin/ping6/Makefile =================================================================== --- sbin/ping6/Makefile +++ sbin/ping6/Makefile @@ -4,6 +4,7 @@ PACKAGE=runtime PROG= ping6 +SRCS= ping6.c options.c MAN= ping6.8 CFLAGS+=-DIPSEC -DKAME_SCOPEID Index: sbin/ping6/options.h =================================================================== --- /dev/null +++ sbin/ping6/options.h @@ -0,0 +1,70 @@ +#ifndef OPTIONS_H +#define OPTIONS_H 1 + +#include + +#include +#include + +#include + +#define F_FLOOD 0x0001 +#define F_INTERVAL 0x0002 +#define F_PINGFILLED 0x0008 +#define F_QUIET 0x0010 +#define F_RROUTE 0x0020 +#define F_SO_DEBUG 0x0040 +#define F_VERBOSE 0x0100 +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +#define F_POLICY 0x0400 +#else +#define F_AUTHHDR 0x0200 +#define F_ENCRYPT 0x0400 +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ +#define F_NODEADDR 0x0800 +#define F_FQDN 0x1000 +#define F_INTERFACE 0x2000 +#define F_SRCADDR 0x4000 +#define F_HOSTNAME 0x10000 +#define F_FQDNOLD 0x20000 +#define F_NIGROUP 0x40000 +#define F_SUPTYPES 0x80000 +#define F_NOMINMTU 0x100000 +#define F_ONCE 0x200000 +#define F_AUDIBLE 0x400000 +#define F_MISSED 0x800000 +#define F_DONTFRAG 0x1000000 +#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES) +#define F_WAITTIME 0x2000000 + +struct option { + char *arg; + unsigned int count; +}; + +/* Options that are counted or have an argument. */ +struct options_processed { + struct option addrtype; + struct option gateway; + struct option hoplimit; + struct option interface; + struct option interval; + struct option ipsec_policy; + struct option nigroup; + struct option packet_count; + struct option packet_size; + struct option pattern; + struct option preload; + struct option sock_buff_size; + struct option source_address; + struct option timeout; + struct option use_min_mtu; + struct option wait_time; +}; + +bool options_parse(int argc, char *argv[], + struct options_processed *const opts, u_int *const flags); + +#endif Index: sbin/ping6/options.c =================================================================== --- /dev/null +++ sbin/ping6/options.c @@ -0,0 +1,164 @@ +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "options.h" + +#ifndef IPSEC +#define ADDOPTS +#else +#ifdef IPSEC_POLICY_IPSEC +#define ADDOPTS "P:" +#else +#define ADDOPTS "ZE" +#endif /*IPSEC_POLICY_IPSEC*/ +#endif + +bool +options_parse(int argc, char *argv[], + struct options_processed *const opts, u_int *const flags) +{ + int ch; + + memset(opts, 0, sizeof(*opts)); + + while ((ch = getopt(argc, argv, + "k:b:c:DdfHe:m:I:i:l:unNop:qaAS:s:OvyYW:t:" ADDOPTS)) != -1) { + struct option *current_option = NULL; + + switch (ch) { + case 'k': + *flags &= ~F_NOUSERDATA; + *flags |= F_NODEADDR; + current_option = &opts->addrtype; + break; + case 'b': +#if defined(SO_SNDBUF) && defined(SO_RCVBUF) + current_option = &opts->sock_buff_size; + break; +#else + errx(1, "-b option ignored: SO_SNDBUF/SO_RCVBUF socket" + "options not supported"); + /*NOTREACHED*/ +#endif + case 'c': + current_option = &opts->packet_count; + break; + case 'D': + *flags |= F_DONTFRAG; + break; + case 'd': + *flags |= F_SO_DEBUG; + break; + case 'f': + *flags |= F_FLOOD; + break; + case 'e': + current_option = &opts->gateway; + break; + case 'H': + *flags |= F_HOSTNAME; + break; + case 'm': + current_option = &opts->hoplimit; + break; + case 'I': + *flags |= F_INTERFACE; + current_option = &opts->interface; + break; + case 'i': + *flags |= F_INTERVAL; + current_option = &opts->interval; + break; + case 'l': + current_option = &opts->preload; + break; + case 'u': +#ifdef IPV6_USE_MIN_MTU + current_option = &opts->use_min_mtu; + break; +#else + errx(1, "-u is not supported on this platform"); + /*NOTREACHED*/ +#endif + case 'n': + *flags &= ~F_HOSTNAME; + break; + case 'N': + *flags |= F_NIGROUP; + current_option = &opts->nigroup; + break; + case 'o': + *flags |= F_ONCE; + break; + case 'p': + *flags |= F_PINGFILLED; + current_option = &opts->pattern; + break; + case 'q': + *flags |= F_QUIET; + break; + case 'a': + *flags |= F_AUDIBLE; + break; + case 'A': + *flags |= F_MISSED; + break; + case 'S': + *flags |= F_SRCADDR; + current_option = &opts->source_address; + break; + case 's': + current_option = &opts->packet_size; + break; + case 'O': + *flags &= ~F_NOUSERDATA; + *flags |= F_SUPTYPES; + break; + case 'v': + *flags |= F_VERBOSE; + break; + case 'y': + *flags &= ~F_NOUSERDATA; + *flags |= F_FQDN; + break; + case 'Y': + *flags &= ~F_NOUSERDATA; + *flags |= F_FQDNOLD; + break; + case 'W': + *flags |= F_WAITTIME; + current_option = &opts->wait_time; + break; + case 't': + current_option = &opts->timeout; + break; +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC + case 'P': + *flags |= F_POLICY; + current_option = &opts->ipsec_policy; + break; +#else + case 'Z': + *flags |= F_AUTHHDR; + break; + case 'E': + *flags |= F_ENCRYPT; + break; +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ + default: + return (false); + } + + if (current_option != NULL) { + current_option->arg = optarg; + (current_option->count)++; + } + } + + return (true); +} Index: sbin/ping6/ping6.c =================================================================== --- sbin/ping6/ping6.c +++ sbin/ping6/ping6.c @@ -142,6 +142,8 @@ #include +#include "options.h" + struct tv32 { u_int32_t tv32_sec; u_int32_t tv32_nsec; @@ -168,36 +170,6 @@ #define CLR(bit) (A(bit) &= (~B(bit))) #define TST(bit) (A(bit) & B(bit)) -#define F_FLOOD 0x0001 -#define F_INTERVAL 0x0002 -#define F_PINGFILLED 0x0008 -#define F_QUIET 0x0010 -#define F_RROUTE 0x0020 -#define F_SO_DEBUG 0x0040 -#define F_VERBOSE 0x0100 -#ifdef IPSEC -#ifdef IPSEC_POLICY_IPSEC -#define F_POLICY 0x0400 -#else -#define F_AUTHHDR 0x0200 -#define F_ENCRYPT 0x0400 -#endif /*IPSEC_POLICY_IPSEC*/ -#endif /*IPSEC*/ -#define F_NODEADDR 0x0800 -#define F_FQDN 0x1000 -#define F_INTERFACE 0x2000 -#define F_SRCADDR 0x4000 -#define F_HOSTNAME 0x10000 -#define F_FQDNOLD 0x20000 -#define F_NIGROUP 0x40000 -#define F_SUPTYPES 0x80000 -#define F_NOMINMTU 0x100000 -#define F_ONCE 0x200000 -#define F_AUDIBLE 0x400000 -#define F_MISSED 0x800000 -#define F_DONTFRAG 0x1000000 -#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES) -#define F_WAITTIME 0x2000000 static u_int options; #define IN6LEN sizeof(struct in6_addr) @@ -299,8 +271,9 @@ struct sockaddr_in6 from, *sin6; struct addrinfo hints, *res; struct sigaction si_sa; + struct options_processed opts; int cc, i; - int almost_done, ch, hold, packlen, preload, optval, error; + int almost_done, hold, packlen, preload, optval, error; int nig_oldmcprefix = -1; u_char *datap; char *e, *target, *ifname = NULL, *gateway = NULL; @@ -341,268 +314,186 @@ alarmtimeout = preload = 0; datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN]; capdns = capdns_setup(); -#ifndef IPSEC -#define ADDOPTS -#else -#ifdef IPSEC_POLICY_IPSEC -#define ADDOPTS "P:" -#else -#define ADDOPTS "ZE" -#endif /*IPSEC_POLICY_IPSEC*/ -#endif - while ((ch = getopt(argc, argv, - "k:b:c:DdfHe:m:I:i:l:unNop:qaAS:s:OvyYW:t:" ADDOPTS)) != -1) { -#undef ADDOPTS - switch (ch) { - case 'k': - { - char *cp; - - options &= ~F_NOUSERDATA; - options |= F_NODEADDR; - for (cp = optarg; *cp != '\0'; cp++) { - switch (*cp) { - case 'a': - naflags |= NI_NODEADDR_FLAG_ALL; - break; - case 'c': - case 'C': - naflags |= NI_NODEADDR_FLAG_COMPAT; - break; - case 'l': - case 'L': - naflags |= NI_NODEADDR_FLAG_LINKLOCAL; - break; - case 's': - case 'S': - naflags |= NI_NODEADDR_FLAG_SITELOCAL; - break; - case 'g': - case 'G': - naflags |= NI_NODEADDR_FLAG_GLOBAL; - break; - case 'A': /* experimental. not in the spec */ + + if (!options_parse(argc, argv, &opts, &options)) + usage(); + /* NOTREACHED */ + + if ((options & F_NODEADDR) != 0) { + char *cp; + + for (cp = opts.addrtype.arg; *cp != '\0'; cp++) { + switch (*cp) { + case 'a': + naflags |= NI_NODEADDR_FLAG_ALL; + break; + case 'c': + case 'C': + naflags |= NI_NODEADDR_FLAG_COMPAT; + break; + case 'l': + case 'L': + naflags |= NI_NODEADDR_FLAG_LINKLOCAL; + break; + case 's': + case 'S': + naflags |= NI_NODEADDR_FLAG_SITELOCAL; + break; + case 'g': + case 'G': + naflags |= NI_NODEADDR_FLAG_GLOBAL; + break; + case 'A': /* experimental. not in the spec */ #ifdef NI_NODEADDR_FLAG_ANYCAST - naflags |= NI_NODEADDR_FLAG_ANYCAST; - break; + naflags |= NI_NODEADDR_FLAG_ANYCAST; + break; #else - errx(1, -"-a A is not supported on the platform"); - /*NOTREACHED*/ + errx(1, + "-a A is not supported on the platform"); + /*NOTREACHED*/ #endif - default: - usage(); - /*NOTREACHED*/ - } + default: + usage(); + /*NOTREACHED*/ } - break; } - case 'b': -#if defined(SO_SNDBUF) && defined(SO_RCVBUF) - errno = 0; - e = NULL; - lsockbufsize = strtoul(optarg, &e, 10); - sockbufsize = (int)lsockbufsize; - if (errno || !*optarg || *e || - lsockbufsize > INT_MAX) - errx(1, "invalid socket buffer size"); -#else + } + if (opts.sock_buff_size.count != 0) { + errno = 0; + e = NULL; + lsockbufsize = strtoul(opts.sock_buff_size.arg, &e, 10); + sockbufsize = (int)lsockbufsize; + if (errno || !*opts.sock_buff_size.arg || *e || + lsockbufsize > INT_MAX) + errx(1, "invalid socket buffer size"); + } + if (opts.packet_count.count != 0) { + npackets = strtol(opts.packet_count.arg, &e, 10); + if (npackets <= 0 || *opts.packet_count.arg == '\0' || + *e != '\0') errx(1, -"-b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported"); -#endif - break; - case 'c': - npackets = strtol(optarg, &e, 10); - if (npackets <= 0 || *optarg == '\0' || *e != '\0') - errx(1, - "illegal number of packets -- %s", optarg); - break; - case 'D': - options |= F_DONTFRAG; - break; - case 'd': - options |= F_SO_DEBUG; - break; - case 'f': - if (getuid()) { - errno = EPERM; - errx(1, "Must be superuser to flood ping"); - } - options |= F_FLOOD; - setbuf(stdout, (char *)NULL); - break; - case 'e': - gateway = optarg; - break; - case 'H': - options |= F_HOSTNAME; - break; - case 'm': /* hoplimit */ - hoplimit = strtol(optarg, &e, 10); - if (*optarg == '\0' || *e != '\0') - errx(1, "illegal hoplimit %s", optarg); - if (255 < hoplimit || hoplimit < -1) - errx(1, - "illegal hoplimit -- %s", optarg); - break; - case 'I': - ifname = optarg; - options |= F_INTERFACE; + "illegal number of packets -- %s", + opts.packet_count.arg); + } + if ((options & F_FLOOD) != 0) { + if (getuid()) { + errno = EPERM; + errx(1, "Must be superuser to flood ping"); + } + setbuf(stdout, (char *)NULL); + } + if (opts.gateway.count != 0) + gateway = opts.gateway.arg; + if (opts.hoplimit.count != 0) { + hoplimit = strtol(opts.hoplimit.arg, &e, 10); + if (*opts.hoplimit.arg == '\0' || *e != '\0') + errx(1, "illegal hoplimit %s", opts.hoplimit.arg); + if (255 < hoplimit || hoplimit < -1) + errx(1, + "illegal hoplimit -- %s", opts.hoplimit.arg); + } + if ((options & F_INTERFACE) != 0) { + ifname = opts.interface.arg; #ifndef USE_SIN6_SCOPE_ID - usepktinfo++; + usepktinfo += opts.interface.count; #endif - break; - case 'i': /* wait between sending packets */ - t = strtod(optarg, &e); - if (*optarg == '\0' || *e != '\0') - errx(1, "illegal timing interval %s", optarg); - if (t < 1 && getuid()) { - errx(1, "%s: only root may use interval < 1s", - strerror(EPERM)); - } - intvl.tv_sec = (time_t)t; - intvl.tv_nsec = - (long)((t - intvl.tv_sec) * 1000000000); - if (intvl.tv_sec < 0) - errx(1, "illegal timing interval %s", optarg); - /* less than 1/hz does not make sense */ - if (intvl.tv_sec == 0 && intvl.tv_nsec < 1000) { - warnx("too small interval, raised to .000001"); - intvl.tv_nsec = 1000; - } - options |= F_INTERVAL; - break; - case 'l': - if (getuid()) { - errno = EPERM; - errx(1, "Must be superuser to preload"); - } - preload = strtol(optarg, &e, 10); - if (preload < 0 || *optarg == '\0' || *e != '\0') - errx(1, "illegal preload value -- %s", optarg); - break; - case 'u': -#ifdef IPV6_USE_MIN_MTU - mflag++; - break; -#else - errx(1, "-%c is not supported on this platform", ch); - /*NOTREACHED*/ -#endif - case 'n': - options &= ~F_HOSTNAME; - break; - case 'N': - options |= F_NIGROUP; - nig_oldmcprefix++; - break; - case 'o': - options |= F_ONCE; - break; - case 'p': /* fill buffer with user pattern */ - options |= F_PINGFILLED; - fill((char *)datap, optarg); - break; - case 'q': - options |= F_QUIET; - break; - case 'a': - options |= F_AUDIBLE; - break; - case 'A': - options |= F_MISSED; - break; - case 'S': - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */ - hints.ai_family = AF_INET6; - hints.ai_socktype = SOCK_RAW; - hints.ai_protocol = IPPROTO_ICMPV6; + } + if ((options & F_INTERVAL) != 0) { + t = strtod(opts.interval.arg, &e); + if (*opts.interval.arg == '\0' || *e != '\0') + errx(1, "illegal timing interval %s", opts.interval.arg); + if (t < 1 && getuid()) { + errx(1, "%s: only root may use interval < 1s", + strerror(EPERM)); + } + intvl.tv_sec = (time_t)t; + intvl.tv_nsec = + (long)((t - intvl.tv_sec) * 1000000000); + if (intvl.tv_sec < 0) + errx(1, "illegal timing interval %s", opts.interval.arg); + /* less than 1/hz does not make sense */ + if (intvl.tv_sec == 0 && intvl.tv_nsec < 1000) { + warnx("too small interval, raised to .000001"); + intvl.tv_nsec = 1000; + } + } + if (opts.preload.count != 0) { + if (getuid()) { + errno = EPERM; + errx(1, "Must be superuser to preload"); + } + preload = strtol(opts.preload.arg, &e, 10); + if (preload < 0 || *opts.preload.arg == '\0' || *e != '\0') + errx(1, "illegal preload value -- %s", + opts.preload.arg); + } + mflag += opts.use_min_mtu.count; + nig_oldmcprefix += opts.nigroup.count; + if ((options & F_PINGFILLED) != 0) + fill((char *)datap, opts.pattern.arg); + if ((options & F_SRCADDR) != 0) { + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; /* allow hostname? */ + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_RAW; + hints.ai_protocol = IPPROTO_ICMPV6; - error = cap_getaddrinfo(capdns, optarg, NULL, &hints, &res); - if (error) { - errx(1, "invalid source address: %s", - gai_strerror(error)); - } - /* - * res->ai_family must be AF_INET6 and res->ai_addrlen - * must be sizeof(src). - */ - memcpy(&src, res->ai_addr, res->ai_addrlen); - srclen = res->ai_addrlen; - freeaddrinfo(res); - options |= F_SRCADDR; - break; - case 's': /* size of packet to send */ - datalen = strtol(optarg, &e, 10); - if (datalen <= 0 || *optarg == '\0' || *e != '\0') - errx(1, "illegal datalen value -- %s", optarg); - if (datalen > MAXDATALEN) { - errx(1, - "datalen value too large, maximum is %d", - MAXDATALEN); - } - break; - case 'O': - options &= ~F_NOUSERDATA; - options |= F_SUPTYPES; - break; - case 'v': - options |= F_VERBOSE; - break; - case 'y': - options &= ~F_NOUSERDATA; - options |= F_FQDN; - break; - case 'Y': - options &= ~F_NOUSERDATA; - options |= F_FQDNOLD; - break; - case 'W': - t = strtod(optarg, &e); - if (*e || e == optarg || t > (double)INT_MAX) - err(EX_USAGE, "invalid timing interval: `%s'", - optarg); - options |= F_WAITTIME; - waittime = (int)t; - break; - case 't': - alarmtimeout = strtoul(optarg, &e, 0); - if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX)) - errx(EX_USAGE, "invalid timeout: `%s'", - optarg); - if (alarmtimeout > MAXALARM) - errx(EX_USAGE, "invalid timeout: `%s' > %d", - optarg, MAXALARM); - alarm((int)alarmtimeout); - break; + error = cap_getaddrinfo(capdns, opts.source_address.arg, NULL, + &hints, &res); + if (error) { + errx(1, "invalid source address: %s", + gai_strerror(error)); + } + /* + * res->ai_family must be AF_INET6 and res->ai_addrlen + * must be sizeof(src). + */ + memcpy(&src, res->ai_addr, res->ai_addrlen); + srclen = res->ai_addrlen; + freeaddrinfo(res); + } + if (opts.packet_size.count != 0) { + datalen = strtol(opts.packet_size.arg, &e, 10); + if (datalen <= 0 || *opts.packet_size.arg == '\0' || *e != '\0') + errx(1, "illegal datalen value -- %s", + opts.packet_size.arg); + if (datalen > MAXDATALEN) { + errx(1, + "datalen value too large, maximum is %d", + MAXDATALEN); + } + } + if ((options & F_WAITTIME) != 0) { + t = strtod(opts.wait_time.arg, &e); + if (*e || e == opts.wait_time.arg || t > (double)INT_MAX) + err(EX_USAGE, "invalid timing interval: `%s'", + opts.wait_time.arg); + waittime = (int)t; + } + if (opts.timeout.count != 0) { + alarmtimeout = strtoul(opts.timeout.arg, &e, 0); + if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX)) + errx(EX_USAGE, "invalid timeout: `%s'", + opts.timeout.arg); + if (alarmtimeout > MAXALARM) + errx(EX_USAGE, "invalid timeout: `%s' > %d", + opts.timeout.arg, MAXALARM); + alarm((int)alarmtimeout); + } #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC - case 'P': - options |= F_POLICY; - if (!strncmp("in", optarg, 2)) { - if ((policy_in = strdup(optarg)) == NULL) - errx(1, "strdup"); - } else if (!strncmp("out", optarg, 3)) { - if ((policy_out = strdup(optarg)) == NULL) - errx(1, "strdup"); - } else - errx(1, "invalid security policy"); - break; -#else - case 'Z': - options |= F_AUTHHDR; - break; - case 'E': - options |= F_ENCRYPT; - break; + if ((options & F_POLICY) != 0) { + if (!strncmp("in", opts.ipsec_policy.arg, 2)) { + if ((policy_in = strdup(opts.ipsec_policy.arg)) == NULL) + errx(1, "strdup"); + } else if (!strncmp("out", opts.ipsec_policy.arg, 3)) { + if ((policy_out = strdup(opts.ipsec_policy.arg)) == NULL) + errx(1, "strdup"); + } else + errx(1, "invalid security policy"); + } #endif /*IPSEC_POLICY_IPSEC*/ #endif /*IPSEC*/ - default: - usage(); - /*NOTREACHED*/ - } - } argc -= optind; argv += optind;