This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
dexter gerrit-no-reply at lists.osmocom.orgdexter has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-mgw/+/18372 ) Change subject: osmo-mgw: refactor endpoint and trunk handling ...................................................................... osmo-mgw: refactor endpoint and trunk handling The trunk and endpoint handling in osmo-mgw is still very complex and implemented in various placed (mostly mgcp_protocol.c). Also we use still integers for endpoint identification, which is not flexible enough to address timeslots/subslots on an E1 trunk. Some refactoring is needed. - get rid of integers as endpoint identifiers, use strings instead and find the endpoint based on its string name on the trunk. - identify the trunk based on the trunk prefix given in the endpoint name. - rename struct mgcp_trunk_config to mgcp_trunk and "tcfg" to "trunk" in order to better match the reality. - refactor trunk and endpoint allocation. Aggregate functionality in in mgcp_endp.c and mgcp_trunk.c. Change-Id: Ice8aaf03faa2fd99074f8665eea3a696d30c5eb3 Related: OS#2659 --- M include/osmocom/mgcp/Makefile.am M include/osmocom/mgcp/mgcp.h M include/osmocom/mgcp/mgcp_common.h M include/osmocom/mgcp/mgcp_endp.h M include/osmocom/mgcp/mgcp_internal.h A include/osmocom/mgcp/mgcp_trunk.h A include/osmocom/mgcp_client/mgcp_common.h M src/libosmo-mgcp/Makefile.am M src/libosmo-mgcp/mgcp_codec.c M src/libosmo-mgcp/mgcp_conn.c M src/libosmo-mgcp/mgcp_endp.c M src/libosmo-mgcp/mgcp_msg.c M src/libosmo-mgcp/mgcp_network.c M src/libosmo-mgcp/mgcp_osmux.c M src/libosmo-mgcp/mgcp_protocol.c M src/libosmo-mgcp/mgcp_sdp.c A src/libosmo-mgcp/mgcp_trunk.c M src/libosmo-mgcp/mgcp_vty.c M src/osmo-mgw/mgw_main.c M tests/mgcp/mgcp_test.c 20 files changed, 1,054 insertions(+), 782 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-mgw refs/changes/72/18372/1 diff --git a/include/osmocom/mgcp/Makefile.am b/include/osmocom/mgcp/Makefile.am index 036b4ca..0d5f5f6 100644 --- a/include/osmocom/mgcp/Makefile.am +++ b/include/osmocom/mgcp/Makefile.am @@ -7,5 +7,6 @@ mgcp_sdp.h \ mgcp_codec.h \ mgcp_ctrl.h \ + mgcp_trunk.h \ debug.h \ $(NULL) diff --git a/include/osmocom/mgcp/mgcp.h b/include/osmocom/mgcp/mgcp.h index 43f480e..99c12a1 100644 --- a/include/osmocom/mgcp/mgcp.h +++ b/include/osmocom/mgcp/mgcp.h @@ -42,7 +42,7 @@ */ struct mgcp_endpoint; struct mgcp_config; -struct mgcp_trunk_config; +struct mgcp_trunk; struct mgcp_rtp_end; #define MGCP_ENDP_CRCX 1 @@ -59,10 +59,9 @@ #define MGCP_POLICY_REJECT 5 #define MGCP_POLICY_DEFER 6 -typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint); -typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state); -typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id); -typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg); +typedef int (*mgcp_change)(struct mgcp_endpoint *endp, int state); +typedef int (*mgcp_policy)(struct mgcp_endpoint *endp, int state, const char *transaction_id); +typedef int (*mgcp_reset)(struct mgcp_trunk *cfg); typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone); /** @@ -178,57 +177,6 @@ MGCP_DLCX_DEFERRED_BY_POLICY, }; -struct mgcp_trunk_config { - struct llist_head entry; - - struct mgcp_config *cfg; - - int trunk_nr; - int trunk_type; - - char *audio_fmtp_extra; - char *audio_name; - int audio_payload; - int audio_send_ptime; - int audio_send_name; - int audio_loop; - - int no_audio_transcoding; - - int omit_rtcp; - int keepalive_interval; - - /* RTP patching */ - int force_constant_ssrc; /* 0: don't, 1: once */ - int force_aligned_timing; - bool rfc5993_hr_convert; - - /* spec handling */ - int force_realloc; - - /* timer */ - struct osmo_timer_list keepalive_timer; - - /* When set, incoming RTP packets are not filtered - * when ports and ip-address do not match (debug) */ - int rtp_accept_all; - - unsigned int number_endpoints; - int vty_number_endpoints; - struct mgcp_endpoint *endpoints; - - /* Rate counter group which contains stats for generic MGCP events. */ - struct rate_ctr_group *mgcp_general_ctr_group; - /* Rate counter group which contains stats for processed CRCX commands. */ - struct rate_ctr_group *mgcp_crcx_ctr_group; - /* Rate counter group which contains stats for processed MDCX commands. */ - struct rate_ctr_group *mgcp_mdcx_ctr_group; - /* Rate counter group which contains stats for processed DLCX commands. */ - struct rate_ctr_group *mgcp_dlcx_ctr_group; - /* Rate counter group which aggregates stats of individual RTP connections. */ - struct rate_ctr_group *all_rtp_conn_stats; -}; - enum mgcp_role { MGCP_BSC = 0, MGCP_BSC_NAT, @@ -256,14 +204,13 @@ mgcp_change change_cb; mgcp_policy policy_cb; mgcp_reset reset_cb; - mgcp_realloc realloc_cb; mgcp_rqnt rqnt_cb; void *data; uint32_t last_call_id; /* trunk handling */ - struct mgcp_trunk_config trunk; + struct mgcp_trunk *trunk; struct llist_head trunks; enum mgcp_role role; @@ -301,8 +248,7 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, enum mgcp_role role); int mgcp_vty_init(void); -int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); -void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); +void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval); /* * format helper functions @@ -310,7 +256,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg); -int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint); +int mgcp_send_reset_ep(struct mgcp_endpoint *endp); int mgcp_send_reset_all(struct mgcp_config *cfg); diff --git a/include/osmocom/mgcp/mgcp_common.h b/include/osmocom/mgcp/mgcp_common.h index a1bbb19..07d8d37 100644 --- a/include/osmocom/mgcp/mgcp_common.h +++ b/include/osmocom/mgcp/mgcp_common.h @@ -100,6 +100,10 @@ /* A prefix to denote the virtual trunk (RTP on both ends) */ #define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/" +/* A prefix to denote the e1 trunk + * (see also RFC3435 section E.2) */ +#define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-" + /* Maximal number of payload types / codecs that can be negotiated via SDP at * at once. */ #define MGCP_MAX_CODECS 10 diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h index 75f093d..79399a4 100644 --- a/include/osmocom/mgcp/mgcp_endp.h +++ b/include/osmocom/mgcp/mgcp_endp.h @@ -63,6 +63,9 @@ /*! MGCP endpoint model */ struct mgcp_endpoint { + /*! Unique endpoint name, used for addressing via MGCP */ + char *name; + /*! Call identifier string (as supplied by the call agant) */ char *callid; @@ -75,8 +78,8 @@ /*! Backpointer to the MGW configuration */ struct mgcp_config *cfg; - /*! Backpointer to the Trunk specific configuration */ - struct mgcp_trunk_config *tcfg; + /*! Backpointer to the related trunk */ + struct mgcp_trunk *trunk; /*! Endpoint properties (see above) */ const struct mgcp_endpoint_type *type; @@ -95,7 +98,9 @@ uint32_t x_osmo_ign; }; -/*! Extract endpoint number for a given endpoint */ -#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) - +struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, char *name); void mgcp_endp_release(struct mgcp_endpoint *endp); +struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname, + struct mgcp_trunk *trunk); +struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname, + struct mgcp_config *cfg); diff --git a/include/osmocom/mgcp/mgcp_internal.h b/include/osmocom/mgcp/mgcp_internal.h index e9d5d2d..557559f 100644 --- a/include/osmocom/mgcp/mgcp_internal.h +++ b/include/osmocom/mgcp/mgcp_internal.h @@ -282,9 +282,6 @@ return endpoint + 60; } -struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); -struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); - char *get_lco_identifier(const char *options); int check_local_cx_options(void *ctx, const char *options); void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, @@ -342,8 +339,8 @@ void mgcp_conn_watchdog_kick(struct mgcp_conn *conn); #define LOGPENDP(endp, cat, level, fmt, args...) \ -LOGP(cat, level, "endpoint:0x%x " fmt, \ - endp ? ENDPOINT_NUMBER(endp) : -1, \ +LOGP(cat, level, "endpoint:%s " fmt, \ + endp ? endp->name : "none", \ ## args) #define LOGPCONN(conn, cat, level, fmt, args...) \ diff --git a/include/osmocom/mgcp/mgcp_trunk.h b/include/osmocom/mgcp/mgcp_trunk.h new file mode 100644 index 0000000..7c37ead --- /dev/null +++ b/include/osmocom/mgcp/mgcp_trunk.h @@ -0,0 +1,57 @@ +#pragma once + +struct mgcp_trunk { + struct llist_head entry; + + struct mgcp_config *cfg; + + int trunk_nr; + int trunk_type; + + char *audio_fmtp_extra; + char *audio_name; + int audio_payload; + int audio_send_ptime; + int audio_send_name; + int audio_loop; + + int no_audio_transcoding; + + int omit_rtcp; + int keepalive_interval; + + /* RTP patching */ + int force_constant_ssrc; /* 0: don't, 1: once */ + int force_aligned_timing; + bool rfc5993_hr_convert; + + /* spec handling */ + int force_realloc; + + /* timer */ + struct osmo_timer_list keepalive_timer; + + /* When set, incoming RTP packets are not filtered + * when ports and ip-address do not match (debug) */ + int rtp_accept_all; + + unsigned int number_endpoints; + int vty_number_endpoints; + struct mgcp_endpoint **endpoints; + + /* Rate counter group which contains stats for generic MGCP events. */ + struct rate_ctr_group *mgcp_general_ctr_group; + /* Rate counter group which contains stats for processed CRCX commands. */ + struct rate_ctr_group *mgcp_crcx_ctr_group; + /* Rate counter group which contains stats for processed MDCX commands. */ + struct rate_ctr_group *mgcp_mdcx_ctr_group; + /* Rate counter group which contains stats for processed DLCX commands. */ + struct rate_ctr_group *mgcp_dlcx_ctr_group; + /* Rate counter group which aggregates stats of individual RTP connections. */ + struct rate_ctr_group *all_rtp_conn_stats; +}; + +struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr, int trunk_type); +int mgcp_trunk_alloc_endpts(struct mgcp_trunk *tcfg); +struct mgcp_trunk *mgcp_trunk_by_num(struct mgcp_config *cfg, int index); +struct mgcp_trunk *mgcp_trunk_by_name(const char *epname, struct mgcp_config *cfg); diff --git a/include/osmocom/mgcp_client/mgcp_common.h b/include/osmocom/mgcp_client/mgcp_common.h new file mode 100644 index 0000000..058d94a --- /dev/null +++ b/include/osmocom/mgcp_client/mgcp_common.h @@ -0,0 +1,118 @@ +/* + + DO NOT EDIT THIS FILE! + THIS IS OVERWRITTEN DURING BUILD + This is an automatic copy of <osmocom/mgcp/mgcp_common.h> + + */ +/* MGCP common implementations. + * These are used in libosmo-mgcp as well as libosmo-mgcp-client. + * To avoid interdependency, these are implemented in .h file only. */ + +/* + * (C) 2017 by sysmocom s.f.m.c. GmbH <info at sysmocom.de> + * (C) 2009-2012 by Holger Hans Peter Freyther <zecke at selfish.org> + * (C) 2009-2012 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/* Two copies of this file are kept in osmocom/mgcp/ and osmocom/mgcp_client/. + * Since both are by definition identical, use the old header exclusion ifdefs + * instead of '#pragma once' to avoid including both of these files. + * Though at the time of writing there are no such users, this allows including + * both libosmo-mgcp and libosmo-mgcp-client headers in the same file. */ +#ifndef OSMO_MGCP_COMMON_H +#define OSMO_MGCP_COMMON_H + +#include <string.h> +#include <errno.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> + +#define for_each_non_empty_line(line, save) \ + for (line = strtok_r(NULL, "\r\n", &save); line; \ + line = strtok_r(NULL, "\r\n", &save)) + +enum mgcp_connection_mode { + MGCP_CONN_NONE = 0, + MGCP_CONN_RECV_ONLY = 1, + MGCP_CONN_SEND_ONLY = 2, + MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, + MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND, +}; + +#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:" +#define MGCP_X_OSMO_OSMUX_HEADER "X-Osmux:" + +/* Values should be bitwise-OR-able */ +enum mgcp_x_osmo_ign { + MGCP_X_OSMO_IGN_NONE = 0, + MGCP_X_OSMO_IGN_CALLID = 1, +}; + +/* Codec parameters (communicated via SDP/fmtp) */ +struct mgcp_codec_param { + bool amr_octet_aligned_present; + bool amr_octet_aligned; +}; + +/* Ensure that the msg->l2h is NUL terminated. */ +static inline int mgcp_msg_terminate_nul(struct msgb *msg) +{ + unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ + if (tail[-1] == '\0') + /* nothing to do */; + else if (msgb_tailroom(msg) > 0) + tail[0] = '\0'; + else if (tail[-1] == '\r' || tail[-1] == '\n') + tail[-1] = '\0'; + else { + LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " + "Length: %d, Buffer size: %d\n", + msgb_l2len(msg), msg->data_len); + return -ENOTSUP; + } + return 0; +} + +/* Maximum length of the comment field */ +#define MGCP_COMMENT_MAXLEN 256 + +/* Maximum allowed String length of Connection Identifiers as per spec + * (see also RFC3435 2.1.3.2 Names of Connections), plus one for '\0'. */ +#define MGCP_CONN_ID_MAXLEN 32+1 + +/* Deprecated: old name of MGCP_CONN_ID_MAXLEN. */ +#define MGCP_CONN_ID_LENGTH MGCP_CONN_ID_MAXLEN + +/* String length of Endpoint Identifiers. +/ (see also RFC3435 section 3.2.1.3) */ +#define MGCP_ENDPOINT_MAXLEN (255*2+1+1) + +/* A prefix to denote the virtual trunk (RTP on both ends) */ +#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/" + +/* A prefix to denote the e1 trunk + * (see also RFC3435 section E.2) */ +#define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-" + +/* Maximal number of payload types / codecs that can be negotiated via SDP at + * at once. */ +#define MGCP_MAX_CODECS 10 + +#endif diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am index a0c015b..142ff75 100644 --- a/src/libosmo-mgcp/Makefile.am +++ b/src/libosmo-mgcp/Makefile.am @@ -40,5 +40,6 @@ mgcp_conn.c \ mgcp_stat.c \ mgcp_endp.c \ + mgcp_trunk.c \ mgcp_ctrl.c \ $(NULL) diff --git a/src/libosmo-mgcp/mgcp_codec.c b/src/libosmo-mgcp/mgcp_codec.c index 9e55ab0..c251317 100644 --- a/src/libosmo-mgcp/mgcp_codec.c +++ b/src/libosmo-mgcp/mgcp_codec.c @@ -19,6 +19,7 @@ */ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> #include <errno.h> /* Helper function to dump codec information of a specified codec to a printable @@ -292,7 +293,7 @@ * We do not compare to the full audio_name because we expect that * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the * audio name of the codec is set to "GSM" */ - if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1) + if (sscanf(endp->trunk->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1) return false; /* Finally we check if the subtype_name we have generated from the @@ -302,9 +303,9 @@ return true; /* FIXME: It is questinable that the method to pick a compatible - * codec can work properly. Since this useses tcfg->audio_name, as + * codec can work properly. Since this useses trunk->audio_name, as * a reference, which is set to "AMR/8000" permanently. - * tcfg->audio_name must be updated by the first connection that + * trunk->audio_name must be updated by the first connection that * has been made on an endpoint, so that the second connection * can make a meaningful decision here */ @@ -335,7 +336,7 @@ for (i = 0; i < rtp->codecs_assigned; i++) { /* When no transcoding is available, avoid codecs that would * require transcoding. */ - if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) { + if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) { LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n", rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name); continue; diff --git a/src/libosmo-mgcp/mgcp_conn.c b/src/libosmo-mgcp/mgcp_conn.c index 7a86274..6bbcbbc 100644 --- a/src/libosmo-mgcp/mgcp_conn.c +++ b/src/libosmo-mgcp/mgcp_conn.c @@ -25,6 +25,7 @@ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/mgcp_common.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> #include <osmocom/mgcp/mgcp_sdp.h> #include <osmocom/mgcp/mgcp_codec.h> #include <osmocom/gsm/gsm_utils.h> @@ -255,7 +256,7 @@ } static void -aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp) +aggregate_rtp_conn_stats(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp) { struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats; struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group; @@ -296,7 +297,7 @@ switch (conn->type) { case MGCP_CONN_TYPE_RTP: - aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp); + aggregate_rtp_conn_stats(endp->trunk, &conn->u.rtp); mgcp_rtp_conn_cleanup(&conn->u.rtp); break; default: diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c index eec46bf..80eb5b8 100644 --- a/src/libosmo-mgcp/mgcp_endp.c +++ b/src/libosmo-mgcp/mgcp_endp.c @@ -1,7 +1,7 @@ /* Endpoint types */ /* - * (C) 2017 by sysmocom s.f.m.c. GmbH <info at sysmocom.de> + * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info at sysmocom.de> * All Rights Reserved * * Author: Philipp Maier @@ -23,6 +23,7 @@ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> /* Endpoint typeset definition */ const struct mgcp_endpoint_typeset ep_typeset = { @@ -32,6 +33,38 @@ .rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb }; +/*! allocate an endpoint and set default values. + * \param[in] trunk configuration + * \returns endpoint on success, NULL on failure */ +struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, char *name) +{ + struct mgcp_endpoint *endp; + + endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint); + if (!endp) + return NULL; + + INIT_LLIST_HEAD(&endp->conns); + endp->cfg = trunk->cfg; + endp->trunk = trunk; + endp->name = talloc_strdup(endp, name); + + switch (trunk->trunk_type) { + case MGCP_TRUNK_VIRTUAL: + endp->type = &ep_typeset.rtp; + break; + case MGCP_TRUNK_E1: + /* FIXME: Implement E1 allocation */ + LOGP(DLMGCP, LOGL_FATAL, "E1 trunks not implemented!\n"); + break; + default: + osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n", + trunk->trunk_type, __FILE__, __LINE__); + } + + return endp; +} + /*! release endpoint, all open connections are closed. * \param[in] endp endpoint to release */ void mgcp_endp_release(struct mgcp_endpoint *endp) @@ -53,3 +86,183 @@ endp->local_options.codec = NULL; endp->wildcarded_req = false; } + +/* Check if the endpoint name contains the prefix, and chop it off, if it + * exists (per trunk the prefix is the same for all endpoints, so no ambiguity + * is introduced) */ +static const char *chop_endp_prefix(struct mgcp_trunk *trunk, + const char *epname) +{ + size_t prefix_len; + switch (trunk->trunk_type) { + case MGCP_TRUNK_VIRTUAL: + prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1; + if (strncmp + (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, + prefix_len) == 0) + return epname + prefix_len; + case MGCP_TRUNK_E1: + prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1; + if (strncmp + (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, + prefix_len) == 0) + return epname + prefix_len; + } + + return epname; +} + +/* Check for suffixes that can be omitted and return the true length of the + * endpoint name */ +static unsigned int ep_name_len(struct mgcp_trunk *trunk, + const char *epname) +{ + char *suffix_begin; + + switch (trunk->trunk_type) { + case MGCP_TRUNK_VIRTUAL: + suffix_begin = strchr(epname, '@'); + if (!suffix_begin) + return strlen(epname); + return suffix_begin - epname; + case MGCP_TRUNK_E1: + return strlen(epname); + } + + return strlen(epname); +} + +/*! Find an endpoint by its name on a specified trunk. + * \param[out] cause, pointer to store cause code, can be NULL. + * \param[in] epname endpoint name to lookup (may lack trunk prefix and domain name). + * \param[in] trunk where the endpoint is located. + * \returns endpoint or NULL if endpoint was not found. */ +struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname, + struct mgcp_trunk *trunk) +{ + struct mgcp_endpoint *endp; + unsigned int i; + char epname_lc[MGCP_ENDPOINT_MAXLEN]; + const char *epname_ch; + unsigned int epname_ch_len; + + if (cause) + *cause = 0; + + osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname); + epname = epname_lc; + epname_ch = chop_endp_prefix(trunk, epname); + epname_ch_len = ep_name_len(trunk, epname_ch); + + /* At the moment we only support a primitive ('*'-only) method of + * wildarded endpoint searches that picks the next free endpoint on + * a trunk. */ + if (strncmp(epname_ch, "*", epname_ch_len) == 0) { + for (i = 0; i < trunk->number_endpoints; i++) { + endp = trunk->endpoints[i]; + if (endp->callid == NULL) { + LOGPENDP(endp, DLMGCP, LOGL_DEBUG, + "(trunk:%i) found free endpoint: %s\n", + trunk->trunk_nr, endp->name); + endp->wildcarded_req = true; + return endp; + } + } + + LOGP(DLMGCP, LOGL_ERROR, + "(trunk:%i) Not able to find a free endpoint\n", + trunk->trunk_nr); + if (cause) + *cause = -403; + return NULL; + } + + /* Find an enspoint by its name (if wildcarded request is not + * applicable) */ + for (i = 0; i < trunk->number_endpoints; i++) { + endp = trunk->endpoints[i]; + if (strncmp + (chop_endp_prefix(trunk, endp->name), epname_ch, + epname_ch_len) == 0) { + LOGPENDP(endp, DLMGCP, LOGL_DEBUG, + "(trunk:%i) found endpoint: %s\n", + trunk->trunk_nr, endp->name); + endp->wildcarded_req = false; + return endp; + } + } + + LOGP(DLMGCP, LOGL_ERROR, + "(trunk:%i) Not able to find specified endpoint: %s\n", + trunk->trunk_nr, epname); + if (cause) + *cause = -500; + + return NULL; +} + +/* Check if the domain name, which is supplied with the endpoint name + * matches the configuration. */ +static int check_domain_name(const char *epname, struct mgcp_config *cfg) +{ + char *domain_to_check; + + domain_to_check = strstr(epname, "@"); + if (!domain_to_check) { + LOGP(DLMGCP, LOGL_ERROR, "(endpoint:%s) missing domain name, expecting '%s'\n", + epname, cfg->domain); + return -EINVAL; + } + + /* Accept any domain if configured as "*" */ + if (!strcmp(cfg->domain, "*")) + return 0; + + if (strcmp(domain_to_check+1, cfg->domain) != 0) { + LOGP(DLMGCP, LOGL_ERROR, "(endpoint:%s) wrong domain name, expecting '%s'\n", + epname, cfg->domain); + return -EINVAL; + } + + return 0; +} + +/*! Find an endpoint by its name, search at all trunks. + * \param[out] cause, pointer to store cause code, can be NULL. + * \param[in] epname, must contain trunk prefix. + * \param[in] cfg, mgcp configuration (trunks). + * \returns endpoint or NULL if endpoint was not found. */ +struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname, + struct mgcp_config *cfg) +{ + struct mgcp_trunk *trunk; + struct mgcp_endpoint *endp; + char epname_lc[MGCP_ENDPOINT_MAXLEN]; + + osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname); + epname = epname_lc; + + if (cause) + *cause = -500; + + /* Identify the trunk where the endpoint is located */ + trunk = mgcp_trunk_by_name(epname, cfg); + if (!trunk) + return NULL; + + /* Virtual endpoints require a domain name (see RFC3435, section E.3) */ + if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) { + if (check_domain_name(epname, cfg)) + return NULL; + } + + /* Identify the endpoint on the trunk */ + endp = mgcp_endp_by_name_trunk(cause, epname, trunk); + if (!endp) { + return NULL; + } + + if (cause) + *cause = 0; + return endp; +} diff --git a/src/libosmo-mgcp/mgcp_msg.c b/src/libosmo-mgcp/mgcp_msg.c index 3e95ed1..019466e 100644 --- a/src/libosmo-mgcp/mgcp_msg.c +++ b/src/libosmo-mgcp/mgcp_msg.c @@ -129,166 +129,6 @@ return ret; } -/* We have a null terminated string with the endpoint name here. We only - * support two kinds. Simple ones as seen on the BSC level and the ones - * seen on the trunk side. (helper function for find_endpoint()) */ -static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg, - const char *mgcp) -{ - char *rest = NULL; - struct mgcp_trunk_config *tcfg; - int trunk, endp; - struct mgcp_endpoint *endp_ptr; - - trunk = strtoul(mgcp + 6, &rest, 10); - if (rest == NULL || rest[0] != '/' || trunk < 1) { - LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp); - return NULL; - } - - endp = strtoul(rest + 1, &rest, 10); - if (rest == NULL || rest[0] != '@') { - LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp); - return NULL; - } - - /* signalling is on timeslot 1 */ - if (endp == 1) - return NULL; - - tcfg = mgcp_trunk_num(cfg, trunk); - if (!tcfg) { - LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n", - trunk); - return NULL; - } - - if (!tcfg->endpoints) { - LOGP(DLMGCP, LOGL_ERROR, - "Endpoints of trunk %d not allocated.\n", trunk); - return NULL; - } - - if (endp < 1 || endp >= tcfg->number_endpoints) { - LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", - mgcp); - return NULL; - } - - endp_ptr = &tcfg->endpoints[endp]; - endp_ptr->wildcarded_req = false; - return endp_ptr; -} - -/* Find an endpoint that is not in use. Do this by going through the endpoint - * array, check the callid. A callid nullpointer indicates that the endpoint - * is free */ -static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints, - unsigned int number_endpoints) -{ - struct mgcp_endpoint *endp; - unsigned int i; - - for (i = 0; i < number_endpoints; i++) { - if (endpoints[i].callid == NULL) { - endp = &endpoints[i]; - LOGPENDP(endp, DLMGCP, LOGL_DEBUG, - "found free endpoint\n"); - endp->wildcarded_req = true; - return endp; - } - } - - LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n"); - return NULL; -} - -/* Check if the domain name, which is supplied with the endpoint name - * matches the configuration. */ -static int check_domain_name(struct mgcp_config *cfg, const char *mgcp) -{ - char *domain_to_check; - - domain_to_check = strstr(mgcp, "@"); - if (!domain_to_check) - return -EINVAL; - - /* Accept any domain if configured as "*" */ - if (!strcmp(cfg->domain, "*")) - return 0; - - if (strcmp(domain_to_check+1, cfg->domain) != 0) { - LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain); - return -EINVAL; - } - - return 0; -} - -/* Search the endpoint pool for the endpoint that had been selected via the - * MGCP message (helper function for mgcp_analyze_header()) */ -static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, - const char *mgcp, - int *cause) -{ - char *endptr = NULL; - unsigned int gw = INT_MAX; - const char *endpoint_number_str; - struct mgcp_endpoint *endp; - - *cause = 0; - - /* Check if the domainname in the request is correct */ - if (check_domain_name(cfg, mgcp)) { - *cause = -500; - return NULL; - } - - /* Check if the E1 trunk is requested */ - if (strncmp(mgcp, "ds/e1", 5) == 0) { - endp = find_e1_endpoint(cfg, mgcp); - if (!endp) - *cause = -500; - return endp; - } - - /* Check if the virtual trunk is addressed (new, correct way with prefix) */ - if (strncmp - (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, - strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) { - endpoint_number_str = - mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK); - if (endpoint_number_str[0] == '*') { - endp = find_free_endpoint(cfg->trunk.endpoints, - cfg->trunk.number_endpoints); - if (!endp) - *cause = -403; - return endp; - } - gw = strtoul(endpoint_number_str, &endptr, 16); - if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { - endp = &cfg->trunk.endpoints[gw]; - endp->wildcarded_req = false; - return endp; - } - } - - /* Deprecated method without prefix */ - LOGP(DLMGCP, LOGL_NOTICE, - "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n", - MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp); - gw = strtoul(mgcp, &endptr, 16); - if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { - endp = &cfg->trunk.endpoints[gw]; - endp->wildcarded_req = false; - return endp; - } - - LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp); - *cause = -500; - return NULL; -} - /*! Analyze and parse the the hader of an MGCP messeage string. * \param[out] pdata caller provided memory to store the parsing results * \param[in] data mgcp message string @@ -316,7 +156,7 @@ pdata->trans = elem; break; case 1: - pdata->endp = find_endpoint(pdata->cfg, elem, &cause); + pdata->endp = mgcp_endp_by_name(&cause, elem, pdata->cfg); if (!pdata->endp) { LOGP(DLMGCP, LOGL_ERROR, "Unable to find Endpoint `%s'\n", elem); @@ -390,8 +230,8 @@ const size_t line_len = strlen(line); if (line[0] != '\0' && line_len < 2) { LOGP(DLMGCP, LOGL_ERROR, - "Wrong MGCP option format: '%s' on 0x%x\n", - line, ENDPOINT_NUMBER(endp)); + "Wrong MGCP option format: '%s' on %s\n", + line, endp->name); return 0; } diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c index 608a93b..d3c7de8 100644 --- a/src/libosmo-mgcp/mgcp_network.c +++ b/src/libosmo-mgcp/mgcp_network.c @@ -42,6 +42,7 @@ #include <osmocom/mgcp/osmux.h> #include <osmocom/mgcp/mgcp_conn.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> #include <osmocom/mgcp/mgcp_codec.h> #include <osmocom/mgcp/debug.h> #include <osmocom/codec/codec.h> @@ -61,7 +62,7 @@ int id, int inc) { struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group; - struct rate_ctr_group *trunk_stats = endp->tcfg->all_rtp_conn_stats; + struct rate_ctr_group *trunk_stats = endp->trunk->all_rtp_conn_stats; /* add to both the per-connection and the per-trunk global stats */ rate_ctr_add(&conn_stats->ctr[id], inc); @@ -187,7 +188,7 @@ if (rc == -1) goto failed; - if (endp->tcfg->omit_rtcp) + if (endp->trunk->omit_rtcp) return rc; was_rtcp = 1; @@ -649,8 +650,8 @@ #if 0 DEBUGP(DRTP, - "endpoint:0x%x payload hdr payload %u -> endp payload %u\n", - ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload); + "endpoint:%s payload hdr payload %u -> endp payload %u\n", + endp->name, rtp_hdr->payload_type, payload); rtp_hdr->payload_type = payload; #endif } @@ -819,7 +820,7 @@ * connection in loopback mode exists), then the source connection * shall be specified as destination connection */ - struct mgcp_trunk_config *tcfg = endp->tcfg; + struct mgcp_trunk *trunk = endp->trunk; struct mgcp_rtp_end *rtp_end; struct mgcp_rtp_state *rtp_state; char *dest_name; @@ -835,7 +836,7 @@ } LOGPENDP(endp, DRTP, LOGL_DEBUG, "loop:%d, mode:%d%s\n", - tcfg->audio_loop, conn_src->conn->mode, + trunk->audio_loop, conn_src->conn->mode, conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : ""); /* FIXME: It is legal that the payload type on the egress connection is @@ -947,7 +948,7 @@ buflen = cont; } while (buflen > 0); return nbytes; - } else if (!tcfg->omit_rtcp) { + } else if (!trunk->omit_rtcp) { LOGPENDP(endp, DRTP, LOGL_DEBUG, "send to %s %s rtp_port:%u rtcp_port:%u\n", dest_name, inet_ntoa(rtp_end->addr), @@ -1150,12 +1151,12 @@ { struct mgcp_endpoint *endp; struct mgcp_conn_rtp *conn; - struct mgcp_trunk_config *tcfg; + struct mgcp_trunk *trunk; int rc; conn = (struct mgcp_conn_rtp*) fd->data; endp = conn->conn->endp; - tcfg = endp->tcfg; + trunk = endp->trunk; LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n"); @@ -1190,7 +1191,7 @@ LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn)); /* Check if the origin of the RTP packet seems plausible */ - if (tcfg->rtp_accept_all == 0) { + if (trunk->rtp_accept_all == 0) { if (check_rtp_origin(conn, addr) != 0) return -1; } @@ -1436,11 +1437,10 @@ /* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, - struct mgcp_rtp_end *rtp_end, int endpno) + struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp) { /* NOTE: The port that is used for RTCP is the RTP port incremented by one * (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */ - struct mgcp_endpoint *endp = &cfg->trunk.endpoints[endpno]; if (mgcp_create_bind(source_addr, &rtp_end->rtp, rtp_end->local_port) != 0) { @@ -1527,8 +1527,7 @@ mgcp_get_local_addr(local_ip_addr, conn); - return bind_rtp(endp->cfg, local_ip_addr, end, - ENDPOINT_NUMBER(endp)); + return bind_rtp(endp->cfg, local_ip_addr, end, endp); } /*! free allocated RTP and RTCP ports. diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c index a1121de..29ad45f 100644 --- a/src/libosmo-mgcp/mgcp_osmux.c +++ b/src/libosmo-mgcp/mgcp_osmux.c @@ -27,6 +27,7 @@ #include <osmocom/mgcp/osmux.h> #include <osmocom/mgcp/mgcp_conn.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> static struct osmo_fd osmux_fd; @@ -202,9 +203,9 @@ struct mgcp_conn_rtp * conn_rtp; int i; - for (i=0; i<cfg->trunk.number_endpoints; i++) { + for (i=0; i<cfg->trunk->number_endpoints; i++) { - endp = &cfg->trunk.endpoints[i]; + endp = cfg->trunk->endpoints[i]; llist_for_each_entry(conn, &endp->conns, entry) { if (conn->type != MGCP_CONN_TYPE_RTP) diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c index 82c10aa..c66d984 100644 --- a/src/libosmo-mgcp/mgcp_protocol.c +++ b/src/libosmo-mgcp/mgcp_protocol.c @@ -32,7 +32,6 @@ #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> #include <osmocom/core/select.h> -#include <osmocom/core/stats.h> #include <osmocom/mgcp/mgcp.h> #include <osmocom/mgcp/mgcp_common.h> @@ -40,6 +39,7 @@ #include <osmocom/mgcp/mgcp_stat.h> #include <osmocom/mgcp/mgcp_msg.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> #include <osmocom/mgcp/mgcp_sdp.h> #include <osmocom/mgcp/mgcp_codec.h> #include <osmocom/mgcp/mgcp_conn.h> @@ -53,101 +53,6 @@ #define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \ { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME }, -static const struct rate_ctr_desc mgcp_general_ctr_desc[] = { - /* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */ - [MGCP_GENERAL_RX_MSGS_TOTAL] = {"mgcp:rx_msgs", "total number of MGCP messages received."}, - [MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = {"mgcp:rx_msgs_retransmitted", "number of received retransmissions."}, - [MGCP_GENERAL_RX_MSGS_HANDLED] = {"mgcp:rx_msgs_handled", "number of handled MGCP messages."}, - [MGCP_GENERAL_RX_MSGS_UNHANDLED] = {"mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages."}, - [MGCP_GENERAL_RX_FAIL_MSG_PARSE] = {"mgcp:err_rx_msg_parse", "error parsing MGCP message."}, - [MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] = {"mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints."}, -}; - -const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = { - .group_name_prefix = "mgcp", - .group_description = "mgcp general statistics", - .class_id = OSMO_STATS_CLASS_GLOBAL, - .num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc), - .ctr_desc = mgcp_general_ctr_desc -}; - -static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = { - [MGCP_CRCX_SUCCESS] = {"crcx:success", "CRCX command processed successfully."}, - [MGCP_CRCX_FAIL_BAD_ACTION] = {"crcx:bad_action", "bad action in CRCX command."}, - [MGCP_CRCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in CRCX command."}, - [MGCP_CRCX_FAIL_MISSING_CALLID] = {"crcx:missing_callid", "missing CallId in CRCX command."}, - [MGCP_CRCX_FAIL_INVALID_MODE] = {"crcx:invalid_mode", "invalid connection mode in CRCX command."}, - [MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = {"crcx:limit_exceeded", "limit of concurrent connections was reached."}, - [MGCP_CRCX_FAIL_UNKNOWN_CALLID] = {"crcx:unkown_callid", "unknown CallId in CRCX command."}, - [MGCP_CRCX_FAIL_ALLOC_CONN] = {"crcx:alloc_conn_fail", "connection allocation failure."}, - [MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] = {"crcx:no_remote_conn_desc", "no opposite end specified for connection."}, - [MGCP_CRCX_FAIL_START_RTP] = {"crcx:start_rtp_failure", "failure to start RTP processing."}, - [MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = {"crcx:conn_rejected", "connection rejected by policy."}, - [MGCP_CRCX_FAIL_NO_OSMUX] = {"crcx:no_osmux", "no osmux offered by peer."}, - [MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = {"crcx:conn_opt", "connection options invalid."}, - [MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = {"crcx:codec_nego", "codec negotiation failure."}, - [MGCP_CRCX_FAIL_BIND_PORT] = {"crcx:bind_port", "port bind failure."}, -}; - -const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = { - .group_name_prefix = "crcx", - .group_description = "crxc statistics", - .class_id = OSMO_STATS_CLASS_GLOBAL, - .num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc), - .ctr_desc = mgcp_crcx_ctr_desc -}; - -static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = { - [MGCP_MDCX_SUCCESS] = {"mdcx:success", "MDCX command processed successfully."}, - [MGCP_MDCX_FAIL_WILDCARD] = {"mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported."}, - [MGCP_MDCX_FAIL_NO_CONN] = {"mdcx:no_conn", "endpoint specified in MDCX command has no active connections."}, - [MGCP_MDCX_FAIL_INVALID_CALLID] = {"mdcx:callid", "invalid CallId specified in MDCX command."}, - [MGCP_MDCX_FAIL_INVALID_CONNID] = {"mdcx:connid", "invalid connection ID specified in MDCX command."}, - [MGCP_MDCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in MDCX command."}, - [MGCP_MDCX_FAIL_NO_CONNID] = {"mdcx:no_connid", "no connection ID specified in MDCX command."}, - [MGCP_MDCX_FAIL_CONN_NOT_FOUND] = {"mdcx:conn_not_found", "connection specified in MDCX command does not exist."}, - [MGCP_MDCX_FAIL_INVALID_MODE] = {"mdcx:invalid_mode", "invalid connection mode in MDCX command."}, - [MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = {"mdcx:conn_opt", "connection options invalid."}, - [MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] = {"mdcx:no_remote_conn_desc", "no opposite end specified for connection."}, - [MGCP_MDCX_FAIL_START_RTP] = {"mdcx:start_rtp_failure", "failure to start RTP processing."}, - [MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = {"mdcx:conn_rejected", "connection rejected by policy."}, - [MGCP_MDCX_DEFERRED_BY_POLICY] = {"mdcx:conn_deferred", "connection deferred by policy."}, -}; - -const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = { - .group_name_prefix = "mdcx", - .group_description = "mdcx statistics", - .class_id = OSMO_STATS_CLASS_GLOBAL, - .num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc), - .ctr_desc = mgcp_mdcx_ctr_desc -}; - -static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = { - [MGCP_DLCX_SUCCESS] = {"dlcx:success", "DLCX command processed successfully."}, - [MGCP_DLCX_FAIL_WILDCARD] = {"dlcx:wildcard", "wildcard names in DLCX commands are unsupported."}, - [MGCP_DLCX_FAIL_NO_CONN] = {"dlcx:no_conn", "endpoint specified in DLCX command has no active connections."}, - [MGCP_DLCX_FAIL_INVALID_CALLID] = {"dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ."}, - [MGCP_DLCX_FAIL_INVALID_CONNID] = {"dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint."}, - [MGCP_DLCX_FAIL_UNHANDLED_PARAM] = {"dlcx:unhandled_param", "unhandled parameter in DLCX command."}, - [MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = {"dlcx:rejected", "connection deletion rejected by policy."}, - [MGCP_DLCX_DEFERRED_BY_POLICY] = {"dlcx:deferred", "connection deletion deferred by policy."}, -}; - -const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = { - .group_name_prefix = "dlcx", - .group_description = "dlcx statistics", - .class_id = OSMO_STATS_CLASS_GLOBAL, - .num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc), - .ctr_desc = mgcp_dlcx_ctr_desc -}; - -const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = { - .group_name_prefix = "all_rtp_conn", - .group_description = "aggregated statistics for all rtp connections", - .class_id = 1, - .num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc), - .ctr_desc = all_rtp_conn_rate_ctr_desc -}; static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data); static struct msgb *handle_create_con(struct mgcp_parse_data *data); @@ -254,11 +159,11 @@ * Remember the last transmission per endpoint. */ if (endp) { - struct mgcp_trunk_config *tcfg = endp->tcfg; + struct mgcp_trunk *trunk = endp->trunk; talloc_free(endp->last_response); talloc_free(endp->last_trans); - endp->last_trans = talloc_strdup(tcfg->endpoints, trans); - endp->last_response = talloc_strndup(tcfg->endpoints, + endp->last_trans = talloc_strdup(trunk->endpoints, trans); + endp->last_response = talloc_strndup(trunk->endpoints, (const char *)res->l2h, msgb_l2len(res)); } @@ -296,10 +201,8 @@ /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */ if (endp->wildcarded_req - && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) { - rc = msgb_printf(msg, "Z: %s%x@%s\r\n", - MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, - ENDPOINT_NUMBER(endp), endp->cfg->domain); + && endp->trunk->trunk_type == MGCP_TRUNK_VIRTUAL) { + rc = msgb_printf(msg, "Z: %s\r\n", endp->name); if (rc < 0) return -EINVAL; } @@ -379,8 +282,8 @@ * - or a response (three numbers, space, transaction id) */ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) { - struct mgcp_trunk_config *tcfg = &cfg->trunk; - struct rate_ctr_group *rate_ctrs = tcfg->mgcp_general_ctr_group; + struct mgcp_trunk *trunk = cfg->trunk; + struct rate_ctr_group *rate_ctrs = trunk->mgcp_general_ctr_group; struct mgcp_parse_data pdata; int rc, i, code, handled = 0; struct msgb *resp = NULL; @@ -692,13 +595,13 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, struct mgcp_rtp_end *rtp) { - struct mgcp_trunk_config *tcfg = endp->tcfg; + struct mgcp_trunk *trunk = endp->trunk; - int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc; + int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc; - rtp->force_aligned_timing = tcfg->force_aligned_timing; + rtp->force_aligned_timing = trunk->force_aligned_timing; rtp->force_constant_ssrc = patch_ssrc ? 1 : 0; - rtp->rfc5993_hr_convert = tcfg->rfc5993_hr_convert; + rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert; LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Configuring RTP endpoint: local port %d%s%s\n", @@ -830,9 +733,9 @@ /* CRCX command handler, processes the received command */ static struct msgb *handle_create_con(struct mgcp_parse_data *p) { - struct mgcp_trunk_config *tcfg = p->endp->tcfg; + struct mgcp_trunk *trunk = p->endp->trunk; struct mgcp_endpoint *endp = p->endp; - struct rate_ctr_group *rate_ctrs = tcfg->mgcp_crcx_ctr_group; + struct rate_ctr_group *rate_ctrs = trunk->mgcp_crcx_ctr_group; int error_code = 400; const char *local_options = NULL; const char *callid = NULL; @@ -915,7 +818,7 @@ LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: endpoint full, max. %i connections allowed!\n", endp->type->max_conns); - if (tcfg->force_realloc) { + if (trunk->force_realloc) { /* There is no more room for a connection, make some * room by blindly tossing the oldest of the two two * connections */ @@ -934,7 +837,7 @@ LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: already seized by other call (%s)\n", endp->callid); - if (tcfg->force_realloc) + if (trunk->force_realloc) /* This is not our call, toss everything by releasing * the entire endpoint. (rude!) */ mgcp_endp_release(endp); @@ -949,10 +852,10 @@ /* Set the callid, creation of another connection will only be possible * when the callid matches up. (Connections are distinguished by their * connection ids) */ - endp->callid = talloc_strdup(tcfg->endpoints, callid); + endp->callid = talloc_strdup(trunk->endpoints, callid); snprintf(conn_name, sizeof(conn_name), "%s", callid); - _conn = mgcp_conn_alloc(tcfg->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name); + _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name); if (!_conn) { LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: unable to allocate RTP connection\n"); @@ -987,7 +890,7 @@ /* Set local connection options, if present */ if (local_options) { - rc = set_local_cx_options(endp->tcfg->endpoints, + rc = set_local_cx_options(endp->trunk->endpoints, &endp->local_options, local_options); if (rc != 0) { LOGPCONN(_conn, DLMGCP, LOGL_ERROR, @@ -1007,8 +910,8 @@ goto error2; } - conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints, - tcfg->audio_fmtp_extra); + conn->end.fmtp_extra = talloc_strdup(trunk->endpoints, + trunk->audio_fmtp_extra); if (p->cfg->force_ptime) { conn->end.packet_duration_ms = p->cfg->force_ptime; @@ -1043,8 +946,7 @@ /* policy CB */ if (p->cfg->policy_cb) { int rc; - rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp), - MGCP_ENDP_CRCX, p->trans); + rc = p->cfg->policy_cb(endp, MGCP_ENDP_CRCX, p->trans); switch (rc) { case MGCP_POLICY_REJECT: LOGPCONN(_conn, DLMGCP, LOGL_NOTICE, @@ -1066,12 +968,12 @@ LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "CRCX: Creating connection: port: %u\n", conn->end.local_port); if (p->cfg->change_cb) - p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX); + p->cfg->change_cb(endp, MGCP_ENDP_CRCX); /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */ - OSMO_ASSERT(tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE); + OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE); if (conn->conn->mode & MGCP_CONN_RECV_ONLY - && tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER) + && trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER) send_dummy(endp, conn); LOGPCONN(_conn, DLMGCP, LOGL_NOTICE, @@ -1085,16 +987,12 @@ return create_err_response(endp, error_code, "CRCX", p->trans); } - - - - /* MDCX command handler, processes the received command */ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) { - struct mgcp_trunk_config *tcfg = p->endp->tcfg; + struct mgcp_trunk *trunk = p->endp->trunk; struct mgcp_endpoint *endp = p->endp; - struct rate_ctr_group *rate_ctrs = tcfg->mgcp_mdcx_ctr_group; + struct rate_ctr_group *rate_ctrs = trunk->mgcp_mdcx_ctr_group; int error_code = 500; int silent = 0; int have_sdp = 0; @@ -1202,7 +1100,7 @@ /* Set local connection options, if present */ if (local_options) { - rc = set_local_cx_options(endp->tcfg->endpoints, + rc = set_local_cx_options(endp->trunk->endpoints, &endp->local_options, local_options); if (rc != 0) { LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, @@ -1261,8 +1159,7 @@ /* policy CB */ if (p->cfg->policy_cb) { int rc; - rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), - MGCP_ENDP_MDCX, p->trans); + rc = p->cfg->policy_cb(endp, MGCP_ENDP_MDCX, p->trans); switch (rc) { case MGCP_POLICY_REJECT: LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE, @@ -1291,13 +1188,12 @@ LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn)); if (p->cfg->change_cb) - p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), - MGCP_ENDP_MDCX); + p->cfg->change_cb(endp, MGCP_ENDP_MDCX); /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */ - OSMO_ASSERT(endp->tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE); + OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE); if (conn->conn->mode & MGCP_CONN_RECV_ONLY - && endp->tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER) + && endp->trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER) send_dummy(endp, conn); rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_SUCCESS]); @@ -1318,9 +1214,9 @@ /* DLCX command handler, processes the received command */ static struct msgb *handle_delete_con(struct mgcp_parse_data *p) { - struct mgcp_trunk_config *tcfg = p->endp->tcfg; + struct mgcp_trunk *trunk = p->endp->trunk; struct mgcp_endpoint *endp = p->endp; - struct rate_ctr_group *rate_ctrs = tcfg->mgcp_dlcx_ctr_group; + struct rate_ctr_group *rate_ctrs = trunk->mgcp_dlcx_ctr_group; int error_code = 400; int silent = 0; char *line; @@ -1381,8 +1277,7 @@ /* policy CB */ if (p->cfg->policy_cb) { int rc; - rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), - MGCP_ENDP_DLCX, p->trans); + rc = p->cfg->policy_cb(endp, MGCP_ENDP_DLCX, p->trans); switch (rc) { case MGCP_POLICY_REJECT: LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n"); @@ -1446,8 +1341,7 @@ } if (p->cfg->change_cb) - p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), - MGCP_ENDP_DLCX); + p->cfg->change_cb(endp, MGCP_ENDP_DLCX); rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS]); if (silent) @@ -1476,7 +1370,7 @@ LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n"); if (p->cfg->reset_cb) - p->cfg->reset_cb(p->endp->tcfg); + p->cfg->reset_cb(p->endp->trunk); return NULL; } @@ -1522,37 +1416,37 @@ /* Connection keepalive timer, will take care that dummy packets are send * regularly, so that NAT connections stay open */ -static void mgcp_keepalive_timer_cb(void *_tcfg) +static void mgcp_keepalive_timer_cb(void *_trunk) { - struct mgcp_trunk_config *tcfg = _tcfg; + struct mgcp_trunk *trunk = _trunk; struct mgcp_conn *conn; int i; LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n", - tcfg->trunk_nr); + trunk->trunk_nr); /* Do not accept invalid configuration values * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and * values greater 0 */ - OSMO_ASSERT(tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE); + OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE); /* The dummy packet functionality has been disabled, we will exit * immediately, no further timer is scheduled, which means we will no * longer send dummy packets even when we did before */ - if (tcfg->keepalive_interval == MGCP_KEEPALIVE_NEVER) + if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER) return; /* In cases where only one dummy packet is sent, we do not need * the timer since the functions that handle the CRCX and MDCX are * triggering the sending of the dummy packet. So we behave like in * the MGCP_KEEPALIVE_NEVER case */ - if (tcfg->keepalive_interval == MGCP_KEEPALIVE_ONCE) + if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE) return; /* Send walk over all endpoints and send out dummy packets through * every connection present on each endpoint */ - for (i = 1; i < tcfg->number_endpoints; ++i) { - struct mgcp_endpoint *endp = &tcfg->endpoints[i]; + for (i = 1; i < trunk->number_endpoints; ++i) { + struct mgcp_endpoint *endp = trunk->endpoints[i]; llist_for_each_entry(conn, &endp->conns, entry) { if (conn->mode == MGCP_CONN_RECV_ONLY) send_dummy(endp, &conn->u.rtp); @@ -1561,77 +1455,21 @@ /* Schedule the keepalive timer for the next round */ LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n", - tcfg->trunk_nr); - osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval, + trunk->trunk_nr); + osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval, 0); } -void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval) +void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval) { - tcfg->keepalive_interval = interval; - osmo_timer_setup(&tcfg->keepalive_timer, mgcp_keepalive_timer_cb, tcfg); + trunk->keepalive_interval = interval; + osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk); if (interval <= 0) - osmo_timer_del(&tcfg->keepalive_timer); + osmo_timer_del(&trunk->keepalive_timer); else - osmo_timer_schedule(&tcfg->keepalive_timer, - tcfg->keepalive_interval, 0); -} - -static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group) -{ - rate_ctr_group_free(rate_ctr_group); - return 0; -} - -static int alloc_mgcp_rate_counters(struct mgcp_trunk_config *trunk, void *ctx) -{ - /* FIXME: Each new rate counter group requires a unique index. At the - * moment we generate an index using a counter, but perhaps there is - * a better way of assigning indices? */ - static unsigned int general_rate_ctr_index = 0; - static unsigned int crcx_rate_ctr_index = 0; - static unsigned int mdcx_rate_ctr_index = 0; - static unsigned int dlcx_rate_ctr_index = 0; - static unsigned int all_rtp_conn_rate_ctr_index = 0; - - if (trunk->mgcp_general_ctr_group == NULL) { - trunk->mgcp_general_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index); - if (!trunk->mgcp_general_ctr_group) - return -1; - talloc_set_destructor(trunk->mgcp_general_ctr_group, free_rate_counter_group); - general_rate_ctr_index++; - } - if (trunk->mgcp_crcx_ctr_group == NULL) { - trunk->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index); - if (!trunk->mgcp_crcx_ctr_group) - return -1; - talloc_set_destructor(trunk->mgcp_crcx_ctr_group, free_rate_counter_group); - crcx_rate_ctr_index++; - } - if (trunk->mgcp_mdcx_ctr_group == NULL) { - trunk->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index); - if (!trunk->mgcp_mdcx_ctr_group) - return -1; - talloc_set_destructor(trunk->mgcp_mdcx_ctr_group, free_rate_counter_group); - mdcx_rate_ctr_index++; - } - if (trunk->mgcp_dlcx_ctr_group == NULL) { - trunk->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index); - if (!trunk->mgcp_dlcx_ctr_group) - return -1; - talloc_set_destructor(trunk->mgcp_dlcx_ctr_group, free_rate_counter_group); - dlcx_rate_ctr_index++; - } - if (trunk->all_rtp_conn_stats == NULL) { - trunk->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc, - all_rtp_conn_rate_ctr_index); - if (!trunk->all_rtp_conn_stats) - return -1; - talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group); - all_rtp_conn_rate_ctr_index++; - } - return 0; + osmo_timer_schedule(&trunk->keepalive_timer, + trunk->keepalive_interval, 0); } /*! allocate configuration with default values. @@ -1661,113 +1499,19 @@ cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default; - /* default trunk handling; TODO: avoid duplication with mgcp_trunk_alloc() below */ - cfg->trunk.cfg = cfg; - cfg->trunk.trunk_nr = 0; - cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL; - cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000"); - cfg->trunk.audio_payload = 126; - cfg->trunk.audio_send_ptime = 1; - cfg->trunk.audio_send_name = 1; - cfg->trunk.vty_number_endpoints = 33; - cfg->trunk.omit_rtcp = 0; - mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE); - if (alloc_mgcp_rate_counters(&cfg->trunk, cfg) < 0) { + /* Allocate virtual trunk */ + cfg->trunk = mgcp_trunk_alloc(cfg, 0, MGCP_TRUNK_VIRTUAL); + if (!cfg->trunk) { talloc_free(cfg); return NULL; } + /* Initalize list head for user configurable trunks */ INIT_LLIST_HEAD(&cfg->trunks); return cfg; } -/*! allocate configuration with default values. - * (called once at startup by VTY) - * \param[in] cfg mgcp configuration - * \param[in] nr trunk number - * \returns pointer to allocated trunk configuration */ -struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr) -{ - struct mgcp_trunk_config *trunk; - - trunk = talloc_zero(cfg, struct mgcp_trunk_config); - if (!trunk) { - LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - trunk->cfg = cfg; - trunk->trunk_type = MGCP_TRUNK_E1; - trunk->trunk_nr = nr; - trunk->audio_name = talloc_strdup(cfg, "AMR/8000"); - trunk->audio_payload = 126; - trunk->audio_send_ptime = 1; - trunk->audio_send_name = 1; - trunk->vty_number_endpoints = 33; - trunk->omit_rtcp = 0; - mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE); - alloc_mgcp_rate_counters(trunk, trunk); - llist_add_tail(&trunk->entry, &cfg->trunks); - - return trunk; -} - -/*! get trunk configuration by trunk number (index). - * \param[in] cfg mgcp configuration - * \param[in] index trunk number - * \returns pointer to trunk configuration, NULL on error */ -struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index) -{ - struct mgcp_trunk_config *trunk; - - llist_for_each_entry(trunk, &cfg->trunks, entry) - if (trunk->trunk_nr == index) - return trunk; - - return NULL; -} - -/*! allocate endpoints and set default values. - * (called once at startup by VTY) - * \param[in] tcfg trunk configuration - * \returns 0 on success, -1 on failure */ -int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg) -{ - int i; - - tcfg->endpoints = _talloc_zero_array(tcfg->cfg, - sizeof(struct mgcp_endpoint), - tcfg->vty_number_endpoints, - "endpoints"); - if (!tcfg->endpoints) - return -1; - - for (i = 0; i < tcfg->vty_number_endpoints; ++i) { - INIT_LLIST_HEAD(&tcfg->endpoints[i].conns); - tcfg->endpoints[i].cfg = tcfg->cfg; - tcfg->endpoints[i].tcfg = tcfg; - - switch (tcfg->trunk_type) { - case MGCP_TRUNK_VIRTUAL: - tcfg->endpoints[i].type = &ep_typeset.rtp; - break; - case MGCP_TRUNK_E1: - /* FIXME: Implement E1 allocation */ - LOGP(DLMGCP, LOGL_FATAL, "E1 trunks not implemented!\n"); - break; - default: - osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n", - tcfg->trunk_type, __FILE__, __LINE__); - } - } - - tcfg->number_endpoints = tcfg->vty_number_endpoints; - alloc_mgcp_rate_counters(tcfg, tcfg->cfg); - - return 0; -} - static int send_agent(struct mgcp_config *cfg, const char *buf, int len) { return write(cfg->gw_fd.bfd.fd, buf, len); @@ -1798,17 +1542,16 @@ /*! Reset a single endpoint by sending RSIP message to self. * (called by VTY) - * \param[in] endp trunk endpoint - * \param[in] endpoint number + * \param[in] endp to reset * \returns 0 on success, -1 on error */ -int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint) +int mgcp_send_reset_ep(struct mgcp_endpoint *endp) { char buf[MGCP_ENDPOINT_MAXLEN + 128]; int len; int rc; len = snprintf(buf, sizeof(buf), - "RSIP 39 %x@%s MGCP 1.0\r\n", endpoint, endp->cfg->domain); + "RSIP 39 %s MGCP 1.0\r\n", endp->name); if (len < 0) return -1; diff --git a/src/libosmo-mgcp/mgcp_sdp.c b/src/libosmo-mgcp/mgcp_sdp.c index 01e7968..f811fac 100644 --- a/src/libosmo-mgcp/mgcp_sdp.c +++ b/src/libosmo-mgcp/mgcp_sdp.c @@ -25,6 +25,7 @@ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/mgcp_msg.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> #include <osmocom/mgcp/mgcp_codec.h> #include <osmocom/mgcp/mgcp_sdp.h> @@ -358,10 +359,11 @@ break; default: if (p->endp) + /* TODO: Check spec: We used the bare endpoint number before, + * now we use the endpoint name as a whole? Is this allowed? */ LOGP(DLMGCP, LOGL_NOTICE, - "Unhandled SDP option: '%c'/%d on 0x%x\n", - line[0], line[0], - ENDPOINT_NUMBER(p->endp)); + "Unhandled SDP option: '%c'/%d on %s\n", + line[0], line[0], endp->name); else LOGP(DLMGCP, LOGL_NOTICE, "Unhandled SDP option: '%c'/%d\n", @@ -381,7 +383,7 @@ codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used); rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param); if (rc < 0) - LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp)); + LOGP(DLMGCP, LOGL_NOTICE, "endpoint:%s, failed to add codec\n", endp->name); } talloc_free(tmp_ctx); @@ -557,7 +559,7 @@ if (rc < 0) goto buffer_too_small; - if (endp->tcfg->audio_send_name) { + if (endp->trunk->audio_send_name) { rc = add_rtpmap(sdp, payload_type, audio_name); if (rc < 0) goto buffer_too_small; @@ -573,7 +575,7 @@ if (rc < 0) goto buffer_too_small; } - if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) { + if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) { rc = msgb_printf(sdp, "a=ptime:%u\r\n", conn->end.packet_duration_ms); if (rc < 0) diff --git a/src/libosmo-mgcp/mgcp_trunk.c b/src/libosmo-mgcp/mgcp_trunk.c new file mode 100644 index 0000000..41495e5 --- /dev/null +++ b/src/libosmo-mgcp/mgcp_trunk.c @@ -0,0 +1,339 @@ +/* Trunk handling */ + +/* + * (C) 2009-2012 by Holger Hans Peter Freyther <zecke at selfish.org> + * (C) 2009-2012 by On-Waves + * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info at sysmocom.de> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/core/stats.h> +#include <osmocom/mgcp/mgcp_internal.h> +#include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> +#include <osmocom/mgcp/mgcp_stat.h> + +static const struct rate_ctr_desc mgcp_general_ctr_desc[] = { + /* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */ + [MGCP_GENERAL_RX_MSGS_TOTAL] = { "mgcp:rx_msgs", "total number of MGCP messages received." }, + [MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = { "mgcp:rx_msgs_retransmitted", "number of received retransmissions." }, + [MGCP_GENERAL_RX_MSGS_HANDLED] = { "mgcp:rx_msgs_handled", "number of handled MGCP messages." }, + [MGCP_GENERAL_RX_MSGS_UNHANDLED] = { "mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages." }, + [MGCP_GENERAL_RX_FAIL_MSG_PARSE] = { "mgcp:err_rx_msg_parse", "error parsing MGCP message." }, + [MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] = + { "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." }, +}; + +const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = { + .group_name_prefix = "mgcp", + .group_description = "mgcp general statistics", + .class_id = OSMO_STATS_CLASS_GLOBAL, + .num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc), + .ctr_desc = mgcp_general_ctr_desc +}; + +static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = { + [MGCP_CRCX_SUCCESS] = { "crcx:success", "CRCX command processed successfully." }, + [MGCP_CRCX_FAIL_BAD_ACTION] = { "crcx:bad_action", "bad action in CRCX command." }, + [MGCP_CRCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in CRCX command." }, + [MGCP_CRCX_FAIL_MISSING_CALLID] = { "crcx:missing_callid", "missing CallId in CRCX command." }, + [MGCP_CRCX_FAIL_INVALID_MODE] = { "crcx:invalid_mode", "invalid connection mode in CRCX command." }, + [MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = { "crcx:limit_exceeded", "limit of concurrent connections was reached." }, + [MGCP_CRCX_FAIL_UNKNOWN_CALLID] = { "crcx:unkown_callid", "unknown CallId in CRCX command." }, + [MGCP_CRCX_FAIL_ALLOC_CONN] = { "crcx:alloc_conn_fail", "connection allocation failure." }, + [MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] = + { "crcx:no_remote_conn_desc", "no opposite end specified for connection." }, + [MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." }, + [MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = { "crcx:conn_rejected", "connection rejected by policy." }, + [MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." }, + [MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." }, + [MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." }, + [MGCP_CRCX_FAIL_BIND_PORT] = { "crcx:bind_port", "port bind failure." }, +}; + +const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = { + .group_name_prefix = "crcx", + .group_description = "crxc statistics", + .class_id = OSMO_STATS_CLASS_GLOBAL, + .num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc), + .ctr_desc = mgcp_crcx_ctr_desc +}; + +static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = { + [MGCP_MDCX_SUCCESS] = { "mdcx:success", "MDCX command processed successfully." }, + [MGCP_MDCX_FAIL_WILDCARD] = { "mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported." }, + [MGCP_MDCX_FAIL_NO_CONN] = { "mdcx:no_conn", "endpoint specified in MDCX command has no active connections." }, + [MGCP_MDCX_FAIL_INVALID_CALLID] = { "mdcx:callid", "invalid CallId specified in MDCX command." }, + [MGCP_MDCX_FAIL_INVALID_CONNID] = { "mdcx:connid", "invalid connection ID specified in MDCX command." }, + [MGCP_MDCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in MDCX command." }, + [MGCP_MDCX_FAIL_NO_CONNID] = { "mdcx:no_connid", "no connection ID specified in MDCX command." }, + [MGCP_MDCX_FAIL_CONN_NOT_FOUND] = + { "mdcx:conn_not_found", "connection specified in MDCX command does not exist." }, + [MGCP_MDCX_FAIL_INVALID_MODE] = { "mdcx:invalid_mode", "invalid connection mode in MDCX command." }, + [MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." }, + [MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] = + { "mdcx:no_remote_conn_desc", "no opposite end specified for connection." }, + [MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." }, + [MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = { "mdcx:conn_rejected", "connection rejected by policy." }, + [MGCP_MDCX_DEFERRED_BY_POLICY] = { "mdcx:conn_deferred", "connection deferred by policy." }, +}; + +const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = { + .group_name_prefix = "mdcx", + .group_description = "mdcx statistics", + .class_id = OSMO_STATS_CLASS_GLOBAL, + .num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc), + .ctr_desc = mgcp_mdcx_ctr_desc +}; + +static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = { + [MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." }, + [MGCP_DLCX_FAIL_WILDCARD] = { "dlcx:wildcard", "wildcard names in DLCX commands are unsupported." }, + [MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." }, + [MGCP_DLCX_FAIL_INVALID_CALLID] = + { "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." }, + [MGCP_DLCX_FAIL_INVALID_CONNID] = + { "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." }, + [MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." }, + [MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = { "dlcx:rejected", "connection deletion rejected by policy." }, + [MGCP_DLCX_DEFERRED_BY_POLICY] = { "dlcx:deferred", "connection deletion deferred by policy." }, +}; + +const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = { + .group_name_prefix = "dlcx", + .group_description = "dlcx statistics", + .class_id = OSMO_STATS_CLASS_GLOBAL, + .num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc), + .ctr_desc = mgcp_dlcx_ctr_desc +}; + +const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = { + .group_name_prefix = "all_rtp_conn", + .group_description = "aggregated statistics for all rtp connections", + .class_id = 1, + .num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc), + .ctr_desc = all_rtp_conn_rate_ctr_desc +}; + +static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group) +{ + rate_ctr_group_free(rate_ctr_group); + return 0; +} + +static int alloc_mgcp_rate_counters(struct mgcp_trunk *trunk, void *ctx) +{ + /* FIXME: Each new rate counter group requires a unique index. At the + * moment we generate an index using a counter, but perhaps there is + * a better way of assigning indices? */ + static unsigned int general_rate_ctr_index = 0; + static unsigned int crcx_rate_ctr_index = 0; + static unsigned int mdcx_rate_ctr_index = 0; + static unsigned int dlcx_rate_ctr_index = 0; + static unsigned int all_rtp_conn_rate_ctr_index = 0; + + if (trunk->mgcp_general_ctr_group == NULL) { + trunk->mgcp_general_ctr_group = + rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index); + if (!trunk->mgcp_general_ctr_group) + return -1; + talloc_set_destructor(trunk->mgcp_general_ctr_group, free_rate_counter_group); + general_rate_ctr_index++; + } + if (trunk->mgcp_crcx_ctr_group == NULL) { + trunk->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index); + if (!trunk->mgcp_crcx_ctr_group) + return -1; + talloc_set_destructor(trunk->mgcp_crcx_ctr_group, free_rate_counter_group); + crcx_rate_ctr_index++; + } + if (trunk->mgcp_mdcx_ctr_group == NULL) { + trunk->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index); + if (!trunk->mgcp_mdcx_ctr_group) + return -1; + talloc_set_destructor(trunk->mgcp_mdcx_ctr_group, free_rate_counter_group); + mdcx_rate_ctr_index++; + } + if (trunk->mgcp_dlcx_ctr_group == NULL) { + trunk->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index); + if (!trunk->mgcp_dlcx_ctr_group) + return -1; + talloc_set_destructor(trunk->mgcp_dlcx_ctr_group, free_rate_counter_group); + dlcx_rate_ctr_index++; + } + if (trunk->all_rtp_conn_stats == NULL) { + trunk->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc, + all_rtp_conn_rate_ctr_index); + if (!trunk->all_rtp_conn_stats) + return -1; + talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group); + all_rtp_conn_rate_ctr_index++; + } + return 0; +} + +/*! allocate trunk and at id (if required) to the trunk list + * (called once at startup by VTY) + * \param[in] cfg mgcp configuration + * \param[in] nr trunk number + * \param[in] type trunk type + * \returns pointer to allocated trunk, NULL on failure */ +struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr, int trunk_type) +{ + struct mgcp_trunk *trunk; + + trunk = talloc_zero(cfg, struct mgcp_trunk); + if (!trunk) { + LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n"); + return NULL; + } + + trunk->cfg = cfg; + trunk->trunk_type = trunk_type; + trunk->trunk_nr = nr; + + trunk->audio_name = talloc_strdup(cfg, "AMR/8000"); + trunk->audio_payload = 126; + trunk->audio_send_ptime = 1; + trunk->audio_send_name = 1; + trunk->vty_number_endpoints = 33; + trunk->omit_rtcp = 0; + + mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE); + + if (alloc_mgcp_rate_counters(trunk, trunk) < 0) { + talloc_free(trunk); + return NULL; + } + + /* Note: Trunk Nr.0 is reserved as "virtual trunk", + * it is not stored using a separate pointer and + * not in the trunk list. */ + if (nr > 0) + llist_add_tail(&trunk->entry, &cfg->trunks); + + return trunk; +} + +/*! allocate endpoints and set default values. + * (called once at startup by VTY) + * \param[in] trunk trunk configuration + * \returns 0 on success, -1 on failure */ +int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk) +{ + int i; + char ep_name_buf[MGCP_ENDPOINT_MAXLEN]; + struct mgcp_endpoint *endp; + + /* (re)allocate pointer array for the endpoints */ + trunk->number_endpoints = 0; + if (trunk->endpoints) + talloc_free(trunk->endpoints); + trunk->endpoints = _talloc_zero_array(trunk->cfg, + sizeof(struct mgcp_endpoint *), trunk->vty_number_endpoints, "endpoints"); + if (!trunk->endpoints) + return -1; + + /* create endpoints */ + for (i = 0; i < trunk->vty_number_endpoints; ++i) { + switch (trunk->trunk_type) { + case MGCP_TRUNK_VIRTUAL: + snprintf(ep_name_buf, sizeof(ep_name_buf), "%s%x@%s", MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, i, + trunk->cfg->domain); + break; + case MGCP_TRUNK_E1: + /* FIXME: E1 trunk implementation is work in progress, this endpoint + * name is incomplete (subslots) */ + snprintf(ep_name_buf, sizeof(ep_name_buf), "%s-1/%x", MGCP_ENDPOINT_PREFIX_E1_TRUNK, i); + break; + default: + osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n", + trunk->trunk_type, __FILE__, __LINE__); + } + + endp = mgcp_endp_alloc(trunk, ep_name_buf); + if (!endp) { + talloc_free(trunk->endpoints); + return -1; + } + trunk->endpoints[i] = endp; + } + + /* make the endpoints we just created available to the MGW code */ + trunk->number_endpoints = trunk->vty_number_endpoints; + + return 0; +} + +/*! get trunk configuration by trunk number (index). + * \param[in] cfg mgcp configuration + * \param[in] index trunk number + * \returns pointer to trunk configuration, NULL on error */ +struct mgcp_trunk *mgcp_trunk_by_num(struct mgcp_config *cfg, int index) +{ + struct mgcp_trunk *trunk; + + llist_for_each_entry(trunk, &cfg->trunks, entry) + if (trunk->trunk_nr == index) + return trunk; + + return NULL; +} + +/*! Find a trunk by the trunk prefix in the endpoint name. + * \param[in] epname endpoint name with trunk prefix to look up. + * \param[in] cfg that contains the trunks where the endpoint is located. + * \returns trunk or NULL if endpoint was not found. */ +struct mgcp_trunk *mgcp_trunk_by_name(const char *epname, struct mgcp_config *cfg) +{ + size_t prefix_len; + char epname_lc[MGCP_ENDPOINT_MAXLEN]; + + osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname); + epname = epname_lc; + + prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1; + if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) { + return cfg->trunk; + } + + /* E1 trunks are not implemented yet, so we deny any request for an + * e1 trunk for now. */ + prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1; + if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) == 0) { + LOGP(DLMGCP, LOGL_ERROR, + "(endpoint:%s) e1 trunks not implemented in this version of osmo-mgw!\n", epname); + return NULL; + } + + /* Earlier versions of osmo-mgw were accepting endpoint names + * without trunk prefix. This is normally not allowed, each MGCP + * request should supply an endpoint name with trunk prefix. + * However in order to stay compatible with old versions of + * osmo-bsc and osmo-msc we still accept endpoint names without + * trunk prefix and just assume that the virtual trunk should + * be selected. There is even a TTCN3 test for this, see also: + * MGCP_Test.TC_crcx_noprefix */ + if ((epname[0] >= '0' && epname[0] <= '9') || (epname[0] >= 'a' && epname[0] <= 'f')) { + LOGP(DLMGCP, LOGL_ERROR, "(endpoint:%s) missing trunk prefix, assuming trunk \"%s\"!\n", epname, + MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK); + return cfg->trunk; + } + + LOGP(DLMGCP, LOGL_ERROR, "(endpoint:%s) unable to find trunk!\n", epname); + return NULL; +} diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c index 76d674f..0df805d 100644 --- a/src/libosmo-mgcp/mgcp_vty.c +++ b/src/libosmo-mgcp/mgcp_vty.c @@ -29,6 +29,7 @@ #include <osmocom/mgcp/vty.h> #include <osmocom/mgcp/mgcp_conn.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> #include <string.h> #include <inttypes.h> @@ -42,14 +43,14 @@ static struct mgcp_config *g_cfg = NULL; -static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg, int nr) +static struct mgcp_trunk *find_trunk(struct mgcp_config *cfg, int nr) { - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; if (nr == 0) - trunk = &cfg->trunk; + trunk = cfg->trunk; else - trunk = mgcp_trunk_num(cfg, nr); + trunk = mgcp_trunk_by_num(cfg, nr); return trunk; } @@ -85,50 +86,50 @@ else vty_out(vty, " no rtp ip-probing%s", VTY_NEWLINE); vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE); - if (g_cfg->trunk.keepalive_interval == MGCP_KEEPALIVE_ONCE) + if (g_cfg->trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE) vty_out(vty, " rtp keep-alive once%s", VTY_NEWLINE); - else if (g_cfg->trunk.keepalive_interval) + else if (g_cfg->trunk->keepalive_interval) vty_out(vty, " rtp keep-alive %d%s", - g_cfg->trunk.keepalive_interval, VTY_NEWLINE); + g_cfg->trunk->keepalive_interval, VTY_NEWLINE); else vty_out(vty, " no rtp keep-alive%s", VTY_NEWLINE); - if (g_cfg->trunk.omit_rtcp) + if (g_cfg->trunk->omit_rtcp) vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); else vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); - if (g_cfg->trunk.force_constant_ssrc - || g_cfg->trunk.force_aligned_timing - || g_cfg->trunk.rfc5993_hr_convert) { + if (g_cfg->trunk->force_constant_ssrc + || g_cfg->trunk->force_aligned_timing + || g_cfg->trunk->rfc5993_hr_convert) { vty_out(vty, " %srtp-patch ssrc%s", - g_cfg->trunk.force_constant_ssrc ? "" : "no ", + g_cfg->trunk->force_constant_ssrc ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %srtp-patch timestamp%s", - g_cfg->trunk.force_aligned_timing ? "" : "no ", + g_cfg->trunk->force_aligned_timing ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %srtp-patch rfc5993hr%s", - g_cfg->trunk.rfc5993_hr_convert ? "" : "no ", + g_cfg->trunk->rfc5993_hr_convert ? "" : "no ", VTY_NEWLINE); } else vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); - if (g_cfg->trunk.audio_payload != -1) + if (g_cfg->trunk->audio_payload != -1) vty_out(vty, " sdp audio-payload number %d%s", - g_cfg->trunk.audio_payload, VTY_NEWLINE); - if (g_cfg->trunk.audio_name) + g_cfg->trunk->audio_payload, VTY_NEWLINE); + if (g_cfg->trunk->audio_name) vty_out(vty, " sdp audio-payload name %s%s", - g_cfg->trunk.audio_name, VTY_NEWLINE); - if (g_cfg->trunk.audio_fmtp_extra) + g_cfg->trunk->audio_name, VTY_NEWLINE); + if (g_cfg->trunk->audio_fmtp_extra) vty_out(vty, " sdp audio fmtp-extra %s%s", - g_cfg->trunk.audio_fmtp_extra, VTY_NEWLINE); + g_cfg->trunk->audio_fmtp_extra, VTY_NEWLINE); vty_out(vty, " %ssdp audio-payload send-ptime%s", - g_cfg->trunk.audio_send_ptime ? "" : "no ", VTY_NEWLINE); + g_cfg->trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %ssdp audio-payload send-name%s", - g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE); - vty_out(vty, " loop %u%s", ! !g_cfg->trunk.audio_loop, VTY_NEWLINE); + g_cfg->trunk->audio_send_name ? "" : "no ", VTY_NEWLINE); + vty_out(vty, " loop %u%s", ! !g_cfg->trunk->audio_loop, VTY_NEWLINE); vty_out(vty, " number endpoints %u%s", - g_cfg->trunk.vty_number_endpoints - 1, VTY_NEWLINE); + g_cfg->trunk->vty_number_endpoints - 1, VTY_NEWLINE); vty_out(vty, " %sallow-transcoding%s", - g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE); + g_cfg->trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE); if (g_cfg->call_agent_addr) vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE); @@ -206,15 +207,13 @@ end->force_output_ptime, VTY_NEWLINE); } -static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx, +static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats) { struct mgcp_conn *conn; - vty_out(vty, "%s trunk %d endpoint %s%.2x:%s", - trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr, - trunk_type == MGCP_TRUNK_VIRTUAL ? MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK : "", - epidx, VTY_NEWLINE); + vty_out(vty, "%s trunk %d endpoint %s:%s", + trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr, endp->name, VTY_NEWLINE); if (llist_empty(&endp->conns)) { vty_out(vty, " No active connections%s", VTY_NEWLINE); @@ -242,7 +241,7 @@ } } -static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int show_stats) +static void dump_trunk(struct vty *vty, struct mgcp_trunk *cfg, int show_stats) { int i; @@ -256,8 +255,8 @@ } for (i = 0; i < cfg->number_endpoints; ++i) { - struct mgcp_endpoint *endp = &cfg->endpoints[i]; - dump_endpoint(vty, endp, i, cfg->trunk_nr, cfg->trunk_type, show_stats); + struct mgcp_endpoint *endp = cfg->endpoints[i]; + dump_endpoint(vty, endp, cfg->trunk_nr, cfg->trunk_type, show_stats); if (i < cfg->number_endpoints - 1) vty_out(vty, "%s", VTY_NEWLINE); } @@ -296,10 +295,10 @@ SHOW_MGCP_STR "Include Statistics\n") { - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; int show_stats = argc >= 1; - dump_trunk(vty, &g_cfg->trunk, show_stats); + dump_trunk(vty, g_cfg->trunk, show_stats); llist_for_each_entry(trunk, &g_cfg->trunks, entry) dump_trunk(vty, trunk, show_stats); @@ -312,34 +311,28 @@ } static void -dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk_config *trunk, const char *epname) +dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk *trunk, const char *epname) { - const size_t virt_prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1; - unsigned long epidx; - char *endp; - int i; + struct mgcp_endpoint *endp; - if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, virt_prefix_len) == 0) - epname += virt_prefix_len; - errno = 0; - epidx = strtoul(epname, &endp, 16); - if (epname[0] == '\0' || *endp != '\0') { - vty_out(vty, "endpoint name '%s' is not a hex number%s", epname, VTY_NEWLINE); - return; - } - if ((errno == ERANGE && epidx == ULONG_MAX) /* parsed value out of range */ - || epidx >= trunk->number_endpoints) { - vty_out(vty, "endpoint %.2lx not configured on trunk %d%s", epidx, trunk->trunk_nr, VTY_NEWLINE); - return; - } - - for (i = 0; i < trunk->number_endpoints; ++i) { - struct mgcp_endpoint *endp = &trunk->endpoints[i]; - if (i == epidx) { - dump_endpoint(vty, endp, i, trunk->trunk_nr, trunk->trunk_type, true); - break; + if (trunk) { + /* If a trunk is given, search on that specific trunk only */ + endp = mgcp_endp_by_name_trunk(NULL, epname, trunk); + if (!endp) { + vty_out(vty, "endpoint %s not configured on trunk %d%s", epname, trunk->trunk_nr, VTY_NEWLINE); + return; + } + } else { + /* If no trunk is given, search on all possible trunks */ + endp = mgcp_endp_by_name(NULL, epname, g_cfg); + if (!endp) { + vty_out(vty, "endpoint %s not configured%s", epname, VTY_NEWLINE); + return; } } + + trunk = endp->trunk; + dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type, true); } DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd, @@ -348,12 +341,7 @@ SHOW_MGCP_STR "Display information about an endpoint\n" "The name of the endpoint\n") { - struct mgcp_trunk_config *trunk; - - dump_mgcp_endpoint(vty, &g_cfg->trunk, argv[0]); - llist_for_each_entry(trunk, &g_cfg->trunks, entry) - dump_mgcp_endpoint(vty, trunk, argv[0]); - + dump_mgcp_endpoint(vty, NULL, argv[0]); return CMD_SUCCESS; } @@ -364,7 +352,7 @@ "Display information about a trunk\n" "Trunk number\n" "Display information about an endpoint\n" "The name of the endpoint\n") { - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; int trunkidx = atoi(argv[0]); trunk = find_trunk(g_cfg, trunkidx); @@ -561,7 +549,7 @@ if (!txt) return CMD_WARNING; - osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_fmtp_extra, txt); + osmo_talloc_replace_string(g_cfg, &g_cfg->trunk->audio_fmtp_extra, txt); talloc_free(txt); return CMD_SUCCESS; } @@ -570,7 +558,7 @@ cfg_mgcp_allow_transcoding_cmd, "allow-transcoding", "Allow transcoding\n") { - g_cfg->trunk.no_audio_transcoding = 0; + g_cfg->trunk->no_audio_transcoding = 0; return CMD_SUCCESS; } @@ -578,7 +566,7 @@ cfg_mgcp_no_allow_transcoding_cmd, "no allow-transcoding", NO_STR "Allow transcoding\n") { - g_cfg->trunk.no_audio_transcoding = 1; + g_cfg->trunk->no_audio_transcoding = 1; return CMD_SUCCESS; } @@ -590,7 +578,7 @@ SDP_STR AUDIO_STR "Number\n" "Payload number\n") { unsigned int payload = atoi(argv[0]); - g_cfg->trunk.audio_payload = payload; + g_cfg->trunk->audio_payload = payload; return CMD_SUCCESS; } @@ -604,7 +592,7 @@ "sdp audio-payload name NAME", SDP_STR AUDIO_STR "Name\n" "Payload name\n") { - osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]); + osmo_talloc_replace_string(g_cfg, &g_cfg->trunk->audio_name, argv[0]); return CMD_SUCCESS; } @@ -617,7 +605,7 @@ "sdp audio-payload send-ptime", SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n") { - g_cfg->trunk.audio_send_ptime = 1; + g_cfg->trunk->audio_send_ptime = 1; return CMD_SUCCESS; } @@ -626,7 +614,7 @@ "no sdp audio-payload send-ptime", NO_STR SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n") { - g_cfg->trunk.audio_send_ptime = 0; + g_cfg->trunk->audio_send_ptime = 0; return CMD_SUCCESS; } @@ -635,7 +623,7 @@ "sdp audio-payload send-name", SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n") { - g_cfg->trunk.audio_send_name = 1; + g_cfg->trunk->audio_send_name = 1; return CMD_SUCCESS; } @@ -644,7 +632,7 @@ "no sdp audio-payload send-name", NO_STR SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n") { - g_cfg->trunk.audio_send_name = 0; + g_cfg->trunk->audio_send_name = 0; return CMD_SUCCESS; } @@ -657,7 +645,7 @@ vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); return CMD_WARNING; } - g_cfg->trunk.audio_loop = atoi(argv[0]); + g_cfg->trunk->audio_loop = atoi(argv[0]); return CMD_SUCCESS; } @@ -667,7 +655,7 @@ "Force endpoint reallocation when the endpoint is still seized\n" "Don't force reallocation\n" "force reallocation\n") { - g_cfg->trunk.force_realloc = atoi(argv[0]); + g_cfg->trunk->force_realloc = atoi(argv[0]); return CMD_SUCCESS; } @@ -677,7 +665,7 @@ "Accept all RTP packets, even when the originating IP/Port does not match\n" "enable filter\n" "disable filter\n") { - g_cfg->trunk.rtp_accept_all = atoi(argv[0]); + g_cfg->trunk->rtp_accept_all = atoi(argv[0]); return CMD_SUCCESS; } @@ -687,20 +675,20 @@ "Number options\n" "Endpoints available\n" "Number endpoints\n") { /* + 1 as we start counting at one */ - g_cfg->trunk.vty_number_endpoints = atoi(argv[0]) + 1; + g_cfg->trunk->vty_number_endpoints = atoi(argv[0]) + 1; return CMD_SUCCESS; } DEFUN(cfg_mgcp_omit_rtcp, cfg_mgcp_omit_rtcp_cmd, "rtcp-omit", RTCP_OMIT_STR) { - g_cfg->trunk.omit_rtcp = 1; + g_cfg->trunk->omit_rtcp = 1; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_omit_rtcp, cfg_mgcp_no_omit_rtcp_cmd, "no rtcp-omit", NO_STR RTCP_OMIT_STR) { - g_cfg->trunk.omit_rtcp = 0; + g_cfg->trunk->omit_rtcp = 0; return CMD_SUCCESS; } @@ -708,7 +696,7 @@ cfg_mgcp_patch_rtp_ssrc_cmd, "rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n") { - g_cfg->trunk.force_constant_ssrc = 1; + g_cfg->trunk->force_constant_ssrc = 1; return CMD_SUCCESS; } @@ -716,7 +704,7 @@ cfg_mgcp_no_patch_rtp_ssrc_cmd, "no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n") { - g_cfg->trunk.force_constant_ssrc = 0; + g_cfg->trunk->force_constant_ssrc = 0; return CMD_SUCCESS; } @@ -724,7 +712,7 @@ cfg_mgcp_patch_rtp_ts_cmd, "rtp-patch timestamp", RTP_PATCH_STR "Adjust RTP timestamp\n") { - g_cfg->trunk.force_aligned_timing = 1; + g_cfg->trunk->force_aligned_timing = 1; return CMD_SUCCESS; } @@ -732,7 +720,7 @@ cfg_mgcp_no_patch_rtp_ts_cmd, "no rtp-patch timestamp", NO_STR RTP_PATCH_STR "Adjust RTP timestamp\n") { - g_cfg->trunk.force_aligned_timing = 0; + g_cfg->trunk->force_aligned_timing = 0; return CMD_SUCCESS; } @@ -740,7 +728,7 @@ cfg_mgcp_patch_rtp_rfc5993hr_cmd, "rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR) { - g_cfg->trunk.rfc5993_hr_convert = true; + g_cfg->trunk->rfc5993_hr_convert = true; return CMD_SUCCESS; } @@ -748,16 +736,16 @@ cfg_mgcp_no_patch_rtp_rfc5993hr_cmd, "no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR) { - g_cfg->trunk.rfc5993_hr_convert = false; + g_cfg->trunk->rfc5993_hr_convert = false; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_patch_rtp, cfg_mgcp_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR) { - g_cfg->trunk.force_constant_ssrc = 0; - g_cfg->trunk.force_aligned_timing = 0; - g_cfg->trunk.rfc5993_hr_convert = false; + g_cfg->trunk->force_constant_ssrc = 0; + g_cfg->trunk->force_aligned_timing = 0; + g_cfg->trunk->rfc5993_hr_convert = false; return CMD_SUCCESS; } @@ -766,7 +754,7 @@ "rtp keep-alive <1-120>", RTP_STR RTP_KEEPALIVE_STR "Keep alive interval in secs\n") { - mgcp_trunk_set_keepalive(&g_cfg->trunk, atoi(argv[0])); + mgcp_trunk_set_keepalive(g_cfg->trunk, atoi(argv[0])); return CMD_SUCCESS; } @@ -775,7 +763,7 @@ "rtp keep-alive once", RTP_STR RTP_KEEPALIVE_STR "Send dummy packet only once after CRCX/MDCX\n") { - mgcp_trunk_set_keepalive(&g_cfg->trunk, MGCP_KEEPALIVE_ONCE); + mgcp_trunk_set_keepalive(g_cfg->trunk, MGCP_KEEPALIVE_ONCE); return CMD_SUCCESS; } @@ -783,7 +771,7 @@ cfg_mgcp_no_rtp_keepalive_cmd, "no rtp keep-alive", NO_STR RTP_STR RTP_KEEPALIVE_STR) { - mgcp_trunk_set_keepalive(&g_cfg->trunk, MGCP_KEEPALIVE_NEVER); + mgcp_trunk_set_keepalive(g_cfg->trunk, MGCP_KEEPALIVE_NEVER); return CMD_SUCCESS; } @@ -805,12 +793,12 @@ DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd, "trunk <1-64>", "Configure a SS7 trunk\n" "Trunk Nr\n") { - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; int index = atoi(argv[0]); - trunk = mgcp_trunk_num(g_cfg, index); + trunk = mgcp_trunk_by_num(g_cfg, index); if (!trunk) - trunk = mgcp_trunk_alloc(g_cfg, index); + trunk = mgcp_trunk_alloc(g_cfg, index, MGCP_TRUNK_E1); if (!trunk) { vty_out(vty, "%%Unable to allocate trunk %u.%s", @@ -825,7 +813,7 @@ static int config_write_trunk(struct vty *vty) { - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; llist_for_each_entry(trunk, &g_cfg->trunks, entry) { vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE); @@ -855,7 +843,7 @@ else vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); if (trunk->force_constant_ssrc || trunk->force_aligned_timing - || g_cfg->trunk.rfc5993_hr_convert) { + || g_cfg->trunk->rfc5993_hr_convert) { vty_out(vty, " %srtp-patch ssrc%s", trunk->force_constant_ssrc ? "" : "no ", VTY_NEWLINE); @@ -883,7 +871,7 @@ "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" "Extra Information\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; char *txt = argv_concat(argv, argc, 0); if (!txt) return CMD_WARNING; @@ -898,7 +886,7 @@ "sdp audio-payload number <0-255>", SDP_STR AUDIO_STR "Number\n" "Payload Number\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; unsigned int payload = atoi(argv[0]); trunk->audio_payload = payload; @@ -914,7 +902,7 @@ "sdp audio-payload name NAME", SDP_STR AUDIO_STR "Payload\n" "Payload Name\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; osmo_talloc_replace_string(g_cfg, &trunk->audio_name, argv[0]); return CMD_SUCCESS; @@ -929,7 +917,7 @@ "loop (0|1)", "Loop audio for all endpoints on this trunk\n" "Don't Loop\n" "Loop\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; if (g_cfg->osmux) { vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); @@ -944,7 +932,7 @@ "sdp audio-payload send-ptime", SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->audio_send_ptime = 1; return CMD_SUCCESS; } @@ -954,7 +942,7 @@ "no sdp audio-payload send-ptime", NO_STR SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->audio_send_ptime = 0; return CMD_SUCCESS; } @@ -964,7 +952,7 @@ "sdp audio-payload send-name", SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->audio_send_name = 1; return CMD_SUCCESS; } @@ -974,14 +962,14 @@ "no sdp audio-payload send-name", NO_STR SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->audio_send_name = 0; return CMD_SUCCESS; } DEFUN(cfg_trunk_omit_rtcp, cfg_trunk_omit_rtcp_cmd, "rtcp-omit", RTCP_OMIT_STR) { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->omit_rtcp = 1; return CMD_SUCCESS; } @@ -989,7 +977,7 @@ DEFUN(cfg_trunk_no_omit_rtcp, cfg_trunk_no_omit_rtcp_cmd, "no rtcp-omit", NO_STR RTCP_OMIT_STR) { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->omit_rtcp = 0; return CMD_SUCCESS; } @@ -998,7 +986,7 @@ cfg_trunk_patch_rtp_ssrc_cmd, "rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->force_constant_ssrc = 1; return CMD_SUCCESS; } @@ -1007,7 +995,7 @@ cfg_trunk_no_patch_rtp_ssrc_cmd, "no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->force_constant_ssrc = 0; return CMD_SUCCESS; } @@ -1016,7 +1004,7 @@ cfg_trunk_patch_rtp_ts_cmd, "rtp-patch timestamp", RTP_PATCH_STR "Adjust RTP timestamp\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->force_aligned_timing = 1; return CMD_SUCCESS; } @@ -1025,7 +1013,7 @@ cfg_trunk_no_patch_rtp_ts_cmd, "no rtp-patch timestamp", NO_STR RTP_PATCH_STR "Adjust RTP timestamp\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->force_aligned_timing = 0; return CMD_SUCCESS; } @@ -1034,7 +1022,7 @@ cfg_trunk_patch_rtp_rfc5993hr_cmd, "rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR) { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->rfc5993_hr_convert = true; return CMD_SUCCESS; } @@ -1043,7 +1031,7 @@ cfg_trunk_no_patch_rtp_rfc5993hr_cmd, "no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR) { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->rfc5993_hr_convert = false; return CMD_SUCCESS; } @@ -1051,7 +1039,7 @@ DEFUN(cfg_trunk_no_patch_rtp, cfg_trunk_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR) { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->force_constant_ssrc = 0; trunk->force_aligned_timing = 0; trunk->rfc5993_hr_convert = false; @@ -1063,7 +1051,7 @@ "rtp keep-alive <1-120>", RTP_STR RTP_KEEPALIVE_STR "Keep-alive interval in secs\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; mgcp_trunk_set_keepalive(trunk, atoi(argv[0])); return CMD_SUCCESS; } @@ -1073,7 +1061,7 @@ "rtp keep-alive once", RTP_STR RTP_KEEPALIVE_STR "Send dummy packet only once after CRCX/MDCX\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE); return CMD_SUCCESS; } @@ -1082,7 +1070,7 @@ cfg_trunk_no_rtp_keepalive_cmd, "no rtp keep-alive", NO_STR RTP_STR RTP_KEEPALIVE_STR) { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; mgcp_trunk_set_keepalive(trunk, 0); return CMD_SUCCESS; } @@ -1091,7 +1079,7 @@ cfg_trunk_allow_transcoding_cmd, "allow-transcoding", "Allow transcoding\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->no_audio_transcoding = 0; return CMD_SUCCESS; } @@ -1100,7 +1088,7 @@ cfg_trunk_no_allow_transcoding_cmd, "no allow-transcoding", NO_STR "Allow transcoding\n") { - struct mgcp_trunk_config *trunk = vty->index; + struct mgcp_trunk *trunk = vty->index; trunk->no_audio_transcoding = 1; return CMD_SUCCESS; } @@ -1112,7 +1100,7 @@ "The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n") { - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; struct mgcp_endpoint *endp; struct mgcp_conn *conn; @@ -1136,7 +1124,7 @@ return CMD_WARNING; } - endp = &trunk->endpoints[endp_no]; + endp = trunk->endpoints[endp_no]; int loop = atoi(argv[2]); llist_for_each_entry(conn, &endp->conns, entry) { if (conn->type == MGCP_CONN_TYPE_RTP) @@ -1170,7 +1158,7 @@ "destination IP of the data\n" "destination port\n") { struct mgcp_rtp_tap *tap; - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; struct mgcp_endpoint *endp; struct mgcp_conn_rtp *conn; const char *conn_id = NULL; @@ -1195,7 +1183,7 @@ return CMD_WARNING; } - endp = &trunk->endpoints[endp_no]; + endp = trunk->endpoints[endp_no]; conn_id = argv[2]; conn = mgcp_conn_get_rtp(endp, conn_id); @@ -1225,7 +1213,7 @@ "free-endpoint <0-64> NUMBER", "Free the given endpoint\n" "Trunk number\n" "Endpoint number in hex.\n") { - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; struct mgcp_endpoint *endp; trunk = find_trunk(g_cfg, atoi(argv[0])); @@ -1248,7 +1236,7 @@ return CMD_WARNING; } - endp = &trunk->endpoints[endp_no]; + endp = trunk->endpoints[endp_no]; mgcp_endp_release(endp); return CMD_SUCCESS; } @@ -1257,7 +1245,7 @@ "reset-endpoint <0-64> NUMBER", "Reset the given endpoint\n" "Trunk number\n" "Endpoint number in hex.\n") { - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; struct mgcp_endpoint *endp; int endp_no, rc; @@ -1281,8 +1269,8 @@ return CMD_WARNING; } - endp = &trunk->endpoints[endp_no]; - rc = mgcp_send_reset_ep(endp, ENDPOINT_NUMBER(endp)); + endp = trunk->endpoints[endp_no]; + rc = mgcp_send_reset_ep(endp); if (rc < 0) { vty_out(vty, "Error %d sending reset.%s", rc, VTY_NEWLINE); return CMD_WARNING; @@ -1318,7 +1306,7 @@ else if (strcmp(argv[0], "only") == 0) g_cfg->osmux = OSMUX_USAGE_ONLY; - if (g_cfg->trunk.audio_loop) { + if (g_cfg->trunk->audio_loop) { vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); return CMD_WARNING; } @@ -1500,7 +1488,7 @@ enum mgcp_role role) { int rc; - struct mgcp_trunk_config *trunk; + struct mgcp_trunk *trunk; cfg->osmux_port = OSMUX_PORT; cfg->osmux_batch = 4; @@ -1519,15 +1507,15 @@ return -1; } - if (mgcp_endpoints_allocate(&g_cfg->trunk) != 0) { + if (mgcp_trunk_alloc_endpts(g_cfg->trunk) != 0) { LOGP(DLMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk (%d endpoints)\n", - g_cfg->trunk.number_endpoints); + g_cfg->trunk->number_endpoints); return -1; } llist_for_each_entry(trunk, &g_cfg->trunks, entry) { - if (mgcp_endpoints_allocate(trunk) != 0) { + if (mgcp_trunk_alloc_endpts(trunk) != 0) { LOGP(DLMGCP, LOGL_ERROR, "Failed to initialize trunk %d (%d endpoints)\n", trunk->trunk_nr, trunk->number_endpoints); diff --git a/src/osmo-mgw/mgw_main.c b/src/osmo-mgw/mgw_main.c index 48869c4..99e2499 100644 --- a/src/osmo-mgw/mgw_main.c +++ b/src/osmo-mgw/mgw_main.c @@ -38,6 +38,7 @@ #include <osmocom/mgcp/vty.h> #include <osmocom/mgcp/debug.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> #include <osmocom/mgcp/mgcp_ctrl.h> #include <osmocom/core/application.h> @@ -71,7 +72,7 @@ /* FIXME: Make use of the rtp proxy code */ static struct mgcp_config *cfg; -static struct mgcp_trunk_config *reset_trunk; +static struct mgcp_trunk *reset_trunk; static int reset_endpoints = 0; static int daemonize = 0; @@ -148,13 +149,13 @@ /* Callback function to be called when the RSIP ("Reset in Progress") mgcp * command is received */ -static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg) +static int mgcp_rsip_cb(struct mgcp_trunk *trunk) { /* Set flag so that, when read_call_agent() is called next time * the reset can progress */ reset_endpoints = 1; - reset_trunk = tcfg; + reset_trunk = trunk; return 0; } @@ -203,7 +204,7 @@ /* Walk over all endpoints and trigger a release, this will release all * endpoints, possible open connections are forcefully dropped */ for (i = 1; i < reset_trunk->number_endpoints; ++i) - mgcp_endp_release(&reset_trunk->endpoints[i]); + mgcp_endp_release(reset_trunk->endpoints[i]); } return 0; diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c index c72382e..1f74b01 100644 --- a/tests/mgcp/mgcp_test.c +++ b/tests/mgcp/mgcp_test.c @@ -26,6 +26,7 @@ #include <osmocom/mgcp/mgcp_stat.h> #include <osmocom/mgcp/mgcp_msg.h> #include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/mgcp/mgcp_trunk.h> #include <osmocom/mgcp/mgcp_sdp.h> #include <osmocom/mgcp/mgcp_codec.h> @@ -70,7 +71,7 @@ } #define AUEP1 "AUEP 158663169 ds/e1-1/2 at mgw MGCP 1.0\r\n" -#define AUEP1_RET "200 158663169 OK\r\n" +#define AUEP1_RET "500 158663169 FAIL\r\n" #define AUEP2 "AUEP 18983213 ds/e1-2/1 at mgw MGCP 1.0\r\n" #define AUEP2_RET "500 18983213 FAIL\r\n" #define EMPTY "\r\n" @@ -81,7 +82,7 @@ #define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1 at mgw MGCP 1.0\r\n" #define MDCX_ERR_RET "500 18983213 FAIL\r\n" #define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2 at mgw MGCP 1.0\r\n" -#define MDCX_RET "400 18983214 FAIL\r\n" +#define MDCX_RET "500 18983214 FAIL\r\n" #define MDCX3 \ "MDCX 18983215 1 at mgw MGCP 1.0\r\n" \ @@ -593,12 +594,22 @@ static int last_endpoint = -1; -static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint, - int state, const char *transactio_id) +static int mgcp_test_policy_cb(struct mgcp_endpoint *endp, + int state, const char *transaction_id) { - fprintf(stderr, "Policy CB got state %d on endpoint 0x%x\n", - state, endpoint); - last_endpoint = endpoint; + unsigned int i; + struct mgcp_trunk *trunk; + + fprintf(stderr, "Policy CB got state %d on endpoint %s\n", + state, endp->name); + + trunk = endp->trunk; + last_endpoint = -1; + for (i = 0; i < trunk->vty_number_endpoints; i++) { + if (strcmp(endp->name, trunk->endpoints[i]->name) == 0) + last_endpoint = i; + } + return MGCP_POLICY_CONT; } @@ -641,11 +652,11 @@ return real_clock_gettime(clk_id, tp); } -static void mgcp_endpoints_release(struct mgcp_trunk_config *trunk) +static void mgcp_endpoints_release(struct mgcp_trunk *trunk) { int i; for (i = 1; i < trunk->number_endpoints; i++) - mgcp_endp_release(&trunk->endpoints[i]); + mgcp_endp_release(trunk->endpoints[i]); } #define CONN_UNMODIFIED (0x1000) @@ -749,7 +760,7 @@ { struct mgcp_config *cfg; struct mgcp_endpoint *endp; - struct mgcp_trunk_config *trunk2; + struct mgcp_trunk *trunk2; int i; struct mgcp_conn_rtp *conn = NULL; char last_conn_id[256]; @@ -757,14 +768,14 @@ cfg = mgcp_config_alloc(); - cfg->trunk.vty_number_endpoints = 64; - mgcp_endpoints_allocate(&cfg->trunk); + cfg->trunk->vty_number_endpoints = 64; + mgcp_trunk_alloc_endpts(cfg->trunk); cfg->policy_cb = mgcp_test_policy_cb; memset(last_conn_id, 0, sizeof(last_conn_id)); - trunk2 = mgcp_trunk_alloc(cfg, 1); - mgcp_endpoints_allocate(trunk2); + trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1); + mgcp_trunk_alloc_endpts(trunk2); for (i = 0; i < ARRAY_SIZE(tests); i++) { const struct mgcp_test *t = &tests[i]; @@ -777,7 +788,7 @@ last_endpoint = -1; dummy_packets = 0; - osmo_talloc_replace_string(cfg, &cfg->trunk.audio_fmtp_extra, + osmo_talloc_replace_string(cfg, &cfg->trunk->audio_fmtp_extra, t->extra_fmtp); inp = create_msg(t->req, last_conn_id); @@ -810,7 +821,7 @@ printf("Dummy packets: %d\n", dummy_packets); if (last_endpoint != -1) { - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; conn = mgcp_conn_get_rtp(endp, "1"); if (conn) { @@ -866,7 +877,7 @@ /* Check detected payload type */ if (conn && t->ptype != PTYPE_IGNORE) { OSMO_ASSERT(last_endpoint != -1); - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; fprintf(stderr, "endpoint 0x%x: " "payload type %d (expected %d)\n", @@ -883,27 +894,27 @@ } mgcp_endpoints_release(trunk2); - mgcp_endpoints_release(&cfg->trunk); + mgcp_endpoints_release(cfg->trunk); talloc_free(cfg); } static void test_retransmission(void) { struct mgcp_config *cfg; - struct mgcp_trunk_config *trunk2; + struct mgcp_trunk *trunk2; int i; char last_conn_id[256]; int rc; cfg = mgcp_config_alloc(); - cfg->trunk.vty_number_endpoints = 64; - mgcp_endpoints_allocate(&cfg->trunk); + cfg->trunk->vty_number_endpoints = 64; + mgcp_trunk_alloc_endpts(cfg->trunk); memset(last_conn_id, 0, sizeof(last_conn_id)); - trunk2 = mgcp_trunk_alloc(cfg, 1); - mgcp_endpoints_allocate(trunk2); + trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1); + mgcp_trunk_alloc_endpts(trunk2); for (i = 0; i < ARRAY_SIZE(retransmit); i++) { const struct mgcp_test *t = &retransmit[i]; @@ -944,7 +955,7 @@ } mgcp_endpoints_release(trunk2); - mgcp_endpoints_release(&cfg->trunk); + mgcp_endpoints_release(cfg->trunk); talloc_free(cfg); } @@ -958,18 +969,18 @@ static void test_rqnt_cb(void) { struct mgcp_config *cfg; - struct mgcp_trunk_config *trunk2; + struct mgcp_trunk *trunk2; struct msgb *inp, *msg; char conn_id[256]; cfg = mgcp_config_alloc(); cfg->rqnt_cb = rqnt_cb; - cfg->trunk.vty_number_endpoints = 64; - mgcp_endpoints_allocate(&cfg->trunk); + cfg->trunk->vty_number_endpoints = 64; + mgcp_trunk_alloc_endpts(cfg->trunk); - trunk2 = mgcp_trunk_alloc(cfg, 1); - mgcp_endpoints_allocate(trunk2); + trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1); + mgcp_trunk_alloc_endpts(trunk2); inp = create_msg(CRCX, NULL); msg = mgcp_handle_message(cfg, inp); @@ -999,7 +1010,7 @@ msgb_free(mgcp_handle_message(cfg, inp)); msgb_free(inp); mgcp_endpoints_release(trunk2); - mgcp_endpoints_release(&cfg->trunk); + mgcp_endpoints_release(cfg->trunk); talloc_free(cfg); } @@ -1035,8 +1046,9 @@ { int i; struct mgcp_endpoint endp; + struct mgcp_endpoint *endpoints[1]; struct mgcp_config cfg = {0}; - struct mgcp_trunk_config trunk; + struct mgcp_trunk trunk; printf("Testing packet loss calculation.\n"); @@ -1046,8 +1058,9 @@ endp.cfg = &cfg; endp.type = &ep_typeset.rtp; trunk.vty_number_endpoints = 1; - trunk.endpoints = &endp; - endp.tcfg = &trunk; + trunk.endpoints = endpoints; + trunk.endpoints[0] = &endp; + endp.trunk = &trunk; INIT_LLIST_HEAD(&endp.conns); for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) { @@ -1262,8 +1275,9 @@ { int i; - struct mgcp_trunk_config trunk; + struct mgcp_trunk trunk; struct mgcp_endpoint endp; + struct mgcp_endpoint *endpoints[1]; struct mgcp_config cfg = {0}; struct mgcp_rtp_state state; struct mgcp_rtp_end *rtp; @@ -1296,11 +1310,12 @@ endp.type = &ep_typeset.rtp; trunk.vty_number_endpoints = 1; - trunk.endpoints = &endp; + trunk.endpoints = endpoints; + trunk.endpoints[0] = &endp; trunk.force_constant_ssrc = patch_ssrc; trunk.force_aligned_timing = patch_ts; - endp.tcfg = &trunk; + endp.trunk = &trunk; INIT_LLIST_HEAD(&endp.conns); _conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP, @@ -1361,7 +1376,7 @@ static void test_multilple_codec(void) { struct mgcp_config *cfg; - struct mgcp_trunk_config *trunk2; + struct mgcp_trunk *trunk2; struct mgcp_endpoint *endp; struct msgb *inp, *resp; struct in_addr addr; @@ -1371,12 +1386,12 @@ printf("Testing multiple payload types\n"); cfg = mgcp_config_alloc(); - cfg->trunk.vty_number_endpoints = 64; - mgcp_endpoints_allocate(&cfg->trunk); + cfg->trunk->vty_number_endpoints = 64; + mgcp_trunk_alloc_endpts(cfg->trunk); cfg->policy_cb = mgcp_test_policy_cb; - trunk2 = mgcp_trunk_alloc(cfg, 1); - mgcp_endpoints_allocate(trunk2); + trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1); + mgcp_trunk_alloc_endpts(trunk2); /* Allocate endpoint 1 at mgw with two codecs */ last_endpoint = -1; @@ -1388,7 +1403,7 @@ msgb_free(resp); OSMO_ASSERT(last_endpoint == 1); - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec->payload_type == 18); @@ -1403,7 +1418,7 @@ msgb_free(resp); OSMO_ASSERT(last_endpoint == 2); - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec->payload_type == 18); @@ -1423,7 +1438,7 @@ msgb_free(resp); OSMO_ASSERT(last_endpoint == 3); - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec->payload_type == 0); @@ -1438,7 +1453,7 @@ msgb_free(resp); OSMO_ASSERT(last_endpoint == 4); - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec->payload_type == 18); @@ -1446,9 +1461,9 @@ /* Allocate 5 at mgw at select GSM.. */ last_endpoint = -1; inp = create_msg(CRCX_MULT_GSM_EXACT, NULL); - talloc_free(cfg->trunk.audio_name); - cfg->trunk.audio_name = "GSM/8000"; - cfg->trunk.no_audio_transcoding = 1; + talloc_free(cfg->trunk->audio_name); + cfg->trunk->audio_name = "GSM/8000"; + cfg->trunk->no_audio_transcoding = 1; resp = mgcp_handle_message(cfg, inp); OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, sizeof(conn_id)) == 0); @@ -1456,7 +1471,7 @@ msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec->payload_type == 3); @@ -1467,7 +1482,7 @@ msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec->payload_type == 3); @@ -1489,7 +1504,7 @@ last_endpoint = -1; inp = create_msg(CRCX_MULT_GSM_EXACT, NULL); - cfg->trunk.no_audio_transcoding = 0; + cfg->trunk->no_audio_transcoding = 0; resp = mgcp_handle_message(cfg, inp); OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, sizeof(conn_id)) == 0); @@ -1497,13 +1512,13 @@ msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); - endp = &cfg->trunk.endpoints[last_endpoint]; + endp = cfg->trunk->endpoints[last_endpoint]; conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec->payload_type == 0); mgcp_endpoints_release(trunk2); - mgcp_endpoints_release(&cfg->trunk); + mgcp_endpoints_release(cfg->trunk); talloc_free(cfg); } @@ -1517,10 +1532,10 @@ printf("Testing no sequence flow on initial packet\n"); cfg = mgcp_config_alloc(); - cfg->trunk.vty_number_endpoints = 64; - mgcp_endpoints_allocate(&cfg->trunk); + cfg->trunk->vty_number_endpoints = 64; + mgcp_trunk_alloc_endpts(cfg->trunk); - endp = &cfg->trunk.endpoints[1]; + endp = cfg->trunk->endpoints[1]; _conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, "test-connection"); @@ -1552,27 +1567,27 @@ OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1); OSMO_ASSERT(conn->state.stats.max_seq == 0); - mgcp_endpoints_release(&cfg->trunk); + mgcp_endpoints_release(cfg->trunk); talloc_free(cfg); } static void test_no_name(void) { - struct mgcp_trunk_config *trunk2; + struct mgcp_trunk *trunk2; struct mgcp_config *cfg; struct msgb *inp, *msg; printf("Testing no rtpmap name\n"); cfg = mgcp_config_alloc(); - cfg->trunk.vty_number_endpoints = 64; - cfg->trunk.audio_send_name = 0; - mgcp_endpoints_allocate(&cfg->trunk); + cfg->trunk->vty_number_endpoints = 64; + cfg->trunk->audio_send_name = 0; + mgcp_trunk_alloc_endpts(cfg->trunk); cfg->policy_cb = mgcp_test_policy_cb; - trunk2 = mgcp_trunk_alloc(cfg, 1); - mgcp_endpoints_allocate(trunk2); + trunk2 = mgcp_trunk_alloc(cfg, 1, MGCP_TRUNK_E1); + mgcp_trunk_alloc_endpts(trunk2); inp = create_msg(CRCX, NULL); msg = mgcp_handle_message(cfg, inp); @@ -1586,7 +1601,7 @@ msgb_free(msg); mgcp_endpoints_release(trunk2); - mgcp_endpoints_release(&cfg->trunk); + mgcp_endpoints_release(cfg->trunk); talloc_free(cfg); } -- To view, visit https://gerrit.osmocom.org/c/osmo-mgw/+/18372 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-mgw Gerrit-Branch: master Gerrit-Change-Id: Ice8aaf03faa2fd99074f8665eea3a696d30c5eb3 Gerrit-Change-Number: 18372 Gerrit-PatchSet: 1 Gerrit-Owner: dexter <pmaier at sysmocom.de> Gerrit-MessageType: newchange -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200519/eed4e750/attachment.htm>