summaryrefslogtreecommitdiff
path: root/res/res_pjsip
diff options
context:
space:
mode:
authorGeorge Joseph <george.joseph@fairview5.com>2016-02-11 10:01:05 -0700
committerGeorge Joseph <george.joseph@fairview5.com>2016-02-19 17:56:27 -0700
commitd2a1457e0b4ecdd512fe58fdb55ecc07fd141bea (patch)
treedb97ca4b365f3bd4bad2110c69a0e4ff9fe268a7 /res/res_pjsip
parentb4fdf93d06753c580b4ef7e34fe07670a8e4aff4 (diff)
res_pjsip/config_transport: Allow reloading transports.
The 'reload' mechanism actually involves closing the underlying socket and calling the appropriate udp, tcp or tls start functions again. Only outbound_registration, pubsub and session needed work to reset the transport before sending requests to insure that the pjsip transport didn't get pulled out from under them. In my testing, no calls were dropped when a transport was changed for any of the 3 transport types even if ip addresses or ports were changed. To be on the safe side however, a new transport option was added (allow_reload) which defaults to 'no'. Unless it's explicitly set to 'yes' for a transport, changes to that transport will be ignored on a reload of res_pjsip. This should preserve the current behavior. Change-Id: I5e759850e25958117d4c02f62ceb7244d7ec9edf
Diffstat (limited to 'res/res_pjsip')
-rw-r--r--res/res_pjsip/config_transport.c139
1 files changed, 110 insertions, 29 deletions
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index 0fcd7d923..e7bda5f05 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -18,6 +18,7 @@
#include "asterisk.h"
+#include <math.h>
#include <pjsip.h>
#include <pjlib.h>
@@ -347,6 +348,44 @@ static void copy_state_to_transport(struct ast_sip_transport *transport)
memcpy(&transport->external_address, &transport->state->external_address, sizeof(transport->external_address));
}
+static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_transport_state *b)
+{
+ if (a->type != b->type) {
+ return -1;
+ }
+
+ if (pj_sockaddr_cmp(&a->host, &b->host)) {
+ return -1;
+ }
+
+ if ((a->localnet || b->localnet)
+ && ((!a->localnet != !b->localnet)
+ || ast_sockaddr_cmp(&a->localnet->addr, &b->localnet->addr)
+ || ast_sockaddr_cmp(&a->localnet->netmask, &b->localnet->netmask)))
+ {
+ return -1;
+ }
+
+ if (ast_sockaddr_cmp(&a->external_address, &b->external_address)) {
+ return -1;
+ }
+
+ if (a->tls.method != b->tls.method
+ || a->tls.ciphers_num != b->tls.ciphers_num
+ || a->tls.proto != b->tls.proto
+ || a->tls.verify_client != b->tls.verify_client
+ || a->tls.verify_server != b->tls.verify_server
+ || a->tls.require_client_cert != b->tls.require_client_cert) {
+ return -1;
+ }
+
+ if (memcmp(a->ciphers, b->ciphers, sizeof(pj_ssl_cipher) * fmax(a->tls.ciphers_num, b->tls.ciphers_num))) {
+ return -1;
+ }
+
+ return 0;
+}
+
static void states_cleanup(void *states)
{
if (states) {
@@ -364,6 +403,9 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup);
RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
pj_status_t res = -1;
+ int i;
+#define BIND_TRIES 3
+#define BIND_DELAY_US 100000
if (!states) {
return -1;
@@ -376,32 +418,39 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
*/
ao2_wrlock(states);
+ temp_state = internal_state_alloc(transport);
+ if (!temp_state) {
+ ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
+ return -1;
+ }
+
perm_state = find_internal_state_by_transport(transport);
if (perm_state) {
ast_sorcery_diff(sorcery, perm_state->transport, transport, &changes);
- if (changes) {
+ if (!changes && !has_state_changed(perm_state->state, temp_state->state)) {
+ /* In case someone is using the deprecated fields, reset them */
+ transport->state = perm_state->state;
+ copy_state_to_transport(transport);
+ ao2_replace(perm_state->transport, transport);
+ return 0;
+ }
+
+ if (!transport->allow_reload) {
if (!perm_state->change_detected) {
perm_state->change_detected = 1;
ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id);
}
+ /* In case someone is using the deprecated fields, reset them */
+ transport->state = perm_state->state;
+ copy_state_to_transport(transport);
+ ao2_replace(perm_state->transport, transport);
+ return 0;
}
-
- /* In case someone is using the deprecated fields, reset them */
- transport->state = perm_state->state;
- copy_state_to_transport(transport);
- ao2_replace(perm_state->transport, transport);
- return 0;
- }
-
- temp_state = internal_state_alloc(transport);
- if (!temp_state) {
- ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
- goto error;
}
if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) {
ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id);
- goto error;
+ return -1;
}
/* Set default port if not present */
@@ -418,20 +467,33 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
} else {
ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
transport_id);
- goto error;
+ return -1;
}
if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) {
ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id);
- goto error;
+ return -1;
}
}
if (transport->type == AST_TRANSPORT_UDP) {
- if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
- res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv4, NULL, transport->async_operations, &temp_state->state->transport);
- } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
- res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv6, NULL, transport->async_operations, &temp_state->state->transport);
+
+ for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+ if (perm_state && perm_state->state && perm_state->state->transport) {
+ pjsip_udp_transport_pause(perm_state->state->transport,
+ PJSIP_UDP_TRANSPORT_DESTROY_SOCKET);
+ usleep(BIND_DELAY_US);
+ }
+
+ if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
+ res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(),
+ &temp_state->state->host.ipv4, NULL, transport->async_operations,
+ &temp_state->state->transport);
+ } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
+ res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(),
+ &temp_state->state->host.ipv6, NULL, transport->async_operations,
+ &temp_state->state->transport);
+ }
}
if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
@@ -451,18 +513,37 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
cfg.async_cnt = transport->async_operations;
set_qos(transport, &cfg.qos_params);
- res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &temp_state->state->factory);
+ for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+ if (perm_state && perm_state->state && perm_state->state->factory
+ && perm_state->state->factory->destroy) {
+ perm_state->state->factory->destroy(perm_state->state->factory);
+ usleep(BIND_DELAY_US);
+ }
+
+ res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg,
+ &temp_state->state->factory);
+ }
} else if (transport->type == AST_TRANSPORT_TLS) {
if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) {
ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n",
ast_sorcery_object_get_id(obj));
- goto error;
+ return -1;
}
temp_state->state->tls.password = pj_str((char*)transport->password);
set_qos(transport, &temp_state->state->tls.qos_params);
- res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, &temp_state->state->host, NULL, transport->async_operations, &temp_state->state->factory);
+ for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+ if (perm_state && perm_state->state && perm_state->state->factory
+ && perm_state->state->factory->destroy) {
+ perm_state->state->factory->destroy(perm_state->state->factory);
+ usleep(BIND_DELAY_US);
+ }
+
+ res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls,
+ &temp_state->state->host, NULL, transport->async_operations,
+ &temp_state->state->factory);
+ }
} else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
if (transport->cos || transport->tos) {
ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
@@ -475,17 +556,16 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
pj_strerror(res, msg, sizeof(msg));
ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
- goto error;
+ return -1;
}
copy_state_to_transport(transport);
- ao2_link(states, temp_state);
+ if (perm_state) {
+ ao2_unlink_flags(states, perm_state, OBJ_NOLOCK);
+ }
+ ao2_link_flags(states, temp_state, OBJ_NOLOCK);
return 0;
-
-error:
- ao2_unlink(states, temp_state);
- return -1;
}
/*! \brief Custom handler for type just makes sure the state is created */
@@ -1209,6 +1289,7 @@ int ast_sip_initialize_sorcery_transport(void)
ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos));
ast_sorcery_object_field_register(sorcery, "transport", "websocket_write_timeout", AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, write_timeout), 1, INT_MAX);
+ ast_sorcery_object_field_register(sorcery, "transport", "allow_reload", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_transport, allow_reload));
internal_sip_register_endpoint_formatter(&endpoint_transport_formatter);