summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Bright <sean.bright@gmail.com>2016-01-27 11:44:10 -0500
committerSean Bright <sean.bright@gmail.com>2016-02-03 18:02:09 -0500
commit4e8e6d3922303711335bee677676c4cddc18dd5d (patch)
tree3ab85db11938a3de00df6f02711d87383568a305
parentf55f79edbaa9066f2ffb36c9a7c2433357698615 (diff)
res_rtp_asterisk: Allow ICE host candidates to be overriden
During ICE negotiation the IPs of the local interfaces are sent to the remote peer as host candidates. In many cases Asterisk is behind a static one-to-one NAT, so these host addresses will be internal IP addresses. To help in hiding the topology of the internal network, this patch adds the ability to override the host candidates by matching them against a user-defined list of replacements. Change-Id: I1c9541af97b83a4c690c8150d19bf7202c8bff1f
-rw-r--r--CHANGES7
-rw-r--r--configs/samples/rtp.conf.sample27
-rw-r--r--res/res_rtp_asterisk.c81
3 files changed, 115 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
index 925911189..4a5ae2cf3 100644
--- a/CHANGES
+++ b/CHANGES
@@ -69,6 +69,13 @@ res_pjsip_sdp_rtp
originate from the media address instead of the operating system's "primary"
ip address.
+res_rtp_asterisk
+------------------
+ * A new configuration section - ice_host_candidates - has been added to
+ rtp.conf, allowing automatically discovered ICE host candidates to be
+ overriden. This allows an Asterisk server behind a 1:1 NAT to send its
+ external IP as a host candidate rather than relying on STUN to discover it.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.6.0 to Asterisk 13.7.0 ------------
------------------------------------------------------------------------------
diff --git a/configs/samples/rtp.conf.sample b/configs/samples/rtp.conf.sample
index c22acaa9f..2ef5dd28a 100644
--- a/configs/samples/rtp.conf.sample
+++ b/configs/samples/rtp.conf.sample
@@ -58,3 +58,30 @@ rtpend=20000
;
; Password used to authenticate with TURN relay server.
; turnpassword=
+;
+[ice_host_candidates]
+;
+; When Asterisk is behind a static one-to-one NAT and ICE is in use, ICE will
+; expose the server's internal IP address as one of the host candidates.
+; Although using STUN (see the 'stunaddr' configuration option) will provide a
+; publicly accessible IP, the internal IP will still be sent to the remote
+; peer. To help hide the topology of your internal network, you can override
+; the host candidates that Asterisk will send to the remote peer.
+;
+; IMPORTANT: Only use this functionality when your Asterisk server is behind a
+; one-to-one NAT and you know what you're doing. If you do define anything
+; here, you almost certainly will NOT want to specify 'stunaddr' or 'turnaddr'
+; above.
+;
+; The format for these overrides is:
+;
+; <local address> => <advertised address>
+;
+; The following will replace 192.168.1.10 with 1.2.3.4 during ICE
+; negotiation:
+;
+;192.168.1.10 => 1.2.3.4
+;
+; You can define an override for more than 1 interface if you have a multihomed
+; server. Any local interface that is not matched will be passed through
+; unaltered. Both IPv4 and IPv6 addresses are supported.
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index f6bf34211..611920e80 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -182,6 +182,16 @@ struct ast_rtp_ioqueue_thread {
/*! \brief List of ioqueue threads */
static AST_LIST_HEAD_STATIC(ioqueues, ast_rtp_ioqueue_thread);
+/*! \brief Structure which contains ICE host candidate mapping information */
+struct ast_ice_host_candidate {
+ pj_sockaddr local;
+ pj_sockaddr advertised;
+ AST_RWLIST_ENTRY(ast_ice_host_candidate) next;
+};
+
+/*! \brief List of ICE host candidate mappings */
+static AST_RWLIST_HEAD_STATIC(host_candidates, ast_ice_host_candidate);
+
#endif
#define FLAG_3389_WARNING (1 << 0)
@@ -451,6 +461,38 @@ static void dtls_srtp_stop_timeout_timer(struct ast_rtp_instance *instance, stru
static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp);
#ifdef HAVE_PJPROJECT
+/*! \brief Helper function which clears the ICE host candidate mapping */
+static void host_candidate_overrides_clear(void)
+{
+ struct ast_ice_host_candidate *candidate;
+
+ AST_RWLIST_WRLOCK(&host_candidates);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&host_candidates, candidate, next) {
+ AST_RWLIST_REMOVE_CURRENT(next);
+ ast_free(candidate);
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&host_candidates);
+}
+
+/*! \brief Applies the ICE host candidate mapping */
+static void host_candidate_overrides_apply(unsigned int count, pj_sockaddr addrs[])
+{
+ int pos;
+ struct ast_ice_host_candidate *candidate;
+
+ AST_RWLIST_RDLOCK(&host_candidates);
+ for (pos = 0; pos < count; pos++) {
+ AST_LIST_TRAVERSE(&host_candidates, candidate, next) {
+ if (!pj_sockaddr_cmp(&candidate->local, &addrs[pos])) {
+ pj_sockaddr_copy_addr(&addrs[pos], &candidate->advertised);
+ break;
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&host_candidates);
+}
+
/*! \brief Helper function which updates an ast_sockaddr with the candidate used for the component */
static void update_address_with_ice_candidate(struct ast_rtp *rtp, enum ast_rtp_ice_component_type component,
struct ast_sockaddr *cand_address)
@@ -2368,6 +2410,8 @@ static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct
pj_enum_ip_interface(pj_AF_INET6(), &count, address);
}
+ host_candidate_overrides_apply(count, address);
+
for (pos = 0; pos < count; pos++) {
pj_sockaddr_set_port(&address[pos], port);
ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_HOST, 65535, &address[pos], &address[pos], NULL,
@@ -5257,6 +5301,11 @@ static int rtp_reload(int reload)
const char *s;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+#ifdef HAVE_PJPROJECT
+ struct ast_variable *var;
+ struct ast_ice_host_candidate *candidate;
+#endif
+
cfg = ast_config_load2("rtp.conf", "rtp", config_flags);
if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
return 0;
@@ -5283,6 +5332,7 @@ static int rtp_reload(int reload)
turnaddr = pj_str(NULL);
turnusername = pj_str(NULL);
turnpassword = pj_str(NULL);
+ host_candidate_overrides_clear();
#endif
if (cfg) {
@@ -5362,6 +5412,36 @@ static int rtp_reload(int reload)
if ((s = ast_variable_retrieve(cfg, "general", "turnpassword"))) {
pj_strdup2_with_null(pool, &turnpassword, s);
}
+
+ AST_RWLIST_WRLOCK(&host_candidates);
+ for (var = ast_variable_browse(cfg, "ice_host_candidates"); var; var = var->next) {
+ struct ast_sockaddr local_addr, advertised_addr;
+ pj_str_t address;
+
+ ast_sockaddr_setnull(&local_addr);
+ ast_sockaddr_setnull(&advertised_addr);
+
+ if (ast_parse_arg(var->name, PARSE_ADDR | PARSE_PORT_IGNORE, &local_addr)) {
+ ast_log(LOG_WARNING, "Invalid local ICE host address: %s\n", var->name);
+ continue;
+ }
+
+ if (ast_parse_arg(var->value, PARSE_ADDR | PARSE_PORT_IGNORE, &advertised_addr)) {
+ ast_log(LOG_WARNING, "Invalid advertised ICE host address: %s\n", var->value);
+ continue;
+ }
+
+ if (!(candidate = ast_calloc(1, sizeof(*candidate)))) {
+ ast_log(LOG_ERROR, "Failed to allocate ICE host candidate mapping.\n");
+ break;
+ }
+
+ pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&address, ast_sockaddr_stringify(&local_addr)), &candidate->local);
+ pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&address, ast_sockaddr_stringify(&advertised_addr)), &candidate->advertised);
+
+ AST_RWLIST_INSERT_TAIL(&host_candidates, candidate, next);
+ }
+ AST_RWLIST_UNLOCK(&host_candidates);
#endif
ast_config_destroy(cfg);
}
@@ -5464,6 +5544,7 @@ static int unload_module(void)
ast_cli_unregister_multiple(cli_rtp, ARRAY_LEN(cli_rtp));
#ifdef HAVE_PJPROJECT
+ host_candidate_overrides_clear();
pj_thread_register_check();
rtp_terminate_pjproject();
#endif