listener: ipv6only socket bind support

According to RFC3493 and most Linux distributions, default value is to
work in IPv4-mapped mode. If there is a requirement to bind same port
on same ip addresses but different handlers for both IPv4 and IPv6,
it is required to set IPV6_V6ONLY socket option to be sure that the
code works as expected without affected by bindv6only sysctl setting
in system.

See an example working with this patch:
https://gist.github.com/demirten/023008a63cd966e48b0ebcf9af7fc113

Closes: #640 (cherry-pick)
(cherry picked from commit 387d91f9ab95df8ac3d7bb58493310ad4a377dcf)
This commit is contained in:
Murat Demirten 2018-06-04 16:43:34 +03:00 committed by Azat Khuzhin
parent b07e43e607
commit ba14879661
No known key found for this signature in database
GPG Key ID: B86086848EF8686D
4 changed files with 37 additions and 0 deletions

View File

@ -385,6 +385,14 @@ evutil_make_listen_socket_reuseable_port(evutil_socket_t sock)
#endif
}
int
evutil_make_listen_socket_ipv6only(evutil_socket_t sock)
{
int one = 1;
return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*) &one,
(ev_socklen_t)sizeof(one));
}
int
evutil_make_tcp_listen_socket_deferred(evutil_socket_t sock)
{

View File

@ -97,6 +97,18 @@ typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *);
* This is only available on Linux and kernel 3.9+
*/
#define LEV_OPT_REUSEABLE_PORT (1u<<7)
/** Flag: Indicates that the listener wants to work only in IPv6 socket.
*
* According to RFC3493 and most Linux distributions, default value is to
* work in IPv4-mapped mode. If there is a requirement to bind same port
* on same ip addresses but different handlers for both IPv4 and IPv6,
* it is required to set IPV6_V6ONLY socket option to be sure that the
* code works as expected without affected by bindv6only sysctl setting in
* system.
*
* This socket option also supported by Windows.
*/
#define LEV_OPT_BIND_IPV6ONLY (1u<<8)
/**
Allocate a new evconnlistener object to listen for incoming TCP connections

View File

@ -426,6 +426,18 @@ int evutil_make_listen_socket_reuseable(evutil_socket_t sock);
EVENT2_EXPORT_SYMBOL
int evutil_make_listen_socket_reuseable_port(evutil_socket_t sock);
/** Set ipv6 only bind socket option to make listener work only in ipv6 sockets.
According to RFC3493 and most Linux distributions, default value for the
sockets is to work in IPv4-mapped mode. In IPv4-mapped mode, it is not possible
to bind same port from different IPv4 and IPv6 handlers.
@param sock The socket to make in ipv6only working mode
@return 0 on success, -1 on failure
*/
EVENT2_EXPORT_SYMBOL
int evutil_make_listen_socket_ipv6only(evutil_socket_t sock);
/** Do platform-specific operations as needed to close a socket upon a
successful execution of one of the exec*() functions.

View File

@ -245,6 +245,11 @@ evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
goto err;
}
if (flags & LEV_OPT_BIND_IPV6ONLY) {
if (evutil_make_listen_socket_ipv6only(fd) < 0)
goto err;
}
if (sa) {
if (bind(fd, sa, socklen)<0)
goto err;