diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2009-03-13 14:00:59 +0100 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2009-03-13 14:00:59 +0100 |
commit | 41e8560ea7c09533d03f523380c1cb5c62d87261 (patch) | |
tree | 684fdff336751ef76b1527c8f9de6af968701b4c /src/udp.c | |
parent | 338d8fc2da19f5d6a75c339d9e6ecac43b68a1e4 (diff) |
sync-mode: add unicast UDP support to propagate state-changes
This patch adds support for unicast UDP to the channel
infrastructure. With this patch, you can select UDP unicast to
propagate state-changes instead of multicast.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/udp.c')
-rw-r--r-- | src/udp.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/src/udp.c b/src/udp.c new file mode 100644 index 0000000..bad8db8 --- /dev/null +++ b/src/udp.c @@ -0,0 +1,261 @@ +/* + * (C) 2009 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "udp.h" + +#include <stdio.h> +#include <stdlib.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <string.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <errno.h> +#include <limits.h> + +struct udp_sock *udp_server_create(struct udp_conf *conf) +{ + int yes = 1; + struct udp_sock *m; + socklen_t socklen = sizeof(int); + + m = calloc(sizeof(struct udp_sock), 1); + if (m == NULL) + return NULL; + + switch(conf->ipproto) { + case AF_INET: + m->addr.ipv4.sin_family = AF_INET; + m->addr.ipv4.sin_port = htons(conf->port); + m->addr.ipv4.sin_addr.s_addr = conf->server.inet_addr.s_addr; + m->sockaddr_len = sizeof(struct sockaddr_in); + break; + + case AF_INET6: + m->addr.ipv6.sin6_family = AF_INET6; + m->addr.ipv6.sin6_port = htons(conf->port); + m->addr.ipv6.sin6_addr = conf->server.inet_addr6; + m->sockaddr_len = sizeof(struct sockaddr_in6); + break; + } + + m->fd = socket(conf->ipproto, SOCK_DGRAM, 0); + if (m->fd == -1) { + free(m); + return NULL; + } + + if (setsockopt(m->fd, SOL_SOCKET, SO_REUSEADDR, &yes, + sizeof(int)) == -1) { + close(m->fd); + free(m); + return NULL; + } + +#ifndef SO_RCVBUFFORCE +#define SO_RCVBUFFORCE 33 +#endif + + if (conf->rcvbuf && + setsockopt(m->fd, SOL_SOCKET, SO_RCVBUFFORCE, &conf->rcvbuf, + sizeof(int)) == -1) { + /* not supported in linux kernel < 2.6.14 */ + if (errno != ENOPROTOOPT) { + close(m->fd); + free(m); + return NULL; + } + } + + getsockopt(m->fd, SOL_SOCKET, SO_RCVBUF, &conf->rcvbuf, &socklen); + + if (bind(m->fd, (struct sockaddr *) &m->addr, m->sockaddr_len) == -1) { + close(m->fd); + free(m); + return NULL; + } + + return m; +} + +void udp_server_destroy(struct udp_sock *m) +{ + close(m->fd); + free(m); +} + +struct udp_sock *udp_client_create(struct udp_conf *conf) +{ + int ret = 0; + struct udp_sock *m; + socklen_t socklen = sizeof(int); + + m = calloc(sizeof(struct udp_sock), 1); + if (m == NULL) + return NULL; + + m->fd = socket(conf->ipproto, SOCK_DGRAM, 0); + if (m->fd == -1) { + free(m); + return NULL; + } + + if (setsockopt(m->fd, SOL_SOCKET, SO_NO_CHECK, &conf->checksum, + sizeof(int)) == -1) { + close(m->fd); + free(m); + return NULL; + } + +#ifndef SO_SNDBUFFORCE +#define SO_SNDBUFFORCE 32 +#endif + + if (conf->sndbuf && + setsockopt(m->fd, SOL_SOCKET, SO_SNDBUFFORCE, &conf->sndbuf, + sizeof(int)) == -1) { + /* not supported in linux kernel < 2.6.14 */ + if (errno != ENOPROTOOPT) { + close(m->fd); + free(m); + return NULL; + } + } + + getsockopt(m->fd, SOL_SOCKET, SO_SNDBUF, &conf->sndbuf, &socklen); + + switch(conf->ipproto) { + case AF_INET: + m->addr.ipv4.sin_family = AF_INET; + m->addr.ipv4.sin_port = htons(conf->port); + m->addr.ipv4.sin_addr = conf->client.inet_addr; + m->sockaddr_len = sizeof(struct sockaddr_in); + break; + case AF_INET6: + m->addr.ipv6.sin6_family = AF_INET6; + m->addr.ipv6.sin6_port = htons(conf->port); + memcpy(&m->addr.ipv6.sin6_addr, &conf->client.inet_addr6, + sizeof(struct in6_addr)); + m->sockaddr_len = sizeof(struct sockaddr_in6); + break; + default: + ret = -1; + break; + } + + if (ret == -1) { + close(m->fd); + free(m); + m = NULL; + } + + return m; +} + +void udp_client_destroy(struct udp_sock *m) +{ + close(m->fd); + free(m); +} + +ssize_t udp_send(struct udp_sock *m, const void *data, int size) +{ + ssize_t ret; + + ret = sendto(m->fd, + data, + size, + 0, + (struct sockaddr *) &m->addr, + m->sockaddr_len); + if (ret == -1) { + m->stats.error++; + return ret; + } + + m->stats.bytes += ret; + m->stats.messages++; + + return ret; +} + +ssize_t udp_recv(struct udp_sock *m, void *data, int size) +{ + ssize_t ret; + socklen_t sin_size = sizeof(struct sockaddr_in); + + ret = recvfrom(m->fd, + data, + size, + 0, + (struct sockaddr *)&m->addr, + &sin_size); + if (ret == -1) { + m->stats.error++; + return ret; + } + + m->stats.bytes += ret; + m->stats.messages++; + + return ret; +} + +int udp_get_fd(struct udp_sock *m) +{ + return m->fd; +} + +int +udp_snprintf_stats(char *buf, size_t buflen, char *ifname, + struct udp_stats *s, struct udp_stats *r) +{ + size_t size; + + size = snprintf(buf, buflen, "UDP traffic (active device=%s):\n" + "%20llu Bytes sent " + "%20llu Bytes recv\n" + "%20llu Pckts sent " + "%20llu Pckts recv\n" + "%20llu Error send " + "%20llu Error recv\n\n", + ifname, + (unsigned long long)s->bytes, + (unsigned long long)r->bytes, + (unsigned long long)s->messages, + (unsigned long long)r->messages, + (unsigned long long)s->error, + (unsigned long long)r->error); + return size; +} + +int +udp_snprintf_stats2(char *buf, size_t buflen, const char *ifname, + const char *status, int active, + struct udp_stats *s, struct udp_stats *r) +{ + size_t size; + + size = snprintf(buf, buflen, + "UDP traffic device=%s status=%s role=%s:\n" + "%20llu Bytes sent " + "%20llu Bytes recv\n" + "%20llu Pckts sent " + "%20llu Pckts recv\n" + "%20llu Error send " + "%20llu Error recv\n\n", + ifname, status, active ? "ACTIVE" : "BACKUP", + (unsigned long long)s->bytes, + (unsigned long long)r->bytes, + (unsigned long long)s->messages, + (unsigned long long)r->messages, + (unsigned long long)s->error, + (unsigned long long)r->error); + return size; +} |