]> granicus.if.org Git - libevent/commitdiff
listener: ipv6only socket bind support
authorMurat Demirten <mdemirten@yh.com.tr>
Mon, 4 Jun 2018 13:43:34 +0000 (16:43 +0300)
committerAzat Khuzhin <a3at.mail@gmail.com>
Fri, 26 Oct 2018 04:24:29 +0000 (07:24 +0300)
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)
evutil.c
include/event2/listener.h
include/event2/util.h
listener.c

index d93eff5fe3c0744f6b3247df3b2f27f1191c99f4..5d385bdc75d30b86e5d5e867cc39ffebbb05c039 100644 (file)
--- a/evutil.c
+++ b/evutil.c
@@ -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)
 {
index 84b4da055d8d24fa3edd1e4b1fa3f40f769991de..789a27c2aacc59cd336567234993b400d5f45d23 100644 (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
index 70aabb667a1ed81f72eb0ff537da08459a9d0672..c0f5f80bef6cc5e2007374639537d79b3d0fa312 100644 (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.
 
index 2835df176114dca164efb02f732cd2d923ada1f6..e803bed11700c77e96c52a35d48368c768be13ca 100644 (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;