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