IP networking single socket, both IPv4 and V6?
Lewis Donzis
lew at perftech.com
Thu Jan 4 16:54:53 UTC 2018
> On Jan 4, 2018, at 10:32 AM, Lewis Donzis <lew at perftech.com> wrote:
>
> On Jan 4, 2018, at 10:17 AM, Karl Denninger <karl at denninger.net> wrote:
>>
>> I've written a fair bit of code that binds to both Ipv4 and v6 for
>> incoming connections, using two sockets (one for each.)
>>
>> Perusing around the 'net I see an implementation note written by IBM
>> that implies that on their Unix implementation you can set up an INET6
>> listener and it will open listeners on *both* IPv4 and v6; you code it
>> as an Ipv6 socket/bind/listen/accept, and if an Ipv4 connection comes in
>> you get a prefix'd IPv4 address back when you call getpeername().
>>
>> This would obviously shorten up code and remove the need to open the
>> second listener socket, but playing with this on FreeBSD it doesn't
>> appear to work -- I only get the IPv6 listener in "netstat -a -n"
>> showing up and as expected a connection to a v4 address on that port
>> fails (refused, since there's no listener.)
>>
>> Is this something that *should* work on FreeBSD?
>
> It works. We do it all the time. You either have to set the sysctl:
>
> net.inet6.ip6.v6only=0
>
> which you can do in /etc/sysctl.conf or with the sysctl utility, or, in your program, use setsockopt to turn off the V6ONLY option, e.g.:
>
> setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof (int)); // Turn off v6-only
>
> We use the first method, which is broken in FreeBSD 11.1 prior to patch level 5 or 6, I can’t remember which, but works in all others. The second method is considered to be more portable.
>
> FWIW, Linux, by default, sets v6only off, so it doesn't require anything special.
I forgot about one other option, which we used to get around the regression in 11.1 until the kernel gets fixed. Because libc functions are generally published with a weak reference, you can overload the socket() function in your own code, like this:
int socket (int domain, int type, int protocol)
{
extern int _socket(int domain, int type, int protocol);
int s = _socket(domain, type, protocol);
if (s >= 0 && domain == PF_INET6)
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof (int)); // Turn off v6-only
return s;
}
We put that in one of our own shared libraries that we bind all of our programs to, and that solves it without having to change a lot of code.
More information about the freebsd-net
mailing list