* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * Helper functions to get/set addresses of UDP packets
- * based on recvfromto by Miquel van Smoorenburg
- *
- * recvfromto Like recvfrom, but also stores the destination
- * IP address. Useful on multihomed hosts.
- *
- * Should work on Linux and BSD.
- *
- * Copyright (C) 2002 Miquel van Smoorenburg.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- * Copyright (C) 2007 Alan DeKok <aland@deployingradius.com>
- *
- * sendfromto added 18/08/2003, Jan Berkel <jan@sitadelle.com>
- * Works on Linux and FreeBSD (5.x)
- *
- * Version: $Id$
*/
-#include <freeradius-devel/ident.h>
+/**
+ * $Id$
+ * @file udpfromto.c
+ * @brief Like recvfrom, but also stores the destination IP address. Useful on multihomed hosts.
+ *
+ * @copyright 2007 Alan DeKok <aland@deployingradius.com>
+ * @copyright 2002 Miquel van Smoorenburg
+ */
RCSID("$Id$")
#include <freeradius-devel/udpfromto.h>
#ifdef WITH_UDPFROMTO
#ifdef HAVE_SYS_UIO_H
-#include <sys/uio.h>
+# include <sys/uio.h>
#endif
#include <fcntl.h>
* Mac OSX Lion doesn't define SOL_IP. But IPPROTO_IP works.
*/
#ifndef SOL_IP
-#define SOL_IP IPPROTO_IP
+# define SOL_IP IPPROTO_IP
#endif
/*
* old kernel interface.
*/
#ifdef __linux__
-# if defined IPV6_RECVPKTINFO
+# ifdef IPV6_RECVPKTINFO
# include <linux/version.h>
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
-# if defined IPV6_2292PKTINFO
+# ifdef IPV6_2292PKTINFO
# undef IPV6_RECVPKTINFO
# undef IPV6_PKTINFO
# define IPV6_RECVPKTINFO IPV6_2292PKTINFO
# define IPV6_PKTINFO IPV6_2292PKTINFO
# endif
# endif
+/* Fall back to the legacy socket option if IPV6_RECVPKTINFO isn't defined */
+# elif defined(IPV6_2292PKTINFO)
+# define IPV6_RECVPKTINFO IPV6_2292PKTINFO
# endif
#endif
* for all three calls.
*/
#ifdef IPV6_PKTINFO
-#ifdef __linux__
-#define FR_IPV6_RECVPKTINFO IPV6_RECVPKTINFO
-#else
-#define FR_IPV6_RECVPKTINFO IPV6_PKTINFO
-#endif
+# ifdef __linux__
+# ifdef IPV6_RECVPKTINFO
+# define FR_IPV6_RECVPKTINFO IPV6_RECVPKTINFO
+/* Fallback to to using recvfrom */
+# else
+# undef IPV6_RECVPKTINFO
+# undef IPV6_PKTINFO
+# endif
+# else
+# define FR_IPV6_RECVPKTINFO IPV6_PKTINFO
+# endif
#endif
int udpfromto_init(int s)
{
- int proto, flag, opt = 1;
+ int proto, flag = 0, opt = 1;
struct sockaddr_storage si;
socklen_t si_len = sizeof(si);
errno = ENOSYS;
- proto = -1;
+ /*
+ * Clang analyzer doesn't see that getsockname initialises
+ * the memory passed to it.
+ */
+#ifdef __clang_analyzer__
+ memset(&si, 0, sizeof(si));
+#endif
if (getsockname(s, (struct sockaddr *) &si, &si_len) < 0) {
return -1;
*/
proto = SOL_IP;
flag = IP_PKTINFO;
-#endif
-
-#ifdef IP_RECVDSTADDR
+#else
+# ifdef IP_RECVDSTADDR
+
/*
* Set the IP_RECVDSTADDR option (BSD). Note:
* IP_RECVDSTADDR == IP_SENDSRCADDR
*/
proto = IPPROTO_IP;
flag = IP_RECVDSTADDR;
+# else
+ return -1;
+# endif
#endif
#ifdef AF_INET6
} else if (si.ss_family == AF_INET6) {
-#ifdef IPV6_PKTINFO
+# ifdef IPV6_PKTINFO
/*
* This should actually be standard IPv6
*/
* Work around Linux-specific hackery.
*/
flag = FR_IPV6_RECVPKTINFO;
-#endif
+#else
+# ifdef EPROTONOSUPPORT
+ errno = EPROTONOSUPPORT;
+# endif
+ return -1;
+# endif
#endif
} else {
/*
*/
return -1;
}
-
- /*
- * Unsupported. Don't worry about it.
- */
- if (proto < 0) return 0;
return setsockopt(s, proto, flag, &opt, sizeof(opt));
}
struct sockaddr_storage si;
socklen_t si_len = sizeof(si);
-#if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) && !defined (IPV6_PKTINFO)
+#if !defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR) && !defined(IPV6_PKTINFO)
/*
* If the recvmsg() flags aren't defined, fall back to
* using recvfrom().
if (!to || !tolen) return recvfrom(s, buf, len, flags, from, fromlen);
/*
+ * Clang analyzer doesn't see that getsockname initialises
+ * the memory passed to it.
+ */
+#ifdef __clang_analyzer__
+ memset(&si, 0, sizeof(si));
+#endif
+
+ /*
* recvmsg doesn't provide sin_port so we have to
* retrieve it using getsockname().
*/
#else
struct sockaddr_in *dst = (struct sockaddr_in *) to;
struct sockaddr_in *src = (struct sockaddr_in *) &si;
-
+
if (*tolen < sizeof(*dst)) {
errno = EINVAL;
return -1;
#else
struct sockaddr_in6 *dst = (struct sockaddr_in6 *) to;
struct sockaddr_in6 *src = (struct sockaddr_in6 *) &si;
-
+
if (*tolen < sizeof(*dst)) {
errno = EINVAL;
return -1;
#endif
/*
* Unknown address family.
- */
+ */
else {
errno = EINVAL;
return -1;
}
/* Set up iov and msgh structures. */
+ memset(&cbuf, 0, sizeof(cbuf));
memset(&msgh, 0, sizeof(struct msghdr));
iov.iov_base = buf;
iov.iov_len = len;
struct iovec iov;
char cbuf[256];
-#if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR) && !defined(IPV6_PKTINFO)
+#ifdef __FreeBSD__
+ /*
+ * FreeBSD is extra pedantic about the use of IP_SENDSRCADDR,
+ * and sendmsg will fail with EINVAL if IP_SENDSRCADDR is used
+ * with a socket which is bound to something other than
+ * INADDR_ANY
+ */
+ struct sockaddr bound;
+ socklen_t bound_len = sizeof(bound);
+
+ if (getsockname(s, &bound, &bound_len) < 0) {
+ return -1;
+ }
+
+ switch (bound.sa_family) {
+ case AF_INET:
+ if (((struct sockaddr_in *) &bound)->sin_addr.s_addr != INADDR_ANY) {
+ from = NULL;
+ }
+ break;
+
+ case AF_INET6:
+ if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) &bound)->sin6_addr))) {
+ from = NULL;
+ }
+ break;
+ }
+#else
+# if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR) && !defined(IPV6_PKTINFO)
/*
* If the sendmsg() flags aren't defined, fall back to
* using sendto().
*/
from = NULL;
+# endif
#endif
/*
return sendto(s, buf, len, flags, to, tolen);
}
- /* Set up iov and msgh structures. */
- memset(&msgh, 0, sizeof(struct msghdr));
+ /* Set up control buffer iov and msgh structures. */
+ memset(&cbuf, 0, sizeof(cbuf));
+ memset(&msgh, 0, sizeof(msgh));
+ memset(&iov, 0, sizeof(iov));
iov.iov_base = buf;
iov.iov_len = len;
msgh.msg_iov = &iov;
#else
struct sockaddr_in *s4 = (struct sockaddr_in *) from;
-#ifdef IP_PKTINFO
+# ifdef IP_PKTINFO
struct in_pktinfo *pkt;
msgh.msg_control = cbuf;
pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
memset(pkt, 0, sizeof(*pkt));
pkt->ipi_spec_dst = s4->sin_addr;
-#endif
+# endif
-#ifdef IP_SENDSRCADDR
+# ifdef IP_SENDSRCADDR
struct in_addr *in;
msgh.msg_control = cbuf;
in = (struct in_addr *) CMSG_DATA(cmsg);
*in = s4->sin_addr;
-#endif
+# endif
#endif /* IP_PKTINFO or IP_SENDSRCADDR */
}
#ifdef AF_INET6
else if (from->sa_family == AF_INET6) {
-#if !defined(IPV6_PKTINFO)
+# if !defined(IPV6_PKTINFO)
return sendto(s, buf, len, flags, to, tolen);
-#else
+# else
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) from;
struct in6_pktinfo *pkt;
pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memset(pkt, 0, sizeof(*pkt));
pkt->ipi6_addr = s6->sin6_addr;
-#endif /* IPV6_PKTINFO */
+# endif /* IPV6_PKTINFO */
}
#endif
/*
* Unknown address family.
- */
+ */
else {
errno = EINVAL;
return -1;
* reply packet should originate from virtual IP and not
* from the default interface the alias is bound to
*/
+# include <sys/wait.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#define DEF_PORT 20000 /* default port to listen on */
-#define DESTIP "127.0.0.1" /* send packet to localhost per default */
-#define TESTSTRING "foo" /* what to send */
-#define TESTLEN 4 /* 4 bytes */
+# define DEF_PORT 20000 /* default port to listen on */
+# define DESTIP "127.0.0.1" /* send packet to localhost per default */
+# define TESTSTRING "foo" /* what to send */
+# define TESTLEN 4 /* 4 bytes */
int main(int argc, char **argv)
{
struct sockaddr_in from, to, in;
char buf[TESTLEN];
char *destip = DESTIP;
- int port = DEF_PORT;
+ uint16_t port = DEF_PORT;
int n, server_socket, client_socket, fl, tl, pid;
if (argc > 1) destip = argv[1];
client_socket = socket(PF_INET, SOCK_DGRAM, 0);
if (udpfromto_init(client_socket) != 0) {
perror("udpfromto_init");
- _exit(0);
+ fr_exit_now(0);
}
/* bind client on different port */
in.sin_port = htons(port+1);
if (bind(client_socket, (struct sockaddr *)&in, sizeof(in)) < 0) {
perror("client: bind");
- _exit(0);
+ fr_exit_now(0);
}
in.sin_port = htons(port);
if (sendto(client_socket, TESTSTRING, TESTLEN, 0,
(struct sockaddr *)&in, sizeof(in)) < 0) {
perror("client: sendto");
- _exit(0);
+ fr_exit_now(0);
}
printf("client: waiting for reply from server on INADDR_ANY:%d\n", port+1);
(struct sockaddr *)&from, &fl,
(struct sockaddr *)&to, &tl)) < 0) {
perror("client: recvfromto");
- _exit(0);
+ fr_exit_now(0);
}
printf("client: received a packet of %d bytes [%s] ", n, buf);
printf(" dst ip:port %s:%d)\n",
inet_ntoa(to.sin_addr), ntohs(to.sin_port));
- _exit(0);
+ fr_exit_now(0);
}
#endif /* TESTING */