Files
libucore/test/udp_heartbeat.c
T
2013-11-26 23:58:50 +01:00

305 lines
7.2 KiB
C

#define DEBUG
#include <getopt.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ucore/logging.h>
#include <ucore/logging.h>
#include <ucore/iomux.h>
#include <ucore/utils.h>
#include <ucore/pack.h>
#define HB_MAGIX 0xBEA7BEA7
#define LMAIN 0
#define PEER_HB_SEC 5
#define PEER_HB_MISS 3
static struct uc_log_module modules[] = {
{
.id = LMAIN,
.short_name = "MAIN",
.long_name = "Main logging module",
.log_level = UC_LL_DEBUG
}
};
static struct uc_log_modules log_modules = {
.mods = modules,
.cnt = ARRAY_SIZE(modules)
};
enum PeerState {
PeerUnknown,
PeerAlive,
PeerDead
};
enum PeerEvent {
PeerReceived,
PeerTimedout,
};
struct HBPeer {
struct UCTimer timer;
struct sockaddr_in peer_addr;
time_t last_ok;
enum PeerState state;
TAILQ_ENTRY(HBPeer) entry;
};
struct HBContext {
struct IOMuxFD sock_fd;
TAILQ_HEAD(, HBPeer) peers;
struct IOMux *mux;
};
struct HBPeer *hb_peer_find(struct HBContext *ctx, const struct sockaddr_in *peer_addr);
void peer_send(struct HBContext *ctx, struct HBPeer *peer);
void peer_do_state(struct HBPeer *peer, enum PeerEvent event);
void peer_timer_cb(struct UCTimers *timers, struct UCTimer *timer)
{
struct HBPeer *peer = UC_CONTAINER_OF(timer, struct HBPeer, timer);
struct HBContext *ctx = timer->cookie_ptr;
time_t now;
uc_timers_add(timers, timer, PEER_HB_MISS , 0);
now = time(NULL);
if (now - peer->last_ok > PEER_HB_MISS * PEER_HB_SEC) {
peer_do_state(peer, PeerTimedout);
}
peer_send(ctx, peer);
}
void peer_add_addr(struct HBContext *ctx, const struct sockaddr_in *addr)
{
struct HBPeer *peer = hb_peer_find(ctx, addr);
int sec;
int usec;
if (peer != NULL) {
UC_LOGF(UC_LL_INFO, LMAIN, "Ignorming adding existing peer %s:%u\n",
inet_ntoa(addr->sin_addr), addr->sin_port);
return;
}
peer = calloc(1, sizeof *peer);
peer->peer_addr = *addr;
TAILQ_INSERT_TAIL(&ctx->peers, peer, entry);
peer->timer.callback = peer_timer_cb;
peer->timer.cookie_ptr = ctx;
peer->state = PeerUnknown;
sec = rand() % PEER_HB_SEC;
usec = rand() % 1000000;
uc_timers_add(iomux_get_timers(ctx->mux), &peer->timer, sec, usec);
}
void peer_add(struct HBContext *ctx, const char *host, uint16_t port)
{
struct sockaddr_in peer_addr;
UC_DEBUGF(LMAIN, "Adding peer %s:%u\n", host, port);
memset(&peer_addr, 0, sizeof peer_addr);
peer_addr.sin_family = AF_INET;
peer_addr.sin_addr.s_addr = inet_addr(host);
peer_addr.sin_port = htons(port);
peer_add_addr(ctx, &peer_addr);
}
void peer_send(struct HBContext *ctx, struct HBPeer *peer)
{
uint8_t buf[4];
ssize_t rc;
uc_pack_32_be(HB_MAGIX, buf);
UC_DEBUGF(LMAIN, "Sending to %s:%u\n", inet_ntoa(peer->peer_addr.sin_addr),
ntohs(peer->peer_addr.sin_port));
rc = sendto(ctx->sock_fd.fd, buf, sizeof buf, 0,
(struct sockaddr*)&peer->peer_addr, sizeof peer->peer_addr);
if (rc == -1) {
UC_DEBUGF(LMAIN, "Could not send packet: %s\n", strerror(errno));
}
}
struct HBPeer *hb_peer_find(struct HBContext *ctx, const struct sockaddr_in *peer_addr)
{
struct HBPeer *peer;
TAILQ_FOREACH(peer, &ctx->peers, entry) {
if (peer_addr->sin_addr.s_addr == peer->peer_addr.sin_addr.s_addr
&& peer_addr->sin_port == peer->peer_addr.sin_port) {
break;
}
}
return peer;
}
void peer_do_state(struct HBPeer *peer, enum PeerEvent event)
{
if (peer->state == PeerUnknown || peer->state == PeerDead) {
if (event == PeerReceived) {
UC_LOGF(UC_LL_INFO, LMAIN, "Peer is now alive\n");
peer->state = PeerAlive;
}
} else if (peer->state == PeerAlive) {
if (event == PeerTimedout) {
UC_LOGF(UC_LL_INFO, LMAIN, "Peer is dead !\n");
peer->state = PeerDead;
}
}
if (peer->state == PeerUnknown && event == PeerTimedout) {
peer->state = PeerDead;
}
}
void peer_received(struct HBPeer *peer)
{
peer->last_ok = time(NULL);
peer_do_state(peer, PeerReceived);
}
void hb_read(struct IOMux *mux, struct IOMuxFD *fd, unsigned int event)
{
struct HBContext *ctx = UC_CONTAINER_OF(fd, struct HBContext, sock_fd);
ssize_t rc;
struct sockaddr_in peer_addr;
socklen_t peer_len = sizeof peer_addr;
uint8_t buf[128];
uint32_t magic;
struct HBPeer *peer;
rc = recvfrom(fd->fd, buf, sizeof buf, 0, (struct sockaddr*)&peer_addr, &peer_len);
if (rc == -1 && errno == EWOULDBLOCK) {
UC_DEBUGF(LMAIN, "recvfrom EWOULDBLOCK\n");
return;
}
UC_LOGF(UC_LL_INFO, LMAIN, "Message from %s:%u\n", inet_ntoa(peer_addr.sin_addr),
ntohs(peer_addr.sin_port));
if (rc < 4) {
UC_DEBUGF(LMAIN, "short read of %u\n", (unsigned)rc);
return;
}
magic = uc_unpack_32_be(buf);
if (magic != HB_MAGIX) {
UC_LOGF(UC_LL_WARNING, LMAIN, "Received unknown message, magic = 0x%X\n", magic);
return;
}
peer = hb_peer_find(ctx, &peer_addr);
if (peer == NULL) {
UC_LOGF(UC_LL_WARNING, LMAIN, "Found no peer\n");
return;
}
peer_received(peer);
}
void init_hb_context(struct HBContext *ctx, uint16_t local_port)
{
int sock_fd;
struct sockaddr_in addr;
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(local_port);
addr.sin_addr.s_addr = INADDR_ANY;
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock_fd == -1) {
UC_LOGF(UC_LL_ERROR, LMAIN, "socket failed : %s\n", strerror(errno));
return;
}
if (bind(sock_fd, (struct sockaddr*)&addr, sizeof addr) != 0) {
UC_LOGF(UC_LL_ERROR, LMAIN, "Cannot bind socket to port %u : %s\n",
local_port, strerror(errno));
close(sock_fd);
return;
}
TAILQ_INIT(&ctx->peers);
ctx->sock_fd.fd = sock_fd;
ctx->sock_fd.what = MUX_EV_READ;
ctx->sock_fd.callback = hb_read;
}
int main(int argc, char *argv[])
{
struct HBContext ctx;
struct IOMux *mux;
uint16_t local_port = 23456;
uint16_t remote_port = 23456;
int c;
struct uc_log_destination *dest;
srand(getpid());
uc_log_init(&log_modules);
dest = uc_log_new_stderr(UC_LL_DEBUG, 1);
uc_log_add_destination(dest);
mux = iomux_create(IOMUX_TYPE_DEFAULT);
ctx.mux = mux;
init_hb_context(&ctx, local_port);
while ((c = getopt(argc, argv, "m:p:P:a:")) != -1) {
switch (c) {
case 'p':
local_port = atoi(optarg);
break;
case 'P':
remote_port = atoi(optarg);
break;
case 'a':
peer_add(&ctx, optarg, remote_port);
break;
case 'm':
uc_log_destination_set_mask(dest, optarg);
break;
default:
printf("Unknown option -%c\n", c);
return 1;
}
}
UC_DEBUGF(LMAIN, "Creating IOMux\n");
iomux_register_fd(mux, &ctx.sock_fd);
iomux_run(mux);
return 0;
}