summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabrice Bellet <fabrice@bellet.info>2016-04-19 13:12:48 +0200
committerFabrice Bellet <fabrice@bellet.info>2017-06-12 17:55:45 +0200
commit0636f9addc041cf93c4ff4eaa351b1768d48a32e (patch)
tree867a195da2be5645d6615464c1f69a8f0a786d47
parenta602ff57aae6a6afdeab843954c48e6fb5d82d31 (diff)
downloadlibnice-0636f9addc041cf93c4ff4eaa351b1768d48a32e.tar.gz
conncheck: implement ice regular nomination method
This patch implements Regular Nomation as described in RFC5245 8.1.1.1. The controlling agent lets valid pairs accumulate, and decides which pair to recheck with the use-candidate attribute set. priv_mark_pair_nominated() follows 7.2.1.5, to update the nominated pair when acting as a STUN server, and priv_map_reply_to_conn_check_request() implements 7.1.3.2.4 to update the nominated pair when acting as a STUN client. A new property is also added to the agent to control the nomination mode, which can be regular of aggressive, with default value set to aggressive. Two new flags are introduced in the CandidateCheckPair structure: - use_candidate_on_next_check indicates the STUN client to add the use-candidate attribute when the pair will be checked. At this time, the nominated flag has not been set on this pair yet. - mark_nominated_on_response_arrival indicates the STUN server to nominate the pair when its succesfull response to a previous triggered check will arrive (7.2.1.5, item #2) Differential Revision: https://phabricator.freedesktop.org/D811
-rw-r--r--agent/Makefile.am23
-rw-r--r--agent/agent-priv.h8
-rw-r--r--agent/agent.c59
-rw-r--r--agent/agent.h43
-rw-r--r--agent/conncheck.c178
-rw-r--r--agent/conncheck.h2
-rw-r--r--configure.ac1
-rw-r--r--docs/reference/libnice/libnice-sections.txt2
8 files changed, 309 insertions, 7 deletions
diff --git a/agent/Makefile.am b/agent/Makefile.am
index b585393..915f312 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -22,6 +22,12 @@ if WINDOWS
AM_CFLAGS += -DWINVER=0x0501 # _WIN32_WINNT_WINXP
endif
+BUILT_SOURCES = \
+ agent-enum-types.h \
+ agent-enum-types.c
+
+CLEANFILES += $(BUILT_SOURCES)
+
noinst_LTLIBRARIES = libagent.la
libagent_la_SOURCES = \
@@ -54,6 +60,23 @@ libagent_la_SOURCES = \
outputstream.c \
$(BUILT_SOURCES)
+agent-enum-types.h: agent.h Makefile
+ $(AM_V_GEN)$(GLIB_MKENUMS) \
+ --fhead "#ifndef __AGENT_ENUM_TYPES_H__\n#define __AGENT_ENUM_TYPES_H__ 1\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+ --fprod "/* enumerations from \"@filename@\" */\n" \
+ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define NICE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
+ --ftail "G_END_DECLS\n\n#endif /* !AGENT_ENUM_TYPES_H */" \
+ $(addprefix $(srcdir)/,agent.h) > $@
+
+agent-enum-types.c: agent.h Makefile agent-enum-types.h
+ $(AM_V_GEN)$(GLIB_MKENUMS) \
+ --fhead "#include <config.h>\n#include <glib-object.h>\n#include \"agent.h\"\n#include \"agent-enum-types.h\"" \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType type = 0;\n if (!type) {\n static const G@Type@Value values[] = {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n type = g_@type@_register_static (\"@EnumName@\", values);\n }\n return type;\n}\n\n" \
+ $(addprefix $(srcdir)/,agent.h) > $@
+
libagent_la_LIBADD = \
$(top_builddir)/random/libnice-random.la \
$(top_builddir)/socket/libsocket.la \
diff --git a/agent/agent-priv.h b/agent/agent-priv.h
index 162ea63..3384180 100644
--- a/agent/agent-priv.h
+++ b/agent/agent-priv.h
@@ -115,6 +115,13 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a,
#define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */
+/* A convenient macro to test if the agent is compatible with RFC5245
+ * or OC2007R2. Specifically these two modes share the support
+ * of the regular or aggressive nomination mode */
+#define NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2(obj) \
+ ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \
+ (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2)
+
struct _NiceAgent
{
GObject parent; /* gobject pointer */
@@ -134,6 +141,7 @@ struct _NiceAgent
guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */
guint stun_initial_timeout; /* property: stun initial timeout, RTO */
guint stun_reliable_timeout; /* property: stun reliable timeout */
+ NiceNominationMode nomination_mode; /* property: Nomination mode */
GSList *local_addresses; /* list of NiceAddresses for local
interfaces */
diff --git a/agent/agent.c b/agent/agent.c
index 25d7886..577a7e0 100644
--- a/agent/agent.c
+++ b/agent/agent.c
@@ -73,6 +73,7 @@
#include "interfaces.h"
#include "pseudotcp.h"
+#include "agent-enum-types.h"
/* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b
* wide. */
@@ -116,6 +117,7 @@ enum
PROP_STUN_MAX_RETRANSMISSIONS,
PROP_STUN_INITIAL_TIMEOUT,
PROP_STUN_RELIABLE_TIMEOUT,
+ PROP_NOMINATION_MODE,
};
@@ -440,6 +442,24 @@ nice_agent_class_init (NiceAgentClass *klass)
G_PARAM_READWRITE));
/**
+ * NiceAgent:nomination-mode:
+ *
+ * The nomination mode used in the ICE specification for describing
+ * the selection of valid pairs to be used upstream.
+ * <para> See also: #NiceNominationMode </para>
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE,
+ g_param_spec_enum (
+ "nomination-mode",
+ "ICE nomination mode",
+ "Nomination mode used in the ICE specification for describing "
+ "the selection of valid pairs to be used upstream",
+ NICE_TYPE_NOMINATION_MODE, NICE_NOMINATION_MODE_AGGRESSIVE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
* NiceAgent:proxy-ip:
*
* The proxy server IP used to bypass a proxy firewall
@@ -1096,6 +1116,7 @@ nice_agent_init (NiceAgent *agent)
agent->stun_server_port = DEFAULT_STUN_PORT;
agent->controlling_mode = TRUE;
agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT;
+ agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE;
agent->discovery_list = NULL;
agent->discovery_unsched_items = 0;
@@ -1144,6 +1165,23 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat)
}
+NICEAPI_EXPORT NiceAgent *
+nice_agent_new_full (GMainContext *ctx,
+ NiceCompatibility compat,
+ gboolean reliable,
+ NiceNominationMode nomination)
+{
+ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
+ "compatibility", compat,
+ "main-context", ctx,
+ "reliable", reliable,
+ "nomination-mode", nomination,
+ NULL);
+
+ return agent;
+}
+
+
static void
nice_agent_get_property (
GObject *object,
@@ -1190,6 +1228,10 @@ nice_agent_get_property (
/* XXX: should we prune the list of already existing checks? */
break;
+ case PROP_NOMINATION_MODE:
+ g_value_set_enum (value, agent->nomination_mode);
+ break;
+
case PROP_PROXY_IP:
g_value_set_string (value, agent->proxy_ip);
break;
@@ -1394,6 +1436,10 @@ nice_agent_set_property (
agent->max_conn_checks = g_value_get_uint (value);
break;
+ case PROP_NOMINATION_MODE:
+ agent->nomination_mode = g_value_get_enum (value);
+ break;
+
case PROP_PROXY_IP:
g_free (agent->proxy_ip);
agent->proxy_ip = g_value_dup_string (value);
@@ -3298,6 +3344,19 @@ static gboolean priv_add_remote_candidate (
username, password, priority);
}
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
+ /* note: If there are TCP candidates for a media stream,
+ * a controlling agent MUST use the regular selection algorithm,
+ * RFC 6544, sect 8, "Concluding ICE Processing"
+ */
+ if (agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE &&
+ transport != NICE_CANDIDATE_TRANSPORT_UDP) {
+ nice_debug ("Agent %p : we have TCP candidates, switching back "
+ "to regular nomination mode", agent);
+ agent->nomination_mode = NICE_NOMINATION_MODE_REGULAR;
+ }
+ }
+
if (base_addr)
candidate->base_addr = *base_addr;
diff --git a/agent/agent.h b/agent/agent.h
index 47c4d5a..6e233c6 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -377,6 +377,26 @@ typedef enum
NICE_PROXY_TYPE_LAST = NICE_PROXY_TYPE_HTTP,
} NiceProxyType;
+/**
+ * NiceNominationMode:
+ * @NICE_NOMINATION_MODE_AGGRESSIVE: Aggressive nomination mode
+ * @NICE_NOMINATION_MODE_REGULAR: Regular nomination mode
+ *
+ * An enum to specity the kind of nomination mode to use by
+ * the agent, as described in RFC 5245. Two modes exists,
+ * regular and aggressive. They differ by the way the controlling
+ * agent chooses to put the USE-CANDIDATE attribute in its STUN
+ * messages. The aggressive mode is supposed to nominate a pair
+ * faster, than the regular mode, potentially causing the nominated
+ * pair to change until the connection check completes.
+ *
+ * Since: UNRELEASED
+ */
+typedef enum
+{
+ NICE_NOMINATION_MODE_REGULAR = 0,
+ NICE_NOMINATION_MODE_AGGRESSIVE,
+} NiceNominationMode;
/**
* NiceAgentRecvFunc:
@@ -429,6 +449,28 @@ NiceAgent *
nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat);
/**
+ * nice_agent_new_full:
+ * @ctx: The Glib Mainloop Context to use for timers
+ * @compat: The compatibility mode of the agent
+ * @reliable: The reliability mode of the agent
+ * @nomination: The nomination mode of the agent
+ *
+ * Create a new #NiceAgent with parameters that must be be defined at
+ * construction time.
+ * The returned object must be freed with g_object_unref()
+ * <para> See also: #NiceNominationMode </para>
+ *
+ * Since: UNRELEASED
+ *
+ * Returns: The new agent GObject
+ */
+NiceAgent *
+nice_agent_new_full (GMainContext *ctx,
+ NiceCompatibility compat,
+ gboolean reliable,
+ NiceNominationMode nomination);
+
+/**
* nice_agent_add_local_address:
* @agent: The #NiceAgent Object
* @addr: The address to listen to
@@ -447,7 +489,6 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat);
gboolean
nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr);
-
/**
* nice_agent_add_stream:
* @agent: The #NiceAgent Object
diff --git a/agent/conncheck.c b/agent/conncheck.c
index 7fc2a1d..6827e6e 100644
--- a/agent/conncheck.c
+++ b/agent/conncheck.c
@@ -502,7 +502,62 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
if (s_nominated < stream->n_components &&
s_waiting_for_nomination) {
keep_timer_going = TRUE;
- if (agent->controlling_mode) {
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
+ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR &&
+ agent->controlling_mode &&
+ ((waiting == 0 && s_inprogress == 0) ||
+ (s_succeeded + s_discovered) >= 5 * stream->n_components)){
+ /* ICE 8.1.1.1 Regular nomination
+ * we choose to nominate the valid pair if
+ * there is no pair left waiting or in-progress or
+ * if there are at least 5 valid pairs per stream on average.
+ *
+ * This is the "stopping criterion" described in 8.1.1.1, and is
+ * a "local optimization" between accumulating more valid pairs,
+ * and limiting the time spent waiting for in-progress connections
+ * checks until they finally fail.
+ */
+ GSList *component_item;
+
+ for (component_item = stream->components; component_item;
+ component_item = component_item->next) {
+ NiceComponent *component = component_item->data;
+ gboolean already_done = FALSE;
+
+ /* verify that the choice of the pair to be nominated
+ * has not already been done
+ */
+ for (k = stream->conncheck_list; k ; k = k->next) {
+ CandidateCheckPair *p = k->data;
+ if (p->component_id == component->id &&
+ p->use_candidate_on_next_check) {
+ already_done = TRUE;
+ break;
+ }
+ }
+
+ /* choose a pair to be nominated in the list of valid
+ * pairs, and add it to the triggered checks list
+ */
+ if (!already_done) {
+ for (k = stream->conncheck_list; k ; k = k->next) {
+ CandidateCheckPair *p = k->data;
+ /* note: highest priority item selected (list always sorted) */
+ if (p->component_id == component->id &&
+ !p->nominated &&
+ !p->use_candidate_on_next_check &&
+ p->valid) {
+ nice_debug ("Agent %p : restarting check %p with "
+ "USE-CANDIDATE attrib (regular nomination)", agent, p);
+ p->use_candidate_on_next_check = TRUE;
+ priv_add_pair_to_triggered_check_queue (agent, p);
+ break; /* move to the next component */
+ }
+ }
+ }
+ }
+ }
+ } else if (agent->controlling_mode) {
GSList *component_item;
for (component_item = stream->components; component_item;
@@ -1571,10 +1626,40 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice
g_assert (component);
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
+ agent->controlling_mode)
+ return;
+
/* step: search for at least one nominated pair */
for (i = stream->conncheck_list; i; i = i->next) {
CandidateCheckPair *pair = i->data;
- if (pair->local == localcand && pair->remote == remotecand) {
+ if (pair->local == localcand && pair->remote == remotecand &&
+ NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
+ /* ICE, 7.2.1.5. Updating the Nominated Flag */
+ /* note: TCP candidates typically produce peer reflexive
+ * candidate, generating a "discovered" pair that can be
+ * nominated.
+ */
+ if (pair->valid) {
+ nice_debug ("Agent %p : marking pair %p (%s) as nominated",
+ agent, pair, pair->foundation);
+ pair->nominated = TRUE;
+ priv_update_selected_pair (agent, component, pair);
+ /* Do not step down to CONNECTED if we're already at state READY*/
+ if (component->state != NICE_COMPONENT_STATE_READY) {
+ /* step: notify the client of a new component state (must be done
+ * before the possible check list state update step */
+ agent_signal_component_state_change (agent,
+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
+ }
+ priv_update_check_list_state_for_ready (agent, stream, component);
+ } else if (pair->state == NICE_CHECK_IN_PROGRESS) {
+ pair->mark_nominated_on_response_arrival = TRUE;
+ nice_debug ("Agent %p : pair %p (%s) is in-progress, "
+ "will be nominated on response receipt.",
+ agent, pair, pair->foundation);
+ }
+ } else if (pair->local == localcand && pair->remote == remotecand) {
nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation);
pair->nominated = TRUE;
if (pair->valid) {
@@ -2174,7 +2259,35 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
pair->prflx_priority, controlling);
}
- if (cand_use)
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
+ switch (agent->nomination_mode) {
+ case NICE_NOMINATION_MODE_REGULAR:
+ {
+ /* We are doing regular nomination, so we set the use-candidate
+ * attrib, when the controlling agent decided which valid pair to
+ * resend with this flag in priv_conn_check_tick_stream()
+ */
+ cand_use = pair->use_candidate_on_next_check;
+ nice_debug ("Agent %p : %s: set cand_use=%d "
+ "(regular nomination).", agent, G_STRFUNC, cand_use);
+ break;
+ }
+ case NICE_NOMINATION_MODE_AGGRESSIVE:
+ {
+ /* We are doing aggressive nomination, we set the use-candidate
+ * attrib in every check we send, when we are the controlling
+ * agent, RFC 5245, 8.1.1.2
+ */
+ cand_use = controlling;
+ nice_debug ("Agent %p : %s: set cand_use=%d "
+ "(aggressive nomination).", agent, G_STRFUNC, cand_use);
+ break;
+ }
+ default:
+ /* Nothing to do. */
+ break;
+ }
+ } else if (cand_use)
pair->nominated = controlling;
if (uname_len > 0) {
@@ -2781,12 +2894,66 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre
local_candidate, remote_candidate);
}
-
+ /* Note: this assignment helps to reduce the numbers of cases
+ * to be tested. If ok_pair and p refer to distinct pairs, it
+ * means that ok_pair is a discovered peer reflexive one,
+ * caused by the check made on pair p. In that case, the
+ * flags to be tested are on p, but the nominated flag will be
+ * set on ok_pair. When there's no discovered pair, p and
+ * ok_pair refer to the same pair.
+ * To summarize : p is a SUCCEEDED pair, ok_pair is a
+ * DISCOVERED, VALID, and eventually NOMINATED pair.
+ */
if (!ok_pair)
ok_pair = p;
/* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
Nominated Flag" (ID-19) */
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
+ nice_debug ("Agent %p : Updating nominated flag (%s): "
+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)",
+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ?
+ "UDP" : "TCP",
+ ok_pair, ok_pair->use_candidate_on_next_check,
+ ok_pair->mark_nominated_on_response_arrival,
+ p, p->use_candidate_on_next_check,
+ p->mark_nominated_on_response_arrival);
+
+ if (agent->controlling_mode) {
+ switch (agent->nomination_mode) {
+ case NICE_NOMINATION_MODE_REGULAR:
+ if (p->use_candidate_on_next_check) {
+ nice_debug ("Agent %p : marking pair %p (%s) as nominated "
+ "(regular nomination, control=1, "
+ "use_cand_on_next_check=1).",
+ agent, ok_pair, ok_pair->foundation);
+ ok_pair->nominated = TRUE;
+ }
+ break;
+ case NICE_NOMINATION_MODE_AGGRESSIVE:
+ if (!p->nominated) {
+ nice_debug ("Agent %p : marking pair %p (%s) as nominated "
+ "(aggressive nomination, control=1).",
+ agent, ok_pair, ok_pair->foundation);
+ ok_pair->nominated = TRUE;
+ }
+ break;
+ default:
+ /* Nothing to do */
+ break;
+ }
+ } else {
+ if (p->mark_nominated_on_response_arrival) {
+ nice_debug ("Agent %p : marking pair %p (%s) as nominated "
+ "(%s nomination, control=0, mark_on_response=1).",
+ agent, ok_pair, ok_pair->foundation,
+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
+ "aggressive" : "regular");
+ ok_pair->nominated = TRUE;
+ }
+ }
+ }
+
if (ok_pair->nominated == TRUE) {
priv_update_selected_pair (agent, component, ok_pair);
priv_print_conn_check_lists (agent, G_STRFUNC,
@@ -3668,8 +3835,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
stun_usage_ice_conncheck_use_candidate (&req);
uint32_t priority = stun_usage_ice_conncheck_priority (&req);
- if (agent->controlling_mode ||
- agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
agent->compatibility == NICE_COMPATIBILITY_MSN ||
agent->compatibility == NICE_COMPATIBILITY_OC2007)
use_candidate = TRUE;
diff --git a/agent/conncheck.h b/agent/conncheck.h
index c204475..0f988de 100644
--- a/agent/conncheck.h
+++ b/agent/conncheck.h
@@ -87,6 +87,8 @@ struct _CandidateCheckPair
gboolean nominated;
gboolean timer_restarted;
gboolean valid;
+ gboolean use_candidate_on_next_check;
+ gboolean mark_nominated_on_response_arrival;
guint64 priority;
guint32 prflx_priority;
GTimeVal next_tick; /* next tick timestamp */
diff --git a/configure.ac b/configure.ac
index 98bbc08..6c106ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,7 @@ AC_PROG_CC
AM_PROG_AR
LT_PREREQ([2.2.6])
LT_INIT([dlopen win32-dll disable-static])
+AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums])
# Check Operating System
AC_MSG_CHECKING([operating system])
diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt
index 88a6cd2..a481106 100644
--- a/docs/reference/libnice/libnice-sections.txt
+++ b/docs/reference/libnice/libnice-sections.txt
@@ -5,6 +5,7 @@ NiceAgent
NiceComponentState
NiceComponentType
NiceProxyType
+NiceNominationMode
NiceCompatibility
NiceAgentRecvFunc
NiceInputMessage
@@ -12,6 +13,7 @@ NiceOutputMessage
NICE_AGENT_MAX_REMOTE_CANDIDATES
nice_agent_new
nice_agent_new_reliable
+nice_agent_new_full
nice_agent_add_local_address
nice_agent_set_port_range
nice_agent_add_stream