mping.c (mrouted-4.3) | : | mping.c (mrouted-4.4) | ||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* all copies or substantial portions of the Software. | * all copies or substantial portions of the Software. | |||
* | * | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | * THE SOFTWARE. | |||
*/ | */ | |||
#define _GNU_SOURCE /* For TIMESPEC_TO_TIMEVAL() in GLIBC */ | ||||
#include <err.h> | #include <err.h> | |||
#include <errno.h> | #include <errno.h> | |||
#include <ifaddrs.h> | #include <ifaddrs.h> | |||
#include <netdb.h> | #include <netdb.h> | |||
#include <signal.h> | #include <signal.h> | |||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <getopt.h> | #include <getopt.h> | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <string.h> | #include <string.h> | |||
#include <signal.h> | #include <signal.h> | |||
#include <time.h> | ||||
#include <unistd.h> | #include <unistd.h> | |||
#include <arpa/inet.h> | #include <arpa/inet.h> | |||
#include <net/if.h> | #include <net/if.h> | |||
#include <netinet/in.h> | #include <netinet/in.h> | |||
#include <sys/time.h> | #include <sys/time.h> | |||
#include <sys/socket.h> | #include <sys/socket.h> | |||
#include <sys/types.h> | #include <sys/types.h> | |||
#ifndef VERSION | ||||
#define VERSION "1.6" | ||||
#endif | ||||
#define dbg(fmt,args...) do { if (debug) printf(fmt "\n", ##args); } while (0) | ||||
#define sig(s,c) do { struct sigaction a = {.sa_handler=c};sigaction(s,&a,0); | ||||
} while(0) | ||||
#define MC_GROUP_DEFAULT "225.1.2.3" | #define MC_GROUP_DEFAULT "225.1.2.3" | |||
#define MC_PORT_DEFAULT 4321 | #define MC_PORT_DEFAULT 4321 | |||
#define MC_TTL_DEFAULT 1 | #define MC_TTL_DEFAULT 1 | |||
#define RESPONSES_MAX 100 | #define RESPONSES_MAX 100 | |||
#define MAX_BUF_LEN 1024 | #define MAX_BUF_LEN 1024 | |||
#define MAX_HOSTNAME_LEN 256 | #define MAX_HOSTNAME_LEN 256 | |||
#define SENDER 's' | #define SENDER 's' | |||
skipping to change at line 68 | skipping to change at line 77 | |||
unsigned char type; | unsigned char type; | |||
unsigned char ttl; | unsigned char ttl; | |||
struct in_addr src_host; | struct in_addr src_host; | |||
struct in_addr dest_host; | struct in_addr dest_host; | |||
unsigned int seq_no; | unsigned int seq_no; | |||
pid_t pid; | pid_t pid; | |||
struct timeval tv; | struct timeval tv; | |||
struct timeval delay; | ||||
} mping; | } mping; | |||
struct resp { | ||||
struct mping pkt; | ||||
struct timeval send_time; | ||||
} *responses[RESPONSES_MAX]; | ||||
int empty_response = 0; | ||||
/*#define BANDWIDTH 10000.0 */ /* bw in bytes/sec for mping */ | /*#define BANDWIDTH 10000.0 */ /* bw in bytes/sec for mping */ | |||
#define BANDWIDTH 100.0 /* bw in bytes/sec for mping */ | #define BANDWIDTH 100.0 /* bw in bytes/sec for mping */ | |||
unsigned int last_pkt_count = 0; /* packets heard in last full second */ | ||||
unsigned int curr_pkt_count = 0; /* packets heard so far this second */ | ||||
/* pointer to mping packet buffer */ | /* pointer to mping packet buffer */ | |||
struct mping *rcvd_pkt; | struct mping *rcvd_pkt; | |||
int sd; /* socket descriptor */ | int sd; /* socket descriptor */ | |||
pid_t pid; /* our process id */ | pid_t pid; /* our process id */ | |||
struct sockaddr_in mcaddr; | struct sockaddr_in mcaddr; | |||
struct ip_mreqn imr; | struct ip_mreqn imr; | |||
struct in_addr myaddr; | struct in_addr myaddr; | |||
struct timeval start; /* start time for sender */ | struct timeval start; /* start time for sender */ | |||
/* Cleared by signal handler */ | ||||
volatile sig_atomic_t running = 1; | ||||
/* counters and statistics variables */ | /* counters and statistics variables */ | |||
int packets_sent = 0; | int packets_sent = 0; | |||
int packets_rcvd = 0; | int packets_rcvd = 0; | |||
double rtt_total = 0; | double rtt_total = 0; | |||
double rtt_max = 0; | double rtt_max = 0; | |||
double rtt_min = 999999999.0; | double rtt_min = 999999999.0; | |||
/* default command-line arguments */ | /* default command-line arguments */ | |||
char arg_mcaddr[16] = MC_GROUP_DEFAULT; | char arg_mcaddr[16] = MC_GROUP_DEFAULT; | |||
skipping to change at line 123 | skipping to change at line 124 | |||
int debug = 0; | int debug = 0; | |||
int quiet = 0; | int quiet = 0; | |||
void init_socket(int ifindex) | void init_socket(int ifindex) | |||
{ | { | |||
int off = 0; | int off = 0; | |||
int on = 1; | int on = 1; | |||
/* create a UDP socket */ | /* create a UDP socket */ | |||
if ((sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) | if ((sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) | |||
err(1, "receive socket() failed"); | err(1, "failed creating UDP socket"); | |||
/* set reuse port to on to allow multiple binds per host */ | /* set reuse port to on to allow multiple binds per host */ | |||
if ((setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) | if ((setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) | |||
err(1, "setsockopt() failed"); | err(1, "Failed enabling SO_REUSEADDR"); | |||
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &arg_ttl, sizeof(arg_ttl )) < 0) | if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &arg_ttl, sizeof(arg_ttl )) < 0) | |||
err(1, "Failed setting IP_MULTICAST_TTL"); | err(1, "Failed setting IP_MULTICAST_TTL"); | |||
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &off, sizeof(off)) < 0) | if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &off, sizeof(off)) < 0) | |||
err(1, "Failed disabling IP_MULTICAST_LOOP"); | err(1, "Failed disabling IP_MULTICAST_LOOP"); | |||
/* construct a multicast address structure */ | /* construct a multicast address structure */ | |||
mcaddr.sin_family = AF_INET; | mcaddr.sin_family = AF_INET; | |||
mcaddr.sin_addr.s_addr = inet_addr(arg_mcaddr); | mcaddr.sin_addr.s_addr = inet_addr(arg_mcaddr); | |||
skipping to change at line 150 | skipping to change at line 151 | |||
/* bind to multicast address to socket */ | /* bind to multicast address to socket */ | |||
if ((bind(sd, (struct sockaddr *)&mcaddr, sizeof(mcaddr))) < 0) | if ((bind(sd, (struct sockaddr *)&mcaddr, sizeof(mcaddr))) < 0) | |||
err(1, "bind() failed"); | err(1, "bind() failed"); | |||
/* construct a IGMP join request structure */ | /* construct a IGMP join request structure */ | |||
imr.imr_multiaddr.s_addr = inet_addr(arg_mcaddr); | imr.imr_multiaddr.s_addr = inet_addr(arg_mcaddr); | |||
imr.imr_ifindex = ifindex; | imr.imr_ifindex = ifindex; | |||
/* send an ADD MEMBERSHIP message via setsockopt */ | /* send an ADD MEMBERSHIP message via setsockopt */ | |||
if ((setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr))) < 0) | if ((setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr))) < 0) | |||
err(1, "setsockopt() failed"); | err(1, "failed joining group %s on ifindex %d", arg_mcaddr, ifind ex); | |||
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr))) | if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr))) | |||
err(1, "Failed setting IP_MULTICAST_IF %d", ifindex); | err(1, "Failed setting IP_MULTICAST_IF %d", ifindex); | |||
} | } | |||
static size_t strlencpy(char *dst, const char *src, size_t len) | static size_t strlencpy(char *dst, const char *src, size_t len) | |||
{ | { | |||
const char *p = src; | const char *p = src; | |||
size_t num = len; | size_t num = len; | |||
skipping to change at line 212 | skipping to change at line 213 | |||
if (!(ifa->ifa_flags & IFF_UP)) | if (!(ifa->ifa_flags & IFF_UP)) | |||
continue; | continue; | |||
if (!(ifa->ifa_flags & IFF_RUNNING)) | if (!(ifa->ifa_flags & IFF_RUNNING)) | |||
continue; | continue; | |||
if (!(ifa->ifa_flags & IFF_MULTICAST)) | if (!(ifa->ifa_flags & IFF_MULTICAST)) | |||
continue; | continue; | |||
ifindex = if_nametoindex(ifa->ifa_name); | ifindex = if_nametoindex(ifa->ifa_name); | |||
if (debug) | dbg("Found iface %s, ifindex %d", ifa->ifa_name, ifindex); | |||
printf("Found iface %s, ifindex %d\n", ifa->ifa_name, if | strlencpy(iface, ifa->ifa_name, len); | |||
index); | ||||
strncpy(iface, ifa->ifa_name, len); | ||||
iface[len] = 0; | iface[len] = 0; | |||
break; | break; | |||
} | } | |||
freeifaddrs(ifaddr); | freeifaddrs(ifaddr); | |||
return iface; | return iface; | |||
} | } | |||
/* The BSD's or SVR4 systems like Solaris don't have /proc/net/route */ | /* The BSD's or SVR4 systems like Solaris don't have /proc/net/route */ | |||
static char *altdefault(char *iface, size_t len) | static char *altdefault(char *iface, size_t len) | |||
skipping to change at line 242 | skipping to change at line 242 | |||
while (fgets(buf, sizeof(buf), fp)) { | while (fgets(buf, sizeof(buf), fp)) { | |||
char *token; | char *token; | |||
if (strncmp(buf, "default", 7) && strncmp(buf, "0.0.0.0", 7)) | if (strncmp(buf, "default", 7) && strncmp(buf, "0.0.0.0", 7)) | |||
continue; | continue; | |||
token = strtok(buf, " \t\n"); | token = strtok(buf, " \t\n"); | |||
while (token) { | while (token) { | |||
if (if_nametoindex(token)) { | if (if_nametoindex(token)) { | |||
strncpy(iface, token, len); | strlencpy(iface, token, len); | |||
pclose(fp); | pclose(fp); | |||
return iface; | return iface; | |||
} | } | |||
token = strtok(NULL, " \t\n"); | token = strtok(NULL, " \t\n"); | |||
} | } | |||
} | } | |||
pclose(fp); | pclose(fp); | |||
skipping to change at line 295 | skipping to change at line 295 | |||
if (rc < 10 || !(flags & 1)) /* IFF_UP */ | if (rc < 10 || !(flags & 1)) /* IFF_UP */ | |||
continue; | continue; | |||
if (dest != 0 || mask != 0) | if (dest != 0 || mask != 0) | |||
continue; | continue; | |||
if (!iface[0] || !strncmp(iface, "tun", 3)) { | if (!iface[0] || !strncmp(iface, "tun", 3)) { | |||
if (metric >= best) | if (metric >= best) | |||
continue; | continue; | |||
strncpy(iface, ifname, len); | strlencpy(iface, ifname, len); | |||
iface[len] = 0; | iface[len] = 0; | |||
best = metric; | best = metric; | |||
found = 1; | found = 1; | |||
if (debug) | dbg("Found default inteface %s", iface); | |||
printf("Found default intefaces %s\n", iface); | ||||
} | } | |||
} | } | |||
end: | end: | |||
fclose(fp); | fclose(fp); | |||
if (found) | if (found) | |||
return iface; | return iface; | |||
fallback: | fallback: | |||
return ifany(iface, len); | return ifany(iface, len); | |||
} | } | |||
/* Find IP address of default outbound LAN interface */ | /* Find IP address of default outbound LAN interface */ | |||
int ifinfo(char *iface, inet_addr_t *addr, int family) | int ifinfo(char *iface, inet_addr_t *addr, int family) | |||
{ | { | |||
struct ifaddrs *ifaddr, *ifa; | ||||
char ifname[17] = { 0 }; | ||||
char buf[INET_ADDRSTR_LEN] = { 0 }; | char buf[INET_ADDRSTR_LEN] = { 0 }; | |||
struct ifaddrs *ifaddr, *ifa; | ||||
char ifname[16] = { 0 }; | ||||
int found = 0; | ||||
int rc = -1; | int rc = -1; | |||
if (!iface || !iface[0]) | if (!iface || !iface[0]) | |||
iface = ifdefault(ifname, sizeof(ifname)); | iface = ifdefault(ifname, sizeof(ifname)); | |||
if (!iface) | if (!iface || !iface[0]) | |||
return -2; | errx(1, "no suitable default interface available"); | |||
rc = getifaddrs(&ifaddr); | rc = getifaddrs(&ifaddr); | |||
if (rc == -1) | if (rc == -1) | |||
return -3; | err(1, "failed querying available interfaces"); | |||
rc = -1; /* Return -1 if iface with family is not found */ | rc = -1; /* Return -1 if iface with family is not found */ | |||
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { | for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { | |||
if (!ifa->ifa_addr) | if (strcmp(iface, ifa->ifa_name)) | |||
continue; | continue; | |||
if (!(ifa->ifa_flags & IFF_MULTICAST)) | found = 1; | |||
if (!(ifa->ifa_flags & IFF_UP)) { | ||||
dbg("%s is not UP, skipping ...", iface); | ||||
continue; | ||||
} | ||||
if (!ifa->ifa_addr) { | ||||
dbg("%s has no address, skipping ...", iface); | ||||
continue; | continue; | |||
} | ||||
if (family == AF_UNSPEC) { | if (family == AF_UNSPEC) { | |||
if (ifa->ifa_addr->sa_family != AF_INET && | if (ifa->ifa_addr->sa_family != AF_INET && | |||
ifa->ifa_addr->sa_family != AF_INET6) | ifa->ifa_addr->sa_family != AF_INET6) | |||
dbg("%s no IPv4 or IPv6 address, skipping ...", i face); | ||||
continue; | continue; | |||
} else if (ifa->ifa_addr->sa_family != family) | } else if (ifa->ifa_addr->sa_family != family) { | |||
dbg("%s not matching address family, skipping ...", iface | ||||
); | ||||
continue; | continue; | |||
} | ||||
if (iface && strcmp(iface, ifa->ifa_name)) | if (!(ifa->ifa_flags & IFF_MULTICAST)) { | |||
continue; | warnx("%s is not multicast capable, check interface flags | |||
.", iface); | ||||
break; | ||||
} | ||||
if (debug) | dbg("Found %s addr %s", ifa->ifa_name, inet_address((inet_addr_t | |||
printf("Found %s addr %s\n", ifa->ifa_name, | *)ifa->ifa_addr, buf, sizeof(buf))); | |||
inet_address((inet_addr_t *)ifa->ifa_addr, buf, s | ||||
izeof(buf))); | ||||
*addr = *(inet_addr_t *)ifa->ifa_addr; | *addr = *(inet_addr_t *)ifa->ifa_addr; | |||
rc = if_nametoindex(ifa->ifa_name); | rc = if_nametoindex(ifa->ifa_name); | |||
if (debug) | dbg("iface %s, ifindex %d, addr %s", ifa->ifa_name, rc, buf); | |||
printf("iface %s, ifindex %d, addr %s\n", ifa->ifa_name, | ||||
rc, buf); | ||||
break; | break; | |||
} | } | |||
freeifaddrs(ifaddr); | freeifaddrs(ifaddr); | |||
if (!found) | ||||
warnx("no such interface %s.", iface); | ||||
return rc; | return rc; | |||
} | } | |||
/* subtract sub from val and leave result in val */ | /* subtract sub from val and leave result in val */ | |||
void subtract_timeval(struct timeval *val, const struct timeval *sub) | void subtract_timeval(struct timeval *val, const struct timeval *sub) | |||
{ | { | |||
val->tv_sec -= sub->tv_sec; | val->tv_sec -= sub->tv_sec; | |||
val->tv_usec -= sub->tv_usec; | val->tv_usec -= sub->tv_usec; | |||
if (val->tv_usec < 0) { | if (val->tv_usec < 0) { | |||
val->tv_sec--; | val->tv_sec--; | |||
val->tv_usec += 1000000; | val->tv_usec += 1000000; | |||
} | } | |||
} | } | |||
/* return the timeval converted to a number of milliseconds */ | /* return the timeval converted to a number of milliseconds */ | |||
double timeval_to_ms(const struct timeval *val) | double timeval_to_ms(const struct timeval *val) | |||
{ | { | |||
return val->tv_sec * 1000.0 + val->tv_usec / 1000.0; | return val->tv_sec * 1000.0 + val->tv_usec / 1000.0; | |||
} | } | |||
static void clean_exit(int signo) | static int cleanup(void) | |||
{ | { | |||
(void)signo; | ||||
if ((setsockopt(sd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr))) < 0) | if ((setsockopt(sd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr))) < 0) | |||
err(1, "setsockopt() failed"); | err(1, "setsockopt() failed"); | |||
close(sd); | close(sd); | |||
printf("\n--- %s mping statistics ---\n", arg_mcaddr); | printf("\n--- %s mping statistics ---\n", arg_mcaddr); | |||
printf("%d packets transmitted, %d packets received\n", packets_sent, pac kets_rcvd); | printf("%d packets transmitted, %d packets received\n", packets_sent, pac kets_rcvd); | |||
if (packets_rcvd == 0) | if (packets_rcvd == 0) | |||
printf("round-trip min/avg/max = NA/NA/NA ms\n"); | printf("round-trip min/avg/max = NA/NA/NA ms\n"); | |||
else | else | |||
printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n", | printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n", | |||
rtt_min, (rtt_total / packets_rcvd), rtt_max); | rtt_min, (rtt_total / packets_rcvd), rtt_max); | |||
if (arg_count > 0 && arg_count > packets_rcvd) | if (arg_count > 0 && arg_count > packets_rcvd) | |||
exit(1); | return 1; | |||
exit(0); | ||||
return 0; | ||||
} | ||||
static void clean_exit(int signo) | ||||
{ | ||||
(void)signo; | ||||
running = 0; | ||||
} | } | |||
void send_packet(struct mping *packet) | void send_packet(struct mping *packet) | |||
{ | { | |||
int pkt_len = sizeof(struct mping); | int pkt_len = sizeof(struct mping); | |||
if ((sendto(sd, packet, pkt_len, 0, (struct sockaddr *)&mcaddr, sizeof(mc addr))) != pkt_len) | if ((sendto(sd, packet, pkt_len, 0, (struct sockaddr *)&mcaddr, sizeof(mc addr))) != pkt_len) | |||
err(1, "sendto() sent incorrect number of bytes"); | err(1, "sendto() sent incorrect number of bytes"); | |||
packets_sent++; | packets_sent++; | |||
} | } | |||
void send_mping(int signo) | void send_mping(int signo) | |||
{ | { | |||
static int seqno = 0; | static int seqno = 0; | |||
struct timeval now; | struct timespec now; | |||
(void)signo; | (void)signo; | |||
clock_gettime(CLOCK_MONOTONIC, &now); | ||||
/* | /* | |||
* Tracks number of sent mpings. If deadline mode is enabled we | * Tracks number of sent mpings. If deadline mode is enabled we | |||
* ignore this exit and wait for arg_count number of replies or | * ignore this exit and wait for arg_count number of replies or | |||
* deadline timeout. | * deadline timeout. | |||
*/ | */ | |||
if (arg_deadline) { | if (arg_deadline) { | |||
if (arg_count > 0 && packets_rcvd >= arg_count) | struct timeval tv; | |||
clean_exit(0); | ||||
TIMESPEC_TO_TIMEVAL(&tv, &now); | ||||
subtract_timeval(&tv, &start); | ||||
gettimeofday(&now, NULL); | if ((arg_count > 0 && packets_rcvd >= arg_count) || | |||
subtract_timeval(&now, &start); | (tv.tv_sec >= arg_deadline)) { | |||
if (now.tv_sec >= arg_deadline) | running = 0; | |||
clean_exit(0); | return; | |||
} | ||||
} else if (arg_count > 0 && seqno >= arg_count) { | } else if (arg_count > 0 && seqno >= arg_count) { | |||
/* set another alarm call to exit in 5 second */ | sig(SIGALRM, clean_exit); | |||
signal(SIGALRM, clean_exit); | ||||
alarm(arg_timeout); | alarm(arg_timeout); | |||
return; | return; | |||
} | } | |||
gettimeofday(&now, NULL); | TIMESPEC_TO_TIMEVAL(&mping.tv, &now); | |||
strlencpy(mping.version, VERSION, sizeof(mping.version)); | strlencpy(mping.version, VERSION, sizeof(mping.version)); | |||
mping.type = SENDER; | mping.type = SENDER; | |||
mping.ttl = arg_ttl; | mping.ttl = arg_ttl; | |||
mping.src_host.s_addr = myaddr.s_addr; | mping.src_host = myaddr; | |||
mping.dest_host.s_addr = inet_addr(arg_mcaddr); | mping.dest_host = mcaddr.sin_addr; | |||
mping.seq_no = htonl(seqno); | mping.seq_no = htonl(seqno); | |||
mping.pid = pid; | mping.pid = pid; | |||
mping.tv.tv_sec = htonl(now.tv_sec); | mping.tv.tv_sec = htonl(mping.tv.tv_sec); | |||
mping.tv.tv_usec = htonl(now.tv_usec); | mping.tv.tv_usec = htonl(mping.tv.tv_usec); | |||
mping.delay.tv_sec = 0; | ||||
mping.delay.tv_usec = 0; | ||||
send_packet(&mping); | send_packet(&mping); | |||
seqno++; | seqno++; | |||
/* set another alarm call to send in 1 second */ | /* set another alarm call to send in 1 second */ | |||
signal(SIGALRM, send_mping); | sig(SIGALRM, send_mping); | |||
alarm(1); | alarm(1); | |||
} | } | |||
int process_mping(char *packet, int len, unsigned char type) | int process_mping(char *packet, int len, unsigned char type) | |||
{ | { | |||
if (len < (int)sizeof(struct mping)) { | if (len < (int)sizeof(struct mping)) { | |||
if (debug) | dbg("Discarding packet: too small (%zu bytes)", strlen(packet)); | |||
printf("Discarding packet: too small (%zu bytes)\n", strl | ||||
en(packet)); | ||||
return -1; | return -1; | |||
} | } | |||
rcvd_pkt = (struct mping *)packet; | rcvd_pkt = (struct mping *)packet; | |||
rcvd_pkt->seq_no = ntohl(rcvd_pkt->seq_no); | rcvd_pkt->seq_no = ntohl(rcvd_pkt->seq_no); | |||
rcvd_pkt->tv.tv_sec = ntohl(rcvd_pkt->tv.tv_sec); | rcvd_pkt->tv.tv_sec = ntohl(rcvd_pkt->tv.tv_sec); | |||
rcvd_pkt->tv.tv_usec = ntohl(rcvd_pkt->tv.tv_usec); | rcvd_pkt->tv.tv_usec = ntohl(rcvd_pkt->tv.tv_usec); | |||
rcvd_pkt->delay.tv_sec = ntohl(rcvd_pkt->delay.tv_sec); | ||||
rcvd_pkt->delay.tv_usec = ntohl(rcvd_pkt->delay.tv_usec); | ||||
if (strcmp(rcvd_pkt->version, VERSION)) { | if (strcmp(rcvd_pkt->version, VERSION)) { | |||
if (debug) | dbg("Discarding packet: version mismatch (%s)", rcvd_pkt->version | |||
printf("Discarding packet: version mismatch (%s)\n", rcvd | ); | |||
_pkt->version); | ||||
return -1; | return -1; | |||
} | } | |||
curr_pkt_count++; | ||||
if (rcvd_pkt->type != type) { | if (rcvd_pkt->type != type) { | |||
if (debug) { | if (debug) { | |||
switch (rcvd_pkt->type) { | switch (rcvd_pkt->type) { | |||
case SENDER: | case SENDER: | |||
printf("Discarding sender packet\n"); | printf("Discarding sender packet\n"); | |||
break; | break; | |||
case RECEIVER: | case RECEIVER: | |||
printf("Discarding receiver packet\n"); | printf("Discarding receiver packet\n"); | |||
break; | break; | |||
skipping to change at line 505 | skipping to change at line 517 | |||
printf("Discarding packet: unknown type(%c)\n", r cvd_pkt->type); | printf("Discarding packet: unknown type(%c)\n", r cvd_pkt->type); | |||
break; | break; | |||
} | } | |||
} | } | |||
return -1; | return -1; | |||
} | } | |||
if (rcvd_pkt->type == RECEIVER) { | if (rcvd_pkt->type == RECEIVER) { | |||
if (rcvd_pkt->pid != pid) { | if (rcvd_pkt->pid != pid) { | |||
if (debug) | dbg("Discarding packet: pid mismatch (%u/%u)", pid, rcvd_ | |||
printf("Discarding packet: pid mismatch (%u/%u)\n | pkt->pid); | |||
", pid, rcvd_pkt->pid); | ||||
return -1; | return -1; | |||
} | } | |||
} | } | |||
packets_rcvd++; | packets_rcvd++; | |||
return 0; | return 0; | |||
} | } | |||
void sender_listen_loop() | void sender_listen_loop(void) | |||
{ | { | |||
while (1) { | send_mping(0); | |||
while (running) { | ||||
char recv_packet[MAX_BUF_LEN + 1] = { 0 }; | char recv_packet[MAX_BUF_LEN + 1] = { 0 }; | |||
int len; | int len; | |||
if ((len = recvfrom(sd, recv_packet, MAX_BUF_LEN, 0, NULL, 0)) < 0) { | if ((len = recvfrom(sd, recv_packet, MAX_BUF_LEN, 0, NULL, 0)) < 0) { | |||
if (errno == EINTR) | if (errno == EINTR) | |||
continue; /* interrupt is ok */ | continue; /* interrupt is ok */ | |||
err(1, "recvfrom() failed"); | err(1, "recvfrom() failed"); | |||
} | } | |||
if (process_mping(recv_packet, len, RECEIVER) == 0) { | if (process_mping(recv_packet, len, RECEIVER) == 0) { | |||
struct timeval now; | struct timespec now; | |||
double actual_rtt; /* rtt - send interval delay */ | struct timeval tv; | |||
double rtt; /* round trip time */ | double rtt; /* round trip time */ | |||
gettimeofday(&now, NULL); | clock_gettime(CLOCK_MONOTONIC, &now); | |||
TIMESPEC_TO_TIMEVAL(&tv, &now); | ||||
/* calculate round trip time in milliseconds */ | /* calculate round trip time in milliseconds */ | |||
subtract_timeval(&now, &rcvd_pkt->tv); | subtract_timeval(&tv, &rcvd_pkt->tv); | |||
rtt = timeval_to_ms(&now); | rtt = timeval_to_ms(&tv); | |||
/* remove the backoff delay to determine actual rtt */ | ||||
subtract_timeval(&now, &rcvd_pkt->delay); | ||||
actual_rtt = timeval_to_ms(&now); | ||||
/* keep rtt total, min and max */ | /* keep rtt total, min and max */ | |||
rtt_total += actual_rtt; | rtt_total += rtt; | |||
if (actual_rtt > rtt_max) | if (rtt > rtt_max) | |||
rtt_max = actual_rtt; | rtt_max = rtt; | |||
if (actual_rtt < rtt_min) | if (rtt < rtt_min) | |||
rtt_min = actual_rtt; | rtt_min = rtt; | |||
/* output received packet information */ | /* output received packet information */ | |||
if (!quiet) { | if (!quiet) { | |||
printf("%d bytes from %s: seqno=%u ttl=%d ", | printf("%d bytes from %s: seqno=%u ttl=%d time=% .1f ms\n", | |||
len, inet_ntoa(rcvd_pkt->src_host), | len, inet_ntoa(rcvd_pkt->src_host), | |||
rcvd_pkt->seq_no, rcvd_pkt->ttl); | rcvd_pkt->seq_no, rcvd_pkt->ttl, rtt); | |||
printf("etime=%.1f ms atime=%.3f ms\n", rtt, act | ||||
ual_rtt); | ||||
} | } | |||
} | } | |||
} | } | |||
} | } | |||
void check_send(int i) | void receiver_listen_loop(void) | |||
{ | ||||
struct timeval now; | ||||
gettimeofday(&now, NULL); | ||||
if ((responses[i]->send_time.tv_sec < now.tv_sec) || | ||||
((responses[i]->send_time.tv_sec == now.tv_sec) && | ||||
(responses[i]->send_time.tv_usec <= now.tv_usec))) { | ||||
size_t len = sizeof(responses[i]->pkt); | ||||
if (!quiet) | ||||
printf("Reply to mping from %s bytes=%zu seqno=%u ttl=%d | ||||
\n", | ||||
inet_ntoa(responses[i]->pkt.dest_host), len, | ||||
ntohl(responses[i]->pkt.seq_no), responses[i]->pk | ||||
t.ttl); | ||||
subtract_timeval(&now, &responses[i]->pkt.delay); | ||||
responses[i]->pkt.delay.tv_sec = htonl(now.tv_sec); | ||||
responses[i]->pkt.delay.tv_usec = htonl(now.tv_usec); | ||||
send_packet(&responses[i]->pkt); | ||||
free(responses[i]); | ||||
responses[i] = NULL; | ||||
} | ||||
} | ||||
void received_packet_count(int signo) | ||||
{ | ||||
(void)signo; | ||||
/* update the packet count for the last full second */ | ||||
last_pkt_count = curr_pkt_count; | ||||
curr_pkt_count = 0; | ||||
/* check if the packets in the send buffer are ready to send */ | ||||
for (int i = empty_response; i < RESPONSES_MAX; i++) { | ||||
if (responses[i] != NULL) | ||||
check_send(i); | ||||
} | ||||
if (empty_response != 0) { | ||||
for (int i = 0; i < empty_response; i++) { | ||||
if (responses[i] != NULL) | ||||
check_send(i); | ||||
} | ||||
} | ||||
if (arg_count > 0 && packets_sent >= arg_count) | ||||
exit(0); | ||||
signal(SIGALRM, received_packet_count); | ||||
alarm(1); | ||||
} | ||||
void receiver_listen_loop() | ||||
{ | { | |||
printf("Listening on %s:%d\n", arg_mcaddr, arg_mcport); | printf("Listening on %s:%d\n", arg_mcaddr, arg_mcport); | |||
while (1) { | while (running) { | |||
char recv_packet[MAX_BUF_LEN + 1]; | char recv_packet[MAX_BUF_LEN + 1]; | |||
int len; | int len; | |||
if ((len = recvfrom(sd, recv_packet, MAX_BUF_LEN, 0, NULL, 0)) < 0) { | if ((len = recvfrom(sd, recv_packet, MAX_BUF_LEN, 0, NULL, 0)) < 0) { | |||
if (errno == EINTR) | if (errno == EINTR) | |||
continue; /* interrupt is ok */ | continue; /* interrupt is ok */ | |||
err(1, "recvfrom() failed"); | err(1, "recvfrom() failed"); | |||
} | } | |||
if (process_mping(recv_packet, len, SENDER) == 0) { | if (process_mping(recv_packet, len, SENDER) == 0) { | |||
int i; | ||||
if (!quiet) | if (!quiet) | |||
printf("Received mping from %s bytes=%d seqno=%u ttl=%d\n", | printf("Received mping from %s bytes=%d seqno=%u ttl=%d\n", | |||
inet_ntoa(rcvd_pkt->src_host), len, | inet_ntoa(rcvd_pkt->src_host), len, | |||
rcvd_pkt->seq_no, rcvd_pkt->ttl); | rcvd_pkt->seq_no, rcvd_pkt->ttl); | |||
i = empty_response; | rcvd_pkt->type = RECEIVER; | |||
if (responses[i] != NULL) { | rcvd_pkt->src_host = myaddr; | |||
if (debug) | rcvd_pkt->dest_host = rcvd_pkt->src_host; | |||
printf("Buffer full, packet dropped\n"); | rcvd_pkt->seq_no = htonl(rcvd_pkt->seq_no); | |||
continue; | rcvd_pkt->tv.tv_sec = htonl(rcvd_pkt->tv.tv_sec); | |||
} | rcvd_pkt->tv.tv_usec = htonl(rcvd_pkt->tv.tv_usec); | |||
responses[i] = (struct resp *)malloc(sizeof(struct resp)) | ||||
; | ||||
if (responses[i] == NULL) | ||||
err(1, "Failed allocating response %d", i); | ||||
strlencpy(responses[i]->pkt.version, VERSION, sizeof(resp | ||||
onses[i]->pkt.version)); | ||||
responses[i]->pkt.type = RECEIVER; | ||||
responses[i]->pkt.ttl = rcvd_pkt->ttl; | ||||
responses[i]->pkt.src_host.s_addr = myaddr.s_addr; | ||||
responses[i]->pkt.dest_host.s_addr = rcvd_pkt->src_host.s | ||||
_addr; | ||||
responses[i]->pkt.seq_no = htonl(rcvd_pkt->seq_ | ||||
no); | ||||
responses[i]->pkt.pid = rcvd_pkt->pid; | ||||
responses[i]->pkt.tv.tv_sec = htonl(rcvd_pkt->tv.t | ||||
v_sec); | ||||
responses[i]->pkt.tv.tv_usec = htonl(rcvd_pkt->tv.t | ||||
v_usec); | ||||
gettimeofday(&responses[i]->send_time, NULL); | ||||
responses[i]->pkt.delay = responses[i]->send_time; | ||||
/* send reply immediately */ | /* send reply immediately */ | |||
received_packet_count(0); | send_packet(rcvd_pkt); | |||
/* increment response buffer pointer */ | if (arg_count > 0 && packets_sent >= arg_count) | |||
empty_response++; | exit(0); | |||
if (empty_response >= RESPONSES_MAX) | ||||
empty_response = 0; | ||||
} | } | |||
} | } | |||
} | } | |||
int usage(void) | int usage(void) | |||
{ | { | |||
fprintf(stderr, | fprintf(stderr, | |||
"Usage:\n" | "Usage:\n" | |||
" mping [-dhqrsv] [-c COUNT] [-i IFNAME] [-p PORT] [-t TTL] [-w SEC] [-W SEC] [GROUP]\n" | " mping [-dhqrsv] [-c COUNT] [-i IFNAME] [-p PORT] [-t TTL] [-w SEC] [-W SEC] [GROUP]\n" | |||
"\n" | "\n" | |||
skipping to change at line 706 | skipping to change at line 640 | |||
return 0; | return 0; | |||
} | } | |||
int main(int argc, char **argv) | int main(int argc, char **argv) | |||
{ | { | |||
char *iface = NULL; | char *iface = NULL; | |||
inet_addr_t addr; | inet_addr_t addr; | |||
char ifname[16]; | char ifname[16]; | |||
int mode = 'r'; | int mode = 'r'; | |||
int ifindex; | ||||
int c; | int c; | |||
while ((c = getopt(argc, argv, "c:dh?i:p:qrst:vW:w:")) != -1) { | while ((c = getopt(argc, argv, "c:dh?i:p:qrst:vW:w:")) != -1) { | |||
switch (c) { | switch (c) { | |||
case 'c': | case 'c': | |||
arg_count = atoi(optarg); | arg_count = atoi(optarg); | |||
break; | break; | |||
case 'd': | case 'd': | |||
debug = 1; | debug = 1; | |||
skipping to change at line 750 | skipping to change at line 685 | |||
arg_ttl = atoi(optarg); | arg_ttl = atoi(optarg); | |||
break; | break; | |||
case 'v': | case 'v': | |||
printf("mping version %s\n" | printf("mping version %s\n" | |||
"\n" | "\n" | |||
"Bug report address: https://github.com/troglobit /mping/issues\n" | "Bug report address: https://github.com/troglobit /mping/issues\n" | |||
"Project homepage: https://github.com/troglobit /mping/\n", VERSION); | "Project homepage: https://github.com/troglobit /mping/\n", VERSION); | |||
return 0; | return 0; | |||
case 'W': | ||||
arg_timeout = atoi(optarg); | ||||
break; | ||||
case 'w': | case 'w': | |||
arg_deadline = atoi(optarg); | arg_deadline = atoi(optarg); | |||
break; | break; | |||
case 'W': | ||||
arg_timeout = atoi(optarg); | ||||
break; | ||||
case '?': | case '?': | |||
case 'h': | case 'h': | |||
default: | default: | |||
return usage(); | return usage(); | |||
} | } | |||
} | } | |||
if (optind < argc) | if (optind < argc) | |||
strlencpy(arg_mcaddr, argv[optind], sizeof(arg_mcaddr)); | strlencpy(arg_mcaddr, argv[optind], sizeof(arg_mcaddr)); | |||
pid = getpid(); | pid = getpid(); | |||
init_socket(ifinfo(iface, &addr, AF_INET)); | ifindex = ifinfo(iface, &addr, AF_INET); | |||
if (ifindex <= 0) | ||||
exit(1); | ||||
init_socket(ifindex); | ||||
if (addr.ss_family == AF_INET) { | if (addr.ss_family == AF_INET) { | |||
struct sockaddr_in *sin = (struct sockaddr_in *)&addr; | struct sockaddr_in *sin = (struct sockaddr_in *)&addr; | |||
myaddr = sin->sin_addr; | myaddr = sin->sin_addr; | |||
} | } | |||
if (mode == 's') { | if (mode == 's') { | |||
gettimeofday(&start, NULL); | struct timespec now; | |||
clock_gettime(CLOCK_MONOTONIC, &now); | ||||
TIMESPEC_TO_TIMEVAL(&start, &now); | ||||
printf("MPING %s:%d (ttl %d)\n", arg_mcaddr, arg_mcport, arg_ttl) ; | printf("MPING %s:%d (ttl %d)\n", arg_mcaddr, arg_mcport, arg_ttl) ; | |||
signal(SIGINT, clean_exit); | sig(SIGINT, clean_exit); | |||
signal(SIGALRM, send_mping); | sig(SIGALRM, send_mping); | |||
send_mping(SIGALRM); | ||||
sender_listen_loop(); | sender_listen_loop(); | |||
} else { | } else | |||
for (int i = 0; i < RESPONSES_MAX; i++) | ||||
responses[i] = NULL; | ||||
signal(SIGALRM, received_packet_count); | ||||
alarm(1); | ||||
receiver_listen_loop(); | receiver_listen_loop(); | |||
} | ||||
return 0; | return cleanup(); | |||
} | } | |||
/** | /** | |||
* Local Variables: | * Local Variables: | |||
* indent-tabs-mode: t | * indent-tabs-mode: t | |||
* c-file-style: "linux" | * c-file-style: "linux" | |||
* End: | * End: | |||
*/ | */ | |||
End of changes. 67 change blocks. | ||||
211 lines changed or deleted | 136 lines changed or added |