#define AST_STIR_SHAKEN_RESPONSE_STR_UNSUPPORTED_CREDENTIAL "Unsupported Credential"
#define AST_STIR_SHAKEN_RESPONSE_STR_INVALID_IDENTITY_HEADER "Invalid Identity Header"
+/* ":12345" */
+#define COLON_PORT_STRLEN 6
+/*
+ * "<ipaddr>:<port>"
+ * PJ_INET6_ADDRSTRLEN includes the NULL terminator
+ */
+#define IP6ADDR_COLON_PORT_BUFLEN (PJ_INET6_ADDRSTRLEN + COLON_PORT_STRLEN)
+
+/*!
+ * \brief Fill a buffer with a pjsip transport's remote ip address and port
+ *
+ * \param transport The pjsip_transport to use
+ * \param dest The destination buffer of at least IP6ADDR_COLON_PORT_BUFLEN bytes
+ */
+#define AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(_transport, _dest) \
+ snprintf(_dest, IP6ADDR_COLON_PORT_BUFLEN, \
+ PJSTR_PRINTF_SPEC ":%d", \
+ PJSTR_PRINTF_VAR(_transport->remote_name.host), \
+ _transport->remote_name.port);
+
/* Forward declarations of PJSIP stuff */
struct pjsip_rx_data;
struct pjsip_module;
/*!
* \brief Register a reliable transport shutdown monitor callback.
+ * \deprecated Replaced with ast_sip_transport_monitor_register_key().
* \since 13.20.0
*
* \param transport Transport to monitor for shutdown.
enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
ast_transport_monitor_shutdown_cb cb, void *ao2_data);
+/*!
+ * \brief Register a reliable transport shutdown monitor callback.
+ *
+ * \param transport_key Key for the transport to monitor for shutdown.
+ * Create the key with AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR.
+ * \param cb Who to call when transport is shutdown.
+ * \param ao2_data Data to pass with the callback.
+ *
+ * \note The data object passed will have its reference count automatically
+ * incremented by this call and automatically decremented after the callback
+ * runs or when the callback is unregistered.
+ *
+ * There is no checking for duplicate registrations.
+ *
+ * \return enum ast_transport_monitor_reg
+ */
+enum ast_transport_monitor_reg ast_sip_transport_monitor_register_key(
+ const char *transport_key, ast_transport_monitor_shutdown_cb cb,
+ void *ao2_data);
+
/*!
* \brief Register a reliable transport shutdown monitor callback replacing any duplicate.
+ * \deprecated Replaced with ast_sip_transport_monitor_register_replace_key().
* \since 13.26.0
* \since 16.3.0
*
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace(pjsip_transport *transport,
ast_transport_monitor_shutdown_cb cb, void *ao2_data, ast_transport_monitor_data_matcher matches);
+/*!
+ * \brief Register a reliable transport shutdown monitor callback replacing any duplicate.
+ *
+ * \param transport_key Key for the transport to monitor for shutdown.
+ * Create the key with AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR.
+ * \param cb Who to call when transport is shutdown.
+ * \param ao2_data Data to pass with the callback.
+ * \param matches Matcher function that returns true if data matches a previously
+ * registered data object
+ *
+ * \note The data object passed will have its reference count automatically
+ * incremented by this call and automatically decremented after the callback
+ * runs or when the callback is unregistered.
+ *
+ * This function checks for duplicates, and overwrites/replaces the old monitor
+ * with the given one.
+ *
+ * \return enum ast_transport_monitor_reg
+ */
+enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace_key(
+ const char *transport_key, ast_transport_monitor_shutdown_cb cb,
+ void *ao2_data, ast_transport_monitor_data_matcher matches);
+
/*!
* \brief Unregister a reliable transport shutdown monitor
+ * \deprecated Replaced with ast_sip_transport_monitor_unregister_key().
* \since 13.20.0
*
* \param transport Transport to monitor for shutdown.
void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches);
+/*!
+ * \brief Unregister a reliable transport shutdown monitor
+ *
+ * \param transport_key Key for the transport to monitor for shutdown.
+ * Create the key with AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR.
+ * \param cb The callback that was used for the original register.
+ * \param data Data to pass to the matcher. May be NULL and does NOT need to be an ao2 object.
+ * If NULL, all monitors with the provided callback are unregistered.
+ * \param matches Matcher function that returns true if data matches the previously
+ * registered data object. If NULL, a simple pointer comparison is done.
+ *
+ * \note The data object passed into the original register will have its reference count
+ * automatically decremented.
+ */
+void ast_sip_transport_monitor_unregister_key(const char *transport_key,
+ ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches);
+
/*!
* \brief Unregister a transport shutdown monitor from all reliable transports
* \since 13.20.0
#include "asterisk.h"
#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_cli.h"
#include "include/res_pjsip_private.h"
#include "asterisk/linkedlists.h"
#include "asterisk/vector.h"
/*! \brief Structure for transport to be monitored */
struct transport_monitor {
+ /*! \brief Key <ipaddr>:<port> */
+ char key[IP6ADDR_COLON_PORT_BUFLEN];
/*! \brief The underlying PJSIP transport */
pjsip_transport *transport;
+ /*! For debugging purposes, we save the obj_name
+ * in case the transport goes away.
+ */
+ char *transport_obj_name;
/*! Who is interested in when this transport shuts down. */
AST_VECTOR(, struct transport_monitor_notifier) monitors;
};
/*! List of registered transport state callbacks. */
static AST_RWLIST_HEAD(, ast_sip_tpmgr_state_callback) transport_state_list;
-
/*! \brief Hashing function for struct transport_monitor */
-AO2_STRING_FIELD_HASH_FN(transport_monitor, transport->obj_name);
+AO2_STRING_FIELD_HASH_FN(transport_monitor, key);
/*! \brief Comparison function for struct transport_monitor */
-AO2_STRING_FIELD_CMP_FN(transport_monitor, transport->obj_name);
+AO2_STRING_FIELD_CMP_FN(transport_monitor, key);
+
+/*! \brief Sort function for struct transport_monitor */
+AO2_STRING_FIELD_SORT_FN(transport_monitor, key);
static const char *transport_state2str(pjsip_transport_state state)
{
ao2_cleanup(notifier->data);
}
AST_VECTOR_FREE(&monitored->monitors);
+ ast_debug(3, "Transport %s(%s,%s) RefCnt: %ld : state:MONITOR_DESTROYED\n",
+ monitored->key, monitored->transport->obj_name,
+ monitored->transport->type_name,pj_atomic_get(monitored->transport->ref_cnt));
+ ast_free(monitored->transport_obj_name);
+ pjsip_transport_dec_ref(monitored->transport);
}
/*!
static void transport_state_do_reg_callbacks(struct ao2_container *transports, pjsip_transport *transport)
{
struct transport_monitor *monitored;
+ char key[IP6ADDR_COLON_PORT_BUFLEN];
+
+ AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(transport, key);
- monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_UNLINK);
+ monitored = ao2_find(transports, key, OBJ_SEARCH_KEY | OBJ_UNLINK);
if (monitored) {
int idx;
struct transport_monitor_notifier *notifier;
notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
- ast_debug(3, "running callback %p(%p) for transport %s\n",
- notifier->cb, notifier->data, transport->obj_name);
+ ast_debug(3, "Transport %s(%s,%s) RefCnt: %ld : running callback %p(%p)\n",
+ monitored->key, monitored->transport->obj_name,
+ monitored->transport->type_name,
+ pj_atomic_get(monitored->transport->ref_cnt), notifier->cb, notifier->data);
notifier->cb(notifier->data);
}
ao2_ref(monitored, -1);
&& (transports = ao2_global_obj_ref(active_transports))) {
struct transport_monitor *monitored;
- ast_debug(3, "Reliable transport '%s' state:%s\n",
- transport->obj_name, transport_state2str(state));
+ ast_debug(3, "Transport " PJSTR_PRINTF_SPEC ":%d(%s,%s): RefCnt: %ld state:%s\n",
+ PJSTR_PRINTF_VAR(transport->remote_name.host),
+ transport->remote_name.port, transport->obj_name,
+ transport->type_name,
+ pj_atomic_get(transport->ref_cnt), transport_state2str(state));
switch (state) {
case PJSIP_TP_STATE_CONNECTED:
if (PJSIP_TRANSPORT_IS_SECURE(transport) &&
break;
}
monitored->transport = transport;
+ AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(transport, monitored->key);
+ monitored->transport_obj_name = ast_strdup(transport->obj_name);
+
if (AST_VECTOR_INIT(&monitored->monitors, 5)) {
ao2_ref(monitored, -1);
break;
}
+ pjsip_transport_add_ref(monitored->transport);
+ ast_debug(3, "Transport %s(%s,%s): RefCnt: %ld state:MONITOR_CREATED\n",
+ monitored->key, monitored->transport_obj_name,
+ monitored->transport->type_name,
+ pj_atomic_get(monitored->transport->ref_cnt));
ao2_link(transports, monitored);
ao2_ref(monitored, -1);
|| cb_data->matches(cb_data->data, notifier->data))) {
ao2_cleanup(notifier->data);
AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
- ast_debug(3, "Unregistered monitor %p(%p) from transport %s\n",
- notifier->cb, notifier->data, monitored->transport->obj_name);
+ ast_debug(3, "Transport %s(%s,%s) RefCnt: %ld : Unregistered monitor %p(%p)\n",
+ monitored->key, monitored->transport_obj_name,
+ monitored->transport->type_name,
+ pj_atomic_get(monitored->transport->ref_cnt), notifier->cb, notifier->data);
}
}
return 0;
void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
+{
+ char key[IP6ADDR_COLON_PORT_BUFLEN];
+ AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(transport, key);
+ ast_sip_transport_monitor_unregister_key(key, cb, data, matches);
+}
+
+void ast_sip_transport_monitor_unregister_key(const char *transport_key,
+ ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
{
struct ao2_container *transports;
struct transport_monitor *monitored;
- ast_assert(transport != NULL && cb != NULL);
+ ast_assert(transport_key != NULL && cb != NULL);
transports = ao2_global_obj_ref(active_transports);
if (!transports) {
}
ao2_lock(transports);
- monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ monitored = ao2_find(transports, transport_key, OBJ_SEARCH_KEY | OBJ_NOLOCK);
if (monitored) {
struct callback_data cb_data = {
.cb = cb,
enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
ast_transport_monitor_shutdown_cb cb, void *ao2_data)
{
- return ast_sip_transport_monitor_register_replace(transport, cb, ao2_data, NULL);
+ char key[IP6ADDR_COLON_PORT_BUFLEN];
+ AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(transport, key);
+
+ return ast_sip_transport_monitor_register_replace_key(key, cb, ao2_data, NULL);
+}
+
+enum ast_transport_monitor_reg ast_sip_transport_monitor_register_key(const char *transport_key,
+ ast_transport_monitor_shutdown_cb cb, void *ao2_data)
+{
+ return ast_sip_transport_monitor_register_replace_key(transport_key, cb, ao2_data, NULL);
}
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace(pjsip_transport *transport,
ast_transport_monitor_shutdown_cb cb, void *ao2_data, ast_transport_monitor_data_matcher matches)
+{
+ char key[IP6ADDR_COLON_PORT_BUFLEN];
+
+ AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(transport, key);
+ return ast_sip_transport_monitor_register_replace_key(key, cb, ao2_data, NULL);
+}
+
+enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace_key(const char *transport_key,
+ ast_transport_monitor_shutdown_cb cb, void *ao2_data, ast_transport_monitor_data_matcher matches)
{
struct ao2_container *transports;
struct transport_monitor *monitored;
enum ast_transport_monitor_reg res = AST_TRANSPORT_MONITOR_REG_NOT_FOUND;
- ast_assert(transport != NULL && cb != NULL);
+ ast_assert(transport_key != NULL && cb != NULL);
transports = ao2_global_obj_ref(active_transports);
if (!transports) {
}
ao2_lock(transports);
- monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ monitored = ao2_find(transports, transport_key, OBJ_SEARCH_KEY | OBJ_NOLOCK);
if (monitored) {
struct transport_monitor_notifier new_monitor;
struct callback_data cb_data = {
if (AST_VECTOR_APPEND(&monitored->monitors, new_monitor)) {
ao2_cleanup(ao2_data);
res = AST_TRANSPORT_MONITOR_REG_FAILED;
- ast_debug(3, "Register monitor %p(%p) to transport %s FAILED\n",
- cb, ao2_data, transport->obj_name);
+ ast_debug(3, "Transport %s(%s) RefCnt: %ld : Monitor registration failed %p(%p)\n",
+ monitored->key, monitored->transport_obj_name,
+ pj_atomic_get(monitored->transport->ref_cnt), cb, ao2_data);
} else {
res = AST_TRANSPORT_MONITOR_REG_SUCCESS;
- ast_debug(3, "Registered monitor %p(%p) to transport %s\n",
- cb, ao2_data, transport->obj_name);
+ ast_debug(3, "Transport %s(%s,%s) RefCnt: %ld : Registered monitor %p(%p)\n",
+ monitored->key, monitored->transport_obj_name,
+ monitored->transport->type_name,
+ pj_atomic_get(monitored->transport->ref_cnt), cb, ao2_data);
}
ao2_ref(monitored, -1);
AST_RWLIST_UNLOCK(&transport_state_list);
}
+static char *cli_show_monitors(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *cli_rc = CLI_FAILURE;
+ int rc = 0;
+ int using_regex = 0;
+ regex_t regex = { 0, };
+ int container_count;
+ struct ao2_iterator iter;
+ struct ao2_container *sorted_monitors = NULL;
+ struct ao2_container *transports;
+ struct transport_monitor *monitored;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pjsip show transport-monitors";
+ e->usage = "Usage: pjsip show transport-monitors [ like <pattern> ]\n"
+ " Show pjsip transport monitors\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3 && a->argc != 5) {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (a->argc == 5) {
+ int regrc;
+ if (strcasecmp(a->argv[3], "like")) {
+ return CLI_SHOWUSAGE;
+ }
+ regrc = regcomp(®ex, a->argv[4], REG_EXTENDED | REG_ICASE | REG_NOSUB);
+ if (regrc) {
+ char err[256];
+ regerror(regrc, ®ex, err, 256);
+ ast_cli(a->fd, "PJSIP Transport Monitor: Error: %s\n", err);
+ return CLI_FAILURE;
+ }
+ using_regex = 1;
+ }
+
+ /* Get a sorted snapshot of the scheduled tasks */
+ sorted_monitors = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ transport_monitor_sort_fn, NULL);
+ if (!sorted_monitors) {
+ ast_cli(a->fd, "PJSIP Transport Monitor: Unable to allocate temporary container\n");
+ goto error;
+ }
+
+ transports = ao2_global_obj_ref(active_transports);
+ if (!transports) {
+ ast_cli(a->fd, "PJSIP Transport Monitor: Unable to get transports\n");
+ goto error;
+ }
+
+ ao2_lock(transports);
+ rc = ao2_container_dup(sorted_monitors, transports, 0);
+ ao2_unlock(transports);
+ ao2_ref(transports, -1);
+ if (rc != 0) {
+ ast_cli(a->fd, "PJSIP Transport Monitors: Unable to sort temporary container\n");
+ goto error;
+ }
+ container_count = ao2_container_count(sorted_monitors);
+
+ ast_cli(a->fd, "PJSIP Transport Monitors:\n\n");
+
+ ast_cli(a->fd,
+ "<Remote Host...................................> <State.....> <Direction> <RefCnt> <Monitors> <ObjName............>\n");
+
+ iter = ao2_iterator_init(sorted_monitors, AO2_ITERATOR_UNLINK);
+ for (; (monitored = ao2_iterator_next(&iter)); ao2_ref(monitored, -1)) {
+ char *state;
+
+ if (using_regex && regexec(®ex, monitored->key, 0, NULL, 0) == REG_NOMATCH) {
+ continue;
+ }
+
+ if (monitored->transport->is_destroying) {
+ state = "DESTROYING";
+ } else if (monitored->transport->is_shutdown) {
+ state = "SHUTDOWN";
+ } else {
+ state = "ACTIVE";
+ }
+
+ ast_cli(a->fd, " %-46.46s %-10s %-9s %6ld %8" PRIu64 " %s\n",
+ monitored->key, state,
+ monitored->transport->dir == PJSIP_TP_DIR_OUTGOING ? "Outgoing" : "Incoming",
+ pj_atomic_get(monitored->transport->ref_cnt),
+ AST_VECTOR_SIZE(&monitored->monitors), monitored->transport->obj_name);
+ }
+ ao2_iterator_destroy(&iter);
+ ast_cli(a->fd, "\nTotal Transport Monitors: %d\n\n", container_count);
+ cli_rc = CLI_SUCCESS;
+error:
+ if (using_regex) {
+ regfree(®ex);
+ }
+ ao2_cleanup(sorted_monitors);
+
+ return cli_rc;
+}
+
+static struct ast_cli_entry cli_commands[] = {
+ AST_CLI_DEFINE(cli_show_monitors, "Show pjsip transport monitors"),
+};
+
void ast_sip_destroy_transport_events(void)
{
pjsip_tpmgr *tpmgr;
+ ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
+
tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
if (tpmgr) {
pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
}
transports = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
- ACTIVE_TRANSPORTS_BUCKETS, transport_monitor_hash_fn, NULL,
+ ACTIVE_TRANSPORTS_BUCKETS, transport_monitor_hash_fn, transport_monitor_sort_fn,
transport_monitor_cmp_fn);
if (!transports) {
return -1;
tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
pjsip_tpmgr_set_state_cb(tpmgr, &transport_state_callback);
+ ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
+
+
return 0;
}
char src_name[PJ_INET6_ADDRSTRLEN];
/*! Source port of the message */
int src_port;
- /*! Local transport key type */
- char transport_key[32];
+ /*! Local transport type (UDP,TCP,TLS)*/
+ char transport_type[32];
/*! Local transport address */
char local_name[PJ_INET6_ADDRSTRLEN];
/*! Local transport port */
/*! The transport the subscription was received on.
* Only used for reliable transports.
*/
- pjsip_transport *transport;
+ char transport_key[IP6ADDR_COLON_PORT_BUFLEN];
/*! Indicator if initial notify should be generated.
* Used to refresh modified RLS.
*/
rdata->tp_info.transport->obj_name,
sub_tree->persistence->endpoint, sub_tree->root->resource,
sub_tree->persistence->prune_on_boot);
- sub_tree->transport = rdata->tp_info.transport;
- ast_sip_transport_monitor_register(rdata->tp_info.transport,
+ AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(rdata->tp_info.transport,
+ sub_tree->transport_key);
+ ast_sip_transport_monitor_register_key(sub_tree->transport_key,
sub_tree_transport_cb, sub_tree);
/*
* FYI: ast_sip_transport_monitor_register holds a reference to the sub_tree
ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name,
sizeof(sub_tree->persistence->src_name));
sub_tree->persistence->src_port = rdata->pkt_info.src_port;
- ast_copy_string(sub_tree->persistence->transport_key, rdata->tp_info.transport->type_name,
- sizeof(sub_tree->persistence->transport_key));
+ ast_copy_string(sub_tree->persistence->transport_type, rdata->tp_info.transport->type_name,
+ sizeof(sub_tree->persistence->transport_type));
ast_copy_pj_str(sub_tree->persistence->local_name, &rdata->tp_info.transport->local_name.host,
sizeof(sub_tree->persistence->local_name));
sub_tree->persistence->local_port = rdata->tp_info.transport->local_name.port;
return;
}
- if (sub_tree->persistence->prune_on_boot && sub_tree->transport) {
+ if (sub_tree->persistence->prune_on_boot && !ast_strlen_zero(sub_tree->transport_key)) {
ast_debug(3, "Unregistering transport monitor on %s '%s->%s'\n",
- sub_tree->transport->obj_name,
+ sub_tree->transport_key,
sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
sub_tree->root ? sub_tree->root->resource : "Unknown");
- ast_sip_transport_monitor_unregister(sub_tree->transport,
+ ast_sip_transport_monitor_unregister_key(sub_tree->transport_key,
sub_tree_transport_cb, sub_tree, NULL);
}
rdata.tp_info.pool = pool;
if (ast_sip_create_rdata_with_contact(&rdata, persistence->packet, persistence->src_name,
- persistence->src_port, persistence->transport_key, persistence->local_name,
+ persistence->src_port, persistence->transport_type, persistence->local_name,
persistence->local_port, persistence->contact_uri)) {
ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The message could not be parsed\n",
persistence->endpoint);
ast_sorcery_object_field_register(sorcery, "subscription_persistence", "src_port", "0", OPT_UINT_T, 0,
FLDSET(struct subscription_persistence, src_port));
ast_sorcery_object_field_register(sorcery, "subscription_persistence", "transport_key", "0", OPT_CHAR_ARRAY_T, 0,
- CHARFLDSET(struct subscription_persistence, transport_key));
+ CHARFLDSET(struct subscription_persistence, transport_type));
ast_sorcery_object_field_register(sorcery, "subscription_persistence", "local_name", "", OPT_CHAR_ARRAY_T, 0,
CHARFLDSET(struct subscription_persistence, local_name));
ast_sorcery_object_field_register(sorcery, "subscription_persistence", "local_port", "0", OPT_UINT_T, 0,