summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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