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@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);
Alexander Clouter wrote:
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 :)
I'll go fix that.
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 :)
I'll do that tomorrow, and make sure it's in 2.1.11. Alan DeKok.
Alexander Clouter wrote:
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.
OK. That should be easy to fix.
Once this was enabled, I noticed that someone thinks IPv6 addresses are integers, so I have provided a patch to fix that.
I'm not sure what that means... struct foo *a,*b; *a = *b; Is valid C code. It mean "copy the entire structure".
I noticed someone has disabled udpfromto.c being used for IPv6 sockets altogether...this is also fixed in the patch :)
It didn't seem to work.
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 :)
That's easy enough to do once you've accepted some autoconf-induced brain damage. Alan DeKok.
Alan DeKok <aland@deployingradius.com> wrote:
Once this was enabled, I noticed that someone thinks IPv6 addresses are integers, so I have provided a patch to fix that.
I'm not sure what that means...
struct foo *a,*b; *a = *b;
Is valid C code. It mean "copy the entire structure".
*darn* I was sure you had to use memcpy(), has this always been valid? Apologies for the accusations, Wackipedia says the same so you must be right! :) Cheers -- Alexander Clouter .sigmonster says: No wonder you're tired! You understood so much today.
Alexander Clouter wrote:
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:
I think that check isn't really necessary. If the "IPV6_PKTINFO" definition exists, the compiler can be pretty sure that the "struct in6_pktinfo" entry exists. I've pushed some changes to the v2.1.x branch which should help. They should also fix Linux kernel version issues with using IPV6_RECVPKTINFO versus IPV6_PKTINFO. The only thing the changes *don't* do is update src/lib/radius.c to allow IPv6 for "udpfromto" sockets. If the rest of the changes work for you, I can then re-add the IPv6 support in src/lib/radius.c. Alan DeKok.
Alan DeKok <aland@deployingradius.com> wrote:
Alexander Clouter wrote:
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:
I think that check isn't really necessary. If the "IPV6_PKTINFO" definition exists, the compiler can be pretty sure that the "struct in6_pktinfo" entry exists.
I've pushed some changes to the v2.1.x branch which should help. They should also fix Linux kernel version issues with using IPV6_RECVPKTINFO versus IPV6_PKTINFO.
The only thing the changes *don't* do is update src/lib/radius.c to allow IPv6 for "udpfromto" sockets.
If the rest of the changes work for you, I can then re-add the IPv6 support in src/lib/radius.c.
rlm_dbm_parser gets upset... ----- make[6]: Entering directory `/usr/src/deb-src/freeradius/freeradius-2.1.10+dfsg/src/modules/rlm_dbm' /usr/bin/libtool --mode=link gcc \ -o rlm_dbm_parser rlm_dbm_parser.lo /usr/src/deb-src/freeradius/freeradius-2.1.10+dfsg/src/lib/libfreeradius-radius.la -lgdbm_compat -lnsl -lresolv -lpthread libtool: link: gcc -o .libs/rlm_dbm_parser .libs/rlm_dbm_parser.o /usr/src/deb-src/freeradius/freeradius-2.1.10+dfsg/src/lib/.libs/libfreeradius-radius.so /usr/lib/libgdbm_compat.so -lnsl -lresolv -lpthread -Wl,-rpath -Wl,/usr/lib/freeradius /usr/src/deb-src/freeradius/freeradius-2.1.10+dfsg/src/lib/.libs/libfreeradius-radius.so: undefined reference to `recvfromto' /usr/src/deb-src/freeradius/freeradius-2.1.10+dfsg/src/lib/.libs/libfreeradius-radius.so: undefined reference to `sendfromto' /usr/src/deb-src/freeradius/freeradius-2.1.10+dfsg/src/lib/.libs/libfreeradius-radius.so: undefined reference to `udpfromto_init' collect2: ld returned 1 exit status make[6]: *** [rlm_dbm_parser] Error 1 ----- Moving '#include <freeradius-devel/udpfromto.h>' back to outside of the '#ifdef WITH_UDPFROMTO ... #endif' clause, gets things cooking again though; although it feels 'wrong'. Having retinkered with src/lib/radius.c and compiled the lot, thinks do not work. IPv6 RADIUS requests come in, nothing goes out, strace'ing tells me that when sendmsg() is called, -EINVAL is returned on IPv6 sockets. I also notice recvmsg() is not being called anywhere for IPv6 sockets: ---- [pid 26841] recvfrom(15, "\1A\0\\", 4, MSG_PEEK, {sa_family=AF_INET6, sin6_port=htons(57447), inet_pton(AF_INET6, "2001:630:1:12a::233", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 4 [pid 26841] getsockname(15, {sa_family=AF_INET6, sin6_port=htons(1812), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0 [pid 26841] recvfrom(15, "\1A\0\\", 4, MSG_PEEK, {sa_family=AF_INET6, sin6_port=htons(57447), inet_pton(AF_INET6, "2001:630:1:12a::233", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 4 [pid 26841] getsockname(15, {sa_family=AF_INET6, sin6_port=htons(1812), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0 [pid 26841] recvfrom(15, "\1A\0\\\270u\200\31H\4\r\317v\266\257\220\335FQ\376\1\25testauth@s"..., 92, 0, {sa_family=AF_INET6, sin6_port=htons(57447), inet_pton(AF_INET6, "2001:630:1:12a::233", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 92 [pid 26841] gettimeofday({1292369722, 367383}, NULL) = 0 [pid 26841] gettimeofday({1292369722, 367639}, NULL) = 0 [pid 26841] futex(0x4c554, FUTEX_WAKE_PRIVATE, 1 <unfinished ...> [pid 26843] <... futex resumed> ) = 0 [pid 26841] <... futex resumed> ) = 1 [pid 26843] wait4(0, <unfinished ...> [pid 26841] gettimeofday( <unfinished ...> [pid 26843] <... wait4 resumed> 0x41378d7c, WNOHANG, NULL) = -1 ECHILD (No child processes) [pid 26841] <... gettimeofday resumed> {1292369722, 368277}, NULL) = 0 [pid 26843] socket(PF_NETLINK, SOCK_RAW, 0 <unfinished ...> [pid 26841] select(36, [13 15 16 17 18 19 20 21 22 23 24 25 32 35], NULL, NULL, {0, 95942} <unfinished ...> [pid 26843] <... socket resumed> ) = 38 [pid 26843] bind(38, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 0 [pid 26843] getsockname(38, {sa_family=AF_NETLINK, pid=26841, groups=00000000}, [12]) = 0 [pid 26843] gettimeofday({1292369722, 369697}, NULL) = 0 [pid 26843] sendto(38, "\24\0\0\0\26\0\1\3:\377\7M\0\0\0\0\0\0\0\0", 20, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 20 [pid 26843] recvmsg(38, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"0\0\0\0\24\0\2\0:\377\7M\331h\0\0\2\10\200\376\1\0\0\0\10\0\1\0\177\0\0\1"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 156 [pid 26843] recvmsg(38, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"@\0\0\0\24\0\2\0:\377\7M\331h\0\0\n\200\200\0\1\0\0\0\24\0\1\0 \1\0060"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 320 [pid 26843] recvmsg(38, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{"\24\0\0\0\3\0\2\0:\377\7M\331h\0\0\0\0\0\0\1\0\0\0\24\0\1\0 \1\0060"..., 4096}], msg_controllen=0, msg_flags=0}, 0) = 20 [pid 26843] close(38) = 0 [pid 26843] gettimeofday({1292369722, 372103}, NULL) = 0 [pid 26843] gettimeofday({1292369722, 374812}, NULL) = 0 [pid 26843] sendmsg(15, {msg_name(28)={sa_family=AF_INET6, sin6_port=htons(57447), inet_pton(AF_INET6, "2001:630:1:12a::233", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, msg_iov(1)=[{"\2A\0)\20JW*D\223\277\323B\300\231\243\215Q\276\320!\25OSC-Extend"..., 41}], msg_controllen=32, {cmsg_len=32, cmsg_level=SOL_IPV6, cmsg_type=, ...}, msg_flags=0}, 0) = -1 EINVAL (Invalid argument) ---- Noseying at 'cpp -I src/ src/lib/udpfromto.c' I see for v6 we never actually get to recvmsg(): ---- else if (si.ss_family == 10) { struct sockaddr_in6 *dst = (struct sockaddr_in6 *) to; struct sockaddr_in6 *src = (struct sockaddr_in6 *) &si; if (*tolen < sizeof(*dst)) { (*__errno_location ()) = 22; return -1; } *tolen = sizeof(*dst); *dst = *src; return recvfrom(s, buf, len, flags, from, fromlen); } ---- Tells me you forgot to 's/IN6_PKTINFO/IPV6_PKTINFO/g' :) I still get -EINVAL for sendmsg() after that, however recvmsg() is now being called. I'm pondering if you do need some memcpy() action (GCC bug?) but it is getting late, and I'll pick this up in the morning. Cheers -- Alexander Clouter .sigmonster says: Does the name Pavlov ring a bell?
participants (2)
-
Alan DeKok -
Alexander Clouter