Browse code

Fix --multihome for IPv6 on 64bit BSD systems.

The old code only worked if "struct openvpn*pktinfo" happened to use
the same structure packing as the CMSG_SPACE() / CMSG_LEN() macros
(which are part of the official API, see RFC 2292).

Get rid of "struct openvpn_*_pktinfo" definitions, replace them by
an opaque buffer sized large enough to fit IPv4 and IPv6 packet info
messages, as defined by CMSG_SPACE(sizeof(struct ...)).

On 32 bit platforms, the net result is the same. On 64 bit platforms,
the new buffer is bigger than openvpn_pktinfo was, fixing an overflow
with ipi6_ifindex corruption on reception, and EINVAL on sendmsg().

The IPv4 related changes are only side effects of using the new buffer.

Fixes: FreeBSD 10.3/amd64, FreeBSD 9.3/sparc64, OpenBSD 6.0/amd64,
NetBSD 7.0.1/i386.

Note: --multihome for IPv4 on NetBSD is still broken and non-fixable(!)
as NetBSD lacks the necessary kernel code for the sendmsg() side.

Verified that "--multihome works as well as before" on FreeBSD 7.4/amd64,
NetBSD 5.1/amd64, OpenBSD 4.9/i386, Linux/x86_64, Linux/i386,
OpenSolaris 10 (--multihome needs -D_XPG4_2, see trac #750)

See also: ip(4), ip6(4), recv(2)

Trac #634, #327, #28

Signed-off-by: Gert Doering <gert@greenie.muc.de>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <20161009100929.46472-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg12626.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Gert Doering authored on 2016/10/09 19:09:29
Showing 1 changed files
... ...
@@ -2863,27 +2863,16 @@ link_socket_read_tcp (struct link_socket *sock,
2863 2863
 
2864 2864
 #if ENABLE_IP_PKTINFO
2865 2865
 
2866
-#pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */
2867
-struct openvpn_in4_pktinfo
2868
-{
2869
-  struct cmsghdr cmsghdr;
2866
+/* make the buffer large enough to handle ancilliary socket data for
2867
+ * both IPv4 and IPv6 destination addresses, plus padding (see RFC 2292)
2868
+ */
2870 2869
 #if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
2871
-  struct in_pktinfo pi4;
2872
-#elif defined(IP_RECVDSTADDR)
2873
-  struct in_addr pi4;
2870
+#define PKTINFO_BUF_SIZE max_int( CMSG_SPACE(sizeof (struct in6_pktinfo)), \
2871
+				  CMSG_SPACE(sizeof (struct in_pktinfo)) )
2872
+#else
2873
+#define PKTINFO_BUF_SIZE max_int( CMSG_SPACE(sizeof (struct in6_pktinfo)), \
2874
+				  CMSG_SPACE(sizeof (struct in_addr)) )
2874 2875
 #endif
2875
-};
2876
-struct openvpn_in6_pktinfo
2877
-{
2878
-  struct cmsghdr cmsghdr;
2879
-  struct in6_pktinfo pi6;
2880
-};
2881
-
2882
-union openvpn_pktinfo {
2883
-	struct openvpn_in4_pktinfo msgpi4;
2884
-	struct openvpn_in6_pktinfo msgpi6;
2885
-};
2886
-#pragma pack()
2887 2876
 
2888 2877
 static socklen_t
2889 2878
 link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
... ...
@@ -2891,7 +2880,7 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
2891 2891
 				    struct link_socket_actual *from)
2892 2892
 {
2893 2893
   struct iovec iov;
2894
-  union openvpn_pktinfo opi;
2894
+  uint8_t pktinfo_buf[PKTINFO_BUF_SIZE];
2895 2895
   struct msghdr mesg;
2896 2896
   socklen_t fromlen = sizeof (from->dest.addr);
2897 2897
 
... ...
@@ -2901,8 +2890,8 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
2901 2901
   mesg.msg_iovlen = 1;
2902 2902
   mesg.msg_name = &from->dest.addr;
2903 2903
   mesg.msg_namelen = fromlen;
2904
-  mesg.msg_control = &opi;
2905
-  mesg.msg_controllen = sizeof opi;
2904
+  mesg.msg_control = pktinfo_buf;
2905
+  mesg.msg_controllen = sizeof pktinfo_buf;
2906 2906
   buf->len = recvmsg (sock->sd, &mesg, 0);
2907 2907
   if (buf->len >= 0)
2908 2908
     {
... ...
@@ -2914,13 +2903,14 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
2914 2914
 #if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
2915 2915
 	  && cmsg->cmsg_level == SOL_IP 
2916 2916
 	  && cmsg->cmsg_type == IP_PKTINFO
2917
+	  && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_pktinfo)) )
2917 2918
 #elif defined(IP_RECVDSTADDR)
