Add ability to set and get DSCP socket option

This commit is contained in:
Chris Laws 2013-11-25 13:31:21 +10:30
parent b91ef997ce
commit 38bceca9ca
12 changed files with 389 additions and 11 deletions

View File

@ -352,6 +352,17 @@ Default value:: 1 (true)
Applicable socket types:: all, when using TCP transports.
ZMQ_TOS: Retrieve the Type-of-Service socket override status
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Retrieve the IP_TOS option for the socket.
[horizontal]
Option value type:: int
Option value unit:: >0
Default value:: 0
Applicable socket types:: all, only for connection-oriented transports
ZMQ_IMMEDIATE: Retrieve attach-on-connect value
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Retrieve the state of the attach on connect value. If set to `1`, will delay the

View File

@ -13,8 +13,8 @@ SYNOPSIS
*int zmq_setsockopt (void '*socket', int 'option_name', const void '*option_value', size_t 'option_len');*
Caution: All options, with the exception of ZMQ_SUBSCRIBE, ZMQ_UNSUBSCRIBE,
ZMQ_LINGER, ZMQ_ROUTER_HANDOVER, ZMQ_ROUTER_MANDATORY, ZMQ_PROBE_ROUTER,
ZMQ_XPUB_VERBOSE, ZMQ_REQ_CORRELATE, and ZMQ_REQ_RELAXED, only take effect for
ZMQ_LINGER, ZMQ_ROUTER_HANDOVER, ZMQ_ROUTER_MANDATORY, ZMQ_PROBE_ROUTER,
ZMQ_XPUB_VERBOSE, ZMQ_REQ_CORRELATE, and ZMQ_REQ_RELAXED, only take effect for
subsequent socket bind/connects.
Specifically, security options take effect for subsequent bind/connect calls,
@ -377,6 +377,21 @@ Default value:: 1 (true)
Applicable socket types:: all, when using TCP transports.
ZMQ_TOS: Set the Type-of-Service on socket
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets the ToS fields (Differentiated services (DS) and Explicit Congestion Notification
(ECN) field of the IP header. The ToS field is typically used to specify a packets
priority. The availability of this option is dependent on intermediate network
equipment that inspect the ToS field andprovide a path for low-delay, high-throughput,
highly-reliable service, etc.
[horizontal]
Option value type:: int
Option value unit:: >0
Default value:: 0
Applicable socket types:: all, only for connection-oriented transports
ZMQ_IMMEDIATE: Queue messages only to completed connections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -397,11 +412,11 @@ ZMQ_ROUTER_HANDOVER: handle peer identity name collisions on ROUTER sockets
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets the ROUTER socket behavior when it encounters peers with the same identity.
By default, if two peers with the same identity connect to the same ROUTER
socket the results will be undefined. A value of `1` will cause the ROUTER
By default, if two peers with the same identity connect to the same ROUTER
socket the results will be undefined. A value of `1` will cause the ROUTER
socket to reassign the identity upon encountering an identity name collision.
Specifically, the first peer to connect with that identity will be terminated
and the second peer will receive any subsequent messages routed to that
and the second peer will receive any subsequent messages routed to that
identity.
Option value type:: int
@ -480,7 +495,7 @@ Applicable socket types:: ZMQ_XPUB
ZMQ_REQ_CORRELATE: match replies with requests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The default behavior of REQ sockets is to rely on the ordering of messages to
The default behavior of REQ sockets is to rely on the ordering of messages to
match requests and responses and that is usually sufficient. When this option
is set to 1, the REQ socket will prefix outgoing messages with an extra frame
containing a request id. That means the full message is (request id, 0,
@ -506,7 +521,7 @@ The request-reply state machine is reset and a new request is sent to the
next available peer.
If set to 1, also enable ZMQ_REQ_CORRELATE to ensure correct matching of
requests and replies. Otherwise a late reply to an aborted request can be
requests and replies. Otherwise a late reply to an aborted request can be
reported as the reply to the superseding request.
[horizontal]

View File

@ -289,6 +289,7 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval);
#define ZMQ_CONFLATE 54
#define ZMQ_ZAP_DOMAIN 55
#define ZMQ_ROUTER_HANDOVER 56
#define ZMQ_TOS 57
/* Message options */
#define ZMQ_MORE 1

View File

@ -148,3 +148,18 @@ bool zmq::get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_)
ip_addr_ = host;
return true;
}
void zmq::set_ip_type_of_service (fd_t s_, int iptos)
{
(void) s_;
int rc = setsockopt(s_, IPPROTO_IP, IP_TOS, &iptos, sizeof(iptos));
#ifdef ZMQ_HAVE_WINDOWS
wsa_assert (rc != SOCKET_ERROR);
#else
errno_assert (rc == 0);
#endif
}

