| /* Endpoint types */ |
| |
| /* |
| * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| * All Rights Reserved |
| * |
| * Author: Philipp Maier |
| * |
| * 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/mgcp/mgcp.h> |
| #include <osmocom/mgcp/mgcp_protocol.h> |
| #include <osmocom/mgcp/mgcp_conn.h> |
| #include <osmocom/mgcp/mgcp_endp.h> |
| #include <osmocom/mgcp/mgcp_trunk.h> |
| |
| #include <osmocom/abis/e1_input.h> |
| #include <osmocom/mgcp/mgcp_e1.h> |
| #include <osmocom/core/stat_item.h> |
| |
| #define E1_RATE_MAX 64 |
| #define E1_OFFS_MAX 8 |
| |
| /* Endpoint typeset definition */ |
| const struct mgcp_endpoint_typeset ep_typeset = { |
| /* Specify endpoint properties for RTP endpoint */ |
| .rtp = { |
| .max_conns = 2, |
| .dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb, |
| .cleanup_cb = mgcp_cleanup_rtp_bridge_cb, |
| }, |
| /* Specify endpoint properties for E1 endpoint */ |
| .e1 = { |
| .max_conns = 1, |
| .dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb, |
| .cleanup_cb = mgcp_cleanup_e1_bridge_cb, |
| }, |
| }; |
| |
| /* Generate virtual endpoint name from given parameters */ |
| static char *gen_virtual_epname(void *ctx, const char *domain, |
| unsigned int index) |
| { |
| return talloc_asprintf(ctx, "%s%x@%s", |
| MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain); |
| } |
| |
| /* Generate E1 endpoint name from given numeric parameters */ |
| static char *gen_e1_epname(void *ctx, const char *domain, unsigned int trunk_nr, |
| uint8_t ts_nr, uint8_t ss_nr) |
| { |
| unsigned int rate; |
| unsigned int offset; |
| |
| OSMO_ASSERT(ss_nr < sizeof(e1_rates)); |
| |
| rate = e1_rates[ss_nr]; |
| offset = e1_offsets[ss_nr]; |
| |
| return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s", |
| MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr, |
| rate, offset, domain); |
| } |
| |
| /*! allocate an endpoint and set default values. |
| * \param[in] trunk configuration. |
| * \param[in] name endpoint index. |
| * \returns endpoint on success, NULL on failure. */ |
| struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, |
| unsigned int index) |
| { |
| struct mgcp_endpoint *endp; |
| |
| endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint); |
| if (!endp) |
| return NULL; |
| |
| INIT_LLIST_HEAD(&endp->conns); |
| endp->trunk = trunk; |
| |
| switch (trunk->trunk_type) { |
| case MGCP_TRUNK_VIRTUAL: |
| endp->type = &ep_typeset.rtp; |
| endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index); |
| break; |
| case MGCP_TRUNK_E1: |
| endp->type = &ep_typeset.e1; |
| endp->name = gen_e1_epname(endp, trunk->cfg->domain, |
| trunk->trunk_nr, |
| index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS); |
| 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) |
| { |
| LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n"); |
| |
| /* Normally this function should only be called when |
| * all connections have been removed already. In case |
| * that there are still connections open (e.g. when |
| * RSIP is executed), free them all at once. */ |
| mgcp_conn_free_all(endp); |
| |
| /* We must only decrement the stat item when the endpoint as actually |
| * claimed. An endpoint is claimed when a call-id is set */ |
| if (endp->callid) |
| osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common, |
| TRUNK_STAT_ENDPOINTS_USED), 1); |
| |
| /* Reset endpoint parameters and states */ |
| talloc_free(endp->callid); |
| endp->callid = NULL; |
| talloc_free(endp->local_options.string); |
| endp->local_options.string = NULL; |
| talloc_free(endp->local_options.codec); |
| endp->local_options.codec = NULL; |
| |
| if (endp->trunk->trunk_type == MGCP_TRUNK_E1) |
| mgcp_e1_endp_release(endp); |
| } |
| |
| /* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or |
| * "ds/e1-") and write the epname without the prefix back to the memory |
| * pointed at by epname. (per trunk the prefix is the same for all endpoints, |
| * so no ambiguity is introduced) */ |
| static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk) |
| { |
| 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) |
| memmove(epname, epname + prefix_len, |
| strlen(epname) - prefix_len + 1); |
| return; |
| case MGCP_TRUNK_E1: |
| prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1; |
| if (strncmp |
| (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, |
| prefix_len) == 0) |
| memmove(epname, epname + prefix_len, |
| strlen(epname) - prefix_len + 1); |
| return; |
| default: |
| OSMO_ASSERT(false); |
| } |
| } |
| |
| /* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate |
| * epname by writing a '\0' char where the suffix starts. */ |
| static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk) |
| { |
| char *suffix_begin; |
| |
| /* Endpoints on the virtual trunk may have a domain name that is |
| * followed after an @ character, this can be chopped off. All |
| * other supported trunk types do not have any suffixes that may |
| * be chopped off */ |
| if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) { |
| suffix_begin = strchr(epname, '@'); |
| if (!suffix_begin) |
| return; |
| *suffix_begin = '\0'; |
| } |
| } |
| |
| |
| /*! Convert all characters in epname to lowercase and strip trunk prefix and |
| * endpoint name suffix (domain name) from epname. The result is written to |
| * to the memory pointed at by epname_stripped. The expected size of the |
| * result is either equal or lower then the length of the input string |
| * (epname) |
| * \param[out] epname_stripped pointer to store the stripped ep name. |
| * \param[in] epname endpoint name to lookup. |
| * \param[in] trunk where the endpoint is located. */ |
| void mgcp_endp_strip_name(char *epname_stripped, const char *epname, |
| const struct mgcp_trunk *trunk) |
| { |
| osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname); |
| chop_epname_prefix(epname_stripped, trunk); |
| chop_epname_suffix(epname_stripped, trunk); |
| } |
| |
| /* Go through the trunk and find a random free (no active calls) endpoint, |
| * this function is called when a wildcarded request is carried out, which |
| * means that it is up to the MGW to choose a random free endpoint. */ |
| static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk) |
| { |
| struct mgcp_endpoint *endp; |
| unsigned int i; |
| |
| for (i = 0; i < trunk->number_endpoints; i++) { |
| endp = trunk->endpoints[i]; |
| /* A free endpoint must not serve a call already and it must |
| * be available. */ |
| if (endp->callid == NULL && mgcp_endp_avail(endp)) |
| return endp; |
| } |
| |
| return NULL; |
| } |
| |
| /*! Find an endpoint of a trunk specified by its name. |
| * \param[in] epname endpoint name to check. |
| * \param[in] trunk mgcp_trunk that might have this endpoint. |
| * \returns NULL if no ep found, else endpoint. */ |
| struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname, |
| const struct mgcp_trunk *trunk) |
| { |
| char epname_stripped[MGCP_ENDPOINT_MAXLEN]; |
| char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN]; |
| struct mgcp_endpoint *endp; |
| unsigned int i; |
| |
| /* Strip irrelevant information from the endpoint name */ |
| mgcp_endp_strip_name(epname_stripped, epname, trunk); |
| |
| for (i = 0; i < trunk->number_endpoints; i++) { |
| endp = trunk->endpoints[i]; |
| mgcp_endp_strip_name(epname_stripped_endp, endp->name, trunk); |
| if (strcmp(epname_stripped_endp, epname_stripped) == 0) |
| return endp; |
| } |
| |
| return NULL; |
| } |
| |
| /*! Check if the given epname refers to a wildcarded request or to a specific |
| * endpoint. |
| * \param[in] epname endpoint name to check |
| * \returns true if epname refers to wildcarded request, else false. */ |
| bool mgcp_endp_is_wildcarded(const char *epname) |
| { |
| if (strstr(epname, "*")) |
| return true; |
| |
| return false; |
| } |
| |
| /*! 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. |
| * \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, |
| const struct mgcp_trunk *trunk) |
| { |
| struct mgcp_endpoint *endp; |
| |
| if (cause) |
| *cause = 0; |
| |
| /* At the moment we only support a primitive ('*'-only) method of |
| * wildcarded endpoint searches that picks the next free endpoint on |
| * a trunk. */ |
| if (mgcp_endp_is_wildcarded(epname)) { |
| endp = find_free_endpoint(trunk); |
| if (endp) { |
| LOGPENDP(endp, DLMGCP, LOGL_DEBUG, |
| "(trunk:%d) found free endpoint: %s\n", |
| trunk->trunk_nr, endp->name); |
| return endp; |
| } |
| |
| LOGP(DLMGCP, LOGL_ERROR, |
| "(trunk:%d) Not able to find a free endpoint\n", |
| trunk->trunk_nr); |
| if (cause) |
| *cause = -403; |
| return NULL; |
| } |
| |
| /* Find an endpoint by its name (if wildcarded request is not |
| * applicable) */ |
| endp = mgcp_endp_find_specific(epname, trunk); |
| if (endp) { |
| LOGPENDP(endp, DLMGCP, LOGL_DEBUG, |
| "(trunk:%d) found endpoint: %s\n", |
| trunk->trunk_nr, endp->name); |
| return endp; |
| } |
| |
| LOGP(DLMGCP, LOGL_ERROR, |
| "(trunk:%d) Not able to find specified endpoint: %s\n", |
| trunk->trunk_nr, epname); |
| if (cause) |
| *cause = -500; |
| |
| return NULL; |
| } |
| |
| /*! 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(cfg, epname); |
| if (!trunk) |
| 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; |
| } |
| |
| /* Get the E1 timeslot number from a given E1 endpoint name |
| * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */ |
| static uint8_t e1_ts_nr_from_epname(const char *epname) |
| { |
| char buf[MGCP_ENDPOINT_MAXLEN + 1]; |
| char *save_ptr = NULL; |
| char *buf_ptr = buf; |
| char *token; |
| unsigned long int res = 0; |
| |
| strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN); |
| |
| while (1) { |
| token = strtok_r(buf_ptr, "/", &save_ptr); |
| buf_ptr = NULL; |
| if (!token) |
| break; |
| if (strncmp(token, "s-", 2) == 0) { |
| errno = 0; |
| res = strtoul(token + 2, NULL, 10); |
| if (errno == ERANGE || res > NUM_E1_TS) |
| return 0xff; |
| return (uint8_t) res; |
| } |
| } |
| |
| return 0xff; |
| } |
| |
| /* Get the E1 timeslot number from a given E1 endpoint name |
| * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */ |
| static uint8_t e1_rate_from_epname(const char *epname) |
| { |
| char buf[MGCP_ENDPOINT_MAXLEN + 1]; |
| char *save_ptr = NULL; |
| char *buf_ptr = buf; |
| char *token; |
| unsigned long int res = 0; |
| unsigned int i; |
| |
| strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN); |
| |
| while (1) { |
| token = strtok_r(buf_ptr, "/", &save_ptr); |
| buf_ptr = NULL; |
| if (!token) |
| break; |
| if (strncmp(token, "su", 2) == 0) { |
| errno = 0; |
| res = strtoul(token + 2, NULL, 10); |
| if (errno == ERANGE || res > E1_RATE_MAX) |
| return 0xff; |
| /* Make sure the rate is a valid rate */ |
| for (i = 0; i < sizeof(e1_rates); i++) { |
| if (res == e1_rates[i]) |
| return (uint8_t) res; |
| } |
| return 0xff; |
| } |
| } |
| |
| return 0xff; |
| } |
| |
| /* Get the E1 bitstream offset from a given E1 endpoint name |
| * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */ |
| static uint8_t e1_offs_from_epname(const char *epname) |
| { |
| char buf[MGCP_ENDPOINT_MAXLEN + 1]; |
| char *save_ptr = NULL; |
| char *buf_ptr = buf; |
| char *token; |
| unsigned long int res = 0; |
| |
| strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN); |
| |
| while (1) { |
| token = strtok_r(buf_ptr, "/", &save_ptr); |
| buf_ptr = NULL; |
| if (!token) |
| break; |
| if (strncmp(token, "su", 2) == 0) { |
| token = strstr(token, "-"); |
| if (!token) |
| return 0xff; |
| token += 1; |
| errno = 0; |
| res = strtoul(token, NULL, 10); |
| if (errno == ERANGE || res > E1_OFFS_MAX) |
| return 0xff; |
| return (uint8_t) res; |
| } |
| } |
| |
| return 0xff; |
| } |
| |
| /* Get the E1 subslot number (internal) from a given E1 endpoint name |
| * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */ |
| static uint8_t e1_ss_nr_from_epname(const char *epname) |
| { |
| uint8_t rate; |
| uint8_t offs; |
| unsigned int i; |
| |
| rate = e1_rate_from_epname(epname); |
| offs = e1_offs_from_epname(epname); |
| |
| osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size); |
| |
| for (i = 0; i < sizeof(e1_rates); i++) { |
| if ((e1_rates[i] == rate) && (e1_offsets[i] == offs)) |
| return i; |
| } |
| |
| return 0xff; |
| } |
| |
| /* Check if the selected E1 endpoint is avalable, which means that none of |
| * the overlapping endpoints are currently serving a call. (if the system |
| * is properly configured such a situation should never ocurr!) */ |
| static bool endp_avail_e1(struct mgcp_endpoint *endp) |
| { |
| /* The following map shows the overlapping of the subslots and their |
| * respective rates. The numbers on the right running from top to bottom |
| * are the bit offsets in the whole 64k timeslot. The numbers inside the |
| * boxes symbolize the internal subslot number (array index) and the |
| * rate in the form: i:r where i is the subslot number and r the |
| * respective rate. |
| * |
| * +--------+--------+--------+--------+ 0 |
| * | | | | 7:8k | |
| * | | + 3:16k +--------+ 1 |
| * | | | | 8:8k | |
| * | | 1:32k +--------+--------+ 2 |
| * | | | | 9:8k | |
| * | | + 4:16k +--------+ 3 |
| * | | | | 10:8k | |
| * | 0:64k +--------+--------+--------+ 4 |
| * | | | | 11:8k | |
| * | | + 5:16k +--------+ 5 |
| * | | | | 12:8k | |
| * | | 2:32k +--------+--------+ 6 |
| * | | | | 13:8k | |
| * | | + 6:16k +--------+ 7 |
| * | | | | 14:8k | |
| * +--------+--------+--------+--------+ 8 |
| * |
| * The following array contains tables with the subslot numbers that must be |
| * unused for each subslot. During this test we do not have to check the |
| * endpoint we need to verify, only the overlaps need to be checked. This is |
| * also the reason why the related subslot number is missing from each each |
| * line. */ |
| const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = { |
| { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 }, |
| { 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } }; |
| |
| const int8_t *interlock; |
| unsigned int i; |
| uint8_t ts_nr = 0; |
| uint8_t ss_nr = 0; |
| char *epname_check; |
| struct mgcp_endpoint *endp_check; |
| bool available = true; |
| |
| /* This function must only be used with E1 type endpoints! */ |
| OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1); |
| |
| ts_nr = e1_ts_nr_from_epname(endp->name); |
| ss_nr = e1_ss_nr_from_epname(endp->name); |
| if (ts_nr == 0xff || ss_nr == 0xff) { |
| LOGPENDP(endp, DLMGCP, LOGL_ERROR, |
| "cannot check endpoint availability, endpoint name not parseable!\n"); |
| return false; |
| } |
| |
| interlock = interlock_tab[ss_nr]; |
| |
| for (i = 0; i < sizeof(interlock_tab[0]); i++) { |
| /* Detect row end */ |
| if (interlock[i] == -1) |
| break; |
| |
| /* Pick overlapping endpoint to check */ |
| epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain, |
| endp->trunk->trunk_nr, ts_nr, |
| interlock[i]); |
| endp_check = mgcp_endp_find_specific(epname_check, endp->trunk); |
| if (!endp_check) { |
| LOGPENDP(endp, DLMGCP, LOGL_ERROR, |
| "cannot check endpoint availability, overlapping endpoint:%s not found!\n", |
| epname_check); |
| talloc_free(epname_check); |
| continue; |
| } |
| talloc_free(epname_check); |
| |
| /* Check if overlapping endpoint currently serves another call |
| * (This is an exceptional situation, that should not occur |
| * in a properly configured environment!) */ |
| if (endp_check->callid) { |
| LOGPENDP(endp, DLMGCP, LOGL_ERROR, |
| "endpoint unavailable - overlapping endpoint:%s already serves a call!\n", |
| endp_check->name); |
| available = false; |
| } |
| } |
| |
| return available; |
| } |
| |
| /*! check if an endpoint is available for any kind of operation. |
| * \param[in] endp endpoint to check. |
| * \returns true if endpoint is avalable, false it is blocked for any reason. */ |
| bool mgcp_endp_avail(struct mgcp_endpoint *endp) |
| { |
| switch (endp->trunk->trunk_type) { |
| case MGCP_TRUNK_VIRTUAL: |
| /* There are no obstacles that may render a virtual trunk |
| * endpoint unusable, so virtual trunk endpoints are always |
| * available */ |
| return true; |
| case MGCP_TRUNK_E1: |
| return endp_avail_e1(endp); |
| default: |
| OSMO_ASSERT(false); |
| } |
| |
| return false; |
| } |
| |
| /*! claim endpoint, sets callid and activates endpoint, should be called at the |
| * beginning of the CRCX procedure when it is clear that a new call should be |
| * created. |
| * \param[in] endp endpoint to claim. |
| * \param[in] callid that is assingned to this endpoint. */ |
| int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid) |
| { |
| int rc = 0; |
| uint8_t ts; |
| uint8_t ss; |
| uint8_t offs; |
| |
| /* TODO: Make this function more intelligent, it should run the |
| * call id checks we currently have in protocol.c directly here. */ |
| |
| /* 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(endp, callid); |
| OSMO_ASSERT(endp->callid); |
| osmo_stat_item_inc(osmo_stat_item_group_get_item(endp->trunk->stats.common, |
| TRUNK_STAT_ENDPOINTS_USED), 1); |
| |
| /* Allocate resources */ |
| switch (endp->trunk->trunk_type) { |
| case MGCP_TRUNK_VIRTUAL: |
| /* No additional initaliziation required here, virtual |
| * endpoints will open/close network sockets themselves |
| * on demand. */ |
| break; |
| case MGCP_TRUNK_E1: |
| ts = e1_ts_nr_from_epname(endp->name); |
| ss = e1_ss_nr_from_epname(endp->name); |
| offs = e1_offs_from_epname(endp->name); |
| OSMO_ASSERT(ts != 0xFF); |
| OSMO_ASSERT(ts != 0); |
| OSMO_ASSERT(ss != 0xFF); |
| OSMO_ASSERT(offs != 0xFF); |
| rc = mgcp_e1_endp_equip(endp, ts, ss, offs); |
| break; |
| default: |
| OSMO_ASSERT(false); |
| } |
| |
| /* Make sure the endpoint is released when claiming the endpoint fails. */ |
| if (rc < 0) |
| mgcp_endp_release(endp); |
| |
| return rc; |
| } |
| |
| /*! update endpoint, updates internal endpoint specific data, should be |
| * after when MDCX or CRCX has been executed successuflly. |
| * \param[in] endp endpoint to update. */ |
| void mgcp_endp_update(struct mgcp_endpoint *endp) |
| { |
| /* Allocate resources */ |
| switch (endp->trunk->trunk_type) { |
| case MGCP_TRUNK_VIRTUAL: |
| /* No updating initaliziation required for virtual endpoints. */ |
| break; |
| case MGCP_TRUNK_E1: |
| mgcp_e1_endp_update(endp); |
| break; |
| default: |
| OSMO_ASSERT(false); |
| } |
| } |
| |
| void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn) |
| { |
| llist_add(&conn->entry, &endp->conns); |
| } |
| |
| void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn) |
| { |
| /* Run endpoint cleanup action. By this we inform the endpoint about |
| * the removal of the connection and allow it to clean up its inner |
| * state accordingly */ |
| if (endp->type->cleanup_cb) |
| endp->type->cleanup_cb(endp, conn); |
| llist_del(&conn->entry); |
| if (llist_empty(&endp->conns)) |
| mgcp_endp_release(endp); |
| } |