blob: 20088b7ca81c23d3e81c2c9bfc0bca9df62e733b [file] [log] [blame]
Philipp Maier87bd9be2017-08-22 16:35:41 +02001/* Endpoint types */
2
3/*
Philipp Maierc66ab2c2020-06-02 20:55:34 +02004 * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
Philipp Maier87bd9be2017-08-22 16:35:41 +02005 * All Rights Reserved
6 *
7 * Author: Philipp Maier
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>
26#include <osmocom/mgcp/mgcp_conn.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010027#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020028#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020029
Philipp Maier99d4d362020-09-07 11:58:09 +020030#include <osmocom/abis/e1_input.h>
Philipp Maier889fe7f2020-07-06 17:44:12 +020031#include <osmocom/mgcp/mgcp_e1.h>
Philipp Maier124a3e02021-07-26 11:17:15 +020032#include <osmocom/core/stat_item.h>
Philipp Maier889fe7f2020-07-06 17:44:12 +020033
Philipp Maier8d6a1932020-06-18 12:19:31 +020034#define E1_RATE_MAX 64
35#define E1_OFFS_MAX 8
36
Philipp Maier87bd9be2017-08-22 16:35:41 +020037/* Endpoint typeset definition */
38const struct mgcp_endpoint_typeset ep_typeset = {
39 /* Specify endpoint properties for RTP endpoint */
Philipp Maier0996a1e2020-06-10 15:27:14 +020040 .rtp = {
41 .max_conns = 2,
42 .dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
43 .cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
44 },
45 /* Specify endpoint properties for E1 endpoint */
46 .e1 = {
47 .max_conns = 1,
48 .dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
49 .cleanup_cb = mgcp_cleanup_e1_bridge_cb,
50 },
Philipp Maier87bd9be2017-08-22 16:35:41 +020051};
Philipp Maieredc00f42018-01-24 11:58:56 +010052
Philipp Maier7462b952020-06-10 14:50:34 +020053/* Generate virtual endpoint name from given parameters */
54static char *gen_virtual_epname(void *ctx, const char *domain,
55 unsigned int index)
56{
57 return talloc_asprintf(ctx, "%s%x@%s",
58 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
59}
60
Philipp Maier98c09b32020-06-18 12:17:55 +020061/* Generate E1 endpoint name from given numeric parameters */
Philipp Maierd70eef62021-07-19 13:53:28 +020062static char *gen_e1_epname(void *ctx, const char *domain, unsigned int trunk_nr,
Philipp Maier0ffa3bd2020-06-30 16:17:03 +020063 uint8_t ts_nr, uint8_t ss_nr)
Philipp Maier98c09b32020-06-18 12:17:55 +020064{
Philipp Maier98c09b32020-06-18 12:17:55 +020065 unsigned int rate;
66 unsigned int offset;
67
Philipp Maier8d6a1932020-06-18 12:19:31 +020068 OSMO_ASSERT(ss_nr < sizeof(e1_rates));
Philipp Maier98c09b32020-06-18 12:17:55 +020069
Philipp Maier8d6a1932020-06-18 12:19:31 +020070 rate = e1_rates[ss_nr];
71 offset = e1_offsets[ss_nr];
Philipp Maier98c09b32020-06-18 12:17:55 +020072
Philipp Maier0ffa3bd2020-06-30 16:17:03 +020073 return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s",
74 MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr,
75 rate, offset, domain);
Philipp Maier98c09b32020-06-18 12:17:55 +020076}
77
Philipp Maierc66ab2c2020-06-02 20:55:34 +020078/*! allocate an endpoint and set default values.
79 * \param[in] trunk configuration.
Philipp Maier7462b952020-06-10 14:50:34 +020080 * \param[in] name endpoint index.
Philipp Maierc66ab2c2020-06-02 20:55:34 +020081 * \returns endpoint on success, NULL on failure. */
Philipp Maier7462b952020-06-10 14:50:34 +020082struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
83 unsigned int index)
Philipp Maierc66ab2c2020-06-02 20:55:34 +020084{
85 struct mgcp_endpoint *endp;
86
87 endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
88 if (!endp)
89 return NULL;
90
91 INIT_LLIST_HEAD(&endp->conns);
Philipp Maierc66ab2c2020-06-02 20:55:34 +020092 endp->trunk = trunk;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020093
94 switch (trunk->trunk_type) {
95 case MGCP_TRUNK_VIRTUAL:
96 endp->type = &ep_typeset.rtp;
Philipp Maier7462b952020-06-10 14:50:34 +020097 endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
Philipp Maierc66ab2c2020-06-02 20:55:34 +020098 break;
99 case MGCP_TRUNK_E1:
Philipp Maier889fe7f2020-07-06 17:44:12 +0200100 endp->type = &ep_typeset.e1;
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200101 endp->name = gen_e1_epname(endp, trunk->cfg->domain,
102 trunk->trunk_nr,
Philipp Maierfbcf3992020-07-02 23:08:37 +0200103 index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200104 break;
105 default:
106 osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
107 trunk->trunk_type, __FILE__, __LINE__);
108 }
109
110 return endp;
111}
112
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200113/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
114 * "ds/e1-") and write the epname without the prefix back to the memory
115 * pointed at by epname. (per trunk the prefix is the same for all endpoints,
116 * so no ambiguity is introduced) */
117static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
118{
119 size_t prefix_len;
120 switch (trunk->trunk_type) {
121 case MGCP_TRUNK_VIRTUAL:
122 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
123 if (strncmp
124 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
125 prefix_len) == 0)
126 memmove(epname, epname + prefix_len,
127 strlen(epname) - prefix_len + 1);
128 return;
129 case MGCP_TRUNK_E1:
130 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
131 if (strncmp
132 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
133 prefix_len) == 0)
134 memmove(epname, epname + prefix_len,
135 strlen(epname) - prefix_len + 1);
136 return;
137 default:
138 OSMO_ASSERT(false);
139 }
140}
141
142/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
143 * epname by writing a '\0' char where the suffix starts. */
144static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
145{
146 char *suffix_begin;
147
148 /* Endpoints on the virtual trunk may have a domain name that is
149 * followed after an @ character, this can be chopped off. All
150 * other supported trunk types do not have any suffixes that may
151 * be chopped off */
152 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
153 suffix_begin = strchr(epname, '@');
154 if (!suffix_begin)
155 return;
156 *suffix_begin = '\0';
157 }
158}
159
Eric25ecc912021-09-06 03:43:50 +0200160 /*! Convert all characters in epname to lowercase and strip trunk prefix and
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200161 * endpoint name suffix (domain name) from epname. The result is written to
162 * to the memory pointed at by epname_stripped. The expected size of the
163 * result is either equal or lower then the length of the input string
Eric25ecc912021-09-06 03:43:50 +0200164 * (epname)
165 * \param[out] epname_stripped pointer to store the stripped ep name.
166 * \param[in] epname endpoint name to lookup.
167 * \param[in] trunk where the endpoint is located. */
168void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200169 const struct mgcp_trunk *trunk)
170{
171 osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
172 chop_epname_prefix(epname_stripped, trunk);
173 chop_epname_suffix(epname_stripped, trunk);
174}
175
176/* Go through the trunk and find a random free (no active calls) endpoint,
177 * this function is called when a wildcarded request is carried out, which
178 * means that it is up to the MGW to choose a random free endpoint. */
179static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
180{
181 struct mgcp_endpoint *endp;
182 unsigned int i;
183
184 for (i = 0; i < trunk->number_endpoints; i++) {
185 endp = trunk->endpoints[i];
Philipp Maier8d6a1932020-06-18 12:19:31 +0200186 /* A free endpoint must not serve a call already and it must
187 * be available. */
188 if (endp->callid == NULL && mgcp_endp_avail(endp))
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200189 return endp;
190 }
191
192 return NULL;
193}
194
Eric25ecc912021-09-06 03:43:50 +0200195/*! Find an endpoint of a trunk specified by its name.
Ericaac84ed2021-11-09 16:22:01 +0100196 * \param[in] epname endpoint name to check.
197 * \param[in] trunk mgcp_trunk that might have this endpoint.
198 * \returns NULL if no ep found, else endpoint. */
Eric25ecc912021-09-06 03:43:50 +0200199struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200200 const struct mgcp_trunk *trunk)
201{
202 char epname_stripped[MGCP_ENDPOINT_MAXLEN];
203 char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
204 struct mgcp_endpoint *endp;
205 unsigned int i;
206
207 /* Strip irrelevant information from the endpoint name */
Eric25ecc912021-09-06 03:43:50 +0200208 mgcp_endp_strip_name(epname_stripped, epname, trunk);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200209
210 for (i = 0; i < trunk->number_endpoints; i++) {
211 endp = trunk->endpoints[i];
Eric25ecc912021-09-06 03:43:50 +0200212 mgcp_endp_strip_name(epname_stripped_endp, endp->name, trunk);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200213 if (strcmp(epname_stripped_endp, epname_stripped) == 0)
214 return endp;
215 }
216
217 return NULL;
218}
219
Philipp Maierd64c0412021-07-14 11:53:49 +0200220/*! Check if the given epname refers to a wildcarded request or to a specific
221 * endpoint.
222 * \param[in] epname endpoint name to check
223 * \returns true if epname refers to wildcarded request, else false. */
224bool mgcp_endp_is_wildcarded(const char *epname)
225{
226 if (strstr(epname, "*"))
227 return true;
228
229 return false;
230}
231
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +0200232/*! Check if the given epname refers to a "null" endpoint.
233 * \param[in] epname endpoint name to check
234 * \returns true if epname refers to "null"" endpoint, else false. */
235bool mgcp_endp_is_null(const char *epname)
236{
237 if (strncasecmp(epname, "null@", 5) == 0)
238 return true;
239
240 return false;
241}
242
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200243/*! Find an endpoint by its name on a specified trunk.
244 * \param[out] cause pointer to store cause code, can be NULL.
245 * \param[in] epname endpoint name to lookup.
246 * \param[in] trunk where the endpoint is located.
247 * \returns endpoint or NULL if endpoint was not found. */
248struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
249 const struct mgcp_trunk *trunk)
250{
251 struct mgcp_endpoint *endp;
252
253 if (cause)
254 *cause = 0;
255
256 /* At the moment we only support a primitive ('*'-only) method of
257 * wildcarded endpoint searches that picks the next free endpoint on
258 * a trunk. */
Philipp Maierd64c0412021-07-14 11:53:49 +0200259 if (mgcp_endp_is_wildcarded(epname)) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200260 endp = find_free_endpoint(trunk);
261 if (endp) {
262 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
263 "(trunk:%d) found free endpoint: %s\n",
264 trunk->trunk_nr, endp->name);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200265 return endp;
266 }
267
268 LOGP(DLMGCP, LOGL_ERROR,
269 "(trunk:%d) Not able to find a free endpoint\n",
270 trunk->trunk_nr);
271 if (cause)
272 *cause = -403;
273 return NULL;
274 }
275
276 /* Find an endpoint by its name (if wildcarded request is not
277 * applicable) */
Eric25ecc912021-09-06 03:43:50 +0200278 endp = mgcp_endp_find_specific(epname, trunk);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200279 if (endp) {
280 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
281 "(trunk:%d) found endpoint: %s\n",
282 trunk->trunk_nr, endp->name);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200283 return endp;
284 }
285
286 LOGP(DLMGCP, LOGL_ERROR,
287 "(trunk:%d) Not able to find specified endpoint: %s\n",
288 trunk->trunk_nr, epname);
289 if (cause)
290 *cause = -500;
291
292 return NULL;
293}
294
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200295/*! Find an endpoint by its name, search at all trunks.
296 * \param[out] cause, pointer to store cause code, can be NULL.
297 * \param[in] epname, must contain trunk prefix.
298 * \param[in] cfg, mgcp configuration (trunks).
299 * \returns endpoint or NULL if endpoint was not found. */
300struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
301 struct mgcp_config *cfg)
302{
303 struct mgcp_trunk *trunk;
304 struct mgcp_endpoint *endp;
305 char epname_lc[MGCP_ENDPOINT_MAXLEN];
306
307 osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
308 epname = epname_lc;
309
310 if (cause)
311 *cause = -500;
312
313 /* Identify the trunk where the endpoint is located */
314 trunk = mgcp_trunk_by_name(cfg, epname);
315 if (!trunk)
316 return NULL;
317
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200318 /* Identify the endpoint on the trunk */
319 endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
320 if (!endp) {
321 return NULL;
322 }
323
324 if (cause)
325 *cause = 0;
326 return endp;
327}
Philipp Maier8d6a1932020-06-18 12:19:31 +0200328
329/* Get the E1 timeslot number from a given E1 endpoint name
330 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
331static uint8_t e1_ts_nr_from_epname(const char *epname)
332{
333 char buf[MGCP_ENDPOINT_MAXLEN + 1];
334 char *save_ptr = NULL;
335 char *buf_ptr = buf;
336 char *token;
337 unsigned long int res = 0;
338
339 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
340
341 while (1) {
342 token = strtok_r(buf_ptr, "/", &save_ptr);
343 buf_ptr = NULL;
344 if (!token)
345 break;
346 if (strncmp(token, "s-", 2) == 0) {
347 errno = 0;
348 res = strtoul(token + 2, NULL, 10);
Philipp Maier99d4d362020-09-07 11:58:09 +0200349 if (errno == ERANGE || res > NUM_E1_TS)
Philipp Maier8d6a1932020-06-18 12:19:31 +0200350 return 0xff;
351 return (uint8_t) res;
352 }
353 }
354
355 return 0xff;
356}
357
358/* Get the E1 timeslot number from a given E1 endpoint name
359 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
360static uint8_t e1_rate_from_epname(const char *epname)
361{
362 char buf[MGCP_ENDPOINT_MAXLEN + 1];
363 char *save_ptr = NULL;
364 char *buf_ptr = buf;
365 char *token;
366 unsigned long int res = 0;
367 unsigned int i;
368
369 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
370
371 while (1) {
372 token = strtok_r(buf_ptr, "/", &save_ptr);
373 buf_ptr = NULL;
374 if (!token)
375 break;
376 if (strncmp(token, "su", 2) == 0) {
377 errno = 0;
378 res = strtoul(token + 2, NULL, 10);
379 if (errno == ERANGE || res > E1_RATE_MAX)
380 return 0xff;
381 /* Make sure the rate is a valid rate */
382 for (i = 0; i < sizeof(e1_rates); i++) {
383 if (res == e1_rates[i])
384 return (uint8_t) res;
385 }
386 return 0xff;
387 }
388 }
389
390 return 0xff;
391}
392
393/* Get the E1 bitstream offset from a given E1 endpoint name
394 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
395static uint8_t e1_offs_from_epname(const char *epname)
396{
397 char buf[MGCP_ENDPOINT_MAXLEN + 1];
398 char *save_ptr = NULL;
399 char *buf_ptr = buf;
400 char *token;
401 unsigned long int res = 0;
402
403 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
404
405 while (1) {
406 token = strtok_r(buf_ptr, "/", &save_ptr);
407 buf_ptr = NULL;
408 if (!token)
409 break;
410 if (strncmp(token, "su", 2) == 0) {
411 token = strstr(token, "-");
412 if (!token)
413 return 0xff;
414 token += 1;
415 errno = 0;
416 res = strtoul(token, NULL, 10);
417 if (errno == ERANGE || res > E1_OFFS_MAX)
418 return 0xff;
419 return (uint8_t) res;
420 }
421 }
422
423 return 0xff;
424}
425
426/* Get the E1 subslot number (internal) from a given E1 endpoint name
427 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
428static uint8_t e1_ss_nr_from_epname(const char *epname)
429{
430 uint8_t rate;
431 uint8_t offs;
432 unsigned int i;
433
434 rate = e1_rate_from_epname(epname);
435 offs = e1_offs_from_epname(epname);
436
437 osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
438
439 for (i = 0; i < sizeof(e1_rates); i++) {
440 if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
441 return i;
442 }
443
444 return 0xff;
445}
446
447/* Check if the selected E1 endpoint is avalable, which means that none of
448 * the overlapping endpoints are currently serving a call. (if the system
449 * is properly configured such a situation should never ocurr!) */
450static bool endp_avail_e1(struct mgcp_endpoint *endp)
451{
452 /* The following map shows the overlapping of the subslots and their
453 * respective rates. The numbers on the right running from top to bottom
454 * are the bit offsets in the whole 64k timeslot. The numbers inside the
455 * boxes symbolize the internal subslot number (array index) and the
456 * rate in the form: i:r where i is the subslot number and r the
457 * respective rate.
458 *
459 * +--------+--------+--------+--------+ 0
460 * | | | | 7:8k |
461 * | | + 3:16k +--------+ 1
462 * | | | | 8:8k |
463 * | | 1:32k +--------+--------+ 2
464 * | | | | 9:8k |
465 * | | + 4:16k +--------+ 3
466 * | | | | 10:8k |
467 * | 0:64k +--------+--------+--------+ 4
468 * | | | | 11:8k |
469 * | | + 5:16k +--------+ 5
470 * | | | | 12:8k |
471 * | | 2:32k +--------+--------+ 6
472 * | | | | 13:8k |
473 * | | + 6:16k +--------+ 7
474 * | | | | 14:8k |
475 * +--------+--------+--------+--------+ 8
476 *
477 * The following array contains tables with the subslot numbers that must be
478 * unused for each subslot. During this test we do not have to check the
479 * endpoint we need to verify, only the overlaps need to be checked. This is
480 * also the reason why the related subslot number is missing from each each
481 * line. */
Philipp Maierfe67e092020-07-07 21:21:45 +0200482 const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
483 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
Philipp Maier8d6a1932020-06-18 12:19:31 +0200484 { 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
485 { 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
486 { 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
487 { 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
488 { 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
489 { 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
490 { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
491 { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
492 { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
493 { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
494 { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
495 { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
496 { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
497 { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
498
499 const int8_t *interlock;
500 unsigned int i;
501 uint8_t ts_nr = 0;
502 uint8_t ss_nr = 0;
503 char *epname_check;
504 struct mgcp_endpoint *endp_check;
505 bool available = true;
506
507 /* This function must only be used with E1 type endpoints! */
508 OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
509
510 ts_nr = e1_ts_nr_from_epname(endp->name);
511 ss_nr = e1_ss_nr_from_epname(endp->name);
512 if (ts_nr == 0xff || ss_nr == 0xff) {
513 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
514 "cannot check endpoint availability, endpoint name not parseable!\n");
515 return false;
516 }
517
518 interlock = interlock_tab[ss_nr];
519
520 for (i = 0; i < sizeof(interlock_tab[0]); i++) {
521 /* Detect row end */
522 if (interlock[i] == -1)
523 break;
524
525 /* Pick overlapping endpoint to check */
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200526 epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
527 endp->trunk->trunk_nr, ts_nr,
528 interlock[i]);
Eric25ecc912021-09-06 03:43:50 +0200529 endp_check = mgcp_endp_find_specific(epname_check, endp->trunk);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200530 if (!endp_check) {
531 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
532 "cannot check endpoint availability, overlapping endpoint:%s not found!\n",
533 epname_check);
534 talloc_free(epname_check);
535 continue;
536 }
537 talloc_free(epname_check);
538
539 /* Check if overlapping endpoint currently serves another call
540 * (This is an exceptional situation, that should not occur
541 * in a properly configured environment!) */
542 if (endp_check->callid) {
543 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
544 "endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
545 endp_check->name);
546 available = false;
547 }
548 }
549
550 return available;
551}
552
553/*! check if an endpoint is available for any kind of operation.
554 * \param[in] endp endpoint to check.
555 * \returns true if endpoint is avalable, false it is blocked for any reason. */
556bool mgcp_endp_avail(struct mgcp_endpoint *endp)
557{
558 switch (endp->trunk->trunk_type) {
559 case MGCP_TRUNK_VIRTUAL:
560 /* There are no obstacles that may render a virtual trunk
561 * endpoint unusable, so virtual trunk endpoints are always
562 * available */
563 return true;
564 case MGCP_TRUNK_E1:
565 return endp_avail_e1(endp);
566 default:
567 OSMO_ASSERT(false);
568 }
569
570 return false;
571}
Philipp Maier889fe7f2020-07-06 17:44:12 +0200572
573/*! claim endpoint, sets callid and activates endpoint, should be called at the
574 * beginning of the CRCX procedure when it is clear that a new call should be
575 * created.
576 * \param[in] endp endpoint to claim.
577 * \param[in] callid that is assingned to this endpoint. */
578int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
579{
580 int rc = 0;
581 uint8_t ts;
582 uint8_t ss;
583 uint8_t offs;
584
585 /* TODO: Make this function more intelligent, it should run the
586 * call id checks we currently have in protocol.c directly here. */
587
588 /* Set the callid, creation of another connection will only be possible
589 * when the callid matches up. (Connections are distinguished by their
590 * connection ids) */
591 endp->callid = talloc_strdup(endp, callid);
592 OSMO_ASSERT(endp->callid);
Philipp Maier124a3e02021-07-26 11:17:15 +0200593 osmo_stat_item_inc(osmo_stat_item_group_get_item(endp->trunk->stats.common,
594 TRUNK_STAT_ENDPOINTS_USED), 1);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200595
596 /* Allocate resources */
597 switch (endp->trunk->trunk_type) {
598 case MGCP_TRUNK_VIRTUAL:
599 /* No additional initaliziation required here, virtual
600 * endpoints will open/close network sockets themselves
601 * on demand. */
602 break;
603 case MGCP_TRUNK_E1:
604 ts = e1_ts_nr_from_epname(endp->name);
605 ss = e1_ss_nr_from_epname(endp->name);
606 offs = e1_offs_from_epname(endp->name);
607 OSMO_ASSERT(ts != 0xFF);
608 OSMO_ASSERT(ts != 0);
609 OSMO_ASSERT(ss != 0xFF);
610 OSMO_ASSERT(offs != 0xFF);
611 rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
612 break;
613 default:
614 OSMO_ASSERT(false);
615 }
616
Pau Espin Pedrol30e01352020-09-21 12:29:41 +0200617 /* Make sure the endpoint is released when claiming the endpoint fails. */
Philipp Maier889fe7f2020-07-06 17:44:12 +0200618 if (rc < 0)
619 mgcp_endp_release(endp);
620
621 return rc;
622}
623
624/*! update endpoint, updates internal endpoint specific data, should be
625 * after when MDCX or CRCX has been executed successuflly.
626 * \param[in] endp endpoint to update. */
627void mgcp_endp_update(struct mgcp_endpoint *endp)
628{
629 /* Allocate resources */
630 switch (endp->trunk->trunk_type) {
631 case MGCP_TRUNK_VIRTUAL:
632 /* No updating initaliziation required for virtual endpoints. */
633 break;
634 case MGCP_TRUNK_E1:
635 mgcp_e1_endp_update(endp);
636 break;
637 default:
638 OSMO_ASSERT(false);
639 }
640}
Pau Espin Pedrol6d0a59a2020-09-08 16:50:22 +0200641
642void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
643{
644 llist_add(&conn->entry, &endp->conns);
645}
646
647void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
648{
649 /* Run endpoint cleanup action. By this we inform the endpoint about
650 * the removal of the connection and allow it to clean up its inner
651 * state accordingly */
652 if (endp->type->cleanup_cb)
653 endp->type->cleanup_cb(endp, conn);
654 llist_del(&conn->entry);
655 if (llist_empty(&endp->conns))
656 mgcp_endp_release(endp);
657}
Philipp Maiera8f13282023-02-09 13:34:28 +0100658
659/*! release endpoint, all open connections are closed.
660 * \param[in] endp endpoint to release */
661void mgcp_endp_release(struct mgcp_endpoint *endp)
662{
663 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
664
665 /* Normally this function should only be called when
666 * all connections have been removed already. In case
667 * that there are still connections open (e.g. when
668 * RSIP is executed), free them all at once. */
669 mgcp_conn_free_all(endp);
670
671 /* We must only decrement the stat item when the endpoint as actually
672 * claimed. An endpoint is claimed when a call-id is set */
673 if (endp->callid)
674 osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
675 TRUNK_STAT_ENDPOINTS_USED), 1);
676
677 /* Reset endpoint parameters and states */
678 talloc_free(endp->callid);
679 endp->callid = NULL;
680 talloc_free(endp->local_options.string);
681 endp->local_options.string = NULL;
682 talloc_free(endp->local_options.codec);
683 endp->local_options.codec = NULL;
684
Philipp Maier2a9ba662023-02-09 13:39:31 +0100685 if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
686 uint8_t ts = e1_ts_nr_from_epname(endp->name);
687 mgcp_e1_endp_release(endp, ts);
688 }
Philipp Maiera8f13282023-02-09 13:34:28 +0100689}
690