View File

@ -39,6 +39,9 @@ namespace zmq
// Socket sockfd_ must be connected. Returns true iff successful.
bool get_peer_ip_address (fd_t sockfd_, std::string &ip_addr_);
// Sets the IP Type-Of-Service for the underlying socket
void set_ip_type_of_service (fd_t s_, int iptos);
}
#endif

View File

@ -33,6 +33,7 @@ zmq::options_t::options_t () :
multicast_hops (1),
sndbuf (0),
rcvbuf (0),
tos (0),
type (-1),
linger (-1),
reconnect_ivl (100),
@ -125,6 +126,13 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
}
break;
case ZMQ_TOS:
if (is_int && value >= 0) {
tos = value;
return 0;
}
break;
case ZMQ_LINGER:
if (is_int && value >= -1) {
linger = value;
@ -424,6 +432,12 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_)
}
break;
case ZMQ_TOS:
if (is_int) {
*value = tos;
return 0;
}
break;
case ZMQ_TYPE:
if (is_int) {
*value = type;

View File

@ -66,6 +66,9 @@ namespace zmq
int sndbuf;
int rcvbuf;
// Type of service (containing DSCP and ECN socket options)
int tos;
// Socket type.
int type;

View File

@ -190,14 +190,14 @@ int zmq::tcp_connecter_t::get_new_reconnect_ivl ()
// Only change the current reconnect interval if the maximum reconnect
// interval was set and if it's larger than the reconnect interval.
if (options.reconnect_ivl_max > 0 &&
if (options.reconnect_ivl_max > 0 &&
options.reconnect_ivl_max > options.reconnect_ivl) {
// Calculate the next interval
current_reconnect_ivl = current_reconnect_ivl * 2;
if(current_reconnect_ivl >= options.reconnect_ivl_max) {
current_reconnect_ivl = options.reconnect_ivl_max;
}
}
}
return this_interval;
}
@ -223,6 +223,10 @@ int zmq::tcp_connecter_t::open ()
if (addr->resolved.tcp_addr->family () == AF_INET6)
enable_ipv4_mapping (s);
// Set the IP Type-Of-Service priority for this socket
if (options.tos != 0)
set_ip_type_of_service (s, options.tos);
// Set the socket to non-blocking mode so that we get async connect().
unblock_socket (s);
@ -232,6 +236,10 @@ int zmq::tcp_connecter_t::open ()
if (options.rcvbuf != 0)
set_tcp_receive_buffer (s, options.rcvbuf);
// Set the IP Type-Of-Service for the underlying socket
if (options.tos != 0)
set_ip_type_of_service (s, options.tos);
// Connect to the remote peer.
int rc = ::connect (
s, addr->resolved.tcp_addr->addr (),

View File

@ -100,7 +100,7 @@ void zmq::tcp_listener_t::in_event ()
io_thread_t *io_thread = choose_io_thread (options.affinity);
zmq_assert (io_thread);
// Create and launch a session object.
// Create and launch a session object.
session_base_t *session = session_base_t::create (io_thread, false, socket,
options, NULL);
errno_assert (session);
@ -188,6 +188,10 @@ int zmq::tcp_listener_t::set_address (const char *addr_)
if (address.family () == AF_INET6)
enable_ipv4_mapping (s);
// Set the IP Type-Of-Service for the underlying socket
if (options.tos != 0)
set_ip_type_of_service (s, options.tos);
// Set the socket buffer limits for the underlying socket.
if (options.sndbuf != 0)
set_tcp_send_buffer (s, options.sndbuf);
@ -300,5 +304,9 @@ zmq::fd_t zmq::tcp_listener_t::accept ()
}
}
// Set the IP Type-Of-Service priority for this client socket
if (options.tos != 0)
set_ip_type_of_service (sock, options.tos);
return sock;
}

View File

