Martin missed an updated patch from before, here it is again with some
extras.

* Fix NIC name binding always returning inaddr-any/in6addr-any.
* Binding upon a NIC name in IPv6 may resolve to multiple interfaces, use
the IPv6 scope identifier to force the bind.
* Use SO_BINDTODEVICE on Linux to workaround lack of in6addr_any:scope_id
support.
* Add support for IPv6 literal addresses.


*NIC NAME BINDING*

Basically NIC binding isn't too hot, so what we have here is when you bind
like this:

*./perf/local_thr tcp://eth0:5555 1 100000*

You are actually getting the same as this:

*./perf/local_thr tcp://*:5555 1 100000*

On Linux I have added a workaround that you can use system capabilities to
force the bind:

*sudo execcap 'cap_net_raw=ep' ./perf/local_thr tcp://eth0:5555 1 100000*
*
*
On modern distro's this utility has disappeared, the file system tools don't
appear to work out-of-the-box, but capsh does:

*sudo capsh --caps="cap_net_raw=ep" -- -c "./perf/local_thr tcp://eth0:5555
1 100000"*
*
*
That is the same capability as supporting ICMP, raw sockets, and IP/PGM.

To test simply try using another adapter such as loopback and it should wait
on reconnecting.

*./perf/remote_thr tcp://127.0.0.1:5555 1 100000*



*IPv6 LITERAL ADDRESSES*

Nothing particularly amazing, but to avoid confusion with port specifiers
the RFC recommend that all IPv6 literal addresses be enclosed within square
brackets.  Note in dual-stack mode IPv4 addresses are also valid.

Example:

*./perf/remote_thr tcp://[fe80::230:1bff:feb7:a209%eth0]:5555 1 100000*


*IPv6 LINK-LOCAL ADDRESSES*

The TCP stack isn't as forgiving as the UDP stack, when using link-local
addresses, even if you only have one available, the scope identifier must be
specified.

Valid:

*./perf/remote_thr tcp://[fe80::230:1bff:feb7:a209%eth0]:5555 1 100000*
*
*
Invalid:

*./perf/remote_thr tcp://[fe80::230:1bff:feb7:a209]:5555 1 100000*
Assertion failed: s == retired_fd (tcp_connecter.cpp:184)
Aborted


-- 
Steve-o
From a4ad727c6549eedcddee72d40a0f850db3c5f298 Mon Sep 17 00:00:00 2001
From: Steven McCoy <[email protected]>
Date: Mon, 22 Aug 2011 14:31:34 +0800
Subject: [PATCH] Binding upon a NIC name in IPv6 may resolve to multiple interfaces, use the IPv6 scope
 identifier to force the bind.
 Use SO_BINDTODEVICE on Linux to workaround lack of in6addr_any:scope_id support.
 Add support for IPv6 literal addresses.


Signed-off-by: Steven McCoy <[email protected]>
---
 src/tcp_address.cpp  |   46 ++++++++++++++++++++++++++++++++++++++--------
 src/tcp_listener.cpp |   20 ++++++++++++++++++++
 2 files changed, 58 insertions(+), 8 deletions(-)

diff --git a/src/tcp_address.cpp b/src/tcp_address.cpp
index 999c015..033449d 100644
--- a/src/tcp_address.cpp
+++ b/src/tcp_address.cpp
@@ -164,7 +164,8 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
     zmq_assert (ifa != NULL);
 
     //  Find the corresponding network interface.
-    bool found = false;
+    int found = 0;
+    uint32_t sin6_scope_id = 0;
     for (ifaddrs *ifp = ifa; ifp != NULL ;ifp = ifp->ifa_next)
     {
         if (ifp->ifa_addr == NULL)
@@ -176,11 +177,19 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
              || (!ipv4only_ && family == AF_INET6))
             && !strcmp (nic_, ifp->ifa_name))
         {
+            found++;
             memcpy (&address, ifp->ifa_addr,
                     (family == AF_INET) ? sizeof (struct sockaddr_in)
                                         : sizeof (struct sockaddr_in6));
-            found = true;
-            break;
+            //  Duplicate interfaces only occur on IPv6 enabled adapters.
+            if (ipv4only_)
+                break;
+            //  Save scope identifier for this adapter.
+            if (family == AF_INET6) {
+                sockaddr_in6 sin6;
+                memcpy (&sin6, ifp->ifa_addr, sizeof (sin6));
+                sin6_scope_id = sin6.sin6_scope_id;
+            }
         }
     }
 
@@ -192,6 +201,16 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
         return -1;
     }
 
