Backport udpfromto IPv6 fixes from v3.0.x
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 11 Nov 2014 19:18:43 +0000 (14:18 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 11 Nov 2014 19:18:54 +0000 (14:18 -0500)
configure
configure.in
src/include/autoconf.h.in
src/lib/udpfromto.c

index b9f9b57..f828932 100755 (executable)
--- a/configure
+++ b/configure
@@ -20779,6 +20779,9 @@ case "$host" in
 *-darwin*)
        CFLAGS="$CFLAGS -DDARWIN"
        LIBS="-framework DirectoryService $LIBS"
+
+$as_echo "#define __APPLE_USE_RFC_3542 1" >>confdefs.h
+
        ;;
 esac
 
index 7e76fa1..d86e925 100644 (file)
@@ -622,6 +622,7 @@ case "$host" in
 *-darwin*)
        CFLAGS="$CFLAGS -DDARWIN"
        LIBS="-framework DirectoryService $LIBS"
+       AC_DEFINE([__APPLE_USE_RFC_3542], 1, [Force OSX >= 10.7 Lion to use RFC2292 IPv6 socket options])
        ;;
 esac
 
index c2fe398..f14cd52 100644 (file)
 /* Define to 1 if you need to in order for `stat' and other things to work. */
 #undef _POSIX_SOURCE
 
+/* Force OSX >= 10.7 Lion to use RFC2292 IPv6 socket options */
+#undef __APPLE_USE_RFC_3542
+
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const
 
index 7665e85..3f4a166 100644 (file)
  *   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$
  */
 
+/**
+ * $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
+ */
 #include <freeradius-devel/ident.h>
 RCSID("$Id$")
 
@@ -43,7 +30,7 @@ RCSID("$Id$")
 #ifdef WITH_UDPFROMTO
 
 #ifdef HAVE_SYS_UIO_H
-#include <sys/uio.h>
+#  include <sys/uio.h>
 #endif
 
 #include <fcntl.h>
@@ -53,57 +40,72 @@ RCSID("$Id$")
  *     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
 
 /*
- * glibc 2.4 and uClibc 0.9.29 introduce IPV6_RECVPKTINFO etc. and
- * change IPV6_PKTINFO This is only supported in Linux kernel >=
- * 2.6.14
+ *  glibc 2.4 and uClibc 0.9.29 introduce IPV6_RECVPKTINFO etc. and
+ *  change IPV6_PKTINFO This is only supported in Linux kernel >=
+ *  2.6.14
  *
- * This is only an approximation because the kernel version that libc
- * was compiled against could be older or newer than the one being
- * run.  But this should not be a problem -- we just keep using the
- * old kernel interface.
+ *  This is only an approximation because the kernel version that libc
+ *  was compiled against could be older or newer than the one being
+ *  run.  But this should not be a problem -- we just keep using the
+ *  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
+#else
 
 /*
- *     Linux requires IPV6_RECVPKTINFO for the setsockopt() call,
- *     but sendmsg() and recvmsg() require IPV6_PKTINFO. <sigh>
+ *  For everything that's not Linux we assume RFC 3542 compliance
+ *  - setsockopt() takes IPV6_RECVPKTINFO
+ *  - cmsg_type is IPV6_PKTINFO (in sendmsg, recvmsg)
  *
- *     We want all *other* (i.e. sane) systems to use IPV6_PKTINFO
- *     for all three calls.
+ *  If we don't have IPV6_RECVPKTINFO defined but do have IPV6_PKTINFO
+ *  defined, chances are the API is RFC2292 compliant and we need to use
+ *  IPV6_PKTINFO for both.
  */
-#ifdef IPV6_PKTINFO
-#ifdef __linux__
-#define FR_IPV6_RECVPKTINFO IPV6_RECVPKTINFO
-#else
-#define FR_IPV6_RECVPKTINFO IPV6_PKTINFO
-#endif
+#  if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)
+#    define IPV6_RECVPKTINFO IPV6_PKTINFO
+
+/*
+ *  Ensure IPV6_RECVPKTINFO is not defined somehow if we have we
+ *  don't have IPV6_PKTINFO.
+ */
+#  elif !defined(IPV6_PKTINFO)
+#    undef IPV6_RECVPKTINFO
+#  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;
@@ -116,20 +118,23 @@ int udpfromto_init(int s)
                 */
                proto = SOL_IP;
                flag = IP_PKTINFO;
-#endif
+#else
+#  ifdef IP_RECVDSTADDR
 
-#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
                 */
@@ -138,8 +143,13 @@ int udpfromto_init(int s)
                /*
                 *      Work around Linux-specific hackery.
                 */
-               flag = FR_IPV6_RECVPKTINFO;
-#endif
+               flag = IPV6_RECVPKTINFO;
+#else
+#  ifdef EPROTONOSUPPORT
+               errno = EPROTONOSUPPORT;
+#  endif
+               return -1;
+#  endif
 #endif
        } else {
                /*
@@ -148,11 +158,6 @@ int udpfromto_init(int s)
                return -1;
        }
 
-       /*
-        *      Unsupported.  Don't worry about it.
-        */
-       if (proto < 0) return 0;
-
        return setsockopt(s, proto, flag, &opt, sizeof(opt));
 }
 
@@ -168,7 +173,7 @@ int recvfromto(int s, void *buf, size_t len, int flags,
        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().
@@ -182,6 +187,14 @@ int recvfromto(int s, void *buf, size_t len, int flags,
        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().
         */
@@ -235,6 +248,7 @@ int recvfromto(int s, void *buf, size_t len, int flags,
        }
 
        /* 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;
@@ -347,8 +361,10 @@ int sendfromto(int s, void *buf, size_t len, int flags,
                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;
@@ -362,7 +378,7 @@ int sendfromto(int s, void *buf, size_t len, int flags,
 #else
                struct sockaddr_in *s4 = (struct sockaddr_in *) from;
 
-#ifdef IP_PKTINFO
+#  ifdef IP_PKTINFO
                struct in_pktinfo *pkt;
 
                msgh.msg_control = cbuf;
@@ -376,9 +392,9 @@ int sendfromto(int s, void *buf, size_t len, int flags,
                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;
@@ -391,15 +407,15 @@ int sendfromto(int s, void *buf, size_t len, int flags,
 
                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;
@@ -415,7 +431,7 @@ int sendfromto(int s, void *buf, size_t len, int flags,
                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
 
@@ -441,23 +457,23 @@ int sendfromto(int s, void *buf, size_t len, int flags,
  *     from the default interface the alias is bound to
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <sys/types.h>
-#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];
@@ -470,7 +486,7 @@ int main(int argc, char **argv)
        memset(&from, 0, sizeof(from));
        memset(&to,   0, sizeof(to));
 
-       switch(pid = fork()) {
+       switch (pid = fork()) {
                case -1:
                        perror("fork");
                        return 0;