@ -42,7 +42,8 @@ noinst_PROGRAMS = test_system \
test_issue_566 \
test_proxy \
test_abstract_ipc \
test_many_sockets
test_many_sockets \
test_diffserv
if !ON_MINGW
noinst_PROGRAMS += test_shutdown_stress \
@ -103,6 +104,7 @@ test_issue_566_SOURCES = test_issue_566.cpp
test_proxy_SOURCES = test_proxy.cpp
test_abstract_ipc_SOURCES = test_abstract_ipc.cpp
test_many_sockets_SOURCES = test_many_sockets.cpp
test_diffserv_SOURCES = test_diffserv.cpp
if !ON_MINGW
test_shutdown_stress_SOURCES = test_shutdown_stress.cpp
test_pair_ipc_SOURCES = test_pair_ipc.cpp testutil.hpp

228
tests/test_diffserv Executable file
View File

@ -0,0 +1,228 @@
#! /bin/bash
# test_diffserv - temporary wrapper script for .libs/test_diffserv
# Generated by libtool (GNU libtool) 2.4.2 Debian-2.4.2-1ubuntu1
#
# The test_diffserv program cannot be directly executed until all the libtool
# libraries that it depends on are installed.
#
# This wrapper script should never be moved out of the build directory.
# If it is, it will not operate correctly.
# Sed substitution that helps us do robust quoting. It backslashifies
# metacharacters that are still active within double-quoted strings.
sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
# Be Bourne compatible
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
emulate sh
NULLCMD=:
# Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
fi
BIN_SH=xpg4; export BIN_SH # for Tru64
DUALCASE=1; export DUALCASE # for MKS sh
# The HP-UX ksh and POSIX shell print the target directory to stdout
# if CDPATH is set.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
relink_command="(cd /home/claws/Development/git-repos/claws_libzmq/tests; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/home/claws/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/claws/bin; export PATH; g++ -g -O2 -o \$progdir/\$file test_diffserv.o ../src/.libs/libzmq.so -lsodium -lrt -lpthread -Wl,-rpath -Wl,/home/claws/Development/git-repos/claws_libzmq/src/.libs)"
# This environment variable determines our operation mode.
if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
# install mode needs the following variables:
generated_by_libtool_version='2.4.2'
notinst_deplibs=' ../src/libzmq.la'
else
# When we are sourced in execute mode, $file and $ECHO are already set.
if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
file="$0"
# A function that is used when there is no print builtin or printf.
func_fallback_echo ()
{
eval 'cat <<_LTECHO_EOF
$1
_LTECHO_EOF'
}
ECHO="printf %s\\n"
fi
# Very basic option parsing. These options are (a) specific to
# the libtool wrapper, (b) are identical between the wrapper
# /script/ and the wrapper /executable/ which is used only on
# windows platforms, and (c) all begin with the string --lt-
# (application programs are unlikely to have options which match
# this pattern).
#
# There are only two supported options: --lt-debug and
# --lt-dump-script. There is, deliberately, no --lt-help.
#
# The first argument to this parsing function should be the
# script's ../libtool value, followed by no.
lt_option_debug=
func_parse_lt_options ()
{
lt_script_arg0=$0
shift
for lt_opt
do
case "$lt_opt" in
--lt-debug) lt_option_debug=1 ;;
--lt-dump-script)
lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'`
cat "$lt_dump_D/$lt_dump_F"
exit 0
;;
--lt-*)
$ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
exit 1
;;
esac
done
# Print the debug banner immediately:
if test -n "$lt_option_debug"; then
echo "test_diffserv:test_diffserv:${LINENO}: libtool wrapper (GNU libtool) 2.4.2 Debian-2.4.2-1ubuntu1" 1>&2
fi
}
# Used when --lt-debug. Prints its arguments to stdout
# (redirection is the responsibility of the caller)
func_lt_dump_args ()
{
lt_dump_args_N=1;
for lt_arg
do
$ECHO "test_diffserv:test_diffserv:${LINENO}: newargv[$lt_dump_args_N]: $lt_arg"
lt_dump_args_N=`expr $lt_dump_args_N + 1`
done
}
# Core function for launching the target application
func_exec_program_core ()
{
if test -n "$lt_option_debug"; then
$ECHO "test_diffserv:test_diffserv:${LINENO}: newargv[0]: $progdir/$program" 1>&2
func_lt_dump_args ${1+"$@"} 1>&2
fi
exec "$progdir/$program" ${1+"$@"}
$ECHO "$0: cannot exec $program $*" 1>&2
exit 1
}
# A function to encapsulate launching the target application
# Strips options in the --lt-* namespace from $@ and
# launches target application with the remaining arguments.
func_exec_program ()
{
case " $* " in
*\ --lt-*)
for lt_wr_arg
do
case $lt_wr_arg in
--lt-*) ;;
*) set x "$@" "$lt_wr_arg"; shift;;
esac
shift
done ;;
esac
func_exec_program_core ${1+"$@"}
}
# Parse options
func_parse_lt_options "$0" ${1+"$@"}
# Find the directory that this script lives in.
thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'`
test "x$thisdir" = "x$file" && thisdir=.
# Follow symbolic links until we get to the real thisdir.
file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'`
while test -n "$file"; do
destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'`
# If there was a directory component, then change thisdir.
if test "x$destdir" != "x$file"; then
case "$destdir" in
[\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
*) thisdir="$thisdir/$destdir" ;;
esac
fi
file=`$ECHO "$file" | /bin/sed 's%^.*/%%'`
file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'`
done
# Usually 'no', except on cygwin/mingw when embedded into
# the cwrapper.
WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
# special case for '.'
if test "$thisdir" = "."; then
thisdir=`pwd`
fi
# remove .libs from thisdir
case "$thisdir" in
*[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;;
.libs ) thisdir=. ;;
esac
fi
# Try to get the absolute directory name.
absdir=`cd "$thisdir" && pwd`
test -n "$absdir" && thisdir="$absdir"
program=lt-'test_diffserv'
progdir="$thisdir/.libs"
if test ! -f "$progdir/$program" ||
{ file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /bin/sed 1q`; \
test "X$file" != "X$progdir/$program"; }; then
file="$$-$program"
if test ! -d "$progdir"; then
mkdir "$progdir"
else
rm -f "$progdir/$file"
fi
# relink executable if necessary
if test -n "$relink_command"; then
if relink_command_output=`eval $relink_command 2>&1`; then :
else
printf %s\n "$relink_command_output" >&2
rm -f "$progdir/$file"
exit 1
fi
fi
mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null ||
{ rm -f "$progdir/$program";
mv -f "$progdir/$file" "$progdir/$program"; }
rm -f "$progdir/$file"
fi
if test -f "$progdir/$program"; then
if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
# Run the actual program with our arguments.
func_exec_program ${1+"$@"}
fi
else
# The program doesn't exist.
$ECHO "$0: error: \`$progdir/$program' does not exist" 1>&2
$ECHO "This script is just a wrapper for $program." 1>&2
$ECHO "See the libtool documentation for more information." 1>&2
exit 1
fi
fi

70
tests/test_diffserv.cpp Normal file
View File

@ -0,0 +1,70 @@
/*
Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "testutil.hpp"
int main (void)
{
int rc;
int tos = 0x28;
int o_tos;
size_t tos_size = sizeof(tos);
setup_test_environment();
void *ctx = zmq_ctx_new ();
assert (ctx);
void *sb = zmq_socket (ctx, ZMQ_PAIR);
assert (sb);
rc = zmq_setsockopt (sb, ZMQ_TOS, &tos, tos_size);
assert (rc == 0);
rc = zmq_bind (sb, "tcp://127.0.0.1:5560");
assert (rc == 0);
rc = zmq_getsockopt (sb, ZMQ_TOS, &o_tos, &tos_size);
assert (rc == 0);
assert (o_tos == tos);
void *sc = zmq_socket (ctx, ZMQ_PAIR);
assert (sc);
tos = 0x58;
rc = zmq_setsockopt (sc, ZMQ_TOS, &tos, tos_size);
assert (rc == 0);
rc = zmq_connect (sc, "tcp://127.0.0.1:5560");
assert (rc == 0);
rc = zmq_getsockopt (sc, ZMQ_TOS, &o_tos, &tos_size);
assert (rc == 0);
assert (o_tos == tos);
// Wireshark can be used to verify that the server socket is
// using DSCP 0x28 in packets to the client while the client
// is using 0x58 in packets to the server.
bounce (sb, sc);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
rc = zmq_ctx_term (ctx);
assert (rc == 0);
return 0 ;
}