+    //  Multiple matching interfaces, therefore return entire adapter.
+    if (found > 1) {
+        sockaddr_in6 sin6;
+        memset (&sin6, 0, sizeof (sin6));
+        sin6.sin6_family = AF_INET6;
+        memcpy (&sin6.sin6_addr, &in6addr_any, sizeof (in6addr_any));
+        sin6.sin6_scope_id = sin6_scope_id;
+        memcpy (&address, &sin6, sizeof (sin6));
+    }
+
     return 0;
 }
 
@@ -248,11 +267,8 @@ int zmq::tcp_address_t::resolve_interface (char const *interface_,
     int rc = resolve_nic_name (interface_, ipv4only_);
     if (rc != 0 && errno != ENODEV)
         return rc;
-    if (rc == 0) {
-        zmq_assert (out_addrlen <= (socklen_t) sizeof (address));
-        memcpy (&address, out_addr, out_addrlen);
+    if (rc == 0)
         return 0;
-    }
 
     //  There's no such interface name. Assume literal address.
 #if defined ZMQ_HAVE_OPENVMS && defined __ia64
@@ -306,6 +322,8 @@ int zmq::tcp_address_t::resolve_interface (char const *interface_,
 int zmq::tcp_address_t::resolve_hostname (const char *hostname_, bool ipv4only_)
 {
     //  Set up the query.
+    char literal_addr[INET6_ADDRSTRLEN];
+    const char *node = hostname_;
     addrinfo req;
     memset (&req, 0, sizeof (req));
 
@@ -325,10 +343,22 @@ int zmq::tcp_address_t::resolve_hostname (const char *hostname_, bool ipv4only_)
         req.ai_flags |= AI_V4MAPPED;
 #endif
 
+    //  IPv6 literal addresses must be surrounded by square brackets.
+    if (!ipv4only_ && hostname_[0] == '[') {
+        size_t hostnamelen = strlen (hostname_);
+        if (']' == hostname_[ hostnamelen - 1 ] && hostnamelen < INET6_ADDRSTRLEN) {
+            req.ai_family = AF_INET6;
+            req.ai_flags |= AI_NUMERICHOST;
+            memcpy (literal_addr, hostname_ + 1, hostnamelen - 2);
+            literal_addr[ hostnamelen - 2 ] = 0;
+            node = &literal_addr[0];
+        }
+    }
+
     //  Resolve host name. Some of the error info is lost in case of error,
     //  however, there's no way to report EAI errors via errno.
     addrinfo *res;
-    int rc = getaddrinfo (hostname_, NULL, &req, &res);
+    int rc = getaddrinfo (node, NULL, &req, &res);
     if (rc) {
         switch (rc) {
         case EAI_MEMORY:
diff --git a/src/tcp_listener.cpp b/src/tcp_listener.cpp
index e78a4a3..716176a 100644
--- a/src/tcp_listener.cpp
+++ b/src/tcp_listener.cpp
@@ -43,6 +43,10 @@
 #include <fcntl.h>
 #endif
 
+#ifdef ZMQ_HAVE_LINUX
+#include <net/if.h>
+#endif
+
 #ifdef ZMQ_HAVE_OPENVMS
 #include <ioctl.h>
 #endif
@@ -156,6 +160,22 @@ int zmq::tcp_listener_t::set_address (const char *addr_)
     if (address.family () == AF_INET6)
         enable_ipv4_mapping (s);
 
+#ifdef ZMQ_HAVE_LINUX
+    //  Force the IPv6 scope identifier by binding the socket to the adapter
+    //  for NIC wildcard bindings.
+    if (address.family () == AF_INET6) {
+        struct sockaddr_in6* sin6 = (struct sockaddr_in6*)address.addr ();
+        if (sin6->sin6_scope_id != 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+            char ifname[IF_NAMESIZE],
+                *rptr = if_indextoname (sin6->sin6_scope_id, ifname);
+            errno_assert (rptr != NULL);
+            //  SO_BINDTODEVICE since Linux 2.2.11 requires CAP_NET_RAW permissions.
+            rc = setsockopt (s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen (ifname));
+            errno_assert (rc == 0 || (rc == -1 && errno == EPERM));
+        }
+    }
+#endif
+
     //  Allow reusing of the address.
     int flag = 1;
 #ifdef ZMQ_HAVE_WINDOWS
-- 
1.7.4.1

_______________________________________________
zeromq-dev mailing list
[email protected]
http://lists.zeromq.org/mailman/listinfo/zeromq-dev

Reply via email to