Bug #4931
closedkernel's inet_pton still botches v4 mapped and compat addresses byte order
100%
Description
While developing something that used ksockets and the kernel's inet_pton to fill out a struct sockaddr_in6 with a v4 mapped IPv6 address, I found that the address was being transmitted on the wire incorrectly and the address "::ffff:10.88.88.70" was being sent to 70.88.88.10. To verify that I was seeing something crazy, I wrote the following sample C program and used DTrace to verify the structure:
$ cat tmp.c #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <strings.h> int main(void) { int ret; struct sockaddr_in6 in6; const char *addr = "::ffff:10.88.88.70"; bzero(&in6, sizeof (struct sockaddr_in6)); (void) inet_pton(AF_INET6, addr, &in6.sin6_addr); return (0); } $ cat tmp.d pid$target::inet_pton:entry { self->a = args[2]; } pid$target::inet_pton:return /self->a/ { print(((userland struct sockaddr_in6 *)self->a)->sin6_addr); } $ gcc -lnsl tmp.c $ dtrace -qs ./tmp.d -c ./a.out struct in6_addr { union _S6_un = { uint32_t [4] _S6_u32 = [ 0xffff0000, 0x4658580a, 0, 0 ] uint8_t [16] _S6_u8 = [ 0, 0, 0xff, 0xff, 0xa, 0x58, 0x58, 0x46, 0, 0, 0, 0, 0, 0, 0, 0 ] uint32_t __S6_align = 0xffff0000 } }
However, when looking at similar code in the kernel the equivalent D script output:
0 22498 _inet_pton:return struct in6_addr { union _S6_un = { uint32_t [4] _S6_u32 = [ 0xffff0000, 0xa585846, 0, 0 ] uint8_t [16] _S6_u8 = [ 0, 0, 0xff, 0xff, 0x46, 0x58, 0x58, 0xa, 0, 0, 0, 0, 0, 0, 0, 0 ] uint32_t __S6_align = 0xffff0000 } }
Note how the data is still in host order in the kernel case, but network order in the user land case.
The problem is an extension of 3105 which addressed the issue for AF_INET, but not for AF_INET6. The solution; however, is similar. In addition, we shouldn't need to create an additional compatibility wrapper, because the existing one should cover this case.