aboutsummaryrefslogtreecommitdiff
path: root/eperd/ping.c
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2015-05-15 10:25:21 +0200
committerBjørn Mork <bjorn@mork.no>2015-05-15 10:25:21 +0200
commitb8c5c3b44362778c099531f7a905c56a0423bcef (patch)
treee9ccaff5d19f39e7bc58734e1b5babf4025a3a9c /eperd/ping.c
parentb1b227fa5e00d08af047ab9a012211b66c6b0f13 (diff)
ripe-atlas-fw: imported version 46504650
Signed-off-by: Bjørn Mork <bjorn@mork.no>
Diffstat (limited to 'eperd/ping.c')
-rw-r--r--eperd/ping.c350
1 files changed, 188 insertions, 162 deletions
diff --git a/eperd/ping.c b/eperd/ping.c
index 16aa8fe..4d17317 100644
--- a/eperd/ping.c
+++ b/eperd/ping.c
@@ -26,7 +26,7 @@
#define DBQ(str) "\"" #str "\""
-#define PING_OPT_STRING ("!46rc:s:A:O:")
+#define PING_OPT_STRING ("!46rc:s:A:O:i:I:")
enum
{
@@ -71,25 +71,13 @@ struct pingbase
{
struct event_base *event_base;
- evutil_socket_t rawfd4; /* Raw socket used to ping hosts (IPv4)
- */
- evutil_socket_t rawfd6; /* Raw socket used to ping hosts (IPv6)
- */
-
pid_t pid; /* Identifier to send with each ICMP
* Request */
- struct timeval tv_interval; /* Ping interval between two subsequent
- * pings */
-
/* A list of hosts to ping. */
struct pingstate **table;
int tabsiz;
- struct event event4; /* Used to detect read events on raw
- * socket */
- struct event event6; /* Used to detect read events on raw
- * socket */
void (*done)(void *state); /* Called when a ping is done */
u_char packet [MAX_DATA_SIZE];
@@ -100,9 +88,11 @@ struct pingstate
/* Parameters */
char *atlas;
char *hostname;
+ char *interface;
int pingcount;
char *out_filename;
char delay_name_res;
+ unsigned interval;
/* State */
struct sockaddr_in6 sin6;
@@ -110,6 +100,7 @@ struct pingstate
struct sockaddr_in6 loc_sin6;
socklen_t loc_socklen;
int busy;
+ int socket;
char got_reply;
char first;
char no_dst;
@@ -117,6 +108,9 @@ struct pingstate
unsigned size;
unsigned psize;
+ struct event event; /* Used to detect read events on raw
+ * socket */
+
char *result;
size_t reslen;
size_t resmax;
@@ -186,19 +180,6 @@ static void add_str(struct pingstate *state, const char *str)
//printf("add_str: result = '%s'\n", state->result);
}
-static int get_timesync(void)
-{
- FILE *fh;
- int lastsync;
-
- fh= fopen(ATLAS_TIMESYNC_FILE, "r");
- if (!fh)
- return -1;
- fscanf(fh, "%d", &lastsync);
- fclose(fh);
- return time(NULL)-lastsync;
-}
-
static void report(struct pingstate *state)
{
FILE *fh;
@@ -259,13 +240,23 @@ static void report(struct pingstate *state)
#endif /* DO_PSIZE */
fprintf(fh, ", \"result\": [ %s ] }\n", state->result);
+
free(state->result);
state->result= NULL;
- state->busy= 0;
-
if (state->out_filename)
fclose(fh);
+
+ /* Kill the event and close socket */
+ event_del(&state->event);
+ if (state->socket != -1)
+ {
+ close(state->socket);
+ state->socket= -1;
+ }
+
+ state->busy= 0;
+
}
static void ping_cb(int result, int bytes, int psize,
@@ -558,8 +549,9 @@ static void fmticmp6(u_char *buffer, size_t *sizep,
static void ping_xmit(struct pingstate *host)
{
struct pingbase *base = host->base;
+ struct timeval tv_interval;
- int nsent, fd4, fd6, t_errno, r;
+ int nsent;
if (host->sentpkts >= host->maxpkts)
{
@@ -587,27 +579,14 @@ static void ping_xmit(struct pingstate *host)
fmticmp6(base->packet, &host->cursize, host->seq, host->index,
base->pid);
- fd6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
- if (fd6 != -1)
- {
- r= connect(fd6, (struct sockaddr *)&host->sin6,
- host->socklen);
- if (r == 0)
- {
- host->loc_socklen=
- sizeof(host->loc_sin6);
- getsockname(fd6, &host->loc_sin6,
- &host->loc_socklen);
- }
- }
+ host->loc_socklen= sizeof(host->loc_sin6);
+ getsockname(host->socket, &host->loc_sin6, &host->loc_socklen);
- nsent = sendto(fd6, base->packet, host->cursize+ICMP6_HDRSIZE,
+ nsent = sendto(host->socket, base->packet,
+ host->cursize+ICMP6_HDRSIZE,
MSG_DONTWAIT, (struct sockaddr *)&host->sin6,
host->socklen);
- t_errno= errno;
- close(fd6);
- errno= t_errno;
}
else
{
@@ -615,28 +594,13 @@ static void ping_xmit(struct pingstate *host)
fmticmp4(base->packet, &host->cursize, host->seq, host->index,
base->pid);
- fd4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
- if (fd4 != -1)
- {
- r= connect(fd4, (struct sockaddr *)&host->sin6,
- host->socklen);
- if (r == 0)
- {
- host->loc_socklen=
- sizeof(host->loc_sin6);
- getsockname(fd4, &host->loc_sin6,
- &host->loc_socklen);
- }
- }
+ host->loc_socklen= sizeof(host->loc_sin6);
+ getsockname(host->socket, &host->loc_sin6, &host->loc_socklen);
-
- nsent = sendto(fd4, base->packet, host->cursize+ICMP_MINLEN,
+ nsent = sendto(host->socket, base->packet,
+ host->cursize+ICMP_MINLEN,
MSG_DONTWAIT, (struct sockaddr *)&host->sin6,
host->socklen);
-
- t_errno= errno;
- close(fd4);
- errno= t_errno;
}
if (nsent > 0)
@@ -660,7 +624,8 @@ static void ping_xmit(struct pingstate *host)
/* Add the timer to handle no reply condition in the given timeout */
- evtimer_add(&host->ping_timer, &host->base->tv_interval);
+ msecstotv(host->interval, &tv_interval);
+ evtimer_add(&host->ping_timer, &tv_interval);
}
@@ -673,9 +638,7 @@ static void noreply_callback(int __attribute((unused)) unused, const short __att
{
ping_cb(PING_ERR_TIMEOUT, host->cursize, -1,
(struct sockaddr *)&host->sin6, host->socklen,
- NULL, 0,
- host->seq, -1, &host->base->tv_interval,
- host);
+ NULL, 0, host->seq, -1, NULL, host);
/* Update the sequence number for the next run */
host->seq = (host->seq + 1) % 256;
@@ -698,29 +661,32 @@ static void noreply_callback(int __attribute((unused)) unused, const short __att
static void ready_callback4 (int __attribute((unused)) unused,
const short __attribute((unused)) event, void * arg)
{
- struct pingbase *base = arg;
-
+ struct pingstate *state;
+ struct pingbase *base;
int nrecv, isDup;
- struct sockaddr_in remote; /* responding internet address */
+ struct sockaddr_in remote; /* responding internet address */
socklen_t slen = sizeof(struct sockaddr);
struct sockaddr_in *sin4p;
struct sockaddr_in loc_sin4;
-
- /* Pointer to relevant portions of the packet (IP, ICMP and user data) */
- struct ip * ip = (struct ip *) base->packet;
+ struct ip * ip;
struct icmphdr * icmp;
- struct evdata * data = (struct evdata *) (base->packet + IPHDR + ICMP_MINLEN);
+ struct evdata * data;
int hlen = 0;
-
struct timeval now;
- struct pingstate * host;
+ state= arg;
+ base = state->base;
+
+ /* Pointer to relevant portions of the packet (IP, ICMP and user
+ * data) */
+ ip = (struct ip *) base->packet;
+ data = (struct evdata *) (base->packet + IPHDR + ICMP_MINLEN);
/* Time the packet has been received */
gettimeofday(&now, NULL);
// printf("ready_callback4: before recvfrom\n");
/* Receive data from the network */
- nrecv = recvfrom(base->rawfd4, base->packet, sizeof(base->packet), MSG_DONTWAIT, (struct sockaddr *) &remote, &slen);
+ nrecv = recvfrom(state->socket, base->packet, sizeof(base->packet), MSG_DONTWAIT, (struct sockaddr *) &remote, &slen);
if (nrecv < 0)
{
goto done;
@@ -766,7 +732,8 @@ printf("ready_callback4: too short\n");
}
/* Get the pointer to the host descriptor in our internal table */
- host= base->table[data->index];
+ if (state != base->table[data->index])
+ goto done; /* Not for us */
/* Check for Destination Host Unreachable */
if (icmp->type == ICMP_ECHO)
@@ -786,31 +753,31 @@ printf("ready_callback4: too short\n");
memset(sin4p, '\0', sizeof(*sin4p));
sin4p->sin_family= AF_INET;
sin4p->sin_addr= ip->ip_dst;
- host->rcvd_ttl= ip->ip_ttl;
+ state->rcvd_ttl= ip->ip_ttl;
/* Report everything with the wrong sequence number as a dup.
* This is not quite right, it could be a late packet. Do we
* care?
*/
- isDup= (ntohs(icmp->un.echo.sequence) != host->seq);
+ isDup= (ntohs(icmp->un.echo.sequence) != state->seq);
ping_cb(isDup ? PING_ERR_DUP : PING_ERR_NONE,
nrecv - IPHDR - ICMP_MINLEN, nrecv,
- (struct sockaddr *)&host->sin6, host->socklen,
+ (struct sockaddr *)&state->sin6, state->socklen,
(struct sockaddr *)&loc_sin4, sizeof(loc_sin4),
ntohs(icmp->un.echo.sequence), ip->ip_ttl, &elapsed,
- host);
+ state);
/* Update the sequence number for the next run */
- host->seq = (host->seq + 1) % 256;
+ state->seq = (state->seq + 1) % 256;
if (!isDup)
- host->got_reply= 1;
+ state->got_reply= 1;
}
else
{
printf("ready_callback4: not an echo reply\n");
/* Handle this condition exactly as the request has expired */
- noreply_callback (-1, -1, host);
+ noreply_callback (-1, -1, state);
}
done:
@@ -831,18 +798,16 @@ done:
static void ready_callback6 (int __attribute((unused)) unused,
const short __attribute((unused)) event, void * arg)
{
- struct pingbase *base = arg;
+ struct pingbase *base;
+ struct pingstate *state;
int nrecv, isDup;
struct sockaddr_in remote; /* responding internet address */
- /* Pointer to relevant portions of the packet (IP, ICMP and user data) */
- struct icmp6_hdr * icmp = (struct icmp6_hdr *) base->packet;
- struct evdata * data = (struct evdata *) (base->packet +
- offsetof(struct icmp6_hdr, icmp6_data16[2]));
+ struct icmp6_hdr *icmp;
+ struct evdata * data;
struct timeval now;
- struct pingstate * host;
struct cmsghdr *cmsgptr;
struct sockaddr_in6 *sin6p;
struct msghdr msg;
@@ -850,6 +815,15 @@ static void ready_callback6 (int __attribute((unused)) unused,
struct iovec iov[1];
char cmsgbuf[256];
+ state= arg;
+ base = state->base;
+
+ /* Pointer to relevant portions of the packet (IP, ICMP and user
+ * data) */
+ icmp = (struct icmp6_hdr *) base->packet;
+ data = (struct evdata *) (base->packet +
+ offsetof(struct icmp6_hdr, icmp6_data16[2]));
+
/* Time the packet has been received */
gettimeofday(&now, NULL);
@@ -864,7 +838,7 @@ static void ready_callback6 (int __attribute((unused)) unused,
msg.msg_flags= 0; /* Not really needed */
/* Receive data from the network */
- nrecv= recvmsg(base->rawfd6, &msg, MSG_DONTWAIT);
+ nrecv= recvmsg(state->socket, &msg, MSG_DONTWAIT);
if (nrecv < 0)
{
goto done;
@@ -885,7 +859,8 @@ static void ready_callback6 (int __attribute((unused)) unused,
}
/* Get the pointer to the host descriptor in our internal table */
- host= base->table[data->index];
+ if (state != base->table[data->index])
+ goto done; /* Not for us */
/* Check for Destination Host Unreachable */
if (icmp->icmp6_type == ICMP6_ECHO_REPLY)
@@ -914,7 +889,7 @@ static void ready_callback6 (int __attribute((unused)) unused,
if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
cmsgptr->cmsg_type == IPV6_HOPLIMIT)
{
- host->rcvd_ttl= *(int *)CMSG_DATA(cmsgptr);
+ state->rcvd_ttl= *(int *)CMSG_DATA(cmsgptr);
}
}
@@ -922,23 +897,23 @@ static void ready_callback6 (int __attribute((unused)) unused,
* This is not quite right, it could be a late packet. Do we
* care?
*/
- isDup= (ntohs(icmp->icmp6_seq) != host->seq);
+ isDup= (ntohs(icmp->icmp6_seq) != state->seq);
ping_cb(isDup ? PING_ERR_DUP : PING_ERR_NONE,
nrecv - ICMP6_HDRSIZE, nrecv + sizeof(struct ip6_hdr),
- (struct sockaddr *)&host->sin6, host->socklen,
+ (struct sockaddr *)&state->sin6, state->socklen,
(struct sockaddr *)&loc_sin6, sizeof(loc_sin6),
- ntohs(icmp->icmp6_seq), host->rcvd_ttl, &elapsed,
- host);
+ ntohs(icmp->icmp6_seq), state->rcvd_ttl, &elapsed,
+ state);
/* Update the sequence number for the next run */
- host->seq = (host->seq + 1) % 256;
+ state->seq = (state->seq + 1) % 256;
if (!isDup)
- host->got_reply= 1;
+ state->got_reply= 1;
}
else
/* Handle this condition exactly as the request has expired */
- noreply_callback (-1, -1, host);
+ noreply_callback (-1, -1, state);
done:
;
@@ -953,53 +928,18 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[],
int i, newsiz, delay_name_res;
uint32_t opt;
unsigned pingcount; /* must be int-sized */
- unsigned size;
+ unsigned size, interval;
sa_family_t af;
const char *hostname;
char *str_Atlas;
char *out_filename;
+ char *interface;
struct pingstate *state;
len_and_sockaddr *lsa;
FILE *fh;
if (!ping_base)
{
- int p_proto, on;
- struct protoent *protop;
- evutil_socket_t fd4, fd6;
-
- /* Check if the ICMP protocol is available on this system */
- protop = getprotobyname("icmp");
- if (protop)
- p_proto= protop->p_proto;
- else
- p_proto= IPPROTO_ICMP;
-
- /* Create an endpoint for communication using raw socket for ICMP calls */
- if ((fd4 = socket(AF_INET, SOCK_RAW, p_proto)) == -1) {
- return NULL;
- }
-
- /* Check if the ICMP6 protocol is available on this system */
- protop = getprotobyname("icmp6");
- if (protop)
- p_proto= protop->p_proto;
- else
- p_proto= IPPROTO_ICMPV6;
-
- if ((fd6 = socket(AF_INET6, SOCK_RAW, p_proto)) == -1) {
- close(fd4);
- return NULL;
- }
-
- on = 1;
- setsockopt(fd6, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
- sizeof(on));
-
- on = 1;
- setsockopt(fd6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
- sizeof(on));
-
ping_base = malloc(sizeof(*ping_base));
if (ping_base == NULL)
return (NULL);
@@ -1011,27 +951,9 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[],
ping_base->table= xzalloc(ping_base->tabsiz *
sizeof(*ping_base->table));
- ping_base->rawfd4 = fd4;
- ping_base->rawfd6 = fd6;
- evutil_make_socket_nonblocking(ping_base->rawfd4);
- evutil_make_socket_nonblocking(ping_base->rawfd6);
-
/* Set default values */
ping_base->pid = getpid();
- msecstotv(DEFAULT_PING_INTERVAL, &ping_base->tv_interval);
-
- /* Define the callback to handle ICMP Echo Reply and add the
- * raw file descriptor to those monitored for read events */
- event_assign(&ping_base->event4, ping_base->event_base,
- ping_base->rawfd4, EV_READ | EV_PERSIST,
- ready_callback4, ping_base);
- event_assign(&ping_base->event6, ping_base->event_base,
- ping_base->rawfd6, EV_READ | EV_PERSIST,
- ready_callback6, ping_base);
- event_add(&ping_base->event4, NULL);
- event_add(&ping_base->event6, NULL);
-
ping_base->done= 0;
}
@@ -1040,10 +962,12 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[],
size= 0;
str_Atlas= NULL;
out_filename= NULL;
+ interval= DEFAULT_PING_INTERVAL;
+ interface= NULL;
/* exactly one argument needed; -c NUM */
- opt_complementary = "=1:c+:s+";
+ opt_complementary = "=1:c+:s+:i+";
opt = getopt32(argv, PING_OPT_STRING, &pingcount, &size,
- &str_Atlas, &out_filename);
+ &str_Atlas, &out_filename, &interval, &interface);
hostname = argv[optind];
if (opt == 0xffffffff)
@@ -1052,6 +976,12 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[],
return NULL;
}
+ if (interval < 1 || interval > 60000)
+ {
+ crondlog(LVL8 "bad interval");
+ return NULL;
+ }
+
if (out_filename)
{
if (!validate_filename(out_filename, SAFE_PREFIX))
@@ -1078,10 +1008,9 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[],
}
}
- af= AF_UNSPEC;
if (opt & opt_4)
af= AF_INET;
- if (opt & opt_6)
+ else
af= AF_INET6;
delay_name_res= !!(opt & opt_r);
@@ -1113,6 +1042,9 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[],
state->base = ping_base;
state->af= af;
state->delay_name_res= delay_name_res;
+ state->interval= interval;
+ state->interface= interface;
+ state->socket= -1;
state->seq = 1;
@@ -1157,7 +1089,9 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[],
static void ping_start2(void *state)
{
+ int p_proto, on, fd;
struct pingstate *pingstate;
+ char line[80];
pingstate= state;
@@ -1167,6 +1101,98 @@ static void ping_start2(void *state)
pingstate->send_error= 0;
pingstate->got_reply= 0;
+ if (pingstate->af == AF_INET)
+ {
+ /* Check if the ICMP protocol is available on this system */
+ p_proto= IPPROTO_ICMP;
+
+ /* Create an endpoint for communication using raw socket for
+ * ICMP calls */
+ if ((fd = socket(AF_INET, SOCK_RAW, p_proto)) == -1) {
+ snprintf(line, sizeof(line),
+ "{ " DBQ(error) ":" DBQ(socket failed: %s)
+ " }", strerror(errno));
+ add_str(pingstate, line);
+ report(pingstate);
+ if (pingstate->base->done)
+ pingstate->base->done(pingstate);
+ return;
+ }
+ pingstate->socket= fd;
+
+ /* Define the callback to handle ICMP Echo Reply and add the
+ * raw file descriptor to those monitored for read events */
+ event_assign(&pingstate->event, pingstate->base->event_base,
+ pingstate->socket, EV_READ | EV_PERSIST,
+ ready_callback4, state);
+ }
+ else
+ {
+ /* Check if the ICMP6 protocol is available on this system */
+ p_proto= IPPROTO_ICMPV6;
+
+ if ((fd = socket(AF_INET6, SOCK_RAW, p_proto)) == -1) {
+ snprintf(line, sizeof(line),
+ "{ " DBQ(error) ":" DBQ(socket failed: %s)
+ " }", strerror(errno));
+ add_str(pingstate, line);
+ report(pingstate);
+ if (pingstate->base->done)
+ pingstate->base->done(pingstate);
+ return;
+ }
+
+ on = 1;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on));
+
+ on = 1;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on));
+
+ pingstate->socket= fd;
+
+ /* Define the callback to handle ICMP Echo Reply and add the
+ * raw file descriptor to those monitored for read events */
+ event_assign(&pingstate->event, pingstate->base->event_base,
+ pingstate->socket, EV_READ | EV_PERSIST,
+ ready_callback6, state);
+ }
+
+ evutil_make_socket_nonblocking(pingstate->socket);
+
+ if (pingstate->interface)
+ {
+ if (bind_interface(pingstate->socket, pingstate->af,
+ pingstate->interface) == -1)
+ {
+ snprintf(line, sizeof(line),
+ "{ " DBQ(error) ":"
+ DBQ(bind to interface failed)
+ " }");
+ add_str(pingstate, line);
+ report(pingstate);
+ if (pingstate->base->done)
+ pingstate->base->done(pingstate);
+ return;
+ }
+ }
+
+ if (connect(pingstate->socket, &pingstate->sin6,
+ pingstate->socklen) == -1)
+ {
+ snprintf(line, sizeof(line),
+ "{ " DBQ(error) ":" DBQ(connect failed: %s)
+ " }", strerror(errno));
+ add_str(pingstate, line);
+ report(pingstate);
+ if (pingstate->base->done)
+ pingstate->base->done(pingstate);
+ return;
+ }
+
+ event_add(&pingstate->event, NULL);
+
ping_xmit(pingstate);
}