/* Trunk handling */ /* * (C) 2009-2012 by Holger Hans Peter Freyther * (C) 2009-2012 by On-Waves * (C) 2017-2020 by sysmocom s.f.m.c. GmbH * 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 . * */ #include #include #include #include #include #include const struct value_string mgcp_trunk_type_strs[] = { { MGCP_TRUNK_VIRTUAL, "virtual" }, { MGCP_TRUNK_E1, "e1" }, { 0, NULL } }; /*! 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; }