|
Packit |
61cb5a |
/*
|
|
Packit |
61cb5a |
* Copyright (c) 2010-2011, Red Hat, Inc.
|
|
Packit |
61cb5a |
*
|
|
Packit |
61cb5a |
* Permission to use, copy, modify, and/or distribute this software for any
|
|
Packit |
61cb5a |
* purpose with or without fee is hereby granted, provided that the above
|
|
Packit |
61cb5a |
* copyright notice and this permission notice appear in all copies.
|
|
Packit |
61cb5a |
*
|
|
Packit |
61cb5a |
* THE SOFTWARE IS PROVIDED "AS IS" AND RED HAT, INC. DISCLAIMS ALL WARRANTIES
|
|
Packit |
61cb5a |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
Packit |
61cb5a |
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RED HAT, INC. BE LIABLE
|
|
Packit |
61cb5a |
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
Packit |
61cb5a |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
Packit |
61cb5a |
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
Packit |
61cb5a |
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
Packit |
61cb5a |
*/
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
/*
|
|
Packit |
61cb5a |
* Author: Jan Friesse <jfriesse@redhat.com>
|
|
Packit |
61cb5a |
*/
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
#include <sys/types.h>
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
#include <sys/socket.h>
|
|
Packit |
61cb5a |
#include <sys/uio.h>
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
#include <net/if.h>
|
|
Packit |
61cb5a |
#include <netinet/in.h>
|
|
Packit |
61cb5a |
#include <arpa/inet.h>
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
#include <err.h>
|
|
Packit |
61cb5a |
#include <errno.h>
|
|
Packit |
61cb5a |
#include <netdb.h>
|
|
Packit |
61cb5a |
#include <poll.h>
|
|
Packit |
61cb5a |
#include <string.h>
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
#include "addrfunc.h"
|
|
Packit |
61cb5a |
#include "logging.h"
|
|
Packit |
61cb5a |
#include "rsfunc.h"
|
|
Packit |
61cb5a |
#include "util.h"
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
/*
|
|
Packit |
61cb5a |
* Wrapper on top of poll. This poll stores old timestamp so it's possible to put always same
|
|
Packit |
61cb5a |
* timeout but correct timeout is computed from old_tstamp and current time. In other words, this
|
|
Packit |
61cb5a |
* function will always after timeout expire return timeout (0) not depending on number of times
|
|
Packit |
61cb5a |
* this function was called.
|
|
Packit |
61cb5a |
* unicast_socket and multicast_socket are two sockets, timeout is absolute timeout (after this
|
|
Packit |
61cb5a |
* value, function returns 0) and old_tstamp is internal state variable (on first call value
|
|
Packit |
61cb5a |
* must be zeroed).
|
|
Packit |
61cb5a |
* Function return bit field (unicast_socket - bit 1, multicast_socket - bit 2) if something was
|
|
Packit |
61cb5a |
* read, 0 on timeout, -1 on fail (use errno) and -2 on interrupt.
|
|
Packit |
61cb5a |
*/
|
|
Packit |
61cb5a |
int
|
|
Packit |
61cb5a |
rs_poll_timeout(int unicast_socket, int multicast_socket, int timeout, struct timeval *old_tstamp)
|
|
Packit |
61cb5a |
{
|
|
Packit |
61cb5a |
struct pollfd pfds[2];
|
|
Packit |
61cb5a |
struct timeval cur_time;
|
|
Packit |
61cb5a |
int poll_timeout;
|
|
Packit |
61cb5a |
int poll_res;
|
|
Packit |
61cb5a |
int res;
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
cur_time = util_get_time();
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (old_tstamp->tv_sec == 0 && old_tstamp->tv_usec == 0) {
|
|
Packit |
61cb5a |
*old_tstamp = cur_time;
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if ((int)util_time_absdiff(cur_time, *old_tstamp) > timeout) {
|
|
Packit |
61cb5a |
memset(old_tstamp, 0, sizeof(*old_tstamp));
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
return (0);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
poll_timeout = timeout - util_time_absdiff(cur_time, *old_tstamp);
|
|
Packit |
61cb5a |
if (poll_timeout < 0) {
|
|
Packit |
61cb5a |
poll_timeout = 0;
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
memset(pfds, 0, sizeof(struct pollfd) * 2);
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
pfds[0].fd = unicast_socket;
|
|
Packit |
61cb5a |
pfds[0].events = POLLIN;
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
pfds[1].fd = multicast_socket;
|
|
Packit |
61cb5a |
pfds[1].events = POLLIN;
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
poll_res = poll(pfds, 2, poll_timeout);
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (poll_res == 0) {
|
|
Packit |
61cb5a |
memset(old_tstamp, 0, sizeof(*old_tstamp));
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
return (0);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (poll_res == -1) {
|
|
Packit |
61cb5a |
if (errno == EINTR) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("poll error - EINTR");
|
|
Packit |
61cb5a |
return (-2);
|
|
Packit |
61cb5a |
} else {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("poll error - errno = %d", errno);
|
|
Packit |
61cb5a |
return (-1);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (pfds[0].revents & POLLERR || pfds[0].revents & POLLHUP || pfds[0].revents & POLLNVAL) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("poll error. pfds[0] revents = %d", pfds[0].revents);
|
|
Packit |
61cb5a |
return (-1);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (pfds[1].revents & POLLERR || pfds[1].revents & POLLHUP || pfds[1].revents & POLLNVAL) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("poll error. pfds[1] revents = %d", pfds[1].revents);
|
|
Packit |
61cb5a |
return (-1);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
res = 0;
|
|
Packit |
61cb5a |
if (pfds[0].revents & POLLIN) {
|
|
Packit |
61cb5a |
res |= 1;
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (pfds[1].revents & POLLIN) {
|
|
Packit |
61cb5a |
res |= 2;
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
return (res);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
/*
|
|
Packit |
61cb5a |
* Wrapper on top of recvmsg which emulates recvfrom but it's also able to return ttl. sock is
|
|
Packit |
61cb5a |
* socket where to make recvmsg. from_addr is address where address of source will be stored. msg is
|
|
Packit |
61cb5a |
* buffer where to store message with maximum msg_len size. ttl is pointer where TTL (time-to-live)
|
|
Packit |
61cb5a |
* from packet will be stored (or 0 if no such information is available). Timestamp is filled
|
|
Packit |
61cb5a |
* either by SCM_TIMESTAMP directly from packet (if supported) or current get gettimeofday.
|
|
Packit |
61cb5a |
* NULL can be passed as timestamp pointer.
|
|
Packit |
61cb5a |
* Return number of received bytes, or -2 on EINTR, -3 on one of EHOSTUNREACH | ENETDOWN |
|
|
Packit |
61cb5a |
* EHOSTDOWN | ECONNRESET, -4 if message is truncated, or -1 on different error.
|
|
Packit |
61cb5a |
*/
|
|
Packit |
61cb5a |
ssize_t
|
|
Packit |
61cb5a |
rs_receive_msg(int sock, struct sockaddr_storage *from_addr, char *msg, size_t msg_len,
|
|
Packit |
61cb5a |
uint8_t *ttl, struct timeval *timestamp)
|
|
Packit |
61cb5a |
{
|
|
Packit |
61cb5a |
char cmsg_buf[CMSG_SPACE(1024)];
|
|
Packit |
61cb5a |
struct cmsghdr *cmsg;
|
|
Packit |
61cb5a |
struct iovec msg_iovec;
|
|
Packit |
61cb5a |
struct msghdr msg_hdr;
|
|
Packit |
61cb5a |
ssize_t recv_size;
|
|
Packit |
61cb5a |
int ittl;
|
|
Packit |
61cb5a |
int timestamp_set;
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
ittl = 0;
|
|
Packit |
61cb5a |
timestamp_set = 0;
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
memset(&msg_iovec, 0, sizeof(msg_iovec));
|
|
Packit |
61cb5a |
msg_iovec.iov_base = msg;
|
|
Packit |
61cb5a |
msg_iovec.iov_len = msg_len;
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
memset(&msg_hdr, 0, sizeof(msg_hdr));
|
|
Packit |
61cb5a |
msg_hdr.msg_name = from_addr;
|
|
Packit |
61cb5a |
msg_hdr.msg_namelen = sizeof(struct sockaddr_storage);
|
|
Packit |
61cb5a |
msg_hdr.msg_iov = &msg_iovec;
|
|
Packit |
61cb5a |
msg_hdr.msg_iovlen = 1;
|
|
Packit |
61cb5a |
msg_hdr.msg_control = cmsg_buf;
|
|
Packit |
61cb5a |
msg_hdr.msg_controllen = sizeof(cmsg_buf);
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
recv_size = recvmsg(sock, &msg_hdr, 0);
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (recv_size == -1) {
|
|
Packit |
61cb5a |
if (errno == EINTR) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("recvmsg error - EINTR");
|
|
Packit |
61cb5a |
return (-2);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (errno == EHOSTUNREACH || errno == EHOSTDOWN || errno == ENETDOWN ||
|
|
Packit |
61cb5a |
errno == ECONNRESET) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("recvmsg error - EHOSTUNREACH || EHOSTDOWN || ENETDOWN ||"
|
|
Packit |
61cb5a |
" ECONNRESET");
|
|
Packit |
61cb5a |
return (-3);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("recvmsg error - errno = %d", errno);
|
|
Packit |
61cb5a |
return (-1);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (msg_hdr.msg_flags & MSG_TRUNC || msg_hdr.msg_flags & MSG_CTRUNC) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("recvmsg error - MSG_TRUNC | MSG_CTRUNC");
|
|
Packit |
61cb5a |
return (-4);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
for (cmsg = CMSG_FIRSTHDR(&msg_hdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg_hdr, cmsg)) {
|
|
Packit |
61cb5a |
switch (cmsg->cmsg_level) {
|
|
Packit |
61cb5a |
case SOL_SOCKET:
|
|
Packit |
61cb5a |
#ifdef SCM_TIMESTAMP
|
|
Packit |
61cb5a |
if (cmsg->cmsg_type == SCM_TIMESTAMP &&
|
|
Packit |
61cb5a |
cmsg->cmsg_len >= sizeof(struct timeval) && timestamp != NULL) {
|
|
Packit |
61cb5a |
memcpy(timestamp, CMSG_DATA(cmsg), sizeof(struct timeval));
|
|
Packit |
61cb5a |
timestamp_set = 1;
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
#endif
|
|
Packit |
61cb5a |
case IPPROTO_IP:
|
|
Packit |
61cb5a |
if (cmsg->cmsg_type == IP_TTL && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
|
Packit |
61cb5a |
memcpy(&ittl, CMSG_DATA(cmsg), sizeof(ittl));
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
#ifdef IP_RECVTTL
|
|
Packit |
61cb5a |
if (cmsg->cmsg_type == IP_RECVTTL && cmsg->cmsg_len > 1) {
|
|
Packit |
61cb5a |
ittl = *(uint8_t *)CMSG_DATA(cmsg);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
#endif
|
|
Packit |
61cb5a |
break;
|
|
Packit |
61cb5a |
case IPPROTO_IPV6:
|
|
Packit |
61cb5a |
if (cmsg->cmsg_type == IPV6_HOPLIMIT && cmsg->cmsg_len ==
|
|
Packit |
61cb5a |
CMSG_LEN(sizeof(int))) {
|
|
Packit |
61cb5a |
memcpy(&ittl, CMSG_DATA(cmsg), sizeof(ittl));
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
break;
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
*ttl = (uint8_t)ittl;
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (!timestamp_set && timestamp != NULL) {
|
|
Packit |
61cb5a |
*timestamp = util_get_time();
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
return (recv_size);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
/*
|
|
Packit |
61cb5a |
* Thin wrapper on top of sendto. sock is socket, msg is message with msg_size length to send and to
|
|
Packit |
61cb5a |
* is address where to send message.
|
|
Packit |
61cb5a |
* Return number of sent bytes or -2 on EINTR, -3 on one of EHOSTDOWN | ENETDOWN | EHOSTUNREACH |
|
|
Packit |
61cb5a |
* ENOBUFS or -1 on some different error (sent != msg_size).
|
|
Packit |
61cb5a |
*/
|
|
Packit |
61cb5a |
ssize_t
|
|
Packit |
61cb5a |
rs_sendto(int sock, const char *msg, size_t msg_size, const struct sockaddr_storage *to)
|
|
Packit |
61cb5a |
{
|
|
Packit |
61cb5a |
ssize_t sent;
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
sent = sendto(sock, msg, msg_size, 0, (struct sockaddr *)to, af_sas_len(to));
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (sent == -1) {
|
|
Packit |
61cb5a |
if (errno == EINTR) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("sendto error - EINTR");
|
|
Packit |
61cb5a |
return (-2);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if (errno == EHOSTUNREACH || errno == EHOSTDOWN || errno == ENETDOWN ||
|
|
Packit |
61cb5a |
errno == ENOBUFS) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("sendto error - EHOSTUNREACH || EHOSTDOWN || ENETDOWN ||"
|
|
Packit |
61cb5a |
"ENOBUFS");
|
|
Packit |
61cb5a |
return (-3);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("sendto error - errno = %d", errno);
|
|
Packit |
61cb5a |
return (-1);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
if ((size_t)sent != msg_size) {
|
|
Packit |
61cb5a |
DEBUG2_PRINTF("sendto error - sent != msg_size");
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
return (-1);
|
|
Packit |
61cb5a |
}
|
|
Packit |
61cb5a |
|
|
Packit |
61cb5a |
return (sent);
|
|
Packit |
61cb5a |
}
|