|
@@ -40,6 +40,7 @@
|
|
|
#include <sys/eventfd.h>
|
|
|
#include <sys/resource.h>
|
|
|
#include <sys/socket.h>
|
|
|
+#include <sys/timerfd.h>
|
|
|
#include <sys/types.h>
|
|
|
#include <unistd.h>
|
|
|
|
|
@@ -95,6 +96,18 @@ struct fast_ping_packet {
|
|
|
struct fast_ping_packet_msg msg;
|
|
|
};
|
|
|
|
|
|
+struct fast_ping_fake_ip {
|
|
|
+ struct hlist_node node;
|
|
|
+ atomic_t ref;
|
|
|
+ PING_TYPE type;
|
|
|
+ FAST_PING_TYPE ping_type;
|
|
|
+ char host[PING_MAX_HOSTLEN];
|
|
|
+ int ttl;
|
|
|
+ float time;
|
|
|
+ struct sockaddr_storage addr;
|
|
|
+ int addr_len;
|
|
|
+};
|
|
|
+
|
|
|
struct ping_host_struct {
|
|
|
atomic_t ref;
|
|
|
atomic_t notified;
|
|
@@ -127,6 +140,9 @@ struct ping_host_struct {
|
|
|
};
|
|
|
socklen_t addr_len;
|
|
|
struct fast_ping_packet packet;
|
|
|
+
|
|
|
+ struct fast_ping_fake_ip *fake;
|
|
|
+ int fake_time_fd;
|
|
|
};
|
|
|
|
|
|
struct fast_ping_notify_event {
|
|
@@ -163,6 +179,8 @@ struct fast_ping_struct {
|
|
|
|
|
|
pthread_mutex_t map_lock;
|
|
|
DECLARE_HASHTABLE(addrmap, 6);
|
|
|
+ DECLARE_HASHTABLE(fake, 6);
|
|
|
+ int fake_ip_num;
|
|
|
};
|
|
|
|
|
|
static struct fast_ping_struct ping;
|
|
@@ -170,6 +188,8 @@ static atomic_t ping_sid = ATOMIC_INIT(0);
|
|
|
static int bool_print_log = 1;
|
|
|
|
|
|
static void _fast_ping_host_put(struct ping_host_struct *ping_host);
|
|
|
+static int _fast_ping_get_addr_by_type(PING_TYPE type, const char *ip_str, int port, struct addrinfo **out_gai,
|
|
|
+ FAST_PING_TYPE *out_ping_type);
|
|
|
|
|
|
static void _fast_ping_wakeup_thread(void)
|
|
|
{
|
|
@@ -376,6 +396,179 @@ errout:
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+static void _fast_ping_fake_put(struct fast_ping_fake_ip *fake)
|
|
|
+{
|
|
|
+ int ref_cnt = atomic_dec_and_test(&fake->ref);
|
|
|
+ if (!ref_cnt) {
|
|
|
+ if (ref_cnt < 0) {
|
|
|
+ tlog(TLOG_ERROR, "invalid refcount of fake ping %s", fake->host);
|
|
|
+ abort();
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ pthread_mutex_lock(&ping.map_lock);
|
|
|
+ if (hash_hashed(&fake->node)) {
|
|
|
+ hash_del(&fake->node);
|
|
|
+ }
|
|
|
+ pthread_mutex_unlock(&ping.map_lock);
|
|
|
+
|
|
|
+ free(fake);
|
|
|
+}
|
|
|
+
|
|
|
+static void _fast_ping_fake_remove(struct fast_ping_fake_ip *fake)
|
|
|
+{
|
|
|
+ pthread_mutex_lock(&ping.map_lock);
|
|
|
+ if (hash_hashed(&fake->node)) {
|
|
|
+ hash_del(&fake->node);
|
|
|
+ }
|
|
|
+ pthread_mutex_unlock(&ping.map_lock);
|
|
|
+
|
|
|
+ _fast_ping_fake_put(fake);
|
|
|
+}
|
|
|
+
|
|
|
+static void _fast_ping_fake_get(struct fast_ping_fake_ip *fake)
|
|
|
+{
|
|
|
+ atomic_inc(&fake->ref);
|
|
|
+}
|
|
|
+
|
|
|
+static struct fast_ping_fake_ip *_fast_ping_fake_find(FAST_PING_TYPE ping_type, struct sockaddr *addr, int addr_len)
|
|
|
+{
|
|
|
+ struct fast_ping_fake_ip *fake = NULL;
|
|
|
+ struct fast_ping_fake_ip *ret = NULL;
|
|
|
+ uint32_t key = 0;
|
|
|
+
|
|
|
+ if (ping.fake_ip_num == 0) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ key = jhash(addr, addr_len, 0);
|
|
|
+ key = jhash(&ping_type, sizeof(ping_type), key);
|
|
|
+ pthread_mutex_lock(&ping.map_lock);
|
|
|
+ hash_for_each_possible(ping.fake, fake, node, key)
|
|
|
+ {
|
|
|
+ if (fake->ping_type != ping_type) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fake->addr_len != addr_len) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (memcmp(&fake->addr, addr, fake->addr_len) != 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = fake;
|
|
|
+ _fast_ping_fake_get(fake);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ pthread_mutex_unlock(&ping.map_lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int fast_ping_fake_ip_add(PING_TYPE type, const char *host, int ttl, float time)
|
|
|
+{
|
|
|
+ struct fast_ping_fake_ip *fake = NULL;
|
|
|
+ struct fast_ping_fake_ip *fake_old = NULL;
|
|
|
+ char ip_str[PING_MAX_HOSTLEN];
|
|
|
+ int port = -1;
|
|
|
+ FAST_PING_TYPE ping_type = FAST_PING_END;
|
|
|
+ uint32_t key = 0;
|
|
|
+ int ret = -1;
|
|
|
+ struct addrinfo *gai = NULL;
|
|
|
+
|
|
|
+ if (parse_ip(host, ip_str, &port) != 0) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = _fast_ping_get_addr_by_type(type, ip_str, port, &gai, &ping_type);
|
|
|
+ if (ret != 0) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ fake_old = _fast_ping_fake_find(ping_type, gai->ai_addr, gai->ai_addrlen);
|
|
|
+ fake = malloc(sizeof(*fake));
|
|
|
+ if (fake == NULL) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+ memset(fake, 0, sizeof(*fake));
|
|
|
+
|
|
|
+ safe_strncpy(fake->host, ip_str, PING_MAX_HOSTLEN);
|
|
|
+ fake->ttl = ttl;
|
|
|
+ fake->time = time;
|
|
|
+ fake->type = type;
|
|
|
+ fake->ping_type = ping_type;
|
|
|
+ memcpy(&fake->addr, gai->ai_addr, gai->ai_addrlen);
|
|
|
+ fake->addr_len = gai->ai_addrlen;
|
|
|
+ INIT_HLIST_NODE(&fake->node);
|
|
|
+ atomic_set(&fake->ref, 1);
|
|
|
+
|
|
|
+ key = jhash(&fake->addr, fake->addr_len, 0);
|
|
|
+ key = jhash(&ping_type, sizeof(ping_type), key);
|
|
|
+ pthread_mutex_lock(&ping.map_lock);
|
|
|
+ hash_add(ping.fake, &fake->node, key);
|
|
|
+ pthread_mutex_unlock(&ping.map_lock);
|
|
|
+ ping.fake_ip_num++;
|
|
|
+
|
|
|
+ if (fake_old != NULL) {
|
|
|
+ _fast_ping_fake_put(fake_old);
|
|
|
+ _fast_ping_fake_remove(fake_old);
|
|
|
+ }
|
|
|
+
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ return 0;
|
|
|
+errout:
|
|
|
+ if (fake != NULL) {
|
|
|
+ free(fake);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fake_old != NULL) {
|
|
|
+ _fast_ping_fake_put(fake_old);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (gai != NULL) {
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+int fast_ping_fake_ip_remove(PING_TYPE type, const char *host)
|
|
|
+{
|
|
|
+ struct fast_ping_fake_ip *fake = NULL;
|
|
|
+ char ip_str[PING_MAX_HOSTLEN];
|
|
|
+ int port = -1;
|
|
|
+ int ret = -1;
|
|
|
+ FAST_PING_TYPE ping_type = FAST_PING_END;
|
|
|
+ struct addrinfo *gai = NULL;
|
|
|
+
|
|
|
+ if (parse_ip(host, ip_str, &port) != 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = _fast_ping_get_addr_by_type(type, ip_str, port, &gai, &ping_type);
|
|
|
+ if (ret != 0) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ fake = _fast_ping_fake_find(ping_type, gai->ai_addr, gai->ai_addrlen);
|
|
|
+ if (fake == NULL) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ _fast_ping_fake_remove(fake);
|
|
|
+ _fast_ping_fake_put(fake);
|
|
|
+ ping.fake_ip_num--;
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ return 0;
|
|
|
+errout:
|
|
|
+ if (gai != NULL) {
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
static void _fast_ping_host_get(struct ping_host_struct *ping_host)
|
|
|
{
|
|
|
if (atomic_inc_return(&ping_host->ref) <= 0) {
|
|
@@ -386,6 +579,15 @@ static void _fast_ping_host_get(struct ping_host_struct *ping_host)
|
|
|
|
|
|
static void _fast_ping_close_host_sock(struct ping_host_struct *ping_host)
|
|
|
{
|
|
|
+ if (ping_host->fake_time_fd > 0) {
|
|
|
+ struct epoll_event *event = NULL;
|
|
|
+ event = (struct epoll_event *)1;
|
|
|
+ epoll_ctl(ping.epoll_fd, EPOLL_CTL_DEL, ping_host->fake_time_fd, event);
|
|
|
+
|
|
|
+ close(ping_host->fake_time_fd);
|
|
|
+ ping_host->fake_time_fd = -1;
|
|
|
+ }
|
|
|
+
|
|
|
if (ping_host->fd < 0) {
|
|
|
return;
|
|
|
}
|
|
@@ -455,6 +657,10 @@ static void _fast_ping_host_put(struct ping_host_struct *ping_host)
|
|
|
}
|
|
|
|
|
|
_fast_ping_close_host_sock(ping_host);
|
|
|
+ if (ping_host->fake != NULL) {
|
|
|
+ _fast_ping_fake_put(ping_host->fake);
|
|
|
+ ping_host->fake = NULL;
|
|
|
+ }
|
|
|
|
|
|
pthread_mutex_lock(&ping.map_lock);
|
|
|
hash_del(&ping_host->addr_node);
|
|
@@ -546,6 +752,38 @@ errout:
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+static int _fast_ping_send_fake(struct ping_host_struct *ping_host, struct fast_ping_fake_ip *fake)
|
|
|
+{
|
|
|
+ struct itimerspec its;
|
|
|
+ int sec = fake->time / 1000;
|
|
|
+ int cent_usec = ((long)(fake->time * 10)) % 10000;
|
|
|
+ its.it_value.tv_sec = sec;
|
|
|
+ its.it_value.tv_nsec = cent_usec * 1000 * 100;
|
|
|
+ its.it_interval.tv_sec = 0;
|
|
|
+ its.it_interval.tv_nsec = 0;
|
|
|
+
|
|
|
+ if (timerfd_settime(ping_host->fake_time_fd, 0, &its, NULL) < 0) {
|
|
|
+ tlog(TLOG_ERROR, "timerfd_settime failed, %s", strerror(errno));
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct epoll_event ev;
|
|
|
+ ev.events = EPOLLIN;
|
|
|
+ ev.data.ptr = ping_host;
|
|
|
+ if (epoll_ctl(ping.epoll_fd, EPOLL_CTL_ADD, ping_host->fake_time_fd, &ev) == -1) {
|
|
|
+ if (errno != EEXIST) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ping_host->seq++;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+errout:
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
static int _fast_ping_sendping_v4(struct ping_host_struct *ping_host)
|
|
|
{
|
|
|
struct fast_ping_packet *packet = &ping_host->packet;
|
|
@@ -710,8 +948,16 @@ errout:
|
|
|
static int _fast_ping_sendping(struct ping_host_struct *ping_host)
|
|
|
{
|
|
|
int ret = -1;
|
|
|
+ struct fast_ping_fake_ip *fake = NULL;
|
|
|
gettimeofday(&ping_host->last, NULL);
|
|
|
|
|
|
+ fake = _fast_ping_fake_find(ping_host->type, &ping_host->addr, ping_host->addr_len);
|
|
|
+ if (fake) {
|
|
|
+ ret = _fast_ping_send_fake(ping_host, fake);
|
|
|
+ _fast_ping_fake_put(fake);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
if (ping_host->type == FAST_PING_ICMP) {
|
|
|
ret = _fast_ping_sendping_v4(ping_host);
|
|
|
} else if (ping_host->type == FAST_PING_ICMP6) {
|
|
@@ -1010,13 +1256,18 @@ static int _fast_ping_get_addr_by_icmp(const char *ip_str, int port, struct addr
|
|
|
goto errout;
|
|
|
}
|
|
|
|
|
|
- gai = _fast_ping_getaddr(ip_str, service, socktype, sockproto);
|
|
|
- if (gai == NULL) {
|
|
|
- goto errout;
|
|
|
+ if (out_gai != NULL) {
|
|
|
+ gai = _fast_ping_getaddr(ip_str, service, socktype, sockproto);
|
|
|
+ if (gai == NULL) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ *out_gai = gai;
|
|
|
}
|
|
|
|
|
|
- *out_gai = gai;
|
|
|
- *out_ping_type = ping_type;
|
|
|
+ if (out_ping_type != NULL) {
|
|
|
+ *out_ping_type = ping_type;
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
errout:
|
|
@@ -1150,6 +1401,8 @@ struct ping_host_struct *fast_ping_start(PING_TYPE type, const char *host, int c
|
|
|
FAST_PING_TYPE ping_type = FAST_PING_END;
|
|
|
unsigned int seed = 0;
|
|
|
int ret = 0;
|
|
|
+ struct fast_ping_fake_ip *fake = NULL;
|
|
|
+ int fake_time_fd = -1;
|
|
|
|
|
|
if (parse_ip(host, ip_str, &port) != 0) {
|
|
|
goto errout;
|
|
@@ -1194,6 +1447,19 @@ struct ping_host_struct *fast_ping_start(PING_TYPE type, const char *host, int c
|
|
|
|
|
|
tlog(TLOG_DEBUG, "ping %s, id = %d", host, ping_host->sid);
|
|
|
|
|
|
+ fake = _fast_ping_fake_find(ping_host->type, gai->ai_addr, gai->ai_addrlen);
|
|
|
+ if (fake) {
|
|
|
+ fake_time_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
|
|
+ if (fake_time_fd < 0) {
|
|
|
+ tlog(TLOG_ERROR, "timerfd_create failed, %s", strerror(errno));
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+ /* already take ownership by find. */
|
|
|
+ ping_host->fake = fake;
|
|
|
+ ping_host->fake_time_fd = fake_time_fd;
|
|
|
+ fake = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
addrkey = _fast_ping_hash_key(ping_host->sid, &ping_host->addr);
|
|
|
|
|
|
_fast_ping_host_get(ping_host);
|
|
@@ -1229,6 +1495,14 @@ errout:
|
|
|
free(ping_host);
|
|
|
}
|
|
|
|
|
|
+ if (fake_time_fd > 0) {
|
|
|
+ close(fake_time_fd);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fake) {
|
|
|
+ _fast_ping_fake_put(fake);
|
|
|
+ }
|
|
|
+
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
@@ -1365,6 +1639,33 @@ errout:
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+static int _fast_ping_process_fake(struct ping_host_struct *ping_host, struct timeval *now)
|
|
|
+{
|
|
|
+ struct timeval tvresult = *now;
|
|
|
+ struct timeval *tvsend = &ping_host->last;
|
|
|
+ uint64_t exp;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = read(ping_host->fake_time_fd, &exp, sizeof(uint64_t));
|
|
|
+ if (ret < 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ping_host->ttl = ping_host->fake->ttl;
|
|
|
+ tv_sub(&tvresult, tvsend);
|
|
|
+ if (ping_host->ping_callback) {
|
|
|
+ _fast_ping_send_notify_event(ping_host, PING_RESULT_RESPONSE, ping_host->seq, ping_host->ttl, &tvresult);
|
|
|
+ }
|
|
|
+
|
|
|
+ ping_host->send = 0;
|
|
|
+
|
|
|
+ if (ping_host->count == 1) {
|
|
|
+ _fast_ping_host_remove(ping_host);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int _fast_ping_process_icmp(struct ping_host_struct *ping_host, struct timeval *now)
|
|
|
{
|
|
|
int len = 0;
|
|
@@ -1592,6 +1893,11 @@ static int _fast_ping_process(struct ping_host_struct *ping_host, struct epoll_e
|
|
|
{
|
|
|
int ret = -1;
|
|
|
|
|
|
+ if (ping_host->fake != NULL) {
|
|
|
+ ret = _fast_ping_process_fake(ping_host, now);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
switch (ping_host->type) {
|
|
|
case FAST_PING_ICMP6:
|
|
|
case FAST_PING_ICMP:
|
|
@@ -1635,6 +1941,18 @@ static void _fast_ping_remove_all(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void _fast_ping_remove_all_fake_ip(void)
|
|
|
+{
|
|
|
+ struct fast_ping_fake_ip *fake = NULL;
|
|
|
+ struct hlist_node *tmp = NULL;
|
|
|
+ unsigned long i = 0;
|
|
|
+
|
|
|
+ hash_for_each_safe(ping.fake, i, tmp, fake, node)
|
|
|
+ {
|
|
|
+ _fast_ping_fake_put(fake);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void _fast_ping_period_run(void)
|
|
|
{
|
|
|
struct ping_host_struct *ping_host = NULL;
|
|
@@ -1890,6 +2208,7 @@ int fast_ping_init(void)
|
|
|
INIT_LIST_HEAD(&ping.notify_event_list);
|
|
|
|
|
|
hash_init(ping.addrmap);
|
|
|
+ hash_init(ping.fake);
|
|
|
ping.no_unprivileged_ping = !has_unprivileged_ping();
|
|
|
ping.ident = (getpid() & 0XFFFF);
|
|
|
atomic_set(&ping.run, 1);
|
|
@@ -1998,6 +2317,7 @@ void fast_ping_exit(void)
|
|
|
|
|
|
_fast_ping_close_fds();
|
|
|
_fast_ping_remove_all();
|
|
|
+ _fast_ping_remove_all_fake_ip();
|
|
|
_fast_ping_remove_all_notify_event();
|
|
|
|
|
|
pthread_cond_destroy(&ping.notify_cond);
|