diff options
author | Natanael Copa <ncopa@alpinelinux.org> | 2011-07-07 15:52:20 +0200 |
---|---|---|
committer | Natanael Copa <ncopa@alpinelinux.org> | 2011-07-07 15:52:20 +0200 |
commit | e8c3caa9a966ba0889170529aa428e7d0d43184d (patch) | |
tree | abc3d6b4c94d6785b8273c94110d0f13374383e5 /pingu_ping.c | |
parent | 0b6bacf11267e28152803c44ee578fa562b8d2c2 (diff) | |
download | pingu-e8c3caa9a966ba0889170529aa428e7d0d43184d.tar.bz2 pingu-e8c3caa9a966ba0889170529aa428e7d0d43184d.tar.xz |
pingu_ping: initial implementation
Diffstat (limited to 'pingu_ping.c')
-rw-r--r-- | pingu_ping.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/pingu_ping.c b/pingu_ping.c new file mode 100644 index 0000000..c6ac66d --- /dev/null +++ b/pingu_ping.c @@ -0,0 +1,133 @@ +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <ev.h> + +#include "icmp.h" +#include "list.h" +#include "log.h" +#include "pingu_burst.h" +#include "pingu_host.h" +#include "pingu_iface.h" +#include "pingu_ping.h" + +#define SOCK_ADDR_IN_PTR(sa) ((struct sockaddr_in *)(sa)) +#define SOCK_ADDR_IN_ADDR(sa) SOCK_ADDR_IN_PTR(sa)->sin_addr + +#define PING_SEQ_MAX 32000 + +static int pingu_ping_get_seq(void) +{ + static int seq = 0; + seq = (seq + 1) % PING_SEQ_MAX; + return seq; +} + +static void pingu_ping_timeout_cb(struct ev_loop *loop, ev_timer *w, + int revents) +{ + struct pingu_ping *ping = container_of(w, struct pingu_ping, timeout_watcher); + log_debug("%s: seq %i timed out", ping->host->host, ping->seq); + list_del(&ping->ping_list_entry); + pingu_host_verify_status(loop, ping->host); + free(ping); +} + +static struct pingu_ping *pingu_ping_add(struct ev_loop *loop, + struct pingu_host *host, int seq) +{ + struct pingu_ping *ping = calloc(1, sizeof(struct pingu_ping)); + if (ping == NULL) + return NULL; + ping->seq = seq; + ping->host = host; + ev_timer_init(&ping->timeout_watcher, pingu_ping_timeout_cb, + host->timeout, 0); + ev_timer_start(loop, &ping->timeout_watcher); + list_add(&ping->ping_list_entry, &host->iface->ping_list); + return ping; +} + +static int sockaddr_cmp(struct sockaddr *a, struct sockaddr *b) +{ + if (a->sa_family != b->sa_family) + return -1; + switch (a->sa_family) { + case AF_INET: + return (SOCK_ADDR_IN_ADDR(a).s_addr - SOCK_ADDR_IN_ADDR(b).s_addr); + break; + } + return -1; +} + +static struct pingu_ping *pingu_ping_find(struct icmphdr *icp, + struct sockaddr *from, + struct list_head *ping_list) +{ + struct pingu_ping *ping; + if (icp->type != ICMP_ECHOREPLY || icp->un.echo.id != getpid()) + return NULL; + + list_for_each_entry(ping, ping_list, ping_list_entry) { + if (sockaddr_cmp(&ping->host->burst.saddr, from) == 0 + && ping->seq == ntohs(icp->un.echo.sequence)) + return ping; + } + return NULL; +} + +static void pingu_ping_handle_reply(struct ev_loop *loop, + struct pingu_ping *ping) +{ + log_debug("%s: got seq %i", ping->host->host, ping->seq); + ping->host->burst.pings_replied++; + list_del(&ping->ping_list_entry); + ev_timer_stop(&ping->timeout_watcher, loop); + pingu_host_verify_status(loop, ping->host); + free(ping); +} + +int pingu_ping_send(struct ev_loop *loop, struct pingu_host *host) +{ + int packetlen = sizeof(struct iphdr) + sizeof(struct icmphdr); + struct pingu_ping *ping; + int seq, r; + + seq = pingu_ping_get_seq(); + r = icmp_send_ping(host->iface->fd, &host->burst.saddr, + sizeof(host->burst.saddr), seq, packetlen); + if (r < 0) + return -1; + + ping = pingu_ping_add(loop, host, seq); + return ping == NULL ? -1 : 0; +} + +void pingu_ping_read_reply(struct ev_loop *loop, struct pingu_iface *iface) +{ + struct sockaddr from; + unsigned char buf[1500]; + struct iphdr *ip = (struct iphdr *) buf; + struct pingu_ping *ping; + + int len = icmp_read_reply(iface->fd, &from, sizeof(from), buf, + sizeof(buf)); + if (len <= 0) + return; + + ping = pingu_ping_find((struct icmphdr *) &buf[ip->ihl * 4], &from, + &iface->ping_list); + if (ping == NULL) + return; + + pingu_ping_handle_reply(loop, ping); +} + |