2918 2919
 	  && cmsg->cmsg_level == IPPROTO_IP
2919 2920
 	  && cmsg->cmsg_type == IP_RECVDSTADDR
2921
+	  && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_addr)) )
2920 2922
 #else
2921 2923
 #error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
2922 2924
 #endif
2923
-	  && cmsg->cmsg_len >= sizeof (struct openvpn_in4_pktinfo))
2924 2925
 	{
2925 2926
 #if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
2926 2927
 	  struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
... ...
@@ -2936,7 +2926,7 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
2936 2936
 	  && CMSG_NXTHDR (&mesg, cmsg) == NULL
2937 2937
 	  && cmsg->cmsg_level == IPPROTO_IPV6 
2938 2938
 	  && cmsg->cmsg_type == IPV6_PKTINFO
2939
-	  && cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo))
2939
+	  && cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in6_pktinfo)) )
2940 2940
 	{
2941 2941
 	  struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
2942 2942
 	  from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex;
... ...
@@ -3007,7 +2997,7 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
3007 3007
   struct iovec iov;
3008 3008
   struct msghdr mesg;
3009 3009
   struct cmsghdr *cmsg;
3010
-  union openvpn_pktinfo opi;
3010
+  uint8_t pktinfo_buf[PKTINFO_BUF_SIZE];
3011 3011
 
3012 3012
   iov.iov_base = BPTR (buf);
3013 3013
   iov.iov_len = BLEN (buf);
... ...
@@ -3019,12 +3009,12 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
3019 3019
       {
3020 3020
         mesg.msg_name = &to->dest.addr.sa;
3021 3021
         mesg.msg_namelen = sizeof (struct sockaddr_in);
3022
-        mesg.msg_control = &opi;
3022
+        mesg.msg_control = pktinfo_buf;
3023 3023
         mesg.msg_flags = 0;
3024 3024
 #if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
3025
-        mesg.msg_controllen = sizeof (struct openvpn_in4_pktinfo);
3025
+        mesg.msg_controllen = CMSG_SPACE(sizeof (struct in_pktinfo));
3026 3026
         cmsg = CMSG_FIRSTHDR (&mesg);
3027
-        cmsg->cmsg_len = sizeof (struct openvpn_in4_pktinfo);
3027
+        cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_pktinfo));
3028 3028
         cmsg->cmsg_level = SOL_IP;
3029 3029
         cmsg->cmsg_type = IP_PKTINFO;
3030 3030
 	{
... ...
@@ -3035,7 +3025,7 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
3035 3035
         pkti->ipi_addr.s_addr = 0;
3036 3036
 	}
3037 3037
 #elif defined(IP_RECVDSTADDR)
3038
-	ASSERT( CMSG_SPACE(sizeof (struct in_addr)) <= sizeof(opi) );
3038
+	ASSERT( CMSG_SPACE(sizeof (struct in_addr)) <= sizeof(pktinfo_buf) );
3039 3039
         mesg.msg_controllen = CMSG_SPACE(sizeof (struct in_addr));
3040 3040
         cmsg = CMSG_FIRSTHDR (&mesg);
3041 3041
         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
... ...
@@ -3052,13 +3042,16 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
3052 3052
         struct in6_pktinfo *pkti6;
3053 3053
         mesg.msg_name = &to->dest.addr.sa;
3054 3054
         mesg.msg_namelen = sizeof (struct sockaddr_in6);
3055
-        mesg.msg_control = &opi;
3056
-        mesg.msg_controllen = sizeof (struct openvpn_in6_pktinfo);
3055
+
3056
+        ASSERT( CMSG_SPACE(sizeof (struct in6_pktinfo)) <= sizeof(pktinfo_buf) );
3057
+        mesg.msg_control = pktinfo_buf;
3058
+        mesg.msg_controllen = CMSG_SPACE(sizeof (struct in6_pktinfo));
3057 3059
         mesg.msg_flags = 0;
3058 3060
         cmsg = CMSG_FIRSTHDR (&mesg);
3059
-        cmsg->cmsg_len = sizeof (struct openvpn_in6_pktinfo);
3061
+        cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
3060 3062
         cmsg->cmsg_level = IPPROTO_IPV6;
3061 3063
         cmsg->cmsg_type = IPV6_PKTINFO;
3064
+
3062 3065
         pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
3063 3066
         pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex;
3064 3067
         pkti6->ipi6_addr = to->pi.in6.ipi6_addr;