blob: ae376b099f44194ee2b0e81fca92ec351468ce96 [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>
32
Philipp Maier8d6a1932020-06-18 12:19:31 +020033#define E1_RATE_MAX 64
34#define E1_OFFS_MAX 8
35
Philipp Maier87bd9be2017-08-22 16:35:41 +020036/* Endpoint typeset definition */
37const struct mgcp_endpoint_typeset ep_typeset = {
38 /* Specify endpoint properties for RTP endpoint */
Philipp Maier0996a1e2020-06-10 15:27:14 +020039 .rtp = {
40 .max_conns = 2,
41 .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);
91 endp->cfg = trunk->cfg;
92 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 Maieredc00f42018-01-24 11:58:56 +0100113/*! release endpoint, all open connections are closed.
114 * \param[in] endp endpoint to release */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100115void mgcp_endp_release(struct mgcp_endpoint *endp)
Philipp Maieredc00f42018-01-24 11:58:56 +0100116{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200117 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
Philipp Maieredc00f42018-01-24 11:58:56 +0100118
119 /* Normally this function should only be called when
120 * all connections have been removed already. In case
121 * that there are still connections open (e.g. when
122 * RSIP is executed), free them all at once. */
123 mgcp_conn_free_all(endp);
124
125 /* Reset endpoint parameters and states */
126 talloc_free(endp->callid);
127 endp->callid = NULL;
128 talloc_free(endp->local_options.string);
129 endp->local_options.string = NULL;
130 talloc_free(endp->local_options.codec);
131 endp->local_options.codec = NULL;
Philipp Maier207ab512018-02-02 14:19:26 +0100132 endp->wildcarded_req = false;
Philipp Maier889fe7f2020-07-06 17:44:12 +0200133
134 if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
135 mgcp_e1_endp_release(endp);
Philipp Maieredc00f42018-01-24 11:58:56 +0100136}
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200137
138/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
139 * "ds/e1-") and write the epname without the prefix back to the memory
140 * pointed at by epname. (per trunk the prefix is the same for all endpoints,
141 * so no ambiguity is introduced) */
142static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
143{
144 size_t prefix_len;
145 switch (trunk->trunk_type) {
146 case MGCP_TRUNK_VIRTUAL:
147 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
148 if (strncmp
149 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
150 prefix_len) == 0)
151 memmove(epname, epname + prefix_len,
152 strlen(epname) - prefix_len + 1);
153 return;
154 case MGCP_TRUNK_E1:
155 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
156 if (strncmp
157 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
158 prefix_len) == 0)
159 memmove(epname, epname + prefix_len,
160 strlen(epname) - prefix_len + 1);
161 return;
162 default:
163 OSMO_ASSERT(false);
164 }
165}
166
167/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
168 * epname by writing a '\0' char where the suffix starts. */
169static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
170{
171 char *suffix_begin;
172
173 /* Endpoints on the virtual trunk may have a domain name that is
174 * followed after an @ character, this can be chopped off. All
175 * other supported trunk types do not have any suffixes that may
176 * be chopped off */
177 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
178 suffix_begin = strchr(epname, '@');
179 if (!suffix_begin)
180 return;
181 *suffix_begin = '\0';
182 }
183}
184
185/* Convert all characters in epname to lowercase and strip trunk prefix and
186 * endpoint name suffix (domain name) from epname. The result is written to
187 * to the memory pointed at by epname_stripped. The expected size of the
188 * result is either equal or lower then the length of the input string
189 * (epname) */
190static void strip_epname(char *epname_stripped, const char *epname,
191 const struct mgcp_trunk *trunk)
192{
193 osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
194 chop_epname_prefix(epname_stripped, trunk);
195 chop_epname_suffix(epname_stripped, trunk);
196}
197
198/* Go through the trunk and find a random free (no active calls) endpoint,
199 * this function is called when a wildcarded request is carried out, which
200 * means that it is up to the MGW to choose a random free endpoint. */
201static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
202{
203 struct mgcp_endpoint *endp;
204 unsigned int i;
205
206 for (i = 0; i < trunk->number_endpoints; i++) {
207 endp = trunk->endpoints[i];
Philipp Maier8d6a1932020-06-18 12:19:31 +0200208 /* A free endpoint must not serve a call already and it must
209 * be available. */
210 if (endp->callid == NULL && mgcp_endp_avail(endp))
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200211 return endp;
212 }
213
214 return NULL;
215}
216
217/* Find an endpoint specified by its name. If the endpoint can not be found,
218 * return NULL */
219static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
220 const struct mgcp_trunk *trunk)
221{
222 char epname_stripped[MGCP_ENDPOINT_MAXLEN];
223 char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
224 struct mgcp_endpoint *endp;
225 unsigned int i;
226
227 /* Strip irrelevant information from the endpoint name */
228 strip_epname(epname_stripped, epname, trunk);
229
230 for (i = 0; i < trunk->number_endpoints; i++) {
231 endp = trunk->endpoints[i];
232 strip_epname(epname_stripped_endp, endp->name, trunk);
233 if (strcmp(epname_stripped_endp, epname_stripped) == 0)
234 return endp;
235 }
236
237 return NULL;
238}
239
Philipp Maierd64c0412021-07-14 11:53:49 +0200240/*! Check if the given epname refers to a wildcarded request or to a specific
241 * endpoint.
242 * \param[in] epname endpoint name to check
243 * \returns true if epname refers to wildcarded request, else false. */
244bool mgcp_endp_is_wildcarded(const char *epname)
245{
246 if (strstr(epname, "*"))
247 return true;
248
249 return false;
250}
251
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200252/*! Find an endpoint by its name on a specified trunk.
253 * \param[out] cause pointer to store cause code, can be NULL.
254 * \param[in] epname endpoint name to lookup.
255 * \param[in] trunk where the endpoint is located.
256 * \returns endpoint or NULL if endpoint was not found. */
257struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
258 const struct mgcp_trunk *trunk)
259{
260 struct mgcp_endpoint *endp;
261
262 if (cause)
263 *cause = 0;
264
265 /* At the moment we only support a primitive ('*'-only) method of
266 * wildcarded endpoint searches that picks the next free endpoint on
267 * a trunk. */
Philipp Maierd64c0412021-07-14 11:53:49 +0200268 if (mgcp_endp_is_wildcarded(epname)) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200269 endp = find_free_endpoint(trunk);
270 if (endp) {
271 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
272 "(trunk:%d) found free endpoint: %s\n",
273 trunk->trunk_nr, endp->name);
274 endp->wildcarded_req = true;
275 return endp;
276 }
277
278 LOGP(DLMGCP, LOGL_ERROR,
279 "(trunk:%d) Not able to find a free endpoint\n",
280 trunk->trunk_nr);
281 if (cause)
282 *cause = -403;
283 return NULL;
284 }
285
286 /* Find an endpoint by its name (if wildcarded request is not
287 * applicable) */
288 endp = find_specific_endpoint(epname, trunk);
289 if (endp) {
290 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
291 "(trunk:%d) found endpoint: %s\n",
292 trunk->trunk_nr, endp->name);
293 endp->wildcarded_req = false;
294 return endp;
295 }
296
297 LOGP(DLMGCP, LOGL_ERROR,
298 "(trunk:%d) Not able to find specified endpoint: %s\n",
299 trunk->trunk_nr, epname);
300 if (cause)
301 *cause = -500;
302
303 return NULL;
304}
305
306/* Check if the domain name, which is supplied with the endpoint name
307 * matches the configuration. */
308static int check_domain_name(const char *epname, struct mgcp_config *cfg)
309{
310 char *domain_to_check;
311
312 domain_to_check = strstr(epname, "@");
313 if (!domain_to_check) {
314 LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
315 epname, cfg->domain);
316 return -EINVAL;
317 }
318
319 /* Accept any domain if configured as "*" */
320 if (!strcmp(cfg->domain, "*"))
321 return 0;
322
323 if (strcmp(domain_to_check+1, cfg->domain) != 0) {
324 LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
325 epname, cfg->domain);
326 return -EINVAL;
327 }
328
329 return 0;
330}
331
332/*! Find an endpoint by its name, search at all trunks.
333 * \param[out] cause, pointer to store cause code, can be NULL.
334 * \param[in] epname, must contain trunk prefix.
335 * \param[in] cfg, mgcp configuration (trunks).
336 * \returns endpoint or NULL if endpoint was not found. */
337struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
338 struct mgcp_config *cfg)
339{
340 struct mgcp_trunk *trunk;
341 struct mgcp_endpoint *endp;
342 char epname_lc[MGCP_ENDPOINT_MAXLEN];
343
344 osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
345 epname = epname_lc;
346
347 if (cause)
348 *cause = -500;
349
350 /* Identify the trunk where the endpoint is located */
351 trunk = mgcp_trunk_by_name(cfg, epname);
352 if (!trunk)
353 return NULL;
354
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200355 /* All endpoint names require a domain as suffix */
356 if (check_domain_name(epname, cfg))
357 return NULL;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200358
359 /* Identify the endpoint on the trunk */
360 endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
361 if (!endp) {
362 return NULL;
363 }
364
365 if (cause)
366 *cause = 0;
367 return endp;
368}
Philipp Maier8d6a1932020-06-18 12:19:31 +0200369
370/* Get the E1 timeslot number from a given E1 endpoint name
371 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
372static uint8_t e1_ts_nr_from_epname(const char *epname)
373{
374 char buf[MGCP_ENDPOINT_MAXLEN + 1];
375 char *save_ptr = NULL;
376 char *buf_ptr = buf;
377 char *token;
378 unsigned long int res = 0;
379
380 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
381
382 while (1) {
383 token = strtok_r(buf_ptr, "/", &save_ptr);
384 buf_ptr = NULL;
385 if (!token)
386 break;
387 if (strncmp(token, "s-", 2) == 0) {
388 errno = 0;
389 res = strtoul(token + 2, NULL, 10);
Philipp Maier99d4d362020-09-07 11:58:09 +0200390 if (errno == ERANGE || res > NUM_E1_TS)
Philipp Maier8d6a1932020-06-18 12:19:31 +0200391 return 0xff;
392 return (uint8_t) res;
393 }
394 }
395
396 return 0xff;
397}
398
399/* Get the E1 timeslot number from a given E1 endpoint name
400 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
401static uint8_t e1_rate_from_epname(const char *epname)
402{
403 char buf[MGCP_ENDPOINT_MAXLEN + 1];
404 char *save_ptr = NULL;
405 char *buf_ptr = buf;
406 char *token;
407 unsigned long int res = 0;
408 unsigned int i;
409
410 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
411
412 while (1) {
413 token = strtok_r(buf_ptr, "/", &save_ptr);
414 buf_ptr = NULL;
415 if (!token)
416 break;
417 if (strncmp(token, "su", 2) == 0) {
418 errno = 0;
419 res = strtoul(token + 2, NULL, 10);
420 if (errno == ERANGE || res > E1_RATE_MAX)
421 return 0xff;
422 /* Make sure the rate is a valid rate */
423 for (i = 0; i < sizeof(e1_rates); i++) {
424 if (res == e1_rates[i])
425 return (uint8_t) res;
426 }
427 return 0xff;
428 }
429 }
430
431 return 0xff;
432}
433
434/* Get the E1 bitstream offset from a given E1 endpoint name
435 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
436static uint8_t e1_offs_from_epname(const char *epname)
437{
438 char buf[MGCP_ENDPOINT_MAXLEN + 1];
439 char *save_ptr = NULL;
440 char *buf_ptr = buf;
441 char *token;
442 unsigned long int res = 0;
443
444 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
445
446 while (1) {
447 token = strtok_r(buf_ptr, "/", &save_ptr);
448 buf_ptr = NULL;
449 if (!token)
450 break;
451 if (strncmp(token, "su", 2) == 0) {
452 token = strstr(token, "-");
453 if (!token)
454 return 0xff;
455 token += 1;
456 errno = 0;
457 res = strtoul(token, NULL, 10);
458 if (errno == ERANGE || res > E1_OFFS_MAX)
459 return 0xff;
460 return (uint8_t) res;
461 }
462 }
463
464 return 0xff;
465}
466
467/* Get the E1 subslot number (internal) from a given E1 endpoint name
468 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
469static uint8_t e1_ss_nr_from_epname(const char *epname)
470{
471 uint8_t rate;
472 uint8_t offs;
473 unsigned int i;
474
475 rate = e1_rate_from_epname(epname);
476 offs = e1_offs_from_epname(epname);
477
478 osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
479
480 for (i = 0; i < sizeof(e1_rates); i++) {
481 if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
482 return i;
483 }
484
485 return 0xff;
486}
487
488/* Check if the selected E1 endpoint is avalable, which means that none of
489 * the overlapping endpoints are currently serving a call. (if the system
490 * is properly configured such a situation should never ocurr!) */
491static bool endp_avail_e1(struct mgcp_endpoint *endp)
492{
493 /* The following map shows the overlapping of the subslots and their
494 * respective rates. The numbers on the right running from top to bottom
495 * are the bit offsets in the whole 64k timeslot. The numbers inside the
496 * boxes symbolize the internal subslot number (array index) and the
497 * rate in the form: i:r where i is the subslot number and r the
498 * respective rate.
499 *
500 * +--------+--------+--------+--------+ 0
501 * | | | | 7:8k |
502 * | | + 3:16k +--------+ 1
503 * | | | | 8:8k |
504 * | | 1:32k +--------+--------+ 2
505 * | | | | 9:8k |
506 * | | + 4:16k +--------+ 3
507 * | | | | 10:8k |
508 * | 0:64k +--------+--------+--------+ 4
509 * | | | | 11:8k |
510 * | | + 5:16k +--------+ 5
511 * | | | | 12:8k |
512 * | | 2:32k +--------+--------+ 6
513 * | | | | 13:8k |
514 * | | + 6:16k +--------+ 7
515 * | | | | 14:8k |
516 * +--------+--------+--------+--------+ 8
517 *
518 * The following array contains tables with the subslot numbers that must be
519 * unused for each subslot. During this test we do not have to check the
520 * endpoint we need to verify, only the overlaps need to be checked. This is
521 * also the reason why the related subslot number is missing from each each
522 * line. */
Philipp Maierfe67e092020-07-07 21:21:45 +0200523 const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
524 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
Philipp Maier8d6a1932020-06-18 12:19:31 +0200525 { 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
526 { 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
527 { 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
528 { 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
529 { 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
530 { 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
531 { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
532 { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
533 { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
534 { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
535 { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
536 { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
537 { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
538 { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
539
540 const int8_t *interlock;
541 unsigned int i;
542 uint8_t ts_nr = 0;
543 uint8_t ss_nr = 0;
544 char *epname_check;
545 struct mgcp_endpoint *endp_check;
546 bool available = true;
547
548 /* This function must only be used with E1 type endpoints! */
549 OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
550
551 ts_nr = e1_ts_nr_from_epname(endp->name);
552 ss_nr = e1_ss_nr_from_epname(endp->name);
553 if (ts_nr == 0xff || ss_nr == 0xff) {
554 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
555 "cannot check endpoint availability, endpoint name not parseable!\n");
556 return false;
557 }
558
559 interlock = interlock_tab[ss_nr];
560
561 for (i = 0; i < sizeof(interlock_tab[0]); i++) {
562 /* Detect row end */
563 if (interlock[i] == -1)
564 break;
565
566 /* Pick overlapping endpoint to check */
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200567 epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
568 endp->trunk->trunk_nr, ts_nr,
569 interlock[i]);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200570 endp_check = find_specific_endpoint(epname_check, endp->trunk);
571 if (!endp_check) {
572 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
573 "cannot check endpoint availability, overlapping endpoint:%s not found!\n",
574 epname_check);
575 talloc_free(epname_check);
576 continue;
577 }
578 talloc_free(epname_check);
579
580 /* Check if overlapping endpoint currently serves another call
581 * (This is an exceptional situation, that should not occur
582 * in a properly configured environment!) */
583 if (endp_check->callid) {
584 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
585 "endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
586 endp_check->name);
587 available = false;
588 }
589 }
590
591 return available;
592}
593
594/*! check if an endpoint is available for any kind of operation.
595 * \param[in] endp endpoint to check.
596 * \returns true if endpoint is avalable, false it is blocked for any reason. */
597bool mgcp_endp_avail(struct mgcp_endpoint *endp)
598{
599 switch (endp->trunk->trunk_type) {
600 case MGCP_TRUNK_VIRTUAL:
601 /* There are no obstacles that may render a virtual trunk
602 * endpoint unusable, so virtual trunk endpoints are always
603 * available */
604 return true;
605 case MGCP_TRUNK_E1:
606 return endp_avail_e1(endp);
607 default:
608 OSMO_ASSERT(false);
609 }
610
611 return false;
612}
Philipp Maier889fe7f2020-07-06 17:44:12 +0200613
614/*! claim endpoint, sets callid and activates endpoint, should be called at the
615 * beginning of the CRCX procedure when it is clear that a new call should be
616 * created.
617 * \param[in] endp endpoint to claim.
618 * \param[in] callid that is assingned to this endpoint. */
619int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
620{
621 int rc = 0;
622 uint8_t ts;
623 uint8_t ss;
624 uint8_t offs;
625
626 /* TODO: Make this function more intelligent, it should run the
627 * call id checks we currently have in protocol.c directly here. */
628
629 /* Set the callid, creation of another connection will only be possible
630 * when the callid matches up. (Connections are distinguished by their
631 * connection ids) */
632 endp->callid = talloc_strdup(endp, callid);
633 OSMO_ASSERT(endp->callid);
634
635 /* Allocate resources */
636 switch (endp->trunk->trunk_type) {
637 case MGCP_TRUNK_VIRTUAL:
638 /* No additional initaliziation required here, virtual
639 * endpoints will open/close network sockets themselves
640 * on demand. */
641 break;
642 case MGCP_TRUNK_E1:
643 ts = e1_ts_nr_from_epname(endp->name);
644 ss = e1_ss_nr_from_epname(endp->name);
645 offs = e1_offs_from_epname(endp->name);
646 OSMO_ASSERT(ts != 0xFF);
647 OSMO_ASSERT(ts != 0);
648 OSMO_ASSERT(ss != 0xFF);
649 OSMO_ASSERT(offs != 0xFF);
650 rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
651 break;
652 default:
653 OSMO_ASSERT(false);
654 }
655
Pau Espin Pedrol30e01352020-09-21 12:29:41 +0200656 /* Make sure the endpoint is released when claiming the endpoint fails. */
Philipp Maier889fe7f2020-07-06 17:44:12 +0200657 if (rc < 0)
658 mgcp_endp_release(endp);
659
660 return rc;
661}
662
663/*! update endpoint, updates internal endpoint specific data, should be
664 * after when MDCX or CRCX has been executed successuflly.
665 * \param[in] endp endpoint to update. */
666void mgcp_endp_update(struct mgcp_endpoint *endp)
667{
668 /* Allocate resources */
669 switch (endp->trunk->trunk_type) {
670 case MGCP_TRUNK_VIRTUAL:
671 /* No updating initaliziation required for virtual endpoints. */
672 break;
673 case MGCP_TRUNK_E1:
674 mgcp_e1_endp_update(endp);
675 break;
676 default:
677 OSMO_ASSERT(false);
678 }
679}
Pau Espin Pedrol6d0a59a2020-09-08 16:50:22 +0200680
681void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
682{
683 llist_add(&conn->entry, &endp->conns);
684}
685
686void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
687{
688 /* Run endpoint cleanup action. By this we inform the endpoint about
689 * the removal of the connection and allow it to clean up its inner
690 * state accordingly */
691 if (endp->type->cleanup_cb)
692 endp->type->cleanup_cb(endp, conn);
693 llist_del(&conn->entry);
694 if (llist_empty(&endp->conns))
695 mgcp_endp_release(endp);
696}