blob: 69750f85b7a859031ae5fa29b1772f3671bbc762 [file] [log] [blame]
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001/* Trunk handling */
2
3/*
4 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2009-2012 by On-Waves
6 * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
Philipp Maier993ea6b2020-08-04 18:26:50 +020024#include <osmocom/mgcp/mgcp.h>
25#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020026#include <osmocom/mgcp/mgcp_endp.h>
27#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier889fe7f2020-07-06 17:44:12 +020028#include <osmocom/mgcp/mgcp_e1.h>
29#include <osmocom/abis/e1_input.h>
Philipp Maier124a3e02021-07-26 11:17:15 +020030#include <osmocom/core/stat_item.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020031
Philipp Maierbd060c32021-07-09 10:24:33 +020032const struct value_string mgcp_trunk_type_strs[] = {
33 { MGCP_TRUNK_VIRTUAL, "virtual" },
34 { MGCP_TRUNK_E1, "e1" },
35 { 0, NULL }
36};
37
Philipp Maier38533ba2021-07-29 17:38:34 +020038/* Free trunk, this function is automatically called by talloc_free when the trunk is freed. It does not free the
39 * endpoints on the trunk, this must be done separately before freeing the trunk. */
40static int trunk_free_talloc_destructor(struct mgcp_trunk *trunk)
41{
42 llist_del(&trunk->entry);
43 mgcp_ratectr_trunk_free(trunk);
44 mgcp_stat_trunk_free(trunk);
45 return 0;
46}
47
48/*! allocate trunk and add it to the trunk list.
Philipp Maierbce5f292020-06-18 11:57:17 +020049 * (called once at startup by VTY).
50 * \param[in] cfg mgcp configuration.
Philipp Maierbce5f292020-06-18 11:57:17 +020051 * \param[in] ttype trunk type.
Philipp Maier080935a2020-07-01 23:08:50 +020052 * \param[in] nr trunk number.
Philipp Maierbce5f292020-06-18 11:57:17 +020053 * \returns pointer to allocated trunk, NULL on failure. */
Philipp Maierd70eef62021-07-19 13:53:28 +020054struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr)
Philipp Maierc66ab2c2020-06-02 20:55:34 +020055{
56 struct mgcp_trunk *trunk;
57
58 trunk = talloc_zero(cfg, struct mgcp_trunk);
59 if (!trunk) {
60 LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
61 return NULL;
62 }
63
64 trunk->cfg = cfg;
65 trunk->trunk_type = ttype;
66 trunk->trunk_nr = nr;
67
68 trunk->audio_send_ptime = 1;
69 trunk->audio_send_name = 1;
Philipp Maierae6858b2020-09-07 12:07:40 +020070 trunk->v.vty_number_endpoints = 512;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020071 trunk->omit_rtcp = 0;
72
73 mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
74
Philipp Maierd19de2e2020-06-03 13:55:33 +020075 llist_add_tail(&trunk->entry, &cfg->trunks);
Philipp Maierc66ab2c2020-06-02 20:55:34 +020076
Philipp Maiera065e632021-07-09 13:22:42 +020077 mgcp_ratectr_trunk_alloc(trunk);
Philipp Maier124a3e02021-07-26 11:17:15 +020078 mgcp_stat_trunk_alloc(trunk);
Philipp Maier38533ba2021-07-29 17:38:34 +020079 talloc_set_destructor(trunk, trunk_free_talloc_destructor);
Philipp Maierc66ab2c2020-06-02 20:55:34 +020080
81 return trunk;
82}
83
Philipp Maierbce5f292020-06-18 11:57:17 +020084/*! allocate endpoints and set default values
85 * (called once at startup by VTY).
86 * \param[in] trunk trunk configuration.
87 * \returns 0 on success, -1 on failure. */
Philipp Maierc66ab2c2020-06-02 20:55:34 +020088int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
89{
90 int i;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020091 struct mgcp_endpoint *endp;
Philipp Maier869b21c2020-07-03 16:04:16 +020092 unsigned int number_endpoints;
93 unsigned int first_endpoint_nr;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020094
95 /* This function is called once on startup by the VTY to allocate the
96 * endpoints. The number of endpoints must not change througout the
97 * runtime of the MGW */
98 OSMO_ASSERT(trunk->number_endpoints == 0);
99 OSMO_ASSERT(trunk->endpoints == NULL);
100
Philipp Maier869b21c2020-07-03 16:04:16 +0200101 switch (trunk->trunk_type) {
102 case MGCP_TRUNK_VIRTUAL:
103 /* Due to historical reasons the endpoints on the virtual
104 * trunk start counting at 1. */
105 first_endpoint_nr = 1;
Philipp Maier889fe7f2020-07-06 17:44:12 +0200106 number_endpoints = trunk->v.vty_number_endpoints;
Philipp Maier869b21c2020-07-03 16:04:16 +0200107 break;
108 case MGCP_TRUNK_E1:
109 /* The first timeslot on an E1 line is reserved for framing
110 * and alignment and can not be used for audio transport */
111 first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;
Philipp Maier889fe7f2020-07-06 17:44:12 +0200112 number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;
Philipp Maier869b21c2020-07-03 16:04:16 +0200113 break;
114 default:
115 OSMO_ASSERT(false);
116 }
117
118 /* Make sure the amount of requested endpoints does not execeed
119 * sane limits. The VTY already limits the possible amount,
120 * however miss-initialization of the struct or memory corruption
121 * could still lead to an excessive allocation of endpoints, so
122 * better stop early if that is the case. */
123 OSMO_ASSERT(number_endpoints < 65534);
124
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200125 /* allocate pointer array for the endpoints */
Eric70a658a2021-08-13 03:40:40 +0200126 trunk->endpoints = talloc_zero_array(trunk, struct mgcp_endpoint*,
Philipp Maier7a755be2020-07-07 15:51:06 +0200127 number_endpoints);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200128 if (!trunk->endpoints)
129 return -1;
130
131 /* create endpoints */
Philipp Maier869b21c2020-07-03 16:04:16 +0200132 for (i = 0; i < number_endpoints; i++) {
133 endp = mgcp_endp_alloc(trunk, i + first_endpoint_nr);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200134 if (!endp) {
135 talloc_free(trunk->endpoints);
136 return -1;
137 }
138 trunk->endpoints[i] = endp;
139 }
140
141 /* make the endpoints we just created available to the MGW code */
Philipp Maier869b21c2020-07-03 16:04:16 +0200142 trunk->number_endpoints = number_endpoints;
Philipp Maier124a3e02021-07-26 11:17:15 +0200143 osmo_stat_item_set(osmo_stat_item_group_get_item(trunk->stats.common, TRUNK_STAT_ENDPOINTS_TOTAL),
144 trunk->number_endpoints);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200145 return 0;
146}
147
Philipp Maier889fe7f2020-07-06 17:44:12 +0200148/*! Equip trunk with endpoints and resources
149 * (called once at startup by VTY).
150 * \param[in] trunk trunk configuration.
151 * \returns 0 on success, -1 on failure. */
152int mgcp_trunk_equip(struct mgcp_trunk *trunk)
153{
154 unsigned int i;
155
156 /* Allocate endpoints */
157 if(mgcp_trunk_alloc_endpts(trunk) != 0)
158 return -1;
159
160 /* Allocate resources */
161 switch (trunk->trunk_type) {
162 case MGCP_TRUNK_VIRTUAL:
163 /* No additional initaliziation required here, virtual
164 * endpoints will open/close network sockets themselves
165 * on demand. */
166 break;
167 case MGCP_TRUNK_E1:
168 /* The TS initalization happens once on startup for all
169 * timeslots. This only affects the i460 multiplexer. Until
170 * now no E1 resources are claimed yet. This happens on demand
171 * when the related endpoint is actually used */
172 memset(trunk->e1.i460_ts, 0, sizeof(trunk->e1.i460_ts));
173 for (i = 0; i < (NUM_E1_TS-1); i++)
174 osmo_i460_ts_init(&trunk->e1.i460_ts[i]);
175 break;
176 default:
177 OSMO_ASSERT(false);
178 }
179
180 return 0;
181}
182
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200183/*! get trunk configuration by trunk number (index).
Philipp Maierbce5f292020-06-18 11:57:17 +0200184 * \param[in] cfg mgcp configuration.
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200185 * \param[in] ttype trunk type.
186 * \param[in] nr trunk number.
Philipp Maierbce5f292020-06-18 11:57:17 +0200187 * \returns pointer to trunk configuration, NULL on error. */
Philipp Maierd70eef62021-07-19 13:53:28 +0200188struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200189{
190 struct mgcp_trunk *trunk;
191
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200192 llist_for_each_entry(trunk, &cfg->trunks, entry) {
193 if (trunk->trunk_nr == nr && trunk->trunk_type == ttype)
194 return trunk;
195 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200196
197 return NULL;
198}
199
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200200/* Made public for unit-testing, do not use from outside this file */
Philipp Maierd70eef62021-07-19 13:53:28 +0200201int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname)
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200202{
Philipp Maierd70eef62021-07-19 13:53:28 +0200203 unsigned long trunk_nr_temp;
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200204 size_t prefix_len;
205 char *str_trunk_nr_end;
206
207 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
208 if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) != 0)
209 return -EINVAL;
210
211 errno = 0;
Philipp Maierd70eef62021-07-19 13:53:28 +0200212 trunk_nr_temp = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
213 if (errno == ERANGE || trunk_nr_temp > 64
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200214 || epname + prefix_len == str_trunk_nr_end
215 || str_trunk_nr_end[0] != '/')
216 return -EINVAL;
Philipp Maierd70eef62021-07-19 13:53:28 +0200217 else {
218 *trunk_nr = (unsigned int)trunk_nr_temp;
219 return 0;
220 }
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200221}
222
Philipp Maier96c6e062021-07-29 15:15:36 +0200223/* Check if the domain name, which is supplied with the endpoint name
224 * matches the configuration. */
225static int check_domain_name(const char *epname, const struct mgcp_config *cfg)
226{
227 char *domain_to_check;
228
229 domain_to_check = strstr(epname, "@");
230 if (!domain_to_check) {
231 LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
232 epname, cfg->domain);
233 return -EINVAL;
234 }
235
236 /* Accept any domain if configured as "*" */
237 if (!strcmp(cfg->domain, "*"))
238 return 0;
239
240 if (strcmp(domain_to_check+1, cfg->domain) != 0) {
241 LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
242 epname, cfg->domain);
243 return -EINVAL;
244 }
245
246 return 0;
247}
248
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200249/*! Find a trunk by the trunk prefix in the endpoint name.
250 * \param[in] epname endpoint name with trunk prefix to look up.
251 * \param[in] cfg that contains the trunks where the endpoint is located.
252 * \returns trunk or NULL if trunk was not found. */
253struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname)
254{
255 size_t prefix_len;
256 char epname_lc[MGCP_ENDPOINT_MAXLEN];
Philipp Maierd70eef62021-07-19 13:53:28 +0200257 unsigned int trunk_nr;
258 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200259
260 osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
261 epname = epname_lc;
262
Philipp Maier96c6e062021-07-29 15:15:36 +0200263 /* All endpoint names require a domain as suffix */
264 if (check_domain_name(epname, cfg))
265 return NULL;
266
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200267 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
268 if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200269 return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200270 }
271
Philipp Maierd70eef62021-07-19 13:53:28 +0200272 rc = e1_trunk_nr_from_epname(&trunk_nr, epname);
273 if (rc == 0)
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200274 return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200275
276 /* Earlier versions of osmo-mgw were accepting endpoint names
277 * without trunk prefix. This is normally not allowed, each MGCP
278 * request should supply an endpoint name with trunk prefix.
279 * However in order to stay compatible with old versions of
280 * osmo-bsc and osmo-msc we still accept endpoint names without
281 * trunk prefix and just assume that the virtual trunk should
282 * be selected. There is even a TTCN3 test for this, see also:
283 * MGCP_Test.TC_crcx_noprefix */
284 if ((epname[0] >= '0' && epname[0] <= '9') || (epname[0] >= 'a' && epname[0] <= 'f')) {
285 LOGP(DLMGCP, LOGL_ERROR, "missing trunk prefix in endpoint name \"%s\", assuming trunk \"%s\"!\n", epname,
286 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200287 return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200288 }
289
290 LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
291 return NULL;
292}
Philipp Maier889fe7f2020-07-06 17:44:12 +0200293
294/*! Find a trunk (E1) by its associated E1 line number.
295 * \param[in] num e1 line number.
296 * \returns trunk or NULL if trunk was not found. */
297struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num)
298{
299 /*! When used on trunks other than E1, the result will always be NULL. */
300 struct mgcp_trunk *trunk;
301
302 llist_for_each_entry(trunk, &cfg->trunks, entry) {
303 if (trunk->trunk_type == MGCP_TRUNK_E1 && trunk->e1.vty_line_nr == num)
304 return trunk;
305 }
306
307 return NULL;
308}