summaryrefslogtreecommitdiff
path: root/src/mcast.c
diff options
context:
space:
mode:
authorroot <root@build-vm.(none)>2010-06-14 15:51:25 -0700
committerroot <root@build-vm.(none)>2010-06-14 15:51:25 -0700
commitc4414d9a8b31bedfb7471cd2365aaf5ea5cf55d5 (patch)
tree86ee5befd8195f56dcd91f9fd4e06368bf507a11 /src/mcast.c
downloadconntrack-tools-c4414d9a8b31bedfb7471cd2365aaf5ea5cf55d5.tar.gz
conntrack-tools-c4414d9a8b31bedfb7471cd2365aaf5ea5cf55d5.zip
debian conntrack 0.9.14-2
Diffstat (limited to 'src/mcast.c')
-rw-r--r--src/mcast.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/src/mcast.c b/src/mcast.c
new file mode 100644
index 0000000..4107d5d
--- /dev/null
+++ b/src/mcast.c
@@ -0,0 +1,358 @@
+/*
+ * (C) 2006-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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Description: multicast socket library
+ */
+
+#include "mcast.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>
+#include <libnfnetlink/libnfnetlink.h>
+
+struct mcast_sock *mcast_server_create(struct mcast_conf *conf)
+{
+ int yes = 1;
+ union {
+ struct ip_mreq ipv4;
+ struct ipv6_mreq ipv6;
+ } mreq;
+ struct mcast_sock *m;
+ socklen_t socklen = sizeof(int);
+
+ m = (struct mcast_sock *) malloc(sizeof(struct mcast_sock));
+ if (!m)
+ return NULL;
+ memset(m, 0, sizeof(struct mcast_sock));
+
+ switch(conf->ipproto) {
+ case AF_INET:
+ mreq.ipv4.imr_multiaddr.s_addr = conf->in.inet_addr.s_addr;
+ mreq.ipv4.imr_interface.s_addr =conf->ifa.interface_addr.s_addr;
+
+ m->addr.ipv4.sin_family = AF_INET;
+ m->addr.ipv4.sin_port = htons(conf->port);
+ m->addr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ m->sockaddr_len = sizeof(struct sockaddr_in);
+ break;
+
+ case AF_INET6:
+ memcpy(&mreq.ipv6.ipv6mr_multiaddr, &conf->in.inet_addr6,
+ sizeof(uint32_t) * 4);
+ mreq.ipv6.ipv6mr_interface = conf->ifa.interface_index6;
+
+ m->addr.ipv6.sin6_family = AF_INET6;
+ m->addr.ipv6.sin6_port = htons(conf->port);
+ m->addr.ipv6.sin6_addr = in6addr_any;
+
+ m->sockaddr_len = sizeof(struct sockaddr_in6);
+ break;
+ }
+
+ if ((m->fd = socket(conf->ipproto, SOCK_DGRAM, 0)) == -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;
+ }
+
+ switch(conf->ipproto) {
+ case AF_INET:
+ if (setsockopt(m->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq.ipv4, sizeof(mreq.ipv4)) < 0) {
+ close(m->fd);
+ free(m);
+ return NULL;
+ }
+ break;
+ case AF_INET6:
+ if (setsockopt(m->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq.ipv6, sizeof(mreq.ipv6)) < 0) {
+ close(m->fd);
+ free(m);
+ return NULL;
+ }
+ break;
+ }
+
+ return m;
+}
+
+void mcast_server_destroy(struct mcast_sock *m)
+{
+ close(m->fd);
+ free(m);
+}
+
+static int
+__mcast_client_create_ipv4(struct mcast_sock *m, struct mcast_conf *conf)
+{
+ int no = 0;
+
+ m->addr.ipv4.sin_family = AF_INET;
+ m->addr.ipv4.sin_port = htons(conf->port);
+ m->addr.ipv4.sin_addr = conf->in.inet_addr;
+ m->sockaddr_len = sizeof(struct sockaddr_in);
+
+ if (setsockopt(m->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &no,
+ sizeof(int)) < 0) {
+ close(m->fd);
+ return -1;
+ }
+
+ if (setsockopt(m->fd, IPPROTO_IP, IP_MULTICAST_IF,
+ &conf->ifa.interface_addr,
+ sizeof(struct in_addr)) == -1) {
+ close(m->fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+__mcast_client_create_ipv6(struct mcast_sock *m, struct mcast_conf *conf)
+{
+ int no = 0;
+
+ m->addr.ipv6.sin6_family = AF_INET6;
+ m->addr.ipv6.sin6_port = htons(conf->port);
+ memcpy(&m->addr.ipv6.sin6_addr,
+ &conf->in.inet_addr6,
+ sizeof(struct in6_addr));
+ m->sockaddr_len = sizeof(struct sockaddr_in6);
+
+ if (setsockopt(m->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no,
+ sizeof(int)) < 0) {
+ close(m->fd);
+ return -1;
+ }
+
+ if (setsockopt(m->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &conf->ifa.interface_index6,
+ sizeof(unsigned int)) == -1) {
+ close(m->fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct mcast_sock *mcast_client_create(struct mcast_conf *conf)
+{
+ int ret;
+ struct mcast_sock *m;
+ socklen_t socklen = sizeof(int);
+
+ m = (struct mcast_sock *) malloc(sizeof(struct mcast_sock));
+ if (!m)
+ return NULL;
+ memset(m, 0, sizeof(struct mcast_sock));
+
+ if ((m->fd = socket(conf->ipproto, SOCK_DGRAM, 0)) == -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:
+ ret = __mcast_client_create_ipv4(m, conf);
+ break;
+ case AF_INET6:
+ ret = __mcast_client_create_ipv6(m, conf);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ if (ret == -1) {
+ close(m->fd);
+ free(m);
+ m = NULL;
+ }
+
+ return m;
+}
+
+void mcast_client_destroy(struct mcast_sock *m)
+{
+ close(m->fd);
+ free(m);
+}
+
+ssize_t mcast_send(struct mcast_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 mcast_recv(struct mcast_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) {
+ if (errno != EAGAIN)
+ m->stats.error++;
+ return ret;
+ }
+
+ m->stats.bytes += ret;
+ m->stats.messages++;
+
+ return ret;
+}
+
+int mcast_get_fd(struct mcast_sock *m)
+{
+ return m->fd;
+}
+
+int mcast_isset(struct mcast_sock *m, fd_set *readfds)
+{
+ return FD_ISSET(m->fd, readfds);
+}
+
+int
+mcast_snprintf_stats(char *buf, size_t buflen, char *ifname,
+ struct mcast_stats *s, struct mcast_stats *r)
+{
+ size_t size;
+
+ size = snprintf(buf, buflen, "multicast 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
+mcast_snprintf_stats2(char *buf, size_t buflen, const char *ifname,
+ const char *status, int active,
+ struct mcast_stats *s, struct mcast_stats *r)
+{
+ size_t size;
+
+ size = snprintf(buf, buflen,
+ "multicast 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;
+}