From c6fc9a72793a647b6d3f25bc2980e6652150f907 Mon Sep 17 00:00:00 2001 From: Aleksandr Lysikov <lysikov@inbox.ru> Date: Fri, 14 Dec 2018 14:53:35 +0500 Subject: [PATCH] make ping_util4(); & ping_util6(); --- iputils/iputils.h | 51 + iputils/ping.c | 100 +- iputils/ping.h | 11 +- iputils/ping6_common.c | 2075 ++++++++++++++++++++-------------------- iputils/ping_common.c | 1565 +++++++++++++++--------------- 5 files changed, 1963 insertions(+), 1839 deletions(-) create mode 100644 iputils/iputils.h diff --git a/iputils/iputils.h b/iputils/iputils.h new file mode 100644 index 0000000000..1e04b5de05 --- /dev/null +++ b/iputils/iputils.h @@ -0,0 +1,51 @@ +/* + * Set utilities for networking + */ + +#ifndef _IPUTILS_H +#define _IPUTILS_H + +#include <stdint.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Send ping for ipv4 + * + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util4(const char *addr, int count); + +/** + * Send ping for ipv6 + * + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util6(const char *addr, int count); + + +/** + * + */ +int tracepath_util(int type, const char *addr); + +//#define PING_DBG +// analog printf() +void log_printf(const char *format, ...); + +#define PACKAGE_NAME "iputils" +#define PACKAGE_VERSION "0.1" +#define IPUTILS_VERSION(_prog) "%s from %s %s\n", _prog, PACKAGE_NAME, PACKAGE_VERSION + +#ifdef __cplusplus +} +#endif + +#endif // _IPUTILS_H diff --git a/iputils/ping.c b/iputils/ping.c index 144c80c878..497906684a 100644 --- a/iputils/ping.c +++ b/iputils/ping.c @@ -58,6 +58,29 @@ #include <math.h> #include <glib.h> +// analog printf() +void log_printf(const char *format, ...) +{ +#ifdef PING_DBG + gchar *log_str = NULL; + va_list args; + + va_start (args, format); + log_str = g_strdup_vprintf (format, args); + va_end (args); + + if(log_str) + { + + printf(log_str); + g_free(log_str); + } +#endif + const char *str = NULL; + if(str) + str = format; +} + #ifndef ICMP_FILTER #define ICMP_FILTER 1 struct icmp_filter { @@ -167,7 +190,7 @@ static void create_socket(socket_st *sock, int family, int socktype, int protoco /* Report error related to disabled IPv6 only when IPv6 also failed or in * verbose mode. Report other errors always. */ - if((errno == EAFNOSUPPORT && socktype == AF_INET6) || options & F_VERBOSE || requisite) + if((errno == EAFNOSUPPORT && socktype == AF_INET6) || (options & F_VERBOSE) || requisite) error(0, errno, "socket"); if(requisite) exit(2); @@ -512,7 +535,15 @@ ping_main(int argc, char **argv) return status; } -int ping_util(const char *addr, int count) +/** + * Send ping + * + * @type for ipv4=4, for ipv6=6 + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util(int type, const char *addr, int count) { /* @@ -522,15 +553,44 @@ int ping_util(const char *addr, int count) net.ipv4.ping_group_range = 1 0 Need change range for other users: # sysctl net.ipv4.ping_group_range="1 65000" - */ int argc = 3; const char *argv[argc]; - argv[0] = "ping4"; + if(type!=4) + argv[0] = "ping6"; + else + argv[0] = "ping4"; argv[1] = g_strdup_printf("-c%d", count); argv[2] = addr; - int res = ping_main(argc, (char**) argv); - return res; + ping_main(argc, (char**) argv); + g_free((char*)argv[1]); + if(ntransmitted > 1 && nreceived > 1) + return tsum; + return -1; +} + +/** + * Send ping for ipv4 + * + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util4(const char *addr, int count) +{ + return ping_util(4, addr, count); +} + +/** + * Send ping for ipv6 + * + * @addr host name or IP address + * @count number of packets to transmit + * @return ping time in microsecond or -1 if error + */ +int ping_util6(const char *addr, int count) +{ + return ping_util(6, addr, count); } int ping4_run(int argc, char **argv, struct addrinfo *ai, socket_st *sock) @@ -830,14 +890,16 @@ int ping4_run(int argc, char **argv, struct addrinfo *ai, socket_st *sock) if(!(packet = (unsigned char *) malloc((unsigned int) packlen))) error(2, errno, "memory allocation failed"); - printf("PING %s (%s) ", hostname, inet_ntoa(whereto.sin_addr)); + //printf("PING %s (%s) ", hostname, inet_ntoa(whereto.sin_addr)); if(device || (options & F_STRICTSOURCE)) printf("from %s %s: ", inet_ntoa(source.sin_addr), device ? device : ""); - printf("%d(%d) bytes of data.\n", datalen, datalen + 8 + optlen + 20); + //printf("%d(%d) bytes of data.\n", datalen, datalen + 8 + optlen + 20); setup(sock); - + log_printf("main_loop start %s (%s)\n", hostname, inet_ntoa(whereto.sin_addr)); main_loop(&ping4_func_set, sock, packet, packlen); + log_printf("main_loop end\n"); + return 0; } int ping4_receive_error_msg(socket_st *sock) @@ -863,7 +925,8 @@ int ping4_receive_error_msg(socket_st *sock) msg.msg_flags = 0; msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); - + if(!sock) + return net_errors; res = recvmsg(sock->fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); if(res < 0) goto out; @@ -983,7 +1046,7 @@ static void pr_echo_reply(uint8_t *_icp, int len __attribute__((__unused__))) { struct icmphdr *icp = (struct icmphdr *) _icp; - printf(" icmp_seq=%u", ntohs(icp->un.echo.sequence)); + log_printf(" icmp_seq=%u", ntohs(icp->un.echo.sequence)); } int @@ -1039,6 +1102,7 @@ ping4_parse_reply(struct socket_st *sock, struct msghdr *msg, int cc, void *addr csfailed = in_cksum((unsigned short *) icp, cc, 0); if(icp->type == ICMP_ECHOREPLY) { + //log_printf("in ping4_parse_reply00\n"); if(!is_ours(sock, icp->un.echo.id)) return 1; /* 'Twas not our ECHO */ if(!contains_pattern_in_payload((uint8_t*) (icp + 1))) @@ -1050,9 +1114,9 @@ ping4_parse_reply(struct socket_st *sock, struct msghdr *msg, int cc, void *addr fflush(stdout); return 0; } + //log_printf("in ping4_parse_reply01\n"); } else { /* We fall here when a redirect or source quench arrived. */ - switch (icp->type) { case ICMP_ECHO: /* MUST NOT */ @@ -1082,11 +1146,11 @@ ping4_parse_reply(struct socket_st *sock, struct msghdr *msg, int cc, void *addr if(options & (F_QUIET | F_FLOOD)) return 1; print_timestamp(); - printf("From %s: icmp_seq=%u ", + log_printf("From %s: icmp_seq=%u ", pr_addr(from, sizeof *from), ntohs(icp1->un.echo.sequence)); if(csfailed) - printf("(BAD CHECKSUM)"); + log_printf("(BAD CHECKSUM)"); pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp); return 1; } @@ -1106,11 +1170,11 @@ ping4_parse_reply(struct socket_st *sock, struct msghdr *msg, int cc, void *addr if(options & F_PTIMEOFDAY) { struct timeval recv_time; gettimeofday(&recv_time, NULL); - printf("%lu.%06lu ", (unsigned long) recv_time.tv_sec, (unsigned long) recv_time.tv_usec); + log_printf("%lu.%06lu ", (unsigned long) recv_time.tv_sec, (unsigned long) recv_time.tv_usec); } printf("From %s: ", pr_addr(from, sizeof *from)); if(csfailed) { - printf("(BAD CHECKSUM)\n"); + log_printf("(BAD CHECKSUM)\n"); return 0; } pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp); @@ -1118,14 +1182,14 @@ ping4_parse_reply(struct socket_st *sock, struct msghdr *msg, int cc, void *addr } if(options & F_AUDIBLE) { - putchar('\a'); + log_printf("\a"); //putchar('\a'); if(options & F_FLOOD) fflush(stdout); } if(!(options & F_FLOOD)) { pr_options(opts, olen + sizeof(struct iphdr)); - putchar('\n'); + log_printf("\n"); //putchar('\n'); fflush(stdout); } return 0; diff --git a/iputils/ping.h b/iputils/ping.h index bc8483cf7c..97a56ee261 100644 --- a/iputils/ping.h +++ b/iputils/ping.h @@ -29,6 +29,8 @@ #include <linux/filter.h> #include <resolv.h> +#include "iputils.h" + #ifdef HAVE_ERROR_H #include <error.h> #else @@ -339,8 +341,8 @@ extern int pinger(ping_func_set_st *fset, socket_st *sock); extern void sock_setbufs(socket_st*, int alloc); extern void setup(socket_st *); extern int contains_pattern_in_payload(uint8_t *ptr); -extern void main_loop(ping_func_set_st *fset, socket_st*, uint8_t *buf, int buflen) __attribute__((noreturn)); -extern void finish(void) __attribute__((noreturn)); +extern void main_loop(ping_func_set_st *fset, socket_st*, uint8_t *buf, int buflen);// __attribute__((noreturn)); +extern void finish(void);// __attribute__((noreturn)); extern void status(void); extern void common_options(int ch); extern int gather_statistics(uint8_t *ptr, int icmplen, @@ -421,9 +423,4 @@ struct ni_hdr { #define NI_IPV4ADDR_F_TRUNCATE NI_IPV6ADDR_F_TRUNCATE #define NI_IPV4ADDR_F_ALL NI_IPV6ADDR_F_ALL -#define PACKAGE_NAME "ping" -#define PACKAGE_VERSION "0.1" -#define IPUTILS_VERSION(_prog) "%s from %s %s\n", _prog, PACKAGE_NAME, PACKAGE_VERSION - -int ping_util(const char *addr, int count); diff --git a/iputils/ping6_common.c b/iputils/ping6_common.c index b0d53daafa..aa1ebc831d 100755 --- a/iputils/ping6_common.c +++ b/iputils/ping6_common.c @@ -60,10 +60,10 @@ #include "ping.h" ping_func_set_st ping6_func_set = { - .send_probe = ping6_send_probe, - .receive_error_msg = ping6_receive_error_msg, - .parse_reply = ping6_parse_reply, - .install_filter = ping6_install_filter + .send_probe = ping6_send_probe, + .receive_error_msg = ping6_receive_error_msg, + .parse_reply = ping6_parse_reply, + .install_filter = ping6_install_filter }; #ifndef SCOPE_DELIMITER @@ -99,25 +99,25 @@ char *ni_group; static inline int ntohsp(uint16_t *p) { - uint16_t v; - memcpy(&v, p, sizeof(v)); - return ntohs(v); + uint16_t v; + memcpy(&v, p, sizeof(v)); + return ntohs(v); } unsigned int if_name2index(const char *ifname) { - unsigned int i = if_nametoindex(ifname); - if (!i) - error(2, 0, "unknown iface: %s", ifname); - return i; + unsigned int i = if_nametoindex(ifname); + if(!i) + error(2, 0, "unknown iface: %s", ifname); + return i; } struct niquery_option { - char *name; - int namelen; - int has_arg; - int data; - int (*handler)(int index, const char *arg); + char *name; + int namelen; + int has_arg; + int data; + int (*handler)(int index, const char *arg); }; #define NIQUERY_OPTION(_name, _has_arg, _data, _handler) \ @@ -129,8 +129,10 @@ struct niquery_option { .handler = _handler \ } -static int niquery_option_name_handler(int index __attribute__((__unused__)), const char *arg __attribute__((__unused__))); -static int niquery_option_ipv6_handler(int index __attribute__((__unused__)), const char *arg __attribute__((__unused__))); +static int niquery_option_name_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))); +static int niquery_option_ipv6_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))); static int niquery_option_ipv6_flag_handler(int index, const char *arg); static int niquery_option_ipv4_handler(int index, const char *arg); static int niquery_option_ipv4_flag_handler(int index, const char *arg); @@ -139,57 +141,57 @@ static int niquery_option_subject_name_handler(int index, const char *arg); static int niquery_option_help_handler(int index, const char *arg); struct niquery_option niquery_options[] = { - NIQUERY_OPTION("name", 0, 0, niquery_option_name_handler), - NIQUERY_OPTION("fqdn", 0, 0, niquery_option_name_handler), - NIQUERY_OPTION("ipv6", 0, 0, niquery_option_ipv6_handler), - NIQUERY_OPTION("ipv6-all", 0, NI_IPV6ADDR_F_ALL, niquery_option_ipv6_flag_handler), - NIQUERY_OPTION("ipv6-compatible", 0, NI_IPV6ADDR_F_COMPAT, niquery_option_ipv6_flag_handler), - NIQUERY_OPTION("ipv6-linklocal", 0, NI_IPV6ADDR_F_LINKLOCAL, niquery_option_ipv6_flag_handler), - NIQUERY_OPTION("ipv6-sitelocal", 0, NI_IPV6ADDR_F_SITELOCAL, niquery_option_ipv6_flag_handler), - NIQUERY_OPTION("ipv6-global", 0, NI_IPV6ADDR_F_GLOBAL, niquery_option_ipv6_flag_handler), - NIQUERY_OPTION("ipv4", 0, 0, niquery_option_ipv4_handler), - NIQUERY_OPTION("ipv4-all", 0, NI_IPV4ADDR_F_ALL, niquery_option_ipv4_flag_handler), - NIQUERY_OPTION("subject-ipv6", 1, NI_SUBJ_IPV6, niquery_option_subject_addr_handler), - NIQUERY_OPTION("subject-ipv4", 1, NI_SUBJ_IPV4, niquery_option_subject_addr_handler), - NIQUERY_OPTION("subject-name", 1, 0, niquery_option_subject_name_handler), - NIQUERY_OPTION("subject-fqdn", 1, -1, niquery_option_subject_name_handler), - NIQUERY_OPTION("help", 0, 0, niquery_option_help_handler), - {NULL, 0, 0, 0, NULL} +NIQUERY_OPTION("name", 0, 0, niquery_option_name_handler), +NIQUERY_OPTION("fqdn", 0, 0, niquery_option_name_handler), +NIQUERY_OPTION("ipv6", 0, 0, niquery_option_ipv6_handler), +NIQUERY_OPTION("ipv6-all", 0, NI_IPV6ADDR_F_ALL, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv6-compatible", 0, NI_IPV6ADDR_F_COMPAT, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv6-linklocal", 0, NI_IPV6ADDR_F_LINKLOCAL, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv6-sitelocal", 0, NI_IPV6ADDR_F_SITELOCAL, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv6-global", 0, NI_IPV6ADDR_F_GLOBAL, niquery_option_ipv6_flag_handler), +NIQUERY_OPTION("ipv4", 0, 0, niquery_option_ipv4_handler), +NIQUERY_OPTION("ipv4-all", 0, NI_IPV4ADDR_F_ALL, niquery_option_ipv4_flag_handler), +NIQUERY_OPTION("subject-ipv6", 1, NI_SUBJ_IPV6, niquery_option_subject_addr_handler), +NIQUERY_OPTION("subject-ipv4", 1, NI_SUBJ_IPV4, niquery_option_subject_addr_handler), +NIQUERY_OPTION("subject-name", 1, 0, niquery_option_subject_name_handler), +NIQUERY_OPTION("subject-fqdn", 1, -1, niquery_option_subject_name_handler), +NIQUERY_OPTION("help", 0, 0, niquery_option_help_handler), + { NULL, 0, 0, 0, NULL } }; static inline int niquery_is_enabled(void) { - return ni_query >= 0; + return ni_query >= 0; } #if PING6_NONCE_MEMORY uint8_t *ni_nonce_ptr; #else struct { - struct timeval tv; - pid_t pid; + struct timeval tv; + pid_t pid; } ni_nonce_secret; #endif static void niquery_init_nonce(void) { #if PING6_NONCE_MEMORY - struct timeval tv; - unsigned long seed; + struct timeval tv; + unsigned long seed; - seed = (unsigned long)getpid(); - if (!gettimeofday(&tv, NULL)) - seed ^= tv.tv_usec; - srand(seed); + seed = (unsigned long)getpid(); + if (!gettimeofday(&tv, NULL)) + seed ^= tv.tv_usec; + srand(seed); - ni_nonce_ptr = calloc(NI_NONCE_SIZE, MAX_DUP_CHK); - if (!ni_nonce_ptr) - error(2, errno, "calloc"); + ni_nonce_ptr = calloc(NI_NONCE_SIZE, MAX_DUP_CHK); + if (!ni_nonce_ptr) + error(2, errno, "calloc"); - ni_nonce_ptr[0] = ~0; + ni_nonce_ptr[0] = ~0; #else - gettimeofday(&ni_nonce_secret.tv, NULL); - ni_nonce_secret.pid = getpid(); + gettimeofday(&ni_nonce_secret.tv, NULL); + ni_nonce_secret.pid = getpid(); #endif } @@ -197,124 +199,130 @@ static void niquery_init_nonce(void) static int niquery_nonce(uint8_t *nonce, int fill) { # ifdef USE_CRYPTO - static uint8_t digest[MD5_DIGEST_LENGTH]; - static int seq = -1; - - if (fill || seq != *(uint16_t *)nonce || seq < 0) { - MD5_CTX ctxt; - - MD5_Init(&ctxt); - MD5_Update(&ctxt, &ni_nonce_secret, sizeof(ni_nonce_secret)); - MD5_Update(&ctxt, nonce, sizeof(uint16_t)); - MD5_Final(digest, &ctxt); - - seq = *(uint16_t *)nonce; - } - - if (fill) { - memcpy(nonce + sizeof(uint16_t), digest, NI_NONCE_SIZE - sizeof(uint16_t)); - return 0; - } else { - if (memcmp(nonce + sizeof(uint16_t), digest, NI_NONCE_SIZE - sizeof(uint16_t))) - return -1; - return ntohsp((uint16_t *)nonce); - } + static uint8_t digest[MD5_DIGEST_LENGTH]; + static int seq = -1; + + if (fill || seq != *(uint16_t *)nonce || seq < 0) { + MD5_CTX ctxt; + + MD5_Init(&ctxt); + MD5_Update(&ctxt, &ni_nonce_secret, sizeof(ni_nonce_secret)); + MD5_Update(&ctxt, nonce, sizeof(uint16_t)); + MD5_Final(digest, &ctxt); + + seq = *(uint16_t *)nonce; + } + + if (fill) { + memcpy(nonce + sizeof(uint16_t), digest, NI_NONCE_SIZE - sizeof(uint16_t)); + return 0; + } else { + if (memcmp(nonce + sizeof(uint16_t), digest, NI_NONCE_SIZE - sizeof(uint16_t))) + return -1; + return ntohsp((uint16_t *)nonce); + } # else - error(3, ENOSYS, "niquery_nonce() crypto disabled"); + error(3, ENOSYS, "niquery_nonce() crypto disabled"); # endif + if(nonce || fill) + return -1; + return -1; } #endif static inline void niquery_fill_nonce(uint16_t seq, uint8_t *nonce) { - uint16_t v = htons(seq); + uint16_t v = htons(seq); #if PING6_NONCE_MEMORY - int i; + int i; - memcpy(&ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], &v, sizeof(v)); + memcpy(&ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], &v, sizeof(v)); - for (i = sizeof(v); i < NI_NONCE_SIZE; i++) - ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK) + i] = 0x100 * (rand() / (RAND_MAX + 1.0)); + for (i = sizeof(v); i < NI_NONCE_SIZE; i++) + ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK) + i] = 0x100 * (rand() / (RAND_MAX + 1.0)); - memcpy(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE); + memcpy(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE); #else - memcpy(nonce, &v, sizeof(v)); - niquery_nonce(nonce, 1); + memcpy(nonce, &v, sizeof(v)); + niquery_nonce(nonce, 1); #endif } static inline int niquery_check_nonce(uint8_t *nonce) { #if PING6_NONCE_MEMORY - uint16_t seq = ntohsp((uint16_t *)nonce); - if (memcmp(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE)) - return -1; - return seq; + uint16_t seq = ntohsp((uint16_t *)nonce); + if (memcmp(nonce, &ni_nonce_ptr[NI_NONCE_SIZE * (seq % MAX_DUP_CHK)], NI_NONCE_SIZE)) + return -1; + return seq; #else - return niquery_nonce(nonce, 0); + return niquery_nonce(nonce, 0); #endif } static int niquery_set_qtype(int type) { - if (niquery_is_enabled() && ni_query != type) { - printf("Qtype conflict\n"); - return -1; - } - ni_query = type; - return 0; + if(niquery_is_enabled() && ni_query != type) { + printf("Qtype conflict\n"); + return -1; + } + ni_query = type; + return 0; } -static int niquery_option_name_handler(int index __attribute__((__unused__)), const char *arg __attribute__((__unused__))) +static int niquery_option_name_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))) { - if (niquery_set_qtype(NI_QTYPE_NAME) < 0) - return -1; - return 0; + if(niquery_set_qtype(NI_QTYPE_NAME) < 0) + return -1; + return 0; } -static int niquery_option_ipv6_handler(int index __attribute__((__unused__)), const char *arg __attribute__((__unused__))) +static int niquery_option_ipv6_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))) { - if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) - return -1; - return 0; + if(niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) + return -1; + return 0; } static int niquery_option_ipv6_flag_handler(int index, const char *arg __attribute__((__unused__))) { - if (niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) - return -1; - ni_flag |= niquery_options[index].data; - return 0; + if(niquery_set_qtype(NI_QTYPE_IPV6ADDR) < 0) + return -1; + ni_flag |= niquery_options[index].data; + return 0; } -static int niquery_option_ipv4_handler(int index __attribute__((__unused__)), const char *arg __attribute__((__unused__))) +static int niquery_option_ipv4_handler(int index __attribute__((__unused__)), + const char *arg __attribute__((__unused__))) { - if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) - return -1; - return 0; + if(niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) + return -1; + return 0; } static int niquery_option_ipv4_flag_handler(int index, const char *arg __attribute__((__unused__))) { - if (niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) - return -1; - ni_flag |= niquery_options[index].data; - return 0; + if(niquery_set_qtype(NI_QTYPE_IPV4ADDR) < 0) + return -1; + ni_flag |= niquery_options[index].data; + return 0; } static inline int niquery_is_subject_valid(void) { - return ni_subject_type >= 0 && ni_subject; + return ni_subject_type >= 0 && ni_subject; } static int niquery_set_subject_type(int type) { - if (niquery_is_subject_valid() && ni_subject_type != type) { - printf("Subject type conflict\n"); - return -1; - } - ni_subject_type = type; - return 0; + if(niquery_is_subject_valid() && ni_subject_type != type) { + printf("Subject type conflict\n"); + return -1; + } + ni_subject_type = type; + return 0; } #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) @@ -322,50 +330,50 @@ static int niquery_set_subject_type(int type) static int niquery_option_subject_addr_handler(int index, const char *arg) { - struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_flags = getaddrinfo_flags }; - struct addrinfo *result, *ai; - int status; - int offset; - - if (niquery_set_subject_type(niquery_options[index].data) < 0) - return -1; - - ni_subject_type = niquery_options[index].data; - - switch (niquery_options[index].data) { - case NI_SUBJ_IPV6: - ni_subject_len = sizeof(struct in6_addr); - offset = OFFSET_OF(struct sockaddr_in6, sin6_addr); - hints.ai_family = AF_INET6; - break; - case NI_SUBJ_IPV4: - ni_subject_len = sizeof(struct in_addr); - offset = OFFSET_OF(struct sockaddr_in, sin_addr); - hints.ai_family = AF_INET; - break; - default: - /* should not happen. */ - offset = -1; - } - - status = getaddrinfo(arg, 0, &hints, &result); - if (status) { - error(0, 0, "%s: %s", arg, gai_strerror(status)); - return -1; - } - - for (ai = result; ai; ai = ai->ai_next) { - void *p = malloc(ni_subject_len); - if (!p) - continue; - memcpy(p, (uint8_t *)ai->ai_addr + offset, ni_subject_len); - free(ni_subject); - ni_subject = p; - break; - } - freeaddrinfo(result); - - return 0; + struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_flags = getaddrinfo_flags }; + struct addrinfo *result, *ai; + int status; + int offset; + + if(niquery_set_subject_type(niquery_options[index].data) < 0) + return -1; + + ni_subject_type = niquery_options[index].data; + + switch (niquery_options[index].data) { + case NI_SUBJ_IPV6: + ni_subject_len = sizeof(struct in6_addr); + offset = OFFSET_OF(struct sockaddr_in6, sin6_addr); + hints.ai_family = AF_INET6; + break; + case NI_SUBJ_IPV4: + ni_subject_len = sizeof(struct in_addr); + offset = OFFSET_OF(struct sockaddr_in, sin_addr); + hints.ai_family = AF_INET; + break; + default: + /* should not happen. */ + offset = -1; + } + + status = getaddrinfo(arg, 0, &hints, &result); + if(status) { + error(0, 0, "%s: %s", arg, gai_strerror(status)); + return -1; + } + + for(ai = result; ai; ai = ai->ai_next) { + void *p = malloc(ni_subject_len); + if(!p) + continue; + memcpy(p, (uint8_t *) ai->ai_addr + offset, ni_subject_len); + free(ni_subject); + ni_subject = p; + break; + } + freeaddrinfo(result); + + return 0; } #ifdef USE_IDN @@ -379,551 +387,553 @@ static int niquery_option_subject_addr_handler(int index, const char *arg) #ifdef USE_CRYPTO static int niquery_option_subject_name_handler(int index, const char *name) { - static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ]; - unsigned char *dnptrs[2], **dpp, **lastdnptr; - int n; - size_t i; - char *p; - char *canonname = NULL, *idn = NULL; - unsigned char *buf = NULL; - size_t namelen; - size_t buflen; - int dots, fqdn = niquery_options[index].data; - MD5_CTX ctxt; - uint8_t digest[MD5_DIGEST_LENGTH]; + static char nigroup_buf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ]; + unsigned char *dnptrs[2], **dpp, **lastdnptr; + int n; + size_t i; + char *p; + char *canonname = NULL, *idn = NULL; + unsigned char *buf = NULL; + size_t namelen; + size_t buflen; + int dots, fqdn = niquery_options[index].data; + MD5_CTX ctxt; + uint8_t digest[MD5_DIGEST_LENGTH]; #ifdef USE_IDN - int rc; + int rc; #endif - if (niquery_set_subject_type(NI_SUBJ_NAME) < 0) - return -1; + if (niquery_set_subject_type(NI_SUBJ_NAME) < 0) + return -1; #ifdef USE_IDN - rc = idn2_lookup_ul(name, &idn, IDN2_FLAGS); - if (rc) - error(2, 0, "IDN encoding error: %s", idn2_strerror(rc)); + rc = idn2_lookup_ul(name, &idn, IDN2_FLAGS); + if (rc) + error(2, 0, "IDN encoding error: %s", idn2_strerror(rc)); #else - idn = strdup(name); - if (!idn) - goto oomexit; + idn = strdup(name); + if (!idn) + goto oomexit; #endif - p = strchr(idn, SCOPE_DELIMITER); - if (p) { - *p = '\0'; - if (strlen(p + 1) >= IFNAMSIZ) - error(1, 0, "too long scope name"); - } - - namelen = strlen(idn); - canonname = malloc(namelen + 1); - if (!canonname) - goto oomexit; - - dots = 0; - for (i = 0; i < namelen + 1; i++) { - canonname[i] = isupper(idn[i]) ? tolower(idn[i]) : idn[i]; - if (idn[i] == '.') - dots++; - } - - if (fqdn == 0) { - /* guess if hostname is FQDN */ - fqdn = dots ? 1 : -1; - } - - buflen = namelen + 3 + 1; /* dn_comp() requrires strlen() + 3, - plus non-fqdn indicator. */ - buf = malloc(buflen); - if (!buf) { - error(0, errno, "memory allocation failed"); - goto errexit; - } - - dpp = dnptrs; - lastdnptr = &dnptrs[ARRAY_SIZE(dnptrs)]; - - *dpp++ = (unsigned char *)buf; - *dpp++ = NULL; - - n = dn_comp(canonname, (unsigned char *)buf, buflen, dnptrs, lastdnptr); - if (n < 0) { - error(0, 0, "inappropriate subject name: %s", canonname); - goto errexit; - } else if ((size_t) n >= buflen) { - error(0, 0, "dn_comp() returned too long result"); - goto errexit; - } - - MD5_Init(&ctxt); - MD5_Update(&ctxt, buf, buf[0]); - MD5_Final(digest, &ctxt); - - sprintf(nigroup_buf, "ff02::2:%02x%02x:%02x%02x%s%s", - digest[0], digest[1], digest[2], digest[3], - p ? "%" : "", - p ? p + 1 : ""); - - if (fqdn < 0) - buf[n] = 0; - - free(ni_subject); - - ni_group = nigroup_buf; - ni_subject = buf; - ni_subject_len = n + (fqdn < 0); - ni_group = nigroup_buf; - - free(canonname); - free(idn); - - return 0; -oomexit: - error(0, errno, "memory allocation failed"); -errexit: - free(buf); - free(canonname); - free(idn); - exit(1); + p = strchr(idn, SCOPE_DELIMITER); + if (p) { + *p = '\0'; + if (strlen(p + 1) >= IFNAMSIZ) + error(1, 0, "too long scope name"); + } + + namelen = strlen(idn); + canonname = malloc(namelen + 1); + if (!canonname) + goto oomexit; + + dots = 0; + for (i = 0; i < namelen + 1; i++) { + canonname[i] = isupper(idn[i]) ? tolower(idn[i]) : idn[i]; + if (idn[i] == '.') + dots++; + } + + if (fqdn == 0) { + /* guess if hostname is FQDN */ + fqdn = dots ? 1 : -1; + } + + buflen = namelen + 3 + 1; /* dn_comp() requrires strlen() + 3, + plus non-fqdn indicator. */ + buf = malloc(buflen); + if (!buf) { + error(0, errno, "memory allocation failed"); + goto errexit; + } + + dpp = dnptrs; + lastdnptr = &dnptrs[ARRAY_SIZE(dnptrs)]; + + *dpp++ = (unsigned char *)buf; + *dpp++ = NULL; + + n = dn_comp(canonname, (unsigned char *)buf, buflen, dnptrs, lastdnptr); + if (n < 0) { + error(0, 0, "inappropriate subject name: %s", canonname); + goto errexit; + } else if ((size_t) n >= buflen) { + error(0, 0, "dn_comp() returned too long result"); + goto errexit; + } + + MD5_Init(&ctxt); + MD5_Update(&ctxt, buf, buf[0]); + MD5_Final(digest, &ctxt); + + sprintf(nigroup_buf, "ff02::2:%02x%02x:%02x%02x%s%s", + digest[0], digest[1], digest[2], digest[3], + p ? "%" : "", + p ? p + 1 : ""); + + if (fqdn < 0) + buf[n] = 0; + + free(ni_subject); + + ni_group = nigroup_buf; + ni_subject = buf; + ni_subject_len = n + (fqdn < 0); + ni_group = nigroup_buf; + + free(canonname); + free(idn); + + return 0; + oomexit: + error(0, errno, "memory allocation failed"); + errexit: + free(buf); + free(canonname); + free(idn); + exit(1); } #else static int niquery_option_subject_name_handler(int index __attribute__((__unused__)), - const char *name __attribute__((__unused__))) + const char *name __attribute__((__unused__))) { - error(3, ENOSYS, "niquery_option_subject_name_handler() crypto disabled"); + error(3, ENOSYS, "niquery_option_subject_name_handler() crypto disabled"); + return -1; } #endif int niquery_option_help_handler(int index __attribute__((__unused__)), const char *arg __attribute__((__unused__))) { - fprintf(stderr, "ping -6 -N <nodeinfo opt>\n" - "Help:\n" - " help\n" - "Query:\n" - " name\n" - " ipv6\n" - " ipv6-all\n" - " ipv6-compatible\n" - " ipv6-global\n" - " ipv6-linklocal\n" - " ipv6-sitelocal\n" - " ipv4\n" - " ipv4-all\n" - "Subject:\n" - " subject-ipv6=addr\n" - " subject-ipv4=addr\n" - " subject-name=name\n" - " subject-fqdn=name\n" - ); - exit(2); + fprintf(stderr, "ping -6 -N <nodeinfo opt>\n" + "Help:\n" + " help\n" + "Query:\n" + " name\n" + " ipv6\n" + " ipv6-all\n" + " ipv6-compatible\n" + " ipv6-global\n" + " ipv6-linklocal\n" + " ipv6-sitelocal\n" + " ipv4\n" + " ipv4-all\n" + "Subject:\n" + " subject-ipv6=addr\n" + " subject-ipv4=addr\n" + " subject-name=name\n" + " subject-fqdn=name\n" + ); + exit(2); } int niquery_option_handler(const char *opt_arg) { - struct niquery_option *p; - int i; - int ret = -1; - for (i = 0, p = niquery_options; p->name; i++, p++) { - if (strncmp(p->name, opt_arg, p->namelen)) - continue; - if (!p->has_arg) { - if (opt_arg[p->namelen] == '\0') { - ret = p->handler(i, NULL); - if (ret >= 0) - break; - } - } else { - if (opt_arg[p->namelen] == '=') { - ret = p->handler(i, &opt_arg[p->namelen] + 1); - if (ret >= 0) - break; - } - } - } - if (!p->name) - ret = niquery_option_help_handler(0, NULL); - return ret; + struct niquery_option *p; + int i; + int ret = -1; + for(i = 0, p = niquery_options; p->name; i++, p++) { + if(strncmp(p->name, opt_arg, p->namelen)) + continue; + if(!p->has_arg) { + if(opt_arg[p->namelen] == '\0') { + ret = p->handler(i, NULL); + if(ret >= 0) + break; + } + } else { + if(opt_arg[p->namelen] == '=') { + ret = p->handler(i, &opt_arg[p->namelen] + 1); + if(ret >= 0) + break; + } + } + } + if(!p->name) + ret = niquery_option_help_handler(0, NULL); + return ret; } int ping6_run(int argc, char **argv, struct addrinfo *ai, struct socket_st *sock) { - static const struct addrinfo hints = { .ai_family = AF_INET6, .ai_flags = getaddrinfo_flags }; - struct addrinfo *result = NULL; - int status; - int hold, packlen; - unsigned char *packet; - char *target; - struct icmp6_filter filter; - int err; - static uint32_t scope_id = 0; - - if (niquery_is_enabled()) { - niquery_init_nonce(); - - if (!niquery_is_subject_valid()) { - ni_subject = &whereto.sin6_addr; - ni_subject_len = sizeof(whereto.sin6_addr); - ni_subject_type = NI_SUBJ_IPV6; - } - } - - if (argc > 1) { - usage(); - } else if (argc == 1) { - target = *argv; - } else { - if (ni_query < 0 && ni_subject_type != NI_SUBJ_NAME) - usage(); - target = ni_group; - } - - if (!ai) { - status = getaddrinfo(target, NULL, &hints, &result); - if (status) - error(2, 0, "%s: %s", target, gai_strerror(status)); - ai = result; - } - - memcpy(&whereto, ai->ai_addr, sizeof(whereto)); - whereto.sin6_port = htons(IPPROTO_ICMPV6); - - if (result) - freeaddrinfo(result); - - if (memchr(target, ':', strlen(target))) - options |= F_NUMERIC; - - if (IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { - memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16); - firsthop.sin6_scope_id = whereto.sin6_scope_id; - /* Verify scope_id is the same as intermediate nodes */ - if (firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) - error(2, 0, "scope discrepancy among the nodes"); - else if (!scope_id) - scope_id = firsthop.sin6_scope_id; - } - - hostname = target; - - if (IN6_IS_ADDR_UNSPECIFIED(&source6.sin6_addr)) { - socklen_t alen; - int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); - - if (probe_fd < 0) - error(2, errno, "socket"); - if (device) { - unsigned int iface = if_name2index(device); + static const struct addrinfo hints = { .ai_family = AF_INET6, .ai_flags = getaddrinfo_flags }; + struct addrinfo *result = NULL; + int status; + int hold, packlen; + unsigned char *packet; + char *target; + struct icmp6_filter filter; + int err; + static uint32_t scope_id = 0; + + if(niquery_is_enabled()) { + niquery_init_nonce(); + + if(!niquery_is_subject_valid()) { + ni_subject = &whereto.sin6_addr; + ni_subject_len = sizeof(whereto.sin6_addr); + ni_subject_type = NI_SUBJ_IPV6; + } + } + + if(argc > 1) { + usage(); + } else if(argc == 1) { + target = *argv; + } else { + if(ni_query < 0 && ni_subject_type != NI_SUBJ_NAME) + usage(); + target = ni_group; + } + + if(!ai) { + status = getaddrinfo(target, NULL, &hints, &result); + if(status) + error(2, 0, "%s: %s", target, gai_strerror(status)); + ai = result; + } + + memcpy(&whereto, ai->ai_addr, sizeof(whereto)); + whereto.sin6_port = htons(IPPROTO_ICMPV6); + + if(result) + freeaddrinfo(result); + + if(memchr(target, ':', strlen(target))) + options |= F_NUMERIC; + + if(IN6_IS_ADDR_UNSPECIFIED(&firsthop.sin6_addr)) { + memcpy(&firsthop.sin6_addr, &whereto.sin6_addr, 16); + firsthop.sin6_scope_id = whereto.sin6_scope_id; + /* Verify scope_id is the same as intermediate nodes */ + if(firsthop.sin6_scope_id && scope_id && firsthop.sin6_scope_id != scope_id) + error(2, 0, "scope discrepancy among the nodes"); + else if(!scope_id) + scope_id = firsthop.sin6_scope_id; + } + + hostname = target; + + if(IN6_IS_ADDR_UNSPECIFIED(&source6.sin6_addr)) { + socklen_t alen; + int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); + + if(probe_fd < 0) + error(2, errno, "socket"); + if(device) { + unsigned int iface = if_name2index(device); #ifdef IPV6_RECVPKTINFO - struct in6_pktinfo ipi; + struct in6_pktinfo ipi; - memset(&ipi, 0, sizeof(ipi)); - ipi.ipi6_ifindex = iface; + memset(&ipi, 0, sizeof(ipi)); + ipi.ipi6_ifindex = iface; #endif - if (IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) || - IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) - firsthop.sin6_scope_id = iface; - enable_capability_raw(); + if(IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) + firsthop.sin6_scope_id = iface; + enable_capability_raw(); #ifdef IPV6_RECVPKTINFO - if ( - setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof ipi) == -1 || - setsockopt(sock->fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof ipi) == -1) { - perror("setsockopt(IPV6_PKTINFO)"); - exit(2); - } + if( + setsockopt(probe_fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof ipi) == -1 || + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_PKTINFO, &ipi, sizeof ipi) == -1) { + perror("setsockopt(IPV6_PKTINFO)"); + exit(2); + } #endif - if ( - setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1 || - setsockopt(sock->fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) { - error(2, errno, "setsockopt(SO_BINDTODEVICE) %s", device); - } - disable_capability_raw(); - } - - if (!IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) && - !IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) - firsthop.sin6_family = AF_INET6; - - firsthop.sin6_port = htons(1025); - if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) - error(2, errno, "connect"); - alen = sizeof source6; - if (getsockname(probe_fd, (struct sockaddr *) &source6, &alen) == -1) - error(2, errno, "getsockname"); - source6.sin6_port = 0; - close(probe_fd); - - if (device) { - struct ifaddrs *ifa0, *ifa; - - if (getifaddrs(&ifa0)) - error(2, errno, "getifaddrs"); - - for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { - if (!ifa->ifa_name || !ifa->ifa_addr || - ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (!strcmp(ifa->ifa_name, device) && - IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, - &source6.sin6_addr)) - break; - } - if (!ifa) - error(0, 0, "Warning: source address might be selected on device other than: %s", device); - - freeifaddrs(ifa0); - } - } - else if (device && (IN6_IS_ADDR_LINKLOCAL(&source6.sin6_addr) || - IN6_IS_ADDR_MC_LINKLOCAL(&source6.sin6_addr))) - source6.sin6_scope_id = if_name2index(device); - - if (device) { - struct cmsghdr *cmsg; - struct in6_pktinfo *ipi; - - cmsg = (struct cmsghdr*)(cmsgbuf+cmsglen); - cmsglen += CMSG_SPACE(sizeof(*ipi)); - cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - - ipi = (struct in6_pktinfo*)CMSG_DATA(cmsg); - memset(ipi, 0, sizeof(*ipi)); - ipi->ipi6_ifindex = if_name2index(device); - } - - if ((whereto.sin6_addr.s6_addr16[0]&htons(0xff00)) == htons (0xff00)) { - if (uid) { - if (interval < 1000) - error(2, 0, "multicast ping with too short interval: %d", interval); - if (pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) - error(2, 0, "multicast ping does not fragment"); - } - if (pmtudisc < 0) - pmtudisc = IPV6_PMTUDISC_DO; - } - - if (pmtudisc >= 0) { - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof pmtudisc) == -1) - error(2, errno, "IPV6_MTU_DISCOVER"); - } - - if ((options&F_STRICTSOURCE) && - bind(sock->fd, (struct sockaddr *) &source6, sizeof source6) == -1) - error(2, errno, "bind icmp socket"); - - if ((ssize_t) datalen >= (ssize_t) sizeof(struct timeval) && (ni_query < 0)) { - /* can we time transfer */ - timing = 1; - } - packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */ - if (!(packet = (unsigned char *)malloc((unsigned int)packlen))) - error(2, errno, "memory allocation failed"); - - hold = 1; - - /* Estimate memory eaten by single packet. It is rough estimate. - * Actually, for small datalen's it depends on kernel side a lot. */ - hold = datalen+8; - hold += ((hold+511)/512)*(40+16+64+160); - sock_setbufs(sock, hold); + if( + setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1) == -1 || + setsockopt(sock->fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1) == -1) { + error(2, errno, "setsockopt(SO_BINDTODEVICE) %s", device); + } + disable_capability_raw(); + } + + if(!IN6_IS_ADDR_LINKLOCAL(&firsthop.sin6_addr) && + !IN6_IS_ADDR_MC_LINKLOCAL(&firsthop.sin6_addr)) + firsthop.sin6_family = AF_INET6; + + firsthop.sin6_port = htons(1025); + if(connect(probe_fd, (struct sockaddr*) &firsthop, sizeof(firsthop)) == -1) + error(2, errno, "connect"); + alen = sizeof source6; + if(getsockname(probe_fd, (struct sockaddr *) &source6, &alen) == -1) + error(2, errno, "getsockname"); + source6.sin6_port = 0; + close(probe_fd); + + if(device) { + struct ifaddrs *ifa0, *ifa; + + if(getifaddrs(&ifa0)) + error(2, errno, "getifaddrs"); + + for(ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if(!ifa->ifa_name || !ifa->ifa_addr || + ifa->ifa_addr->sa_family != AF_INET6) + continue; + if(!strcmp(ifa->ifa_name, device) && + IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 * )ifa->ifa_addr)->sin6_addr, + &source6.sin6_addr)) + break; + } + if(!ifa) + error(0, 0, "Warning: source address might be selected on device other than: %s", device); + + freeifaddrs(ifa0); + } + } + else if(device && (IN6_IS_ADDR_LINKLOCAL(&source6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&source6.sin6_addr))) + source6.sin6_scope_id = if_name2index(device); + + if(device) { + struct cmsghdr *cmsg; + struct in6_pktinfo *ipi; + + cmsg = (struct cmsghdr*) (cmsgbuf + cmsglen); + cmsglen += CMSG_SPACE(sizeof(*ipi)); + cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + ipi = (struct in6_pktinfo*) CMSG_DATA(cmsg); + memset(ipi, 0, sizeof(*ipi)); + ipi->ipi6_ifindex = if_name2index(device); + } + + if((whereto.sin6_addr.s6_addr16[0] & htons(0xff00)) == htons(0xff00)) { + if(uid) { + if(interval < 1000) + error(2, 0, "multicast ping with too short interval: %d", interval); + if(pmtudisc >= 0 && pmtudisc != IPV6_PMTUDISC_DO) + error(2, 0, "multicast ping does not fragment"); + } + if(pmtudisc < 0) + pmtudisc = IPV6_PMTUDISC_DO; + } + + if(pmtudisc >= 0) { + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &pmtudisc, sizeof pmtudisc) == -1) + error(2, errno, "IPV6_MTU_DISCOVER"); + } + + if((options & F_STRICTSOURCE) && + bind(sock->fd, (struct sockaddr *) &source6, sizeof source6) == -1) + error(2, errno, "bind icmp socket"); + + if((ssize_t) datalen >= (ssize_t) sizeof(struct timeval) && (ni_query < 0)) { + /* can we time transfer */ + timing = 1; + } + packlen = datalen + 8 + 4096 + 40 + 8; /* 4096 for rthdr */ + if(!(packet = (unsigned char *) malloc((unsigned int) packlen))) + error(2, errno, "memory allocation failed"); + + hold = 1; + + /* Estimate memory eaten by single packet. It is rough estimate. + * Actually, for small datalen's it depends on kernel side a lot. */ + hold = datalen + 8; + hold += ((hold + 511) / 512) * (40 + 16 + 64 + 160); + sock_setbufs(sock, hold); #ifdef __linux__ - if (sock->socktype == SOCK_RAW) { - int csum_offset = 2; - int sz_opt = sizeof(int); - - err = setsockopt(sock->fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt); - if (err < 0) { - /* checksum should be enabled by default and setting this - * option might fail anyway. - */ - error(0, errno, "setsockopt(RAW_CHECKSUM) failed - try to continue"); - } + if(sock->socktype == SOCK_RAW) { + int csum_offset = 2; + int sz_opt = sizeof(int); + + err = setsockopt(sock->fd, SOL_RAW, IPV6_CHECKSUM, &csum_offset, sz_opt); + if(err < 0) { + /* checksum should be enabled by default and setting this + * option might fail anyway. + */ + error(0, errno, "setsockopt(RAW_CHECKSUM) failed - try to continue"); + } #else - { + { #endif - /* - * select icmp echo reply as icmp type to receive - */ - - ICMP6_FILTER_SETBLOCKALL(&filter); - - ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filter); - ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filter); - ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filter); - ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filter); - - if (niquery_is_enabled()) - ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter); - else - ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter); - - err = setsockopt(sock->fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof filter); - - if (err < 0) - error(2, errno, "setsockopt(ICMP6_FILTER)"); - } - - if (options & F_NOLOOP) { - int loop = 0; - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof loop) == -1) - error(2, errno, "can't disable multicast loopback"); - } - if (options & F_TTL) { - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof ttl) == -1) - error(2, errno, "can't set multicast hop limit"); - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof ttl) == -1) - error(2, errno, "can't set unicast hop limit"); - } - - const int on = 1; - if ( -#ifdef IPV6_RECVHOPLIMIT - setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof on) == -1 && - setsockopt(sock->fd, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof on) == -1 -#else - setsockopt(sock->fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof on) == -1 + /* + * select icmp echo reply as icmp type to receive + */ + + ICMP6_FILTER_SETBLOCKALL(&filter); + + ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &filter); + ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &filter); + ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &filter); + ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &filter); + + if(niquery_is_enabled()) + ICMP6_FILTER_SETPASS(ICMPV6_NI_REPLY, &filter); + else + ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter); + + err = setsockopt(sock->fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof filter); + + if(err < 0) + error(2, errno, "setsockopt(ICMP6_FILTER)"); + } + + if(options & F_NOLOOP) { + int loop = 0; + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof loop) == -1) + error(2, errno, "can't disable multicast loopback"); + } + if(options & F_TTL) { + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof ttl) == -1) + error(2, errno, "can't set multicast hop limit"); + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof ttl) == -1) + error(2, errno, "can't set unicast hop limit"); + } + + const int on = 1; + if( + #ifdef IPV6_RECVHOPLIMIT + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof on) == -1 && + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof on) == -1 + #else + setsockopt(sock->fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof on) == -1 #endif - ) - error(2, errno, "can't receive hop limit"); + ) + error(2, errno, "can't receive hop limit"); - if (options & F_TCLASS) { + if(options & F_TCLASS) { #ifdef IPV6_TCLASS - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof tclass) == -1) - error(2, errno, "setsockopt(IPV6_TCLASS)"); + if(setsockopt(sock->fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof tclass) == -1) + error(2, errno, "setsockopt(IPV6_TCLASS)"); #else - error(0, 0, "traffic class is not supported"); + error(0, 0, "traffic class is not supported"); #endif - } + } - if (options&F_FLOWINFO) { + if(options & F_FLOWINFO) { #ifdef IPV6_FLOWLABEL_MGR - char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen]; - struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf; - int freq_len = sizeof(*freq); - memset(freq, 0, sizeof(*freq)); - freq->flr_label = htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL); - freq->flr_action = IPV6_FL_A_GET; - freq->flr_flags = IPV6_FL_F_CREATE; - freq->flr_share = IPV6_FL_S_EXCL; - memcpy(&freq->flr_dst, &whereto.sin6_addr, 16); - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) == -1) - error(2, errno, "can't set flowlabel"); - flowlabel = freq->flr_label; + char freq_buf[CMSG_ALIGN(sizeof(struct in6_flowlabel_req)) + cmsglen]; + struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf; + int freq_len = sizeof(*freq); + memset(freq, 0, sizeof(*freq)); + freq->flr_label = htonl(flowlabel & IPV6_FLOWINFO_FLOWLABEL); + freq->flr_action = IPV6_FL_A_GET; + freq->flr_flags = IPV6_FL_F_CREATE; + freq->flr_share = IPV6_FL_S_EXCL; + memcpy(&freq->flr_dst, &whereto.sin6_addr, 16); + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) == -1) + error(2, errno, "can't set flowlabel"); + flowlabel = freq->flr_label; #else - error(2, 0, "flow labels are not supported"); + error(2, 0, "flow labels are not supported"); #endif #ifdef IPV6_FLOWINFO_SEND - whereto.sin6_flowinfo = flowlabel; - if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &on, sizeof on) == -1) - error(2, errno, "can't send flowinfo"); + whereto.sin6_flowinfo = flowlabel; + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &on, sizeof on) == -1) + error(2, errno, "can't send flowinfo"); #else - error(2, 0, "flowinfo is not supported"); + error(2, 0, "flowinfo is not supported"); #endif - } + } - printf("PING %s(%s) ", hostname, pr_addr(&whereto, sizeof whereto)); - if (flowlabel) - printf(", flow 0x%05x, ", (unsigned)ntohl(flowlabel)); - if (device || (options&F_STRICTSOURCE)) { - int saved_options = options; + printf("PING %s(%s) ", hostname, pr_addr(&whereto, sizeof whereto)); + if(flowlabel) + printf(", flow 0x%05x, ", (unsigned) ntohl(flowlabel)); + if(device || (options & F_STRICTSOURCE)) { + int saved_options = options; - options |= F_NUMERIC; - printf("from %s %s: ", pr_addr(&source6, sizeof source6), device ? device : ""); - options = saved_options; - } - printf("%d data bytes\n", datalen); + options |= F_NUMERIC; + printf("from %s %s: ", pr_addr(&source6, sizeof source6), device ? device : ""); + options = saved_options; + } + printf("%d data bytes\n", datalen); - setup(sock); + setup(sock); - drop_capabilities(); + drop_capabilities(); - main_loop(&ping6_func_set, sock, packet, packlen); + main_loop(&ping6_func_set, sock, packet, packlen); + return 0; } int ping6_receive_error_msg(socket_st *sock) { - ssize_t res; - char cbuf[512]; - struct iovec iov; - struct msghdr msg; - struct cmsghdr *cmsg; - struct sock_extended_err *e; - struct icmp6_hdr icmph; - struct sockaddr_in6 target; - int net_errors = 0; - int local_errors = 0; - int saved_errno = errno; - - iov.iov_base = &icmph; - iov.iov_len = sizeof(icmph); - msg.msg_name = (void*)⌖ - msg.msg_namelen = sizeof(target); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = cbuf; - msg.msg_controllen = sizeof(cbuf); - - res = recvmsg(sock->fd, &msg, MSG_ERRQUEUE|MSG_DONTWAIT); - if (res < 0) - goto out; - - e = NULL; - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == IPPROTO_IPV6) { - if (cmsg->cmsg_type == IPV6_RECVERR) - e = (struct sock_extended_err *)CMSG_DATA(cmsg); - } - } - if (e == NULL) - abort(); - - if (e->ee_origin == SO_EE_ORIGIN_LOCAL) { - local_errors++; - if (options & F_QUIET) - goto out; - if (options & F_FLOOD) - write_stdout("E", 1); - else if (e->ee_errno != EMSGSIZE) - error(0, e->ee_errno, "local error"); - else - error(0, 0, "local error: message too long, mtu: %u", e->ee_info); - nerrors++; - } else if (e->ee_origin == SO_EE_ORIGIN_ICMP6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1); - - if ((size_t) res < sizeof(icmph) || - memcmp(&target.sin6_addr, &whereto.sin6_addr, 16) || - icmph.icmp6_type != ICMP6_ECHO_REQUEST || - !is_ours(sock, icmph.icmp6_id)) { - /* Not our error, not an error at all. Clear. */ - saved_errno = 0; - goto out; - } - - net_errors++; - nerrors++; - if (options & F_QUIET) - goto out; - if (options & F_FLOOD) { - write_stdout("\bE", 2); - } else { - print_timestamp(); - printf("From %s icmp_seq=%u ", pr_addr(sin6, sizeof *sin6), ntohs(icmph.icmp6_seq)); - pr_icmph(e->ee_type, e->ee_code, e->ee_info); - putchar('\n'); - fflush(stdout); - } - } - -out: - errno = saved_errno; - return net_errors ? net_errors : -local_errors; + ssize_t res; + char cbuf[512]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct sock_extended_err *e; + struct icmp6_hdr icmph; + struct sockaddr_in6 target; + int net_errors = 0; + int local_errors = 0; + int saved_errno = errno; + + iov.iov_base = &icmph; + iov.iov_len = sizeof(icmph); + msg.msg_name = (void*) ⌖ + msg.msg_namelen = sizeof(target); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + res = recvmsg(sock->fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + if(res < 0) + goto out; + + e = NULL; + for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if(cmsg->cmsg_level == IPPROTO_IPV6) { + if(cmsg->cmsg_type == IPV6_RECVERR) + e = (struct sock_extended_err *) CMSG_DATA(cmsg); + } + } + if(e == NULL) + abort(); + + if(e->ee_origin == SO_EE_ORIGIN_LOCAL) { + local_errors++; + if(options & F_QUIET) + goto out; + if(options & F_FLOOD) + write_stdout("E", 1); + else if(e->ee_errno != EMSGSIZE) + error(0, e->ee_errno, "local error"); + else + error(0, 0, "local error: message too long, mtu: %u", e->ee_info); + nerrors++; + } else if(e->ee_origin == SO_EE_ORIGIN_ICMP6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*) (e + 1); + + if((size_t) res < sizeof(icmph) || + memcmp(&target.sin6_addr, &whereto.sin6_addr, 16) || + icmph.icmp6_type != ICMP6_ECHO_REQUEST || + !is_ours(sock, icmph.icmp6_id)) { + /* Not our error, not an error at all. Clear. */ + saved_errno = 0; + goto out; + } + + net_errors++; + nerrors++; + if(options & F_QUIET) + goto out; + if(options & F_FLOOD) { + write_stdout("\bE", 2); + } else { + print_timestamp(); + printf("From %s icmp_seq=%u ", pr_addr(sin6, sizeof *sin6), ntohs(icmph.icmp6_seq)); + pr_icmph(e->ee_type, e->ee_code, e->ee_info); + putchar('\n'); + fflush(stdout); + } + } + + out: + errno = saved_errno; + return net_errors ? net_errors : -local_errors; } /* @@ -936,227 +946,226 @@ out: */ int build_echo(uint8_t *_icmph, unsigned packet_size __attribute__((__unused__))) { - struct icmp6_hdr *icmph; - int cc; + struct icmp6_hdr *icmph; + int cc; - icmph = (struct icmp6_hdr *)_icmph; - icmph->icmp6_type = ICMP6_ECHO_REQUEST; - icmph->icmp6_code = 0; - icmph->icmp6_cksum = 0; - icmph->icmp6_seq = htons(ntransmitted+1); - icmph->icmp6_id = ident; + icmph = (struct icmp6_hdr *) _icmph; + icmph->icmp6_type = ICMP6_ECHO_REQUEST; + icmph->icmp6_code = 0; + icmph->icmp6_cksum = 0; + icmph->icmp6_seq= htons(ntransmitted+1); + icmph->icmp6_id= ident; - if (timing) - gettimeofday((struct timeval *)&_icmph[8], - (struct timezone *)NULL); + if(timing) + gettimeofday((struct timeval *) &_icmph[8], + (struct timezone *) NULL); - cc = datalen + 8; /* skips ICMP portion */ + cc = datalen + 8; /* skips ICMP portion */ - return cc; + return cc; } - int build_niquery(uint8_t *_nih, unsigned packet_size __attribute__((__unused__))) { - struct ni_hdr *nih; - int cc; + struct ni_hdr *nih; + int cc; - nih = (struct ni_hdr *)_nih; - nih->ni_cksum = 0; + nih = (struct ni_hdr *) _nih; + nih->ni_cksum = 0; - nih->ni_type = ICMPV6_NI_QUERY; - cc = sizeof(*nih); - datalen = 0; + nih->ni_type = ICMPV6_NI_QUERY; + cc = sizeof(*nih); + datalen = 0; - niquery_fill_nonce(ntransmitted + 1, nih->ni_nonce); - nih->ni_code = ni_subject_type; - nih->ni_qtype = htons(ni_query); - nih->ni_flags = ni_flag; - memcpy(nih + 1, ni_subject, ni_subject_len); - cc += ni_subject_len; + niquery_fill_nonce(ntransmitted + 1, nih->ni_nonce); + nih->ni_code = ni_subject_type; + nih->ni_qtype= htons(ni_query); + nih->ni_flags= ni_flag; + memcpy(nih + 1, ni_subject, ni_subject_len); + cc += ni_subject_len; - return cc; + return cc; } int ping6_send_probe(socket_st *sock, void *packet, unsigned packet_size) { - int len, cc; - - rcvd_clear(ntransmitted + 1); - - if (niquery_is_enabled()) - len = build_niquery(packet, packet_size); - else - len = build_echo(packet, packet_size); - - if (cmsglen == 0) { - cc = sendto(sock->fd, (char *)packet, len, confirm, - (struct sockaddr *) &whereto, - sizeof(struct sockaddr_in6)); - } else { - struct msghdr mhdr; - struct iovec iov; - - iov.iov_len = len; - iov.iov_base = packet; - - memset(&mhdr, 0, sizeof(mhdr)); - mhdr.msg_name = &whereto; - mhdr.msg_namelen = sizeof(struct sockaddr_in6); - mhdr.msg_iov = &iov; - mhdr.msg_iovlen = 1; - mhdr.msg_control = cmsgbuf; - mhdr.msg_controllen = cmsglen; - - cc = sendmsg(sock->fd, &mhdr, confirm); - } - confirm = 0; - - return (cc == len ? 0 : cc); + int len, cc; + + rcvd_clear(ntransmitted + 1); + + if(niquery_is_enabled()) + len = build_niquery(packet, packet_size); + else + len = build_echo(packet, packet_size); + + if(cmsglen == 0) { + cc = sendto(sock->fd, (char *) packet, len, confirm, + (struct sockaddr *) &whereto, + sizeof(struct sockaddr_in6)); + } else { + struct msghdr mhdr; + struct iovec iov; + + iov.iov_len = len; + iov.iov_base = packet; + + memset(&mhdr, 0, sizeof(mhdr)); + mhdr.msg_name = &whereto; + mhdr.msg_namelen = sizeof(struct sockaddr_in6); + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = cmsgbuf; + mhdr.msg_controllen = cmsglen; + + cc = sendmsg(sock->fd, &mhdr, confirm); + } + confirm = 0; + + return (cc == len ? 0 : cc); } void pr_echo_reply(uint8_t *_icmph, int cc __attribute__((__unused__))) { - struct icmp6_hdr *icmph = (struct icmp6_hdr *) _icmph; - printf(" icmp_seq=%u", ntohs(icmph->icmp6_seq)); + struct icmp6_hdr *icmph = (struct icmp6_hdr *) _icmph; + log_printf(" icmp_seq=%u", ntohs(icmph->icmp6_seq)); } static void putchar_safe(char c) { - if (isprint(c)) - putchar(c); - else - printf("\\%03o", c); + if(isprint(c)) + putchar(c); + else + printf("\\%03o", c); } static void pr_niquery_reply_name(struct ni_hdr *nih, int len) { - uint8_t *h = (uint8_t *)(nih + 1); - uint8_t *p = h + 4; - uint8_t *end = (uint8_t *)nih + len; - int continued = 0; - char buf[1024]; - int ret; - - len -= sizeof(struct ni_hdr) + 4; - - if (len < 0) { - printf(" parse error (too short)"); - return; - } - while (p < end) { - int fqdn = 1; - size_t i; - - memset(buf, 0xff, sizeof(buf)); - - if (continued) - putchar(','); - - ret = dn_expand(h, end, p, buf, sizeof(buf)); - if (ret < 0) { - printf(" parse error (truncated)"); - break; - } - if (p + ret < end && *(p + ret) == '\0') - fqdn = 0; - - putchar(' '); - for (i = 0; i < strlen(buf); i++) - putchar_safe(buf[i]); - if (fqdn) - putchar('.'); - - p += ret + !fqdn; - - continued = 1; - } + uint8_t *h = (uint8_t *) (nih + 1); + uint8_t *p = h + 4; + uint8_t *end = (uint8_t *) nih + len; + int continued = 0; + char buf[1024]; + int ret; + + len -= sizeof(struct ni_hdr) + 4; + + if(len < 0) { + printf(" parse error (too short)"); + return; + } + while(p < end) { + int fqdn = 1; + size_t i; + + memset(buf, 0xff, sizeof(buf)); + + if(continued) + putchar(','); + + ret = dn_expand(h, end, p, buf, sizeof(buf)); + if(ret < 0) { + printf(" parse error (truncated)"); + break; + } + if(p + ret < end && *(p + ret) == '\0') + fqdn = 0; + + putchar(' '); + for(i = 0; i < strlen(buf); i++) + putchar_safe(buf[i]); + if(fqdn) + putchar('.'); + + p += ret + !fqdn; + + continued = 1; + } } static void pr_niquery_reply_addr(struct ni_hdr *nih, int len) { - uint8_t *h = (uint8_t *)(nih + 1); - uint8_t *p; - uint8_t *end = (uint8_t *)nih + len; - int af; - int aflen; - int continued = 0; - int truncated; - char buf[1024]; - - switch (ntohs(nih->ni_qtype)) { - case NI_QTYPE_IPV4ADDR: - af = AF_INET; - aflen = sizeof(struct in_addr); - truncated = nih->ni_flags & NI_IPV6ADDR_F_TRUNCATE; - break; - case NI_QTYPE_IPV6ADDR: - af = AF_INET6; - aflen = sizeof(struct in6_addr); - truncated = nih->ni_flags & NI_IPV4ADDR_F_TRUNCATE; - break; - default: - /* should not happen */ - af = aflen = truncated = 0; - } - p = h; - if (len < 0) { - printf(" parse error (too short)"); - return; - } - - while (p < end) { - if (continued) - putchar(','); - - if (p + sizeof(uint32_t) + aflen > end) { - printf(" parse error (truncated)"); - break; - } - if (!inet_ntop(af, p + sizeof(uint32_t), buf, sizeof(buf))) - printf(" unexpeced error in inet_ntop(%s)", - strerror(errno)); - else - printf(" %s", buf); - p += sizeof(uint32_t) + aflen; - - continued = 1; - } - if (truncated) - printf(" (truncated)"); + uint8_t *h = (uint8_t *) (nih + 1); + uint8_t *p; + uint8_t *end = (uint8_t *) nih + len; + int af; + int aflen; + int continued = 0; + int truncated; + char buf[1024]; + + switch (ntohs(nih->ni_qtype)) { + case NI_QTYPE_IPV4ADDR: + af = AF_INET; + aflen = sizeof(struct in_addr); + truncated = nih->ni_flags & NI_IPV6ADDR_F_TRUNCATE; + break; + case NI_QTYPE_IPV6ADDR: + af = AF_INET6; + aflen = sizeof(struct in6_addr); + truncated = nih->ni_flags & NI_IPV4ADDR_F_TRUNCATE; + break; + default: + /* should not happen */ + af = aflen = truncated = 0; + } + p = h; + if(len < 0) { + printf(" parse error (too short)"); + return; + } + + while(p < end) { + if(continued) + putchar(','); + + if(p + sizeof(uint32_t) + aflen > end) { + printf(" parse error (truncated)"); + break; + } + if(!inet_ntop(af, p + sizeof(uint32_t), buf, sizeof(buf))) + printf(" unexpeced error in inet_ntop(%s)", + strerror(errno)); + else + printf(" %s", buf); + p += sizeof(uint32_t) + aflen; + + continued = 1; + } + if(truncated) + printf(" (truncated)"); } static void pr_niquery_reply(uint8_t *_nih, int len) { - struct ni_hdr *nih = (struct ni_hdr *)_nih; - - switch (nih->ni_code) { - case NI_SUCCESS: - switch (ntohs(nih->ni_qtype)) { - case NI_QTYPE_NAME: - pr_niquery_reply_name(nih, len); - break; - case NI_QTYPE_IPV4ADDR: - case NI_QTYPE_IPV6ADDR: - pr_niquery_reply_addr(nih, len); - break; - default: - printf(" unknown qtype(0x%02x)", ntohs(nih->ni_qtype)); - } - break; - case NI_REFUSED: - printf(" refused"); - break; - case NI_UNKNOWN: - printf(" unknown"); - break; - default: - printf(" unknown code(%02x)", ntohs(nih->ni_code)); - } - printf("; seq=%u;", ntohsp((uint16_t*)nih->ni_nonce)); + struct ni_hdr *nih = (struct ni_hdr *) _nih; + + switch (nih->ni_code) { + case NI_SUCCESS: + switch (ntohs(nih->ni_qtype)) { + case NI_QTYPE_NAME: + pr_niquery_reply_name(nih, len); + break; + case NI_QTYPE_IPV4ADDR: + case NI_QTYPE_IPV6ADDR: + pr_niquery_reply_addr(nih, len); + break; + default: + printf(" unknown qtype(0x%02x)", ntohs(nih->ni_qtype)); + } + break; + case NI_REFUSED: + printf(" refused"); + break; + case NI_UNKNOWN: + printf(" unknown"); + break; + default: + printf(" unknown code(%02x)", ntohs(nih->ni_code)); + } + printf("; seq=%u;", ntohsp((uint16_t*) nih->ni_nonce)); } /* @@ -1169,215 +1178,213 @@ void pr_niquery_reply(uint8_t *_nih, int len) int ping6_parse_reply(socket_st *sock, struct msghdr *msg, int cc, void *addr, struct timeval *tv) { - struct sockaddr_in6 *from = addr; - uint8_t *buf = msg->msg_iov->iov_base; - struct cmsghdr *c; - struct icmp6_hdr *icmph; - int hops = -1; - - for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) { - if (c->cmsg_level != IPPROTO_IPV6) - continue; - switch(c->cmsg_type) { - case IPV6_HOPLIMIT: -#ifdef IPV6_2292HOPLIMIT - case IPV6_2292HOPLIMIT: -#endif - if (c->cmsg_len < CMSG_LEN(sizeof(int))) - continue; - memcpy(&hops, CMSG_DATA(c), sizeof(hops)); - } - } - - - /* Now the ICMP part */ - - icmph = (struct icmp6_hdr *) buf; - if (cc < 8) { - if (options & F_VERBOSE) - error(0, 0, "packet too short: %d bytes", cc); - return 1; - } - - if (icmph->icmp6_type == ICMP6_ECHO_REPLY) { - if (!is_ours(sock, icmph->icmp6_id)) - return 1; - if (!contains_pattern_in_payload((uint8_t*)(icmph+1))) - return 1; /* 'Twas really not our ECHO */ - if (gather_statistics((uint8_t*)icmph, sizeof(*icmph), cc, - ntohs(icmph->icmp6_seq), - hops, 0, tv, pr_addr(from, sizeof *from), - pr_echo_reply)) { - fflush(stdout); - return 0; - } - } else if (icmph->icmp6_type == ICMPV6_NI_REPLY) { - struct ni_hdr *nih = (struct ni_hdr *)icmph; - int seq = niquery_check_nonce(nih->ni_nonce); - if (seq < 0) - return 1; - if (gather_statistics((uint8_t*)icmph, sizeof(*icmph), cc, - seq, - hops, 0, tv, pr_addr(from, sizeof *from), - pr_niquery_reply)) - return 0; - } else { - int nexthdr; - struct ip6_hdr *iph1 = (struct ip6_hdr*)(icmph+1); - struct icmp6_hdr *icmph1 = (struct icmp6_hdr *)(iph1+1); - - /* We must not ever fall here. All the messages but - * echo reply are blocked by filter and error are - * received with IPV6_RECVERR. Ugly code is preserved - * however, just to remember what crap we avoided - * using RECVRERR. :-) - */ - - if (cc < (int) (8 + sizeof(struct ip6_hdr) + 8)) - return 1; - - if (memcmp(&iph1->ip6_dst, &whereto.sin6_addr, 16)) - return 1; - - nexthdr = iph1->ip6_nxt; - - if (nexthdr == 44) { - nexthdr = *(uint8_t*)icmph1; - icmph1++; - } - if (nexthdr == IPPROTO_ICMPV6) { - if (icmph1->icmp6_type != ICMP6_ECHO_REQUEST || - !is_ours(sock, icmph1->icmp6_id)) - return 1; - acknowledge(ntohs(icmph1->icmp6_seq)); - nerrors++; - if (options & F_FLOOD) { - write_stdout("\bE", 2); - return 0; - } - print_timestamp(); - printf("From %s: icmp_seq=%u ", pr_addr(from, sizeof *from), ntohs(icmph1->icmp6_seq)); - } else { - /* We've got something other than an ECHOREPLY */ - if (!(options & F_VERBOSE) || uid) - return 1; - print_timestamp(); - printf("From %s: ", pr_addr(from, sizeof *from)); - } - pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu)); - } - - if (options & F_AUDIBLE) { - putchar('\a'); - if(options & F_FLOOD) - fflush(stdout); - } - if (!(options & F_FLOOD)) { - putchar('\n'); - fflush(stdout); - } - return 0; + struct sockaddr_in6 *from = addr; + uint8_t *buf = msg->msg_iov->iov_base; + struct cmsghdr *c; + struct icmp6_hdr *icmph; + int hops = -1; + + for(c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) { + if(c->cmsg_level != IPPROTO_IPV6) + continue; + switch (c->cmsg_type) { + case IPV6_HOPLIMIT: + #ifdef IPV6_2292HOPLIMIT + case IPV6_2292HOPLIMIT: + #endif + if(c->cmsg_len < CMSG_LEN(sizeof(int))) + continue; + memcpy(&hops, CMSG_DATA(c), sizeof(hops)); + } + } + + /* Now the ICMP part */ + + icmph = (struct icmp6_hdr *) buf; + if(cc < 8) { + if(options & F_VERBOSE) + error(0, 0, "packet too short: %d bytes", cc); + return 1; + } + + if(icmph->icmp6_type == ICMP6_ECHO_REPLY) { + if(!is_ours(sock, icmph->icmp6_id)) + return 1; + if (!contains_pattern_in_payload((uint8_t*)(icmph+1))) + return 1; /* 'Twas really not our ECHO */ + if (gather_statistics((uint8_t*)icmph, sizeof(*icmph), cc, + ntohs(icmph->icmp6_seq), + hops, 0, tv, pr_addr(from, sizeof *from), + pr_echo_reply)) { + fflush(stdout); + return 0; + } + } else if (icmph->icmp6_type == ICMPV6_NI_REPLY) { + struct ni_hdr *nih = (struct ni_hdr *)icmph; + int seq = niquery_check_nonce(nih->ni_nonce); + if (seq < 0) + return 1; + if (gather_statistics((uint8_t*)icmph, sizeof(*icmph), cc, + seq, + hops, 0, tv, pr_addr(from, sizeof *from), + pr_niquery_reply)) + return 0; + } else { + int nexthdr; + struct ip6_hdr *iph1 = (struct ip6_hdr*)(icmph+1); + struct icmp6_hdr *icmph1 = (struct icmp6_hdr *)(iph1+1); + + /* We must not ever fall here. All the messages but + * echo reply are blocked by filter and error are + * received with IPV6_RECVERR. Ugly code is preserved + * however, just to remember what crap we avoided + * using RECVRERR. :-) + */ + + if (cc < (int) (8 + sizeof(struct ip6_hdr) + 8)) + return 1; + + if (memcmp(&iph1->ip6_dst, &whereto.sin6_addr, 16)) + return 1; + + nexthdr = iph1->ip6_nxt; + + if (nexthdr == 44) { + nexthdr = *(uint8_t*)icmph1; + icmph1++; + } + if (nexthdr == IPPROTO_ICMPV6) { + if (icmph1->icmp6_type != ICMP6_ECHO_REQUEST || + !is_ours(sock, icmph1->icmp6_id)) + return 1; + acknowledge(ntohs(icmph1->icmp6_seq)); + nerrors++; + if (options & F_FLOOD) { + write_stdout("\bE", 2); + return 0; + } + print_timestamp(); + printf("From %s: icmp_seq=%u ", pr_addr(from, sizeof *from), ntohs(icmph1->icmp6_seq)); + } else { + /* We've got something other than an ECHOREPLY */ + if (!(options & F_VERBOSE) || uid) + return 1; + print_timestamp(); + printf("From %s: ", pr_addr(from, sizeof *from)); + } + pr_icmph(icmph->icmp6_type, icmph->icmp6_code, ntohl(icmph->icmp6_mtu)); + } + + if(options & F_AUDIBLE) { + putchar('\a'); + if(options & F_FLOOD) + fflush(stdout); + } + if(!(options & F_FLOOD)) { + putchar('\n'); + fflush(stdout); + } + return 0; } - int pr_icmph(uint8_t type, uint8_t code, uint32_t info) { - switch(type) { - case ICMP6_DST_UNREACH: - printf("Destination unreachable: "); - switch (code) { - case ICMP6_DST_UNREACH_NOROUTE: - printf("No route"); - break; - case ICMP6_DST_UNREACH_ADMIN: - printf("Administratively prohibited"); - break; - case ICMP6_DST_UNREACH_BEYONDSCOPE: - printf("Beyond scope of source address"); - break; - case ICMP6_DST_UNREACH_ADDR: - printf("Address unreachable"); - break; - case ICMP6_DST_UNREACH_NOPORT: - printf("Port unreachable"); - break; - default: - printf("Unknown code %d", code); - break; - } - break; - case ICMP6_PACKET_TOO_BIG: - printf("Packet too big: mtu=%u", info); - if (code) - printf(", code=%d", code); - break; - case ICMP6_TIME_EXCEEDED: - printf("Time exceeded: "); - if (code == ICMP6_TIME_EXCEED_TRANSIT) - printf("Hop limit"); - else if (code == ICMP6_TIME_EXCEED_REASSEMBLY) - printf("Defragmentation failure"); - else - printf("code %d", code); - break; - case ICMP6_PARAM_PROB: - printf("Parameter problem: "); - if (code == ICMP6_PARAMPROB_HEADER) - printf("Wrong header field "); - else if (code == ICMP6_PARAMPROB_NEXTHEADER) - printf("Unknown header "); - else if (code == ICMP6_PARAMPROB_OPTION) - printf("Unknown option "); - else - printf("code %d ", code); - printf ("at %u", info); - break; - case ICMP6_ECHO_REQUEST: - printf("Echo request"); - break; - case ICMP6_ECHO_REPLY: - printf("Echo reply"); - break; - case MLD_LISTENER_QUERY: - printf("MLD Query"); - break; - case MLD_LISTENER_REPORT: - printf("MLD Report"); - break; - case MLD_LISTENER_REDUCTION: - printf("MLD Reduction"); - break; - default: - printf("unknown icmp type: %u", type); - - } - return 0; + switch (type) { + case ICMP6_DST_UNREACH: + printf("Destination unreachable: "); + switch (code) { + case ICMP6_DST_UNREACH_NOROUTE: + printf("No route"); + break; + case ICMP6_DST_UNREACH_ADMIN: + printf("Administratively prohibited"); + break; + case ICMP6_DST_UNREACH_BEYONDSCOPE: + printf("Beyond scope of source address"); + break; + case ICMP6_DST_UNREACH_ADDR: + printf("Address unreachable"); + break; + case ICMP6_DST_UNREACH_NOPORT: + printf("Port unreachable"); + break; + default: + printf("Unknown code %d", code); + break; + } + break; + case ICMP6_PACKET_TOO_BIG: + printf("Packet too big: mtu=%u", info); + if(code) + printf(", code=%d", code); + break; + case ICMP6_TIME_EXCEEDED: + printf("Time exceeded: "); + if(code == ICMP6_TIME_EXCEED_TRANSIT) + printf("Hop limit"); + else if(code == ICMP6_TIME_EXCEED_REASSEMBLY) + printf("Defragmentation failure"); + else + printf("code %d", code); + break; + case ICMP6_PARAM_PROB: + printf("Parameter problem: "); + if(code == ICMP6_PARAMPROB_HEADER) + printf("Wrong header field "); + else if(code == ICMP6_PARAMPROB_NEXTHEADER) + printf("Unknown header "); + else if(code == ICMP6_PARAMPROB_OPTION) + printf("Unknown option "); + else + printf("code %d ", code); + printf("at %u", info); + break; + case ICMP6_ECHO_REQUEST: + printf("Echo request"); + break; + case ICMP6_ECHO_REPLY: + printf("Echo reply"); + break; + case MLD_LISTENER_QUERY: + printf("MLD Query"); + break; + case MLD_LISTENER_REPORT: + printf("MLD Report"); + break; + case MLD_LISTENER_REDUCTION: + printf("MLD Reduction"); + break; + default: + printf("unknown icmp type: %u", type); + + } + return 0; } void ping6_install_filter(socket_st *sock) { - static int once; - static struct sock_filter insns[] = { - BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4), /* Load icmp echo ident */ - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ - BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ - BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), /* Load icmp type */ - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */ - BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */ - BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */ - }; - static struct sock_fprog filter = { - sizeof insns / sizeof(insns[0]), - insns - }; - - if (once) - return; - once = 1; - - /* Patch bpflet for current identifier. */ - insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1); - - if (setsockopt(sock->fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) - error(0, errno, "WARNING: failed to install socket filter"); + static int once; + static struct sock_filter insns[] = { + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4), /* Load icmp echo ident */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ + BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ + BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), /* Load icmp type */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */ + BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */ + BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */ + }; + static struct sock_fprog filter = { + sizeof insns / sizeof(insns[0]), + insns + }; + + if(once) + return; + once = 1; + + /* Patch bpflet for current identifier. */ + insns[1] = (struct sock_filter )BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1); + + if(setsockopt(sock->fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) + error(0, errno, "WARNING: failed to install socket filter"); } diff --git a/iputils/ping_common.c b/iputils/ping_common.c index 3e092f104f..bf58d31fae 100755 --- a/iputils/ping_common.c +++ b/iputils/ping_common.c @@ -49,21 +49,21 @@ unsigned char outpack[MAXPACKET]; struct rcvd_table rcvd_tbl; /* counters */ -long npackets; /* max packets to transmit */ -long nreceived; /* # of packets we got back */ -long nrepeats; /* number of duplicates */ -long ntransmitted; /* sequence # for outbound packets = #sent */ -long nchecksum; /* replies with bad checksum */ -long nerrors; /* icmp errors */ -int interval = 1000; /* interval between packets (msec) */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +long nchecksum; /* replies with bad checksum */ +long nerrors; /* icmp errors */ +int interval = 1000; /* interval between packets (msec) */ int preload = 1; -int deadline = 0; /* time to die */ -int lingertime = MAXWAIT*1000; +int deadline = 0; /* time to die */ +int lingertime = MAXWAIT * 1000; struct timeval start_time, cur_time; volatile int exiting; volatile int status_snapshot; int confirm = 0; -volatile int in_pr_addr = 0; /* pr_addr() is executing */ +volatile int in_pr_addr = 0; /* pr_addr() is executing */ jmp_buf pr_addr_jmp; /* Stupid workarounds for bugs/missing functionality in older linuces. @@ -72,24 +72,24 @@ jmp_buf pr_addr_jmp; int confirm_flag = MSG_CONFIRM; /* timing */ -int timing; /* flag to do timing */ -long tmin = LONG_MAX; /* minimum round trip time */ -long tmax; /* maximum round trip time */ +int timing; /* flag to do timing */ +long tmin = LONG_MAX; /* minimum round trip time */ +long tmax; /* maximum round trip time */ /* Message for rpm maintainers: have _shame_. If you want * to fix something send the patch to me for sanity checking. * "sparcfix" patch is a complete non-sense, apparenly the person * prepared it was stoned. */ -long long tsum; /* sum of all times, for doing average */ +long long tsum; /* sum of all times, for doing average */ long long tsum2; -int pipesize = -1; +int pipesize = -1; int datalen = DEFDATALEN; char *hostname; int uid; uid_t euid; -int ident; /* process id to identify our packets */ +int ident; /* process id to identify our packets */ static int screen_width = INT_MAX; @@ -102,149 +102,149 @@ static cap_value_t cap_admin = CAP_NET_ADMIN; void usage(void) { - fprintf(stderr, - "\nUsage\n" - " ping [options] <destination>\n" - "\nOptions:\n" - " <destination> dns name or ip address\n" - " -a use audible ping\n" - " -A use adaptive ping\n" - " -B sticky source address\n" - " -c <count> stop after <count> replies\n" - " -D print timestamps\n" - " -d use SO_DEBUG socket option\n" - " -f flood ping\n" - " -h print help and exit\n" - " -I <interface> either interface name or address\n" - " -i <interval> seconds between sending each packet\n" - " -L suppress loopback of multicast packets\n" - " -l <preload> send <preload> number of packages while waiting replies\n" - " -m <mark> tag the packets going out\n" - " -M <pmtud opt> define mtu discovery, can be one of <do|dont|want>\n" - " -n no dns name resolution\n" - " -O report outstanding replies\n" - " -p <pattern> contents of padding byte\n" - " -q quiet output\n" - " -Q <tclass> use quality of service <tclass> bits\n" - " -s <size> use <size> as number of data bytes to be sent\n" - " -S <size> use <size> as SO_SNDBUF socket option value\n" - " -t <ttl> define time to live\n" - " -U print user-to-user latency\n" - " -v verbose output\n" - " -V print version and exit\n" - " -w <deadline> reply wait <deadline> in seconds\n" - " -W <timeout> time to wait for response\n" - "\nIPv4 options:\n" - " -4 use IPv4\n" - " -b allow pinging broadcast\n" - " -R record route\n" - " -T <timestamp> define timestamp, can be one of <tsonly|tsandaddr|tsprespec>\n" - "\nIPv6 options:\n" - " -6 use IPv6\n" - " -F <flowlabel> define flow label, default is random\n" - " -N <nodeinfo opt> use icmp6 node info query, try <help> as argument\n" - "\nFor more details see ping(8).\n" - ); - exit(2); + fprintf(stderr, + "\nUsage\n" + " ping [options] <destination>\n" + "\nOptions:\n" + " <destination> dns name or ip address\n" + " -a use audible ping\n" + " -A use adaptive ping\n" + " -B sticky source address\n" + " -c <count> stop after <count> replies\n" + " -D print timestamps\n" + " -d use SO_DEBUG socket option\n" + " -f flood ping\n" + " -h print help and exit\n" + " -I <interface> either interface name or address\n" + " -i <interval> seconds between sending each packet\n" + " -L suppress loopback of multicast packets\n" + " -l <preload> send <preload> number of packages while waiting replies\n" + " -m <mark> tag the packets going out\n" + " -M <pmtud opt> define mtu discovery, can be one of <do|dont|want>\n" + " -n no dns name resolution\n" + " -O report outstanding replies\n" + " -p <pattern> contents of padding byte\n" + " -q quiet output\n" + " -Q <tclass> use quality of service <tclass> bits\n" + " -s <size> use <size> as number of data bytes to be sent\n" + " -S <size> use <size> as SO_SNDBUF socket option value\n" + " -t <ttl> define time to live\n" + " -U print user-to-user latency\n" + " -v verbose output\n" + " -V print version and exit\n" + " -w <deadline> reply wait <deadline> in seconds\n" + " -W <timeout> time to wait for response\n" + "\nIPv4 options:\n" + " -4 use IPv4\n" + " -b allow pinging broadcast\n" + " -R record route\n" + " -T <timestamp> define timestamp, can be one of <tsonly|tsandaddr|tsprespec>\n" + "\nIPv6 options:\n" + " -6 use IPv6\n" + " -F <flowlabel> define flow label, default is random\n" + " -N <nodeinfo opt> use icmp6 node info query, try <help> as argument\n" + "\nFor more details see ping(8).\n" + ); + exit(2); } void limit_capabilities(void) { #ifdef HAVE_LIBCAP - cap_t cap_cur_p; - cap_t cap_p; - cap_flag_value_t cap_ok; - - cap_cur_p = cap_get_proc(); - if (!cap_cur_p) - error(-1, errno, "cap_get_proc"); - cap_p = cap_init(); - if (!cap_p) - error(-1, errno, "cap_init"); - cap_ok = CAP_CLEAR; - cap_get_flag(cap_cur_p, CAP_NET_ADMIN, CAP_PERMITTED, &cap_ok); - if (cap_ok != CAP_CLEAR) - cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_admin, CAP_SET); - cap_ok = CAP_CLEAR; - cap_get_flag(cap_cur_p, CAP_NET_RAW, CAP_PERMITTED, &cap_ok); - if (cap_ok != CAP_CLEAR) - cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_raw, CAP_SET); - if (cap_set_proc(cap_p) < 0) - error(-1, errno, "cap_set_proc"); - if (prctl(PR_SET_KEEPCAPS, 1) < 0) - error(-1, errno, "prctl"); - if (setuid(getuid()) < 0) - error(-1, errno, "setuid"); - if (prctl(PR_SET_KEEPCAPS, 0) < 0) - error(-1, errno, "prctl"); - cap_free(cap_p); - cap_free(cap_cur_p); + cap_t cap_cur_p; + cap_t cap_p; + cap_flag_value_t cap_ok; + + cap_cur_p = cap_get_proc(); + if (!cap_cur_p) + error(-1, errno, "cap_get_proc"); + cap_p = cap_init(); + if (!cap_p) + error(-1, errno, "cap_init"); + cap_ok = CAP_CLEAR; + cap_get_flag(cap_cur_p, CAP_NET_ADMIN, CAP_PERMITTED, &cap_ok); + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_admin, CAP_SET); + cap_ok = CAP_CLEAR; + cap_get_flag(cap_cur_p, CAP_NET_RAW, CAP_PERMITTED, &cap_ok); + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &cap_raw, CAP_SET); + if (cap_set_proc(cap_p) < 0) + error(-1, errno, "cap_set_proc"); + if (prctl(PR_SET_KEEPCAPS, 1) < 0) + error(-1, errno, "prctl"); + if (setuid(getuid()) < 0) + error(-1, errno, "setuid"); + if (prctl(PR_SET_KEEPCAPS, 0) < 0) + error(-1, errno, "prctl"); + cap_free(cap_p); + cap_free(cap_cur_p); #endif - uid = getuid(); - euid = geteuid(); + uid = getuid(); + euid = geteuid(); #ifndef HAVE_LIBCAP - if (seteuid(uid)) - error(-1, errno, "setuid"); + if(seteuid(uid)) + error(-1, errno, "setuid"); #endif } #ifdef HAVE_LIBCAP int modify_capability(cap_value_t cap, cap_flag_value_t on) { - cap_t cap_p = cap_get_proc(); - cap_flag_value_t cap_ok; - int rc = -1; - - if (!cap_p) { - error(0, errno, "cap_get_proc"); - goto out; - } - - cap_ok = CAP_CLEAR; - cap_get_flag(cap_p, cap, CAP_PERMITTED, &cap_ok); - if (cap_ok == CAP_CLEAR) { - rc = on ? -1 : 0; - goto out; - } - - cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap, on); - - if (cap_set_proc(cap_p) < 0) { - error(0, errno, "cap_set_proc"); - goto out; - } - - cap_free(cap_p); - cap_p = NULL; - - rc = 0; -out: - if (cap_p) - cap_free(cap_p); - return rc; + cap_t cap_p = cap_get_proc(); + cap_flag_value_t cap_ok; + int rc = -1; + + if (!cap_p) { + error(0, errno, "cap_get_proc"); + goto out; + } + + cap_ok = CAP_CLEAR; + cap_get_flag(cap_p, cap, CAP_PERMITTED, &cap_ok); + if (cap_ok == CAP_CLEAR) { + rc = on ? -1 : 0; + goto out; + } + + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap, on); + + if (cap_set_proc(cap_p) < 0) { + error(0, errno, "cap_set_proc"); + goto out; + } + + cap_free(cap_p); + cap_p = NULL; + + rc = 0; + out: + if (cap_p) + cap_free(cap_p); + return rc; } #else int modify_capability(int on) { - if (seteuid(on ? euid : getuid())) { - error(0, errno, "seteuid"); - return -1; - } + if(seteuid(on ? euid : getuid())) { + error(0, errno, "seteuid"); + return -1; + } - return 0; + return 0; } #endif void drop_capabilities(void) { #ifdef HAVE_LIBCAP - cap_t cap = cap_init(); - if (cap_set_proc(cap) < 0) - error(-1, errno, "cap_set_proc"); - cap_free(cap); + cap_t cap = cap_init(); + if (cap_set_proc(cap) < 0) + error(-1, errno, "cap_set_proc"); + cap_free(cap); #else - if (setuid(getuid())) - error(-1, errno, "setuid"); + if(setuid(getuid())) + error(-1, errno, "setuid"); #endif } @@ -253,89 +253,88 @@ void drop_capabilities(void) */ void fill(char *patp, unsigned char *packet, unsigned packet_size) { - int ii, jj; - unsigned int pat[16]; - char *cp; - unsigned char *bp = packet+8; + int ii, jj; + unsigned int pat[16]; + char *cp; + unsigned char *bp = packet + 8; #ifdef USE_IDN - setlocale(LC_ALL, "C"); + setlocale(LC_ALL, "C"); #endif - for (cp = patp; *cp; cp++) { - if (!isxdigit(*cp)) - error(2, 0, "patterns must be specified as hex digits: %s", cp); - } - ii = sscanf(patp, - "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", - &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], - &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], - &pat[13], &pat[14], &pat[15]); - - if (ii > 0) { - unsigned kk; - for (kk = 0; kk <= packet_size - (8 + ii); kk += ii) - for (jj = 0; jj < ii; ++jj) - bp[jj + kk] = pat[jj]; - } - if (!(options & F_QUIET)) { - printf("PATTERN: 0x"); - for (jj = 0; jj < ii; ++jj) - printf("%02x", bp[jj] & 0xFF); - printf("\n"); - } + for(cp = patp; *cp; cp++) { + if(!isxdigit(*cp)) + error(2, 0, "patterns must be specified as hex digits: %s", cp); + } + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + + if(ii > 0) { + unsigned kk; + for(kk = 0; kk <= packet_size - (8 + ii); kk += ii) + for(jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + } + if(!(options & F_QUIET)) { + printf("PATTERN: 0x"); + for(jj = 0; jj < ii; ++jj) + printf("%02x", bp[jj] & 0xFF); + printf("\n"); + } #ifdef USE_IDN - setlocale(LC_ALL, ""); + setlocale(LC_ALL, ""); #endif } static void sigexit(int signo __attribute__((__unused__))) { - exiting = 1; - if (in_pr_addr) - longjmp(pr_addr_jmp, 0); + exiting = 1; + if(in_pr_addr) + longjmp(pr_addr_jmp, 0); } static void sigstatus(int signo __attribute__((__unused__))) { - status_snapshot = 1; + status_snapshot = 1; } - int __schedule_exit(int next) { - static unsigned long waittime; - struct itimerval it; - - if (waittime) - return next; - - if (nreceived) { - waittime = 2 * tmax; - if (waittime < (unsigned long) (1000*interval)) - waittime = 1000*interval; - } else - waittime = lingertime*1000; - - if (next < 0 || (unsigned long)next < waittime/1000) - next = waittime/1000; - - it.it_interval.tv_sec = 0; - it.it_interval.tv_usec = 0; - it.it_value.tv_sec = waittime/1000000; - it.it_value.tv_usec = waittime%1000000; - setitimer(ITIMER_REAL, &it, NULL); - return next; + static unsigned long waittime; + struct itimerval it; + + if(waittime) + return next; + + if(nreceived) { + waittime = 2 * tmax; + if(waittime < (unsigned long) (1000 * interval)) + waittime = 1000 * interval; + } else + waittime = lingertime * 1000; + + if(next < 0 || (unsigned long) next < waittime / 1000) + next = waittime / 1000; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = waittime / 1000000; + it.it_value.tv_usec = waittime % 1000000; + setitimer(ITIMER_REAL, &it, NULL); + return next; } static inline void update_interval(void) { - int est = rtt ? rtt/8 : interval*1000; + int est = rtt ? rtt / 8 : interval * 1000; - interval = (est+rtt_addend+500)/1000; - if (uid && interval < MINUSERINTERVAL) - interval = MINUSERINTERVAL; + interval = (est + rtt_addend + 500) / 1000; + if(uid && interval < MINUSERINTERVAL) + interval = MINUSERINTERVAL; } /* @@ -343,12 +342,12 @@ static inline void update_interval(void) */ void print_timestamp(void) { - if (options & F_PTIMEOFDAY) { - struct timeval tv; - gettimeofday(&tv, NULL); - printf("[%lu.%06lu] ", - (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec); - } + if(options & F_PTIMEOFDAY) { + struct timeval tv; + gettimeofday(&tv, NULL); + printf("[%lu.%06lu] ", + (unsigned long) tv.tv_sec, (unsigned long) tv.tv_usec); + } } /* @@ -361,257 +360,257 @@ void print_timestamp(void) */ int pinger(ping_func_set_st *fset, socket_st *sock) { - static int oom_count; - static int tokens; - int i; - - /* Have we already sent enough? If we have, return an arbitrary positive value. */ - if (exiting || (npackets && ntransmitted >= npackets && !deadline)) - return 1000; - - /* Check that packets < rate*time + preload */ - if (cur_time.tv_sec == 0) { - gettimeofday(&cur_time, NULL); - tokens = interval*(preload-1); - } else { - long ntokens; - struct timeval tv; - - gettimeofday(&tv, NULL); - ntokens = (tv.tv_sec - cur_time.tv_sec)*1000 + - (tv.tv_usec-cur_time.tv_usec)/1000; - if (!interval) { - /* Case of unlimited flood is special; - * if we see no reply, they are limited to 100pps */ - if (ntokens < MININTERVAL && in_flight() >= preload) - return MININTERVAL-ntokens; - } - ntokens += tokens; - if (ntokens > interval*preload) - ntokens = interval*preload; - if (ntokens < interval) - return interval - ntokens; - - cur_time = tv; - tokens = ntokens - interval; - } - - if (options & F_OUTSTANDING) { - if (ntransmitted > 0 && !rcvd_test(ntransmitted)) { - print_timestamp(); - printf("no answer yet for icmp_seq=%lu\n", (ntransmitted % MAX_DUP_CHK)); - fflush(stdout); - } - } - -resend: - i = fset->send_probe(sock, outpack, sizeof(outpack)); - - if (i == 0) { - oom_count = 0; - advance_ntransmitted(); - if (!(options & F_QUIET) && (options & F_FLOOD)) { - /* Very silly, but without this output with - * high preload or pipe size is very confusing. */ - if ((preload < screen_width && pipesize < screen_width) || - in_flight() < screen_width) - write_stdout(".", 1); - } - return interval - tokens; - } - - /* And handle various errors... */ - if (i > 0) { - /* Apparently, it is some fatal bug. */ - abort(); - } else if (errno == ENOBUFS || errno == ENOMEM) { - int nores_interval; - - /* Device queue overflow or OOM. Packet is not sent. */ - tokens = 0; - /* Slowdown. This works only in adaptive mode (option -A) */ - rtt_addend += (rtt < 8*50000 ? rtt/8 : 50000); - if (options&F_ADAPTIVE) - update_interval(); - nores_interval = SCHINT(interval/2); - if (nores_interval > 500) - nores_interval = 500; - oom_count++; - if (oom_count*nores_interval < lingertime) - return nores_interval; - i = 0; - /* Fall to hard error. It is to avoid complete deadlock - * on stuck output device even when dealine was not requested. - * Expected timings are screwed up in any case, but we will - * exit some day. :-) */ - } else if (errno == EAGAIN) { - /* Socket buffer is full. */ - tokens += interval; - return MININTERVAL; - } else { - if ((i=fset->receive_error_msg(sock)) > 0) { - /* An ICMP error arrived. In this case, we've received - * an error from sendto(), but we've also received an - * ICMP message, which means the packet did in fact - * send in some capacity. So, in this odd case, report - * the more specific errno as the error, and treat this - * as a hard local error. */ - i = 0; - goto hard_local_error; - } - /* Compatibility with old linuces. */ - if (i == 0 && confirm_flag && errno == EINVAL) { - confirm_flag = 0; - errno = 0; - } - if (!errno) - goto resend; - } - -hard_local_error: - /* Hard local error. Pretend we sent packet. */ - advance_ntransmitted(); - - if (i == 0 && !(options & F_QUIET)) { - if (options & F_FLOOD) - write_stdout("E", 1); - else - perror("ping: sendmsg"); - } - tokens = 0; - return SCHINT(interval); + static int oom_count; + static int tokens; + int i; + + /* Have we already sent enough? If we have, return an arbitrary positive value. */ + if(exiting || (npackets && ntransmitted >= npackets && !deadline)) + return 1000; + + /* Check that packets < rate*time + preload */ + if(cur_time.tv_sec == 0) { + gettimeofday(&cur_time, NULL); + tokens = interval * (preload - 1); + } else { + long ntokens; + struct timeval tv; + + gettimeofday(&tv, NULL); + ntokens = (tv.tv_sec - cur_time.tv_sec) * 1000 + + (tv.tv_usec - cur_time.tv_usec) / 1000; + if(!interval) { + /* Case of unlimited flood is special; + * if we see no reply, they are limited to 100pps */ + if(ntokens < MININTERVAL && in_flight() >= preload) + return MININTERVAL - ntokens; + } + ntokens += tokens; + if(ntokens > interval * preload) + ntokens = interval * preload; + if(ntokens < interval) + return interval - ntokens; + + cur_time = tv; + tokens = ntokens - interval; + } + + if(options & F_OUTSTANDING) { + if(ntransmitted > 0 && !rcvd_test(ntransmitted)) { + print_timestamp(); + printf("no answer yet for icmp_seq=%lu\n", (ntransmitted % MAX_DUP_CHK)); + fflush(stdout); + } + } + + resend: + i = fset->send_probe(sock, outpack, sizeof(outpack)); + + if(i == 0) { + oom_count = 0; + advance_ntransmitted(); + if(!(options & F_QUIET) && (options & F_FLOOD)) { + /* Very silly, but without this output with + * high preload or pipe size is very confusing. */ + if((preload < screen_width && pipesize < screen_width) || + in_flight() < screen_width) + write_stdout(".", 1); + } + return interval - tokens; + } + + /* And handle various errors... */ + if(i > 0) { + /* Apparently, it is some fatal bug. */ + abort(); + } else if(errno == ENOBUFS || errno == ENOMEM) { + int nores_interval; + + /* Device queue overflow or OOM. Packet is not sent. */ + tokens = 0; + /* Slowdown. This works only in adaptive mode (option -A) */ + rtt_addend += (rtt < 8 * 50000 ? rtt / 8 : 50000); + if(options & F_ADAPTIVE) + update_interval(); + nores_interval = SCHINT(interval / 2); + if(nores_interval > 500) + nores_interval = 500; + oom_count++; + if(oom_count * nores_interval < lingertime) + return nores_interval; + i = 0; + /* Fall to hard error. It is to avoid complete deadlock + * on stuck output device even when dealine was not requested. + * Expected timings are screwed up in any case, but we will + * exit some day. :-) */ + } else if(errno == EAGAIN) { + /* Socket buffer is full. */ + tokens += interval; + return MININTERVAL; + } else { + if((i = fset->receive_error_msg(sock)) > 0) { + /* An ICMP error arrived. In this case, we've received + * an error from sendto(), but we've also received an + * ICMP message, which means the packet did in fact + * send in some capacity. So, in this odd case, report + * the more specific errno as the error, and treat this + * as a hard local error. */ + i = 0; + goto hard_local_error; + } + /* Compatibility with old linuces. */ + if(i == 0 && confirm_flag && errno == EINVAL) { + confirm_flag = 0; + errno = 0; + } + if(!errno) + goto resend; + } + + hard_local_error: + /* Hard local error. Pretend we sent packet. */ + advance_ntransmitted(); + + if(i == 0 && !(options & F_QUIET)) { + if(options & F_FLOOD) + write_stdout("E", 1); + else + perror("ping: sendmsg"); + } + tokens = 0; + return SCHINT(interval); } /* Set socket buffers, "alloc" is an estimate of memory taken by single packet. */ void sock_setbufs(socket_st *sock, int alloc) { - int rcvbuf, hold; - socklen_t tmplen = sizeof(hold); - - if (!sndbuf) - sndbuf = alloc; - setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, (char *)&sndbuf, sizeof(sndbuf)); - - rcvbuf = hold = alloc * preload; - if (hold < 65536) - hold = 65536; - setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *)&hold, sizeof(hold)); - if (getsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *)&hold, &tmplen) == 0) { - if (hold < rcvbuf) - error(0, 0, "WARNING: probably, rcvbuf is not enough to hold preload"); - } + int rcvbuf, hold; + socklen_t tmplen = sizeof(hold); + + if(!sndbuf) + sndbuf = alloc; + setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbuf, sizeof(sndbuf)); + + rcvbuf = hold = alloc * preload; + if(hold < 65536) + hold = 65536; + setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *) &hold, sizeof(hold)); + if(getsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *) &hold, &tmplen) == 0) { + if(hold < rcvbuf) + error(0, 0, "WARNING: probably, rcvbuf is not enough to hold preload"); + } } /* Protocol independent setup and parameter checks. */ void setup(socket_st *sock) { - int hold; - struct timeval tv; - sigset_t sset; + int hold; + struct timeval tv; + sigset_t sset; - if ((options & F_FLOOD) && !(options & F_INTERVAL)) - interval = 0; + if((options & F_FLOOD) && !(options & F_INTERVAL)) + interval = 0; - if (uid && interval < MINUSERINTERVAL) - error(2, 0, "cannot flood; minimal interval allowed for user is %dms", MINUSERINTERVAL); + if(uid && interval < MINUSERINTERVAL) + error(2, 0, "cannot flood; minimal interval allowed for user is %dms", MINUSERINTERVAL); - if (interval >= INT_MAX/preload) - error(2, 0, "illegal preload and/or interval: %d", interval); + if(interval >= INT_MAX / preload) + error(2, 0, "illegal preload and/or interval: %d", interval); - hold = 1; - if (options & F_SO_DEBUG) - setsockopt(sock->fd, SOL_SOCKET, SO_DEBUG, (char *)&hold, sizeof(hold)); - if (options & F_SO_DONTROUTE) - setsockopt(sock->fd, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, sizeof(hold)); + hold = 1; + if(options & F_SO_DEBUG) + setsockopt(sock->fd, SOL_SOCKET, SO_DEBUG, (char *) &hold, sizeof(hold)); + if(options & F_SO_DONTROUTE) + setsockopt(sock->fd, SOL_SOCKET, SO_DONTROUTE, (char *) &hold, sizeof(hold)); #ifdef SO_TIMESTAMP - if (!(options&F_LATENCY)) { - int on = 1; - if (setsockopt(sock->fd, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) - error(0, 0, "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"); - } + if(!(options & F_LATENCY)) { + int on = 1; + if(setsockopt(sock->fd, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) + error(0, 0, "Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP"); + } #endif #ifdef SO_MARK - if (options & F_MARK) { - int ret; - int errno_save; - - enable_capability_admin(); - ret = setsockopt(sock->fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)); - errno_save = errno; - disable_capability_admin(); - - if (ret == -1) { - /* we probably dont wanna exit since old kernels - * dont support mark .. - */ - error(0, errno_save, "Warning: Failed to set mark: %d", mark); - } - } + if(options & F_MARK) { + int ret; + int errno_save; + + enable_capability_admin(); + ret = setsockopt(sock->fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)); + errno_save = errno; + disable_capability_admin(); + + if(ret == -1) { + /* we probably dont wanna exit since old kernels + * dont support mark .. + */ + error(0, errno_save, "Warning: Failed to set mark: %d", mark); + } + } #endif - /* Set some SNDTIMEO to prevent blocking forever - * on sends, when device is too slow or stalls. Just put limit - * of one second, or "interval", if it is less. - */ - tv.tv_sec = 1; - tv.tv_usec = 0; - if (interval < 1000) { - tv.tv_sec = 0; - tv.tv_usec = 1000 * SCHINT(interval); - } - setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv)); - - /* Set RCVTIMEO to "interval". Note, it is just an optimization - * allowing to avoid redundant poll(). */ - tv.tv_sec = SCHINT(interval)/1000; - tv.tv_usec = 1000*(SCHINT(interval)%1000); - if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv))) - options |= F_FLOOD_POLL; - - if (!(options & F_PINGFILLED)) { - int i; - unsigned char *p = outpack+8; - - /* Do not forget about case of small datalen, - * fill timestamp area too! - */ - for (i = 0; i < datalen; ++i) - *p++ = i; - } - - if (sock->socktype == SOCK_RAW) - ident = htons(getpid() & 0xFFFF); - - set_signal(SIGINT, sigexit); - set_signal(SIGALRM, sigexit); - set_signal(SIGQUIT, sigstatus); - - sigemptyset(&sset); - sigprocmask(SIG_SETMASK, &sset, NULL); - - gettimeofday(&start_time, NULL); - - if (deadline) { - struct itimerval it; - - it.it_interval.tv_sec = 0; - it.it_interval.tv_usec = 0; - it.it_value.tv_sec = deadline; - it.it_value.tv_usec = 0; - setitimer(ITIMER_REAL, &it, NULL); - } - - if (isatty(STDOUT_FILENO)) { - struct winsize w; - - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { - if (w.ws_col > 0) - screen_width = w.ws_col; - } - } + /* Set some SNDTIMEO to prevent blocking forever + * on sends, when device is too slow or stalls. Just put limit + * of one second, or "interval", if it is less. + */ + tv.tv_sec = 1; + tv.tv_usec = 0; + if(interval < 1000) { + tv.tv_sec = 0; + tv.tv_usec = 1000 * SCHINT(interval); + } + setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (char*) &tv, sizeof(tv)); + + /* Set RCVTIMEO to "interval". Note, it is just an optimization + * allowing to avoid redundant poll(). */ + tv.tv_sec = SCHINT(interval) / 1000; + tv.tv_usec = 1000 * (SCHINT(interval) % 1000); + if(setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (char*) &tv, sizeof(tv))) + options |= F_FLOOD_POLL; + + if(!(options & F_PINGFILLED)) { + int i; + unsigned char *p = outpack + 8; + + /* Do not forget about case of small datalen, + * fill timestamp area too! + */ + for(i = 0; i < datalen; ++i) + *p++ = i; + } + + if(sock->socktype == SOCK_RAW) + ident = htons(getpid() & 0xFFFF); + + set_signal(SIGINT, sigexit); + set_signal(SIGALRM, sigexit); + set_signal(SIGQUIT, sigstatus); + + sigemptyset(&sset); + sigprocmask(SIG_SETMASK, &sset, NULL); + + gettimeofday(&start_time, NULL); + + if(deadline) { + struct itimerval it; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = deadline; + it.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &it, NULL); + } + + if(isatty(STDOUT_FILENO)) { + struct winsize w; + + if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { + if(w.ws_col > 0) + screen_width = w.ws_col; + } + } } /* @@ -619,307 +618,308 @@ void setup(socket_st *sock) */ int contains_pattern_in_payload(uint8_t *ptr) { - int i; - uint8_t *cp, *dp; - - /* check the data */ - cp = ((u_char*)ptr) + sizeof(struct timeval); - dp = &outpack[8 + sizeof(struct timeval)]; - for (i = sizeof(struct timeval); i < datalen; ++i, ++cp, ++dp) { - if (*cp != *dp) - return 0; - } - return 1; + int i; + uint8_t *cp, *dp; + + /* check the data */ + cp = ((u_char*) ptr) + sizeof(struct timeval); + dp = &outpack[8 + sizeof(struct timeval)]; + for(i = sizeof(struct timeval); i < datalen; ++i, ++cp, ++dp) { + if(*cp != *dp) + return 0; + } + return 1; } void main_loop(ping_func_set_st *fset, socket_st *sock, uint8_t *packet, int packlen) { - char addrbuf[128]; - char ans_data[4096]; - struct iovec iov; - struct msghdr msg; - struct cmsghdr *c; - int cc; - int next; - int polling; - int recv_error; - - iov.iov_base = (char *)packet; - - for (;;) { - /* Check exit conditions. */ - if (exiting) - break; - if (npackets && nreceived + nerrors >= npackets) - break; - if (deadline && nerrors) - break; - /* Check for and do special actions. */ - if (status_snapshot) - status(); - - /* Send probes scheduled to this time. */ - do { - next = pinger(fset, sock); - next = schedule_exit(next); - } while (next <= 0); - - /* "next" is time to send next probe, if positive. - * If next<=0 send now or as soon as possible. */ - - /* Technical part. Looks wicked. Could be dropped, - * if everyone used the newest kernel. :-) - * Its purpose is: - * 1. Provide intervals less than resolution of scheduler. - * Solution: spinning. - * 2. Avoid use of poll(), when recvmsg() can provide - * timed waiting (SO_RCVTIMEO). */ - polling = 0; - recv_error = 0; - if ((options & (F_ADAPTIVE|F_FLOOD_POLL)) || next<SCHINT(interval)) { - int recv_expected = in_flight(); - - /* If we are here, recvmsg() is unable to wait for - * required timeout. */ - if (1000 % HZ == 0 ? next <= 1000 / HZ : (next < INT_MAX / HZ && next * HZ <= 1000)) { - /* Very short timeout... So, if we wait for - * something, we sleep for MININTERVAL. - * Otherwise, spin! */ - if (recv_expected) { - next = MININTERVAL; - } else { - next = 0; - /* When spinning, no reasons to poll. - * Use nonblocking recvmsg() instead. */ - polling = MSG_DONTWAIT; - /* But yield yet. */ - sched_yield(); - } - } - - if (!polling && - ((options & (F_ADAPTIVE|F_FLOOD_POLL)) || interval)) { - struct pollfd pset; - pset.fd = sock->fd; - pset.events = POLLIN; - pset.revents = 0; - if (poll(&pset, 1, next) < 1 || - !(pset.revents&(POLLIN|POLLERR))) - continue; - polling = MSG_DONTWAIT; - recv_error = pset.revents&POLLERR; - } - } - - for (;;) { - struct timeval *recv_timep = NULL; - struct timeval recv_time; - int not_ours = 0; /* Raw socket can receive messages - * destined to other running pings. */ - - iov.iov_len = packlen; - memset(&msg, 0, sizeof(msg)); - msg.msg_name = addrbuf; - msg.msg_namelen = sizeof(addrbuf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = ans_data; - msg.msg_controllen = sizeof(ans_data); - - cc = recvmsg(sock->fd, &msg, polling); - polling = MSG_DONTWAIT; - - if (cc < 0) { - /* If there was a POLLERR and there is no packet - * on the socket, try to read the error queue. - * Otherwise, give up. - */ - if ((errno == EAGAIN && !recv_error) || - errno == EINTR) - break; - recv_error = 0; - if (!fset->receive_error_msg(sock)) { - if (errno) { - error(0, errno, "recvmsg"); - break; - } - not_ours = 1; - } - } else { + char addrbuf[128]; + char ans_data[4096]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *c; + int cc; + int next; + int polling; + int recv_error; + + iov.iov_base = (char *) packet; + + for(;;) { + /* Check exit conditions. */ + if(exiting) + break; + if(npackets && nreceived + nerrors >= npackets) + break; + if(deadline && nerrors) + break; + /* Check for and do special actions. */ + if(status_snapshot) + status(); + + /* Send probes scheduled to this time. */ + do { + next = pinger(fset, sock); + next = schedule_exit(next); + } while(next <= 0); + + /* "next" is time to send next probe, if positive. + * If next<=0 send now or as soon as possible. */ + + /* Technical part. Looks wicked. Could be dropped, + * if everyone used the newest kernel. :-) + * Its purpose is: + * 1. Provide intervals less than resolution of scheduler. + * Solution: spinning. + * 2. Avoid use of poll(), when recvmsg() can provide + * timed waiting (SO_RCVTIMEO). */ + polling = 0; + recv_error = 0; + if((options & (F_ADAPTIVE | F_FLOOD_POLL)) || next < SCHINT(interval)) { + int recv_expected = in_flight(); + + /* If we are here, recvmsg() is unable to wait for + * required timeout. */ + if(1000 % HZ == 0 ? next <= 1000 / HZ : (next < INT_MAX / HZ && next * HZ <= 1000)) { + /* Very short timeout... So, if we wait for + * something, we sleep for MININTERVAL. + * Otherwise, spin! */ + if(recv_expected) { + next = MININTERVAL; + } else { + next = 0; + /* When spinning, no reasons to poll. + * Use nonblocking recvmsg() instead. */ + polling = MSG_DONTWAIT; + /* But yield yet. */ + sched_yield(); + } + } + + if(!polling && + ((options & (F_ADAPTIVE | F_FLOOD_POLL)) || interval)) { + struct pollfd pset; + pset.fd = sock->fd; + pset.events = POLLIN; + pset.revents = 0; + if(poll(&pset, 1, next) < 1 || + !(pset.revents & (POLLIN | POLLERR))) + continue; + polling = MSG_DONTWAIT; + recv_error = pset.revents & POLLERR; + } + } + + for(;;) { + struct timeval *recv_timep = NULL; + struct timeval recv_time; + int not_ours = 0; /* Raw socket can receive messages + * destined to other running pings. */ + + iov.iov_len = packlen; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = addrbuf; + msg.msg_namelen = sizeof(addrbuf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ans_data; + msg.msg_controllen = sizeof(ans_data); + + cc = recvmsg(sock->fd, &msg, polling); + polling = MSG_DONTWAIT; + + if(cc < 0) { + /* If there was a POLLERR and there is no packet + * on the socket, try to read the error queue. + * Otherwise, give up. + */ + if((errno == EAGAIN && !recv_error) || + errno == EINTR) + break; + recv_error = 0; + if(!fset->receive_error_msg(sock)) { + if(errno) { + error(0, errno, "recvmsg"); + break; + } + not_ours = 1; + } + } else { #ifdef SO_TIMESTAMP - for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) { - if (c->cmsg_level != SOL_SOCKET || - c->cmsg_type != SO_TIMESTAMP) - continue; - if (c->cmsg_len < CMSG_LEN(sizeof(struct timeval))) - continue; - recv_timep = (struct timeval*)CMSG_DATA(c); - } + for(c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) { + if(c->cmsg_level != SOL_SOCKET || + c->cmsg_type != SO_TIMESTAMP) + continue; + if(c->cmsg_len < CMSG_LEN(sizeof(struct timeval))) + continue; + recv_timep = (struct timeval*) CMSG_DATA(c); + } #endif - if ((options&F_LATENCY) || recv_timep == NULL) { - if ((options&F_LATENCY) || - ioctl(sock->fd, SIOCGSTAMP, &recv_time)) - gettimeofday(&recv_time, NULL); - recv_timep = &recv_time; - } - - not_ours = fset->parse_reply(sock, &msg, cc, addrbuf, recv_timep); - } - - /* See? ... someone runs another ping on this host. */ - if (not_ours && sock->socktype == SOCK_RAW) - fset->install_filter(sock); - - /* If nothing is in flight, "break" returns us to pinger. */ - if (in_flight() == 0) - break; - - /* Otherwise, try to recvmsg() again. recvmsg() - * is nonblocking after the first iteration, so that - * if nothing is queued, it will receive EAGAIN - * and return to pinger. */ - } - } - finish(); + if((options & F_LATENCY) || recv_timep == NULL) { + if((options & F_LATENCY) || + ioctl(sock->fd, SIOCGSTAMP, &recv_time)) + gettimeofday(&recv_time, NULL); + recv_timep = &recv_time; + } + not_ours = fset->parse_reply(sock, &msg, cc, addrbuf, recv_timep); + } + + /* See? ... someone runs another ping on this host. */ + if(not_ours && sock->socktype == SOCK_RAW) + fset->install_filter(sock); + + /* If nothing is in flight, "break" returns us to pinger. */ + if(in_flight() == 0) + break; + + /* Otherwise, try to recvmsg() again. recvmsg() + * is nonblocking after the first iteration, so that + * if nothing is queued, it will receive EAGAIN + * and return to pinger. */ + } + } + // here present exit() from app + finish(); } int gather_statistics(uint8_t *icmph, int icmplen, - int cc, uint16_t seq, int hops, - int csfailed, struct timeval *tv, char *from, - void (*pr_reply)(uint8_t *icmph, int cc)) + int cc, uint16_t seq, int hops, + int csfailed, struct timeval *tv, char *from, + void (*pr_reply)(uint8_t *icmph, int cc)) { - int dupflag = 0; - long triptime = 0; - uint8_t *ptr = icmph + icmplen; - - ++nreceived; - if (!csfailed) - acknowledge(seq); - - if (timing && cc >= (int) (8+sizeof(struct timeval))) { - struct timeval tmp_tv; - memcpy(&tmp_tv, ptr, sizeof(tmp_tv)); - -restamp: - tvsub(tv, &tmp_tv); - triptime = tv->tv_sec * 1000000 + tv->tv_usec; - if (triptime < 0) { - error(0, 0, "Warning: time of day goes back (%ldus), taking countermeasures", triptime); - triptime = 0; - if (!(options & F_LATENCY)) { - gettimeofday(tv, NULL); - options |= F_LATENCY; - goto restamp; - } - } - if (!csfailed) { - tsum += triptime; - tsum2 += (long long)triptime * (long long)triptime; - if (triptime < tmin) - tmin = triptime; - if (triptime > tmax) - tmax = triptime; - if (!rtt) - rtt = triptime*8; - else - rtt += triptime-rtt/8; - if (options&F_ADAPTIVE) - update_interval(); - } - } - - if (csfailed) { - ++nchecksum; - --nreceived; - } else if (rcvd_test(seq)) { - ++nrepeats; - --nreceived; - dupflag = 1; - } else { - rcvd_set(seq); - dupflag = 0; - } - confirm = confirm_flag; - - if (options & F_QUIET) - return 1; - - if (options & F_FLOOD) { - if (!csfailed) - write_stdout("\b \b", 3); - else - write_stdout("\bC", 2); - } else { - int i; - uint8_t *cp, *dp; - - print_timestamp(); - printf("%d bytes from %s:", cc, from); - - if (pr_reply) - pr_reply(icmph, cc); - - if (hops >= 0) - printf(" ttl=%d", hops); - - if (cc < datalen+8) { - printf(" (truncated)\n"); - return 1; - } - if (timing) { - if (triptime >= 100000) - printf(" time=%ld ms", (triptime+500)/1000); - else if (triptime >= 10000) - printf(" time=%ld.%01ld ms", (triptime+50)/1000, - ((triptime+50)%1000)/100); - else if (triptime >= 1000) - printf(" time=%ld.%02ld ms", (triptime+5)/1000, - ((triptime+5)%1000)/10); - else - printf(" time=%ld.%03ld ms", triptime/1000, - triptime%1000); - } - if (dupflag) - printf(" (DUP!)"); - if (csfailed) - printf(" (BAD CHECKSUM!)"); - - /* check the data */ - cp = ((unsigned char*)ptr) + sizeof(struct timeval); - dp = &outpack[8 + sizeof(struct timeval)]; - for (i = sizeof(struct timeval); i < datalen; ++i, ++cp, ++dp) { - if (*cp != *dp) { - printf("\nwrong data byte #%d should be 0x%x but was 0x%x", - i, *dp, *cp); - cp = (unsigned char*)ptr + sizeof(struct timeval); - for (i = sizeof(struct timeval); i < datalen; ++i, ++cp) { - if ((i % 32) == sizeof(struct timeval)) - printf("\n#%d\t", i); - printf("%x ", *cp); - } - break; - } - } - } - return 0; + int dupflag = 0; + long triptime = 0; + uint8_t *ptr = icmph + icmplen; + + ++nreceived; + if(!csfailed) + acknowledge(seq); + + if(timing && cc >= (int) (8 + sizeof(struct timeval))) { + struct timeval tmp_tv; + memcpy(&tmp_tv, ptr, sizeof(tmp_tv)); + + restamp: + tvsub(tv, &tmp_tv); + triptime = tv->tv_sec * 1000000 + tv->tv_usec; + if(triptime < 0) { + error(0, 0, "Warning: time of day goes back (%ldus), taking countermeasures", triptime); + triptime = 0; + if(!(options & F_LATENCY)) { + gettimeofday(tv, NULL); + options |= F_LATENCY; + goto restamp; + } + } + if(!csfailed) { + tsum += triptime; + tsum2 += (long long) triptime * (long long) triptime; + if(triptime < tmin) + tmin = triptime; + if(triptime > tmax) + tmax = triptime; + if(!rtt) + rtt = triptime * 8; + else + rtt += triptime - rtt / 8; + if(options & F_ADAPTIVE) + update_interval(); + } + } + + if(csfailed) { + ++nchecksum; + --nreceived; + } else if(rcvd_test(seq)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + rcvd_set(seq); + dupflag = 0; + } + confirm = confirm_flag; + + if(options & F_QUIET) + return 1; + + if(options & F_FLOOD) { + if(!csfailed) + write_stdout("\b \b", 3); + else + write_stdout("\bC", 2); + } else { + int i; + uint8_t *cp, *dp; + + print_timestamp(); + log_printf("%d bytes from %s:", cc, from); + + if(pr_reply) + pr_reply(icmph, cc); + + if(hops >= 0) + log_printf(" ttl=%d", hops); + + if(cc < datalen + 8) { + log_printf(" (truncated)\n"); + return 1; + } + if(timing) { + if(triptime >= 100000) + log_printf(" time=%ld ms", (triptime + 500) / 1000); + else if(triptime >= 10000) + log_printf(" time=%ld.%01ld ms", (triptime + 50) / 1000, + ((triptime + 50) % 1000) / 100); + else if(triptime >= 1000) + log_printf(" time=%ld.%02ld ms", (triptime + 5) / 1000, + ((triptime + 5) % 1000) / 10); + else + log_printf(" time=%ld.%03ld ms", triptime / 1000, + triptime % 1000); + } + if(dupflag) + log_printf(" (DUP!)"); + if(csfailed) + log_printf(" (BAD CHECKSUM!)"); + + /* check the data */ + cp = ((unsigned char*) ptr) + sizeof(struct timeval); + dp = &outpack[8 + sizeof(struct timeval)]; + for(i = sizeof(struct timeval); i < datalen; ++i, ++cp, ++dp) { + if(*cp != *dp) { + log_printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + cp = (unsigned char*) ptr + sizeof(struct timeval); + for(i = sizeof(struct timeval); i < datalen; ++i, ++cp) { + if((i % 32) == sizeof(struct timeval)) + log_printf("\n#%d\t", i); + log_printf("%x ", *cp); + } + break; + } + } + } + return 0; } - +#ifdef PING_DBG static long llsqrt(long long a) { - long long prev = LLONG_MAX; - long long x = a; + long long prev = LLONG_MAX; + long long x = a; - if (x > 0) { - while (x < prev) { - prev = x; - x = (x+(a/x))/2; - } - } + if (x > 0) { + while (x < prev) { + prev = x; + x = (x+(a/x))/2; + } + } - return (long)x; + return (long)x; } +#endif /* * finish -- @@ -927,87 +927,92 @@ static long llsqrt(long long a) */ void finish(void) { - struct timeval tv = cur_time; - char *comma = ""; - - tvsub(&tv, &start_time); - - putchar('\n'); - fflush(stdout); - printf("--- %s ping statistics ---\n", hostname); - printf("%ld packets transmitted, ", ntransmitted); - printf("%ld received", nreceived); - if (nrepeats) - printf(", +%ld duplicates", nrepeats); - if (nchecksum) - printf(", +%ld corrupted", nchecksum); - if (nerrors) - printf(", +%ld errors", nerrors); - if (ntransmitted) { + struct timeval tv = cur_time; +#ifdef PING_DBG + char *comma = ""; +#endif + + tvsub(&tv, &start_time); +#ifdef PING_DBG + putchar('\n'); + fflush(stdout); + printf("--- %s ping statistics ---\n", hostname); + printf("%ld packets transmitted, ", ntransmitted); + printf("%ld received", nreceived); + if (nrepeats) + log_printf(", +%ld duplicates", nrepeats); + if (nchecksum) + printf(", +%ld corrupted", nchecksum); + if (nerrors) + printf(", +%ld errors", nerrors); + if (ntransmitted) { #ifdef USE_IDN - setlocale(LC_ALL, "C"); + setlocale(LC_ALL, "C"); +#endif + printf(", %g%% packet loss", + (float) ((((long long)(ntransmitted - nreceived)) * 100.0) / + ntransmitted)); + printf(", time %ldms", 1000*tv.tv_sec+(tv.tv_usec+500)/1000); + } + putchar('\n'); #endif - printf(", %g%% packet loss", - (float) ((((long long)(ntransmitted - nreceived)) * 100.0) / - ntransmitted)); - printf(", time %ldms", 1000*tv.tv_sec+(tv.tv_usec+500)/1000); - } - putchar('\n'); - - if (nreceived && timing) { - long tmdev; - - tsum /= nreceived + nrepeats; - tsum2 /= nreceived + nrepeats; - tmdev = llsqrt(tsum2 - tsum * tsum); - - printf("rtt min/avg/max/mdev = %ld.%03ld/%lu.%03ld/%ld.%03ld/%ld.%03ld ms", - (long)tmin/1000, (long)tmin%1000, - (unsigned long)(tsum/1000), (long)(tsum%1000), - (long)tmax/1000, (long)tmax%1000, - (long)tmdev/1000, (long)tmdev%1000 - ); - comma = ", "; - } - if (pipesize > 1) { - printf("%spipe %d", comma, pipesize); - comma = ", "; - } - if (nreceived && (!interval || (options&(F_FLOOD|F_ADAPTIVE))) && ntransmitted > 1) { - int ipg = (1000000*(long long)tv.tv_sec+tv.tv_usec)/(ntransmitted-1); - printf("%sipg/ewma %d.%03d/%d.%03d ms", - comma, ipg/1000, ipg%1000, rtt/8000, (rtt/8)%1000); - } - putchar('\n'); - exit(!nreceived || (deadline && nreceived < npackets)); -} + if(nreceived && timing) { + + tsum /= nreceived + nrepeats; + tsum2 /= nreceived + nrepeats; +#ifdef PING_DBG + long tmdev; + tmdev = llsqrt(tsum2 - tsum * tsum); + printf("rtt min/avg/max/mdev = %ld.%03ld/%lu.%03ld/%ld.%03ld/%ld.%03ld ms", + (long)tmin/1000, (long)tmin%1000, + (unsigned long)(tsum/1000), (long)(tsum%1000), + (long)tmax/1000, (long)tmax%1000, + (long)tmdev/1000, (long)tmdev%1000 + ); + comma = ", "; +#endif + } +#ifdef PING_DBG + if (pipesize > 1) { + printf("%spipe %d", comma, pipesize); + comma = ", "; + } + if (nreceived && (!interval || (options&(F_FLOOD|F_ADAPTIVE))) && ntransmitted > 1) { + int ipg = (1000000*(long long)tv.tv_sec+tv.tv_usec)/(ntransmitted-1); + printf("%sipg/ewma %d.%03d/%d.%03d ms", + comma, ipg/1000, ipg%1000, rtt/8000, (rtt/8)%1000); + } + putchar('\n'); + //exit(!nreceived || (deadline && nreceived < npackets)); +#endif +} void status(void) { - int loss = 0; - long tavg = 0; + int loss = 0; + long tavg = 0; - status_snapshot = 0; + status_snapshot = 0; - if (ntransmitted) - loss = (((long long)(ntransmitted - nreceived)) * 100) / ntransmitted; + if(ntransmitted) + loss = (((long long) (ntransmitted - nreceived)) * 100) / ntransmitted; - fprintf(stderr, "\r%ld/%ld packets, %d%% loss", nreceived, ntransmitted, loss); + fprintf(stderr, "\r%ld/%ld packets, %d%% loss", nreceived, ntransmitted, loss); - if (nreceived && timing) { - tavg = tsum / (nreceived + nrepeats); + if(nreceived && timing) { + tavg = tsum / (nreceived + nrepeats); - fprintf(stderr, ", min/avg/ewma/max = %ld.%03ld/%lu.%03ld/%d.%03d/%ld.%03ld ms", - (long)tmin/1000, (long)tmin%1000, - tavg/1000, tavg%1000, - rtt/8000, (rtt/8)%1000, - (long)tmax/1000, (long)tmax%1000 - ); - } - fprintf(stderr, "\n"); + fprintf(stderr, ", min/avg/ewma/max = %ld.%03ld/%lu.%03ld/%d.%03d/%ld.%03ld ms", + (long) tmin / 1000, (long) tmin % 1000, + tavg / 1000, tavg % 1000, + rtt / 8000, (rtt / 8) % 1000, + (long) tmax / 1000, (long) tmax % 1000 + ); + } + fprintf(stderr, "\n"); } inline int is_ours(socket_st *sock, uint16_t id) { - return sock->socktype == SOCK_DGRAM || id == ident; + return sock->socktype == SOCK_DGRAM || id == ident; } -- GitLab