blob: d0a7f5b546e6b7ca74650b4db734faaaaf989844 [file] [log] [blame]
/*
* (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 = conf->server.ipv4.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);
m->addr.ipv6.sin6_addr = conf->server.ipv6.inet_addr6;
m->addr.ipv6.sin6_scope_id = conf->server.ipv6.scope_id;
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);
/* Bind the sender side to the same interface that we use to
* receive sync messages.
*/
m->addr.ipv6.sin6_scope_id = conf->server.ipv6.scope_id;
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) {
if (errno != EAGAIN)
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_isset(struct udp_sock *m, fd_set *readfds)
{
return FD_ISSET(m->fd, readfds);
}
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;
}