mirror of
https://github.com/zeromq/libzmq.git
synced 2024-12-12 18:40:27 +01:00
Add socket option BINDTODEVICE
Linux now supports Virtual Routing and Forwarding (VRF) as per: https://www.kernel.org/doc/Documentation/networking/vrf.txt In order for an application to bind or connect to a socket with an address in a VRF, they need to first bind the socket to the VRF device: setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, dev, strlen(dev)+1); Note "dev" is the VRF device, eg. VRF "blue", rather than an interface enslaved to the VRF. Add a new socket option, ZMQ_BINDTODEVICE, to bind a socket to a device. In general, if a socket is bound to a device, eg. an interface, only packets received from that particular device are processed by the socket. If device is a VRF device, then subsequent binds/connects to that socket use addresses in the VRF routing table.
This commit is contained in:
parent
4a37ce9aeb
commit
b963542e8f
@ -324,6 +324,7 @@ endif ()
|
|||||||
if (NOT CMAKE_CROSSCOMPILING)
|
if (NOT CMAKE_CROSSCOMPILING)
|
||||||
zmq_check_sock_cloexec ()
|
zmq_check_sock_cloexec ()
|
||||||
zmq_check_o_cloexec ()
|
zmq_check_o_cloexec ()
|
||||||
|
zmq_check_so_bindtodevice ()
|
||||||
zmq_check_so_keepalive ()
|
zmq_check_so_keepalive ()
|
||||||
zmq_check_tcp_keepcnt ()
|
zmq_check_tcp_keepcnt ()
|
||||||
zmq_check_tcp_keepidle ()
|
zmq_check_tcp_keepidle ()
|
||||||
|
27
acinclude.m4
27
acinclude.m4
@ -682,6 +682,33 @@ int main (int, char **)
|
|||||||
)
|
)
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
dnl ################################################################################
|
||||||
|
dnl # LIBZMQ_CHECK_SO_BINDTODEVICE([action-if-found], [action-if-not-found]) #
|
||||||
|
dnl # Check if SO_BINDTODEVICE is supported #
|
||||||
|
dnl ################################################################################
|
||||||
|
AC_DEFUN([LIBZMQ_CHECK_SO_BINDTODEVICE], [{
|
||||||
|
AC_CACHE_CHECK([whether SO_BINDTODEVICE is supported], [libzmq_cv_so_bindtodevice],
|
||||||
|
[AC_TRY_RUN([/* SO_BINDTODEVICE test */
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
int main (int argc, char *argv [])
|
||||||
|
{
|
||||||
|
/* Actually making the setsockopt() call requires CAP_NET_RAW */
|
||||||
|
#ifndef SO_BINDTODEVICE
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[libzmq_cv_so_bindtodevice="yes"],
|
||||||
|
[libzmq_cv_so_bindtodevice="no"],
|
||||||
|
[libzmq_cv_so_bindtodevice="not during cross-compile"]
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
AS_IF([test "x$libzmq_cv_so_bindtodevice" = "xyes"], [$1], [$2])
|
||||||
|
}])
|
||||||
|
|
||||||
dnl ################################################################################
|
dnl ################################################################################
|
||||||
dnl # LIBZMQ_CHECK_SO_KEEPALIVE([action-if-found], [action-if-not-found]) #
|
dnl # LIBZMQ_CHECK_SO_KEEPALIVE([action-if-found], [action-if-not-found]) #
|
||||||
dnl # Check if SO_KEEPALIVE is supported #
|
dnl # Check if SO_KEEPALIVE is supported #
|
||||||
|
@ -48,6 +48,25 @@ int main(int argc, char *argv [])
|
|||||||
ZMQ_HAVE_O_CLOEXEC)
|
ZMQ_HAVE_O_CLOEXEC)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
macro(zmq_check_so_bindtodevice)
|
||||||
|
message(STATUS "Checking whether SO_BINDTODEVICE is supported")
|
||||||
|
check_c_source_runs(
|
||||||
|
"
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv [])
|
||||||
|
{
|
||||||
|
/* Actually making the setsockopt() call requires CAP_NET_RAW */
|
||||||
|
#ifndef SO_BINDTODEVICE
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
"
|
||||||
|
ZMQ_HAVE_SO_BINDTODEVICE)
|
||||||
|
endmacro()
|
||||||
|
|
||||||
# TCP keep-alives Checks.
|
# TCP keep-alives Checks.
|
||||||
|
|
||||||
macro(zmq_check_so_keepalive)
|
macro(zmq_check_so_keepalive)
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
# define ZMQ_HAVE_EVENTFD 1
|
# define ZMQ_HAVE_EVENTFD 1
|
||||||
# define ZMQ_HAVE_IFADDRS 1
|
# define ZMQ_HAVE_IFADDRS 1
|
||||||
# define ZMQ_HAVE_SOCK_CLOEXEC 1
|
# define ZMQ_HAVE_SOCK_CLOEXEC 1
|
||||||
|
# define ZMQ_HAVE_SO_BINDTODEVICE 1
|
||||||
# define ZMQ_HAVE_SO_KEEPALIVE 1
|
# define ZMQ_HAVE_SO_KEEPALIVE 1
|
||||||
# define ZMQ_HAVE_SO_PEERCRED 1
|
# define ZMQ_HAVE_SO_PEERCRED 1
|
||||||
# define ZMQ_HAVE_TCP_KEEPCNT 1
|
# define ZMQ_HAVE_TCP_KEEPCNT 1
|
||||||
|
@ -668,6 +668,12 @@ LIBZMQ_CHECK_O_CLOEXEC([
|
|||||||
[Whether O_CLOEXEC is defined and functioning.])
|
[Whether O_CLOEXEC is defined and functioning.])
|
||||||
])
|
])
|
||||||
|
|
||||||
|
LIBZMQ_CHECK_SO_BINDTODEVICE([
|
||||||
|
AC_DEFINE([ZMQ_HAVE_SO_BINDTODEVICE],
|
||||||
|
[1],
|
||||||
|
[Whether SO_BINDTODEVICE is supported.])
|
||||||
|
])
|
||||||
|
|
||||||
# TCP keep-alives Checks.
|
# TCP keep-alives Checks.
|
||||||
LIBZMQ_CHECK_SO_KEEPALIVE([
|
LIBZMQ_CHECK_SO_KEEPALIVE([
|
||||||
AC_DEFINE([ZMQ_HAVE_SO_KEEPALIVE],
|
AC_DEFINE([ZMQ_HAVE_SO_KEEPALIVE],
|
||||||
|
@ -63,6 +63,23 @@ Default value:: 100
|
|||||||
Applicable socket types:: all, only for connection-oriented transports
|
Applicable socket types:: all, only for connection-oriented transports
|
||||||
|
|
||||||
|
|
||||||
|
ZMQ_BINDTODEVICE: Retrieve name of device the socket is bound to
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
The 'ZMQ_BINDTODEVICE' option retrieves the name of the device this socket is
|
||||||
|
bound to, eg. an interface or VRF. If a socket is bound to an interface, only
|
||||||
|
packets received from that interface are processed by the socket. If device
|
||||||
|
is a VRF device, then subsequent binds/connects to that socket use addresses
|
||||||
|
in the VRF routing table.
|
||||||
|
|
||||||
|
NOTE: in DRAFT state, not yet available in stable releases.
|
||||||
|
|
||||||
|
[horizontal]
|
||||||
|
Option value type:: character string
|
||||||
|
Option value unit:: N/A
|
||||||
|
Default value:: not set
|
||||||
|
Applicable socket types:: all, when using TCP or UDP transports.
|
||||||
|
|
||||||
|
|
||||||
ZMQ_CONNECT_TIMEOUT: Retrieve connect() timeout
|
ZMQ_CONNECT_TIMEOUT: Retrieve connect() timeout
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Retrieves how long to wait before timing-out a connect() system call.
|
Retrieves how long to wait before timing-out a connect() system call.
|
||||||
|
@ -70,6 +70,24 @@ Default value:: 100
|
|||||||
Applicable socket types:: all, only for connection-oriented transports.
|
Applicable socket types:: all, only for connection-oriented transports.
|
||||||
|
|
||||||
|
|
||||||
|
ZMQ_BINDTODEVICE: Set name of device to bind the socket to
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
The 'ZMQ_BINDTODEVICE' option binds this socket to a particular device, eg.
|
||||||
|
an interface or VRF. If a socket is bound to an interface, only packets
|
||||||
|
received from that particular interface are processed by the socket. If device
|
||||||
|
is a VRF device, then subsequent binds/connects to that socket use addresses
|
||||||
|
in the VRF routing table.
|
||||||
|
|
||||||
|
NOTE: requires setting CAP_NET_RAW on the compiled program.
|
||||||
|
NOTE: in DRAFT state, not yet available in stable releases.
|
||||||
|
|
||||||
|
[horizontal]
|
||||||
|
Option value type:: character string
|
||||||
|
Option value unit:: N/A
|
||||||
|
Default value:: not set
|
||||||
|
Applicable socket types:: all, when using TCP or UDP transports.
|
||||||
|
|
||||||
|
|
||||||
ZMQ_CONNECT_RID: Assign the next outbound connection id
|
ZMQ_CONNECT_RID: Assign the next outbound connection id
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
The 'ZMQ_CONNECT_RID' option sets the peer id of the next host connected
|
The 'ZMQ_CONNECT_RID' option sets the peer id of the next host connected
|
||||||
|
@ -368,6 +368,7 @@ ZMQ_EXPORT const char *zmq_msg_gets (const zmq_msg_t *msg, const char *property)
|
|||||||
#define ZMQ_VMCI_BUFFER_MAX_SIZE 87
|
#define ZMQ_VMCI_BUFFER_MAX_SIZE 87
|
||||||
#define ZMQ_VMCI_CONNECT_TIMEOUT 88
|
#define ZMQ_VMCI_CONNECT_TIMEOUT 88
|
||||||
#define ZMQ_USE_FD 89
|
#define ZMQ_USE_FD 89
|
||||||
|
#define ZMQ_BINDTODEVICE 90
|
||||||
|
|
||||||
/* Message options */
|
/* Message options */
|
||||||
#define ZMQ_MORE 1
|
#define ZMQ_MORE 1
|
||||||
|
13
src/ip.cpp
13
src/ip.cpp
@ -217,3 +217,16 @@ int zmq::set_nosigpipe (fd_t s_)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void zmq::bind_to_device (fd_t s_, std::string &bound_device_)
|
||||||
|
{
|
||||||
|
#ifdef ZMQ_HAVE_SO_BINDTODEVICE
|
||||||
|
int rc = setsockopt(s_, SOL_SOCKET, SO_BINDTODEVICE, bound_device_.c_str (), bound_device_.length ());
|
||||||
|
|
||||||
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
wsa_assert (rc != SOCKET_ERROR);
|
||||||
|
#else
|
||||||
|
errno_assert (rc == 0);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@ -56,6 +56,9 @@ namespace zmq
|
|||||||
// Return 0 on success, -1 if the connection has been closed by the peer
|
// Return 0 on success, -1 if the connection has been closed by the peer
|
||||||
int set_nosigpipe (fd_t s_);
|
int set_nosigpipe (fd_t s_);
|
||||||
|
|
||||||
|
// Binds the underlying socket to the given device, eg. VRF or interface
|
||||||
|
void bind_to_device (fd_t s_, std::string &bound_device_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -34,6 +34,16 @@
|
|||||||
#include "err.hpp"
|
#include "err.hpp"
|
||||||
#include "macros.hpp"
|
#include "macros.hpp"
|
||||||
|
|
||||||
|
#ifndef ZMQ_HAVE_WINDOWS
|
||||||
|
#include <net/if.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined IFNAMSIZ
|
||||||
|
#define BINDDEVSIZ IFNAMSIZ
|
||||||
|
#else
|
||||||
|
#define BINDDEVSIZ 16
|
||||||
|
#endif
|
||||||
|
|
||||||
zmq::options_t::options_t () :
|
zmq::options_t::options_t () :
|
||||||
sndhwm (1000),
|
sndhwm (1000),
|
||||||
rcvhwm (1000),
|
rcvhwm (1000),
|
||||||
@ -605,6 +615,19 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ZMQ_BINDTODEVICE:
|
||||||
|
if (optval_ == NULL && optvallen_ == 0) {
|
||||||
|
bound_device.clear ();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (optval_ != NULL && optvallen_ > 0 && optvallen_ <= BINDDEVSIZ) {
|
||||||
|
bound_device =
|
||||||
|
std::string ((const char *) optval_, optvallen_);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
#if defined (ZMQ_ACT_MILITANT)
|
#if defined (ZMQ_ACT_MILITANT)
|
||||||
// There are valid scenarios for probing with unknown socket option
|
// There are valid scenarios for probing with unknown socket option
|
||||||
@ -1021,6 +1044,14 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ZMQ_BINDTODEVICE:
|
||||||
|
if (*optvallen_ >= bound_device.size () + 1) {
|
||||||
|
memcpy (optval_, bound_device.c_str (), bound_device.size () + 1);
|
||||||
|
*optvallen_ = bound_device.size () + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
#if defined (ZMQ_ACT_MILITANT)
|
#if defined (ZMQ_ACT_MILITANT)
|
||||||
malformed = false;
|
malformed = false;
|
||||||
|
@ -240,6 +240,9 @@ namespace zmq
|
|||||||
// will be used as the File Descriptor instead of allocating a new
|
// will be used as the File Descriptor instead of allocating a new
|
||||||
// one via the socket () system call.
|
// one via the socket () system call.
|
||||||
int use_fd;
|
int use_fd;
|
||||||
|
|
||||||
|
// Device to bind the underlying socket to, eg. VRF or interface
|
||||||
|
std::string bound_device;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,6 +334,10 @@ int zmq::socks_connecter_t::connect_to_proxy ()
|
|||||||
if (options.tos != 0)
|
if (options.tos != 0)
|
||||||
set_ip_type_of_service (s, options.tos);
|
set_ip_type_of_service (s, options.tos);
|
||||||
|
|
||||||
|
// Bind the socket to a device if applicable
|
||||||
|
if (!options.bound_device.empty ())
|
||||||
|
bind_to_device (s, options.bound_device);
|
||||||
|
|
||||||
// Set the socket to non-blocking mode so that we get async connect().
|
// Set the socket to non-blocking mode so that we get async connect().
|
||||||
unblock_socket (s);
|
unblock_socket (s);
|
||||||
|
|
||||||
|
@ -305,6 +305,10 @@ int zmq::tcp_connecter_t::open ()
|
|||||||
if (options.tos != 0)
|
if (options.tos != 0)
|
||||||
set_ip_type_of_service (s, options.tos);
|
set_ip_type_of_service (s, options.tos);
|
||||||
|
|
||||||
|
// Bind the socket to a device if applicable
|
||||||
|
if (!options.bound_device.empty ())
|
||||||
|
bind_to_device (s, options.bound_device);
|
||||||
|
|
||||||
// Set the socket to non-blocking mode so that we get async connect().
|
// Set the socket to non-blocking mode so that we get async connect().
|
||||||
unblock_socket (s);
|
unblock_socket (s);
|
||||||
|
|
||||||
|
@ -212,6 +212,10 @@ int zmq::tcp_listener_t::set_address (const char *addr_)
|
|||||||
if (options.tos != 0)
|
if (options.tos != 0)
|
||||||
set_ip_type_of_service (s, options.tos);
|
set_ip_type_of_service (s, options.tos);
|
||||||
|
|
||||||
|
// Bind the socket to a device if applicable
|
||||||
|
if (!options.bound_device.empty ())
|
||||||
|
bind_to_device (s, options.bound_device);
|
||||||
|
|
||||||
// Set the socket buffer limits for the underlying socket.
|
// Set the socket buffer limits for the underlying socket.
|
||||||
if (options.sndbuf >= 0)
|
if (options.sndbuf >= 0)
|
||||||
set_tcp_send_buffer (s, options.sndbuf);
|
set_tcp_send_buffer (s, options.sndbuf);
|
||||||
|
@ -123,6 +123,10 @@ void zmq::udp_engine_t::plug (io_thread_t* io_thread_, session_base_t *session_)
|
|||||||
errno_assert (rc == 0);
|
errno_assert (rc == 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Bind the socket to a device if applicable
|
||||||
|
if (!options.bound_device.empty ())
|
||||||
|
bind_to_device (fd, options.bound_device);
|
||||||
|
|
||||||
rc = bind (fd, address->resolved.udp_addr->bind_addr (),
|
rc = bind (fd, address->resolved.udp_addr->bind_addr (),
|
||||||
address->resolved.udp_addr->bind_addrlen ());
|
address->resolved.udp_addr->bind_addrlen ());
|
||||||
#ifdef ZMQ_HAVE_WINDOWS
|
#ifdef ZMQ_HAVE_WINDOWS
|
||||||
|
@ -110,9 +110,42 @@ void test_setsockopt_use_fd ()
|
|||||||
zmq_ctx_term (ctx);
|
zmq_ctx_term (ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BOUNDDEVBUFSZ 16
|
||||||
|
void test_setsockopt_bindtodevice ()
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
void *ctx = zmq_ctx_new ();
|
||||||
|
void *socket = zmq_socket (ctx, ZMQ_PUSH);
|
||||||
|
|
||||||
|
char devname[BOUNDDEVBUFSZ];
|
||||||
|
size_t buflen = BOUNDDEVBUFSZ;
|
||||||
|
|
||||||
|
rc = zmq_getsockopt (socket, ZMQ_BINDTODEVICE, devname, &buflen);
|
||||||
|
assert(rc == 0);
|
||||||
|
assert(devname[0] == '\0');
|
||||||
|
assert(buflen == 1);
|
||||||
|
|
||||||
|
sprintf(devname, "testdev");
|
||||||
|
buflen = strlen(devname);
|
||||||
|
|
||||||
|
rc = zmq_setsockopt (socket, ZMQ_BINDTODEVICE, devname, buflen);
|
||||||
|
assert(rc == 0);
|
||||||
|
|
||||||
|
buflen = BOUNDDEVBUFSZ;
|
||||||
|
memset(devname, 0, buflen);
|
||||||
|
|
||||||
|
rc = zmq_getsockopt (socket, ZMQ_BINDTODEVICE, devname, &buflen);
|
||||||
|
assert(rc == 0);
|
||||||
|
assert(!strncmp("testdev", devname, buflen));
|
||||||
|
|
||||||
|
zmq_close (socket);
|
||||||
|
zmq_ctx_term (ctx);
|
||||||
|
}
|
||||||
|
|
||||||
int main (void)
|
int main (void)
|
||||||
{
|
{
|
||||||
test_setsockopt_tcp_recv_buffer ();
|
test_setsockopt_tcp_recv_buffer ();
|
||||||
test_setsockopt_tcp_send_buffer ();
|
test_setsockopt_tcp_send_buffer ();
|
||||||
test_setsockopt_use_fd ();
|
test_setsockopt_use_fd ();
|
||||||
|
test_setsockopt_bindtodevice ();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user