IPV6_PKTINFO/recvfromto

Alexander Clouter alex at digriz.org.uk
Mon Dec 13 20:28:16 CET 2010


Hi,

Whilst rolling out our new FreeRADIUS infrastructure I stumbled on a 
Linux bug where for IPv6 clients the reply source IP address from the 
server does not necessary match the request's destination address; as a 
result the client ignores the reply packet.

I use OSPF to anycast our RADIUS servers and so have to bind to '::', 
the box as a result is obviously multi-homed too.  I am guessing this is 
why I am seeing the bug whilst most other (in)?sane sysadmin's bind to 
the EUI64 address and so do not see this problem.

Turns out 'struct in6_pktinfo' is a GNU extension[1] and so you need to 
define _GNU_SOURCE when ./configure is running otherwise you get 
HAVE_IN6_PKTINFO is undefined.  I just gave it a whirl, config.cache 
shows 'yes' now for the presence of ipi6_pktinfo:
----
ac56 at domokun:/usr/src/deb-src/freeradius/freeradius-2.1.10+dfsg$ grep pktinfo config.cache
ac_cv_type_struct_in6_pktinfo_has_ipi6_addr=${ac_cv_type_struct_in6_pktinfo_has_ipi6_addr=yes}
ac_cv_type_struct_in_pktinfo_has_ipi_addr=${ac_cv_type_struct_in_pktinfo_has_ipi_addr=yes}
----

Until I forced _GNU_SOURCE into CFLAGS I was seeing 
'${ac_cv_type_struct_in6_pktinfo_has_ipi6_addr=}', note the lack of a 
'yes' on the end there, which would explain why things were not 
behaving.

Once this was enabled, I noticed that someone thinks IPv6 addresses are 
integers, so I have provided a patch to fix that.  I noticed someone has 
disabled udpfromto.c being used for IPv6 sockets altogether...this is 
also fixed in the patch :)

I would provide a patch for the autoconf bit but I have no idea how to 
persuade autoconf to do what I want...from what I have heard I do not 
want to either :)

Anyway, with the following, my IPv6 client support works once again, 
although someone with Clue(tm) probably should look over it to make sure 
all is well with it :)

Cheers

[1] http://linux.derkeiler.com/Mailing-Lists/Fedora/2009-02/msg00318.html

--- freeradius-2.1.10+dfsg.orig/src/lib/udpfromto.c	2010-09-28 11:03:56.000000000 +0000
+++ freeradius-2.1.10+dfsg/src/lib/udpfromto.c	2010-12-13 19:12:40.000000000 +0000
@@ -87,7 +87,7 @@
 		 *	This should actually be standard IPv6
 		 */
 		proto = IPPROTO_IPV6;
-		flag = IPV6_PKTINFO;
+		flag = IPV6_RECVPKTINFO;
 #endif
 #endif
 	} else {
@@ -172,7 +172,7 @@
 			return -1;
 		}
 		*tolen = sizeof(*dst);
-		*dst = *src;
+		memcpy(dst, src, sizeof(*dst));
 
 #if !defined(HAVE_IN6_PKTINFO)
 		/*
@@ -240,7 +240,7 @@
 		    (cmsg->cmsg_type == IPV6_PKTINFO)) {
 			struct in6_pktinfo *i =
 				(struct in6_pktinfo *) CMSG_DATA(cmsg);
-			((struct sockaddr_in6 *)to)->sin6_addr = i->ipi6_addr;
+			memcpy(&((struct sockaddr_in6 *) to)->sin6_addr, &i->ipi6_addr, sizeof(struct in6_addr));
 			*tolen = sizeof(struct sockaddr_in6);
 			break;
 		}
@@ -335,7 +335,7 @@
 
 		pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
 		memset(pkt, 0, sizeof(*pkt));
-		pkt->ipi6_addr = s6->sin6_addr;
+		memcpy(&pkt->ipi6_addr, &s6->sin6_addr, sizeof(struct in6_addr));
 #endif
 	}
 #endif
--- freeradius-2.1.10+dfsg.orig/src/lib/radius.c	2010-09-28 11:03:56.000000000 +0000
+++ freeradius-2.1.10+dfsg/src/lib/radius.c	2010-12-13 19:11:34.000000000 +0000
@@ -228,13 +228,11 @@
 
 #ifdef WITH_UDPFROMTO
 	/*
-	 *	Only IPv4 is supported for udpfromto.
-	 *
 	 *	And if they don't specify a source IP address, don't
 	 *	use udpfromto.
 	 */
-	if ((dst_ipaddr->af == AF_INET) &&
-	    (src_ipaddr->af != AF_UNSPEC)) {
+	if ((dst_ipaddr->af == AF_INET || dst_ipaddr->af == AF_INET6)
+	    && (src_ipaddr->af != AF_UNSPEC)) {
 		return sendfromto(sockfd, data, data_len, flags,
 				  (struct sockaddr *)&src, sizeof_src,
 				  (struct sockaddr *)&dst, sizeof_dst);
@@ -244,7 +242,7 @@
 #endif
 
 	/*
-	 *	No udpfromto, OR an IPv6 socket, fail gracefully.
+	 *	No udpfromto so fail gracefully.
 	 */
 	return sendto(sockfd, data, data_len, flags,
 		      (struct sockaddr *) &dst, sizeof_dst);
@@ -415,14 +413,14 @@
 	 *	packet after "len" bytes.
 	 */
 #ifdef WITH_UDPFROMTO
-	if (dst.ss_family == AF_INET) {
+	if (dst.ss_family == AF_INET || dst.ss_family == AF_INET6) {
 		data_len = recvfromto(sockfd, buf, len, flags,
 				      (struct sockaddr *)&src, &sizeof_src,
 				      (struct sockaddr *)&dst, &sizeof_dst);
 	} else
 #endif
 		/*
-		 *	No udpfromto, OR an IPv6 socket.  Fail gracefully.
+		 *	No udpfromto so fail gracefully.
 		 */
 		data_len = recvfrom(sockfd, buf, len, flags,
 				    (struct sockaddr *)&src, &sizeof_src);




More information about the Freeradius-Devel mailing list