1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
#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"
#include "sockaddr_util.h"
#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 (%i/%i) timed out", ping->host->label, ping->seq,
ping->host->burst.pings_sent, ping->host->max_retries);
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;
ping->host->burst.pings_sent++;
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 struct pingu_ping *pingu_ping_find(struct icmphdr *icp,
union sockaddr_any *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)
{
ping->host->burst.pings_replied++;
log_debug("%s: got seq %i (%i/%i)", ping->host->label, ping->seq,
ping->host->burst.pings_replied,
ping->host->required_replies);
list_del(&ping->ping_list_entry);
ev_timer_stop(loop, &ping->timeout_watcher);
pingu_host_verify_status(loop, ping->host);
free(ping);
}
int pingu_ping_send(struct ev_loop *loop, struct pingu_host *host,
int set_status_on_failure)
{
int packetlen = sizeof(struct iphdr) + sizeof(struct icmphdr);
struct pingu_ping *ping;
int seq, r;
if (!pingu_iface_usable(host->iface))
return pingu_host_set_status(host, 0) - 1;
seq = pingu_ping_get_seq();
r = icmp_send_ping(host->iface->fd, &host->burst.saddr.sa,
sizeof(host->burst.saddr), seq, packetlen);
if (r < 0) {
if (set_status_on_failure)
pingu_host_set_status(host, 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)
{
union sockaddr_any from;
unsigned char buf[1500];
struct iphdr *ip = (struct iphdr *) buf;
struct pingu_ping *ping;
int len = icmp_read_reply(iface->fd, &from.sa, 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);
}
|