256 lines
8.1 KiB
C
256 lines
8.1 KiB
C
/* Trunk handling */
|
|
|
|
/*
|
|
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
* (C) 2009-2012 by On-Waves
|
|
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@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/mgcp/mgcp.h>
|
|
#include <osmocom/mgcp/mgcp_protocol.h>
|
|
#include <osmocom/mgcp/mgcp_endp.h>
|
|
#include <osmocom/mgcp/mgcp_trunk.h>
|
|
#include <osmocom/mgcp/mgcp_e1.h>
|
|
#include <osmocom/abis/e1_input.h>
|
|
|
|
/*! allocate trunk and add it (if required) to the trunk list.
|
|
* (called once at startup by VTY).
|
|
* \param[in] cfg mgcp configuration.
|
|
* \param[in] ttype trunk type.
|
|
* \param[in] nr trunk number.
|
|
* \returns pointer to allocated trunk, NULL on failure. */
|
|
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
|
|
{
|
|
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 = ttype;
|
|
trunk->trunk_nr = nr;
|
|
|
|
trunk->audio_send_ptime = 1;
|
|
trunk->audio_send_name = 1;
|
|
trunk->v.vty_number_endpoints = 512;
|
|
trunk->omit_rtcp = 0;
|
|
|
|
mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
|
|
|
|
llist_add_tail(&trunk->entry, &cfg->trunks);
|
|
|
|
mgcp_ratectr_trunk_alloc(cfg, &trunk->ratectr);
|
|
|
|
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;
|
|
struct mgcp_endpoint *endp;
|
|
unsigned int number_endpoints;
|
|
unsigned int first_endpoint_nr;
|
|
|
|
/* This function is called once on startup by the VTY to allocate the
|
|
* endpoints. The number of endpoints must not change througout the
|
|
* runtime of the MGW */
|
|
OSMO_ASSERT(trunk->number_endpoints == 0);
|
|
OSMO_ASSERT(trunk->endpoints == NULL);
|
|
|
|
switch (trunk->trunk_type) {
|
|
case MGCP_TRUNK_VIRTUAL:
|
|
/* Due to historical reasons the endpoints on the virtual
|
|
* trunk start counting at 1. */
|
|
first_endpoint_nr = 1;
|
|
number_endpoints = trunk->v.vty_number_endpoints;
|
|
break;
|
|
case MGCP_TRUNK_E1:
|
|
/* The first timeslot on an E1 line is reserved for framing
|
|
* and alignment and can not be used for audio transport */
|
|
first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;
|
|
number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(false);
|
|
}
|
|
|
|
/* Make sure the amount of requested endpoints does not execeed
|
|
* sane limits. The VTY already limits the possible amount,
|
|
* however miss-initialization of the struct or memory corruption
|
|
* could still lead to an excessive allocation of endpoints, so
|
|
* better stop early if that is the case. */
|
|
OSMO_ASSERT(number_endpoints < 65534);
|
|
|
|
/* allocate pointer array for the endpoints */
|
|
trunk->endpoints = talloc_zero_array(trunk->cfg, struct mgcp_endpoint*,
|
|
number_endpoints);
|
|
if (!trunk->endpoints)
|
|
return -1;
|
|
|
|
/* create endpoints */
|
|
for (i = 0; i < number_endpoints; i++) {
|
|
endp = mgcp_endp_alloc(trunk, i + first_endpoint_nr);
|
|
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 = number_endpoints;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! Equip trunk with endpoints and resources
|
|
* (called once at startup by VTY).
|
|
* \param[in] trunk trunk configuration.
|
|
* \returns 0 on success, -1 on failure. */
|
|
int mgcp_trunk_equip(struct mgcp_trunk *trunk)
|
|
{
|
|
unsigned int i;
|
|
|
|
/* Allocate endpoints */
|
|
if(mgcp_trunk_alloc_endpts(trunk) != 0)
|
|
return -1;
|
|
|
|
/* Allocate resources */
|
|
switch (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:
|
|
/* The TS initalization happens once on startup for all
|
|
* timeslots. This only affects the i460 multiplexer. Until
|
|
* now no E1 resources are claimed yet. This happens on demand
|
|
* when the related endpoint is actually used */
|
|
memset(trunk->e1.i460_ts, 0, sizeof(trunk->e1.i460_ts));
|
|
for (i = 0; i < (NUM_E1_TS-1); i++)
|
|
osmo_i460_ts_init(&trunk->e1.i460_ts[i]);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*! get trunk configuration by trunk number (index).
|
|
* \param[in] cfg mgcp configuration.
|
|
* \param[in] ttype trunk type.
|
|
* \param[in] nr trunk number.
|
|
* \returns pointer to trunk configuration, NULL on error. */
|
|
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
|
|
{
|
|
struct mgcp_trunk *trunk;
|
|
|
|
llist_for_each_entry(trunk, &cfg->trunks, entry) {
|
|
if (trunk->trunk_nr == nr && trunk->trunk_type == ttype)
|
|
return trunk;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Made public for unit-testing, do not use from outside this file */
|
|
int e1_trunk_nr_from_epname(const char *epname)
|
|
{
|
|
unsigned long int trunk_nr;
|
|
size_t prefix_len;
|
|
char *str_trunk_nr_end;
|
|
|
|
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
|
|
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) != 0)
|
|
return -EINVAL;
|
|
|
|
errno = 0;
|
|
trunk_nr = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
|
|
if (errno == ERANGE || trunk_nr > 64
|
|
|| epname + prefix_len == str_trunk_nr_end
|
|
|| str_trunk_nr_end[0] != '/')
|
|
return -EINVAL;
|
|
else
|
|
return trunk_nr;
|
|
}
|
|
|
|
/*! 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 trunk was not found. */
|
|
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname)
|
|
{
|
|
size_t prefix_len;
|
|
char epname_lc[MGCP_ENDPOINT_MAXLEN];
|
|
int trunk_nr;
|
|
|
|
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 mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
|
}
|
|
|
|
trunk_nr = e1_trunk_nr_from_epname(epname);
|
|
if (trunk_nr >= 0)
|
|
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
|
|
|
|
/* 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, "missing trunk prefix in endpoint name \"%s\", assuming trunk \"%s\"!\n", epname,
|
|
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
|
|
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
|
}
|
|
|
|
LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
|
|
return NULL;
|
|
}
|
|
|
|
/*! Find a trunk (E1) by its associated E1 line number.
|
|
* \param[in] num e1 line number.
|
|
* \returns trunk or NULL if trunk was not found. */
|
|
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num)
|
|
{
|
|
/*! When used on trunks other than E1, the result will always be NULL. */
|
|
struct mgcp_trunk *trunk;
|
|
|
|
llist_for_each_entry(trunk, &cfg->trunks, entry) {
|
|
if (trunk->trunk_type == MGCP_TRUNK_E1 && trunk->e1.vty_line_nr == num)
|
|
return trunk;
|
|
}
|
|
|
|
return NULL;
|
|
}
|