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