On Mon, May 22, 2017 at 10:26:09AM -0500, Eric Blake wrote: > On 05/19/2017 01:03 PM, Daniel P. Berrange wrote: > > When binding to an IPv6 socket we currently force the > > IPV6_V6ONLY flag to off. This means that the IPv6 socket > > will accept both IPv4 & IPv6 sockets when QEMU is launched > > with something like > > > > -vnc :::1 > > > > While this is good for that case, it is bad for other > > cases. For example if an empty hostname is given, > > getaddrinfo resolves it to 2 addresses 0.0.0.0 and ::, > > in that order. We will thus bind to 0.0.0.0 first, and > > then fail to bind to :: on the same port. The same > > problem can happen if any other hostname lookup causes > > the IPv4 address to be reported before the IPv6 address. > > > > When we get an IPv6 bind failure, we should re-try the > > same port, but with IPV6_V6ONLY turned on again, to > > avoid clash with any IPv4 listener. > > > > This ensures that > > > > -vnc :1 > > > > will bind successfully to both 0.0.0.0 and ::, and also > > avoid > > > > -vnc :1,to=2 > > > > from mistakenly using a 2nd port for the :: listener. > > > > Signed-off-by: Daniel P. Berrange <berra...@redhat.com> > > --- > > util/qemu-sockets.c | 31 +++++++++++++++++++++++-------- > > 1 file changed, 23 insertions(+), 8 deletions(-) > > > > > for (p = port_min; p <= port_max; p++) { > > +#ifdef IPV6_V6ONLY > > + /* listen on both ipv4 and ipv6 */ > > + int v6only = 0; > > +#endif > > inet_setport(e, p); > > +#ifdef IPV6_V6ONLY > > + rebind: > > + if (e->ai_family == PF_INET6) { > > The rebind: label could go here with no change in semantics and one less > conditional when compiled without optimization. But hopefully the > compiler is smart enough to see that under -O2, so I don't see a reason > for you to change the code.
Also bear in mind the running time of this method is going to be dominated by the getaddrinfo() call most of the time, so I don't think there's any measurable perf difference to moving the label. > > + qemu_setsockopt(slisten, IPPROTO_IPV6, IPV6_V6ONLY, > > &v6only, > > + sizeof(v6only)); > > + } > > +#endif > > if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) { > > goto listen; > > } > > + > > +#ifdef IPV6_V6ONLY > > + /* If we got EADDRINUSE from an IPv6 bind & V6ONLY is unset, > > + * it could be that the IPv4 port is already claimed, so retry > > + * with V6ONLY set > > + */ > > + if (e->ai_family == PF_INET6 && errno == EADDRINUSE && > > !v6only) { > > + v6only = 1; > > + goto rebind; > > + } > > +#endif > > + > > Reviewed-by: Eric Blake <ebl...@redhat.com> > > -- > Eric Blake, Principal Software Engineer > Red Hat, Inc. +1-919-301-3266 > Virtualization: qemu.org | libvirt.org > Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|