blob: 5d9ec27505653fd95ec8c2b3e9d0c9e1f106ab07 [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 Maier87bd9be2017-08-22 16:35:41 +020024#include <osmocom/mgcp/mgcp_internal.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010025#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020026#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020027
Philipp Maier8d6a1932020-06-18 12:19:31 +020028#define E1_TIMESLOTS 32
29#define E1_RATE_MAX 64
30#define E1_OFFS_MAX 8
31
32/* A 64k timeslot on an E1 line can be subdevied into the following
33 * subslot combinations:
34 *
35 * subslot: offset:
36 * [ ][ ][ 16k ][8k_subslot] 0
37 * [ ][ 32k ][_subslot__][8k_subslot] 1
38 * [ ][ subslot ][ 16k ][8k_subslot] 2
39 * [ 64k ][__________][_subslot__][8k_subslot] 3
40 * [ timeslot ][ ][ 16k ][8k_subslot] 4
41 * [ ][ 32K ][_subslot__][8k_subslot] 5
42 * [ ][ subslot ][ 16k ][8k_subslot] 6
43 * [ ][ ][ subslot ][8k_subslot] 7
44 *
45 * Since overlapping assignment of subslots is not possible there is a limited
46 * set of subslot assignments possible. The e1_rates array lists the possible
47 * assignments as depicted above. Also each subslot assignment comes along with
48 * a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit
49 * offsets. */
50static const uint8_t e1_rates[] =
51 { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
52static const uint8_t e1_offsets[] =
53 { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
54
Philipp Maier87bd9be2017-08-22 16:35:41 +020055/* Endpoint typeset definition */
56const struct mgcp_endpoint_typeset ep_typeset = {
57 /* Specify endpoint properties for RTP endpoint */
Philipp Maier0996a1e2020-06-10 15:27:14 +020058 .rtp = {
59 .max_conns = 2,
60 .dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
61 .cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
62 },
63 /* Specify endpoint properties for E1 endpoint */
64 .e1 = {
65 .max_conns = 1,
66 .dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
67 .cleanup_cb = mgcp_cleanup_e1_bridge_cb,
68 },
Philipp Maier87bd9be2017-08-22 16:35:41 +020069};
Philipp Maieredc00f42018-01-24 11:58:56 +010070
Philipp Maier7462b952020-06-10 14:50:34 +020071/* Generate virtual endpoint name from given parameters */
72static char *gen_virtual_epname(void *ctx, const char *domain,
73 unsigned int index)
74{
75 return talloc_asprintf(ctx, "%s%x@%s",
76 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
77}
78
Philipp Maier98c09b32020-06-18 12:17:55 +020079/* Generate E1 endpoint name from given numeric parameters */
Philipp Maier0ffa3bd2020-06-30 16:17:03 +020080static char *gen_e1_epname(void *ctx, const char *domain, uint8_t trunk_nr,
81 uint8_t ts_nr, uint8_t ss_nr)
Philipp Maier98c09b32020-06-18 12:17:55 +020082{
Philipp Maier98c09b32020-06-18 12:17:55 +020083 unsigned int rate;
84 unsigned int offset;
85
Philipp Maier8d6a1932020-06-18 12:19:31 +020086 OSMO_ASSERT(ss_nr < sizeof(e1_rates));
Philipp Maier98c09b32020-06-18 12:17:55 +020087
Philipp Maier8d6a1932020-06-18 12:19:31 +020088 rate = e1_rates[ss_nr];
89 offset = e1_offsets[ss_nr];
Philipp Maier98c09b32020-06-18 12:17:55 +020090
Philipp Maier0ffa3bd2020-06-30 16:17:03 +020091 return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s",
92 MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr,
93 rate, offset, domain);
Philipp Maier98c09b32020-06-18 12:17:55 +020094}
95
Philipp Maierc66ab2c2020-06-02 20:55:34 +020096/*! allocate an endpoint and set default values.
97 * \param[in] trunk configuration.
Philipp Maier7462b952020-06-10 14:50:34 +020098 * \param[in] name endpoint index.
Philipp Maierc66ab2c2020-06-02 20:55:34 +020099 * \returns endpoint on success, NULL on failure. */
Philipp Maier7462b952020-06-10 14:50:34 +0200100struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
101 unsigned int index)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200102{
103 struct mgcp_endpoint *endp;
104
105 endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
106 if (!endp)
107 return NULL;
108
109 INIT_LLIST_HEAD(&endp->conns);
110 endp->cfg = trunk->cfg;
111 endp->trunk = trunk;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200112
113 switch (trunk->trunk_type) {
114 case MGCP_TRUNK_VIRTUAL:
115 endp->type = &ep_typeset.rtp;
Philipp Maier7462b952020-06-10 14:50:34 +0200116 endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200117 break;
118 case MGCP_TRUNK_E1:
Philipp Maier7462b952020-06-10 14:50:34 +0200119 endp->type = &ep_typeset.rtp;
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200120 endp->name = gen_e1_epname(endp, trunk->cfg->domain,
121 trunk->trunk_nr,
Philipp Maierfbcf3992020-07-02 23:08:37 +0200122 index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200123 break;
124 default:
125 osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
126 trunk->trunk_type, __FILE__, __LINE__);
127 }
128
129 return endp;
130}
131
Philipp Maieredc00f42018-01-24 11:58:56 +0100132/*! release endpoint, all open connections are closed.
133 * \param[in] endp endpoint to release */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100134void mgcp_endp_release(struct mgcp_endpoint *endp)
Philipp Maieredc00f42018-01-24 11:58:56 +0100135{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200136 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
Philipp Maieredc00f42018-01-24 11:58:56 +0100137
138 /* Normally this function should only be called when
139 * all connections have been removed already. In case
140 * that there are still connections open (e.g. when
141 * RSIP is executed), free them all at once. */
142 mgcp_conn_free_all(endp);
143
144 /* Reset endpoint parameters and states */
145 talloc_free(endp->callid);
146 endp->callid = NULL;
147 talloc_free(endp->local_options.string);
148 endp->local_options.string = NULL;
149 talloc_free(endp->local_options.codec);
150 endp->local_options.codec = NULL;
Philipp Maier207ab512018-02-02 14:19:26 +0100151 endp->wildcarded_req = false;
Philipp Maieredc00f42018-01-24 11:58:56 +0100152}
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200153
154/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
155 * "ds/e1-") and write the epname without the prefix back to the memory
156 * pointed at by epname. (per trunk the prefix is the same for all endpoints,
157 * so no ambiguity is introduced) */
158static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
159{
160 size_t prefix_len;
161 switch (trunk->trunk_type) {
162 case MGCP_TRUNK_VIRTUAL:
163 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
164 if (strncmp
165 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
166 prefix_len) == 0)
167 memmove(epname, epname + prefix_len,
168 strlen(epname) - prefix_len + 1);
169 return;
170 case MGCP_TRUNK_E1:
171 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
172 if (strncmp
173 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
174 prefix_len) == 0)
175 memmove(epname, epname + prefix_len,
176 strlen(epname) - prefix_len + 1);
177 return;
178 default:
179 OSMO_ASSERT(false);
180 }
181}
182
183/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
184 * epname by writing a '\0' char where the suffix starts. */
185static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
186{
187 char *suffix_begin;
188
189 /* Endpoints on the virtual trunk may have a domain name that is
190 * followed after an @ character, this can be chopped off. All
191 * other supported trunk types do not have any suffixes that may
192 * be chopped off */
193 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
194 suffix_begin = strchr(epname, '@');
195 if (!suffix_begin)
196 return;
197 *suffix_begin = '\0';
198 }
199}
200
201/* Convert all characters in epname to lowercase and strip trunk prefix and
202 * endpoint name suffix (domain name) from epname. The result is written to
203 * to the memory pointed at by epname_stripped. The expected size of the
204 * result is either equal or lower then the length of the input string
205 * (epname) */
206static void strip_epname(char *epname_stripped, const char *epname,
207 const struct mgcp_trunk *trunk)
208{
209 osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
210 chop_epname_prefix(epname_stripped, trunk);
211 chop_epname_suffix(epname_stripped, trunk);
212}
213
214/* Go through the trunk and find a random free (no active calls) endpoint,
215 * this function is called when a wildcarded request is carried out, which
216 * means that it is up to the MGW to choose a random free endpoint. */
217static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
218{
219 struct mgcp_endpoint *endp;
220 unsigned int i;
221
222 for (i = 0; i < trunk->number_endpoints; i++) {
223 endp = trunk->endpoints[i];
Philipp Maier8d6a1932020-06-18 12:19:31 +0200224 /* A free endpoint must not serve a call already and it must
225 * be available. */
226 if (endp->callid == NULL && mgcp_endp_avail(endp))
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200227 return endp;
228 }
229
230 return NULL;
231}
232
233/* Find an endpoint specified by its name. If the endpoint can not be found,
234 * return NULL */
235static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
236 const struct mgcp_trunk *trunk)
237{
238 char epname_stripped[MGCP_ENDPOINT_MAXLEN];
239 char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
240 struct mgcp_endpoint *endp;
241 unsigned int i;
242
243 /* Strip irrelevant information from the endpoint name */
244 strip_epname(epname_stripped, epname, trunk);
245
246 for (i = 0; i < trunk->number_endpoints; i++) {
247 endp = trunk->endpoints[i];
248 strip_epname(epname_stripped_endp, endp->name, trunk);
249 if (strcmp(epname_stripped_endp, epname_stripped) == 0)
250 return endp;
251 }
252
253 return NULL;
254}
255
256/*! Find an endpoint by its name on a specified trunk.
257 * \param[out] cause pointer to store cause code, can be NULL.
258 * \param[in] epname endpoint name to lookup.
259 * \param[in] trunk where the endpoint is located.
260 * \returns endpoint or NULL if endpoint was not found. */
261struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
262 const struct mgcp_trunk *trunk)
263{
264 struct mgcp_endpoint *endp;
265
266 if (cause)
267 *cause = 0;
268
269 /* At the moment we only support a primitive ('*'-only) method of
270 * wildcarded endpoint searches that picks the next free endpoint on
271 * a trunk. */
272 if (strstr(epname, "*")) {
273 endp = find_free_endpoint(trunk);
274 if (endp) {
275 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
276 "(trunk:%d) found free endpoint: %s\n",
277 trunk->trunk_nr, endp->name);
278 endp->wildcarded_req = true;
279 return endp;
280 }
281
282 LOGP(DLMGCP, LOGL_ERROR,
283 "(trunk:%d) Not able to find a free endpoint\n",
284 trunk->trunk_nr);
285 if (cause)
286 *cause = -403;
287 return NULL;
288 }
289
290 /* Find an endpoint by its name (if wildcarded request is not
291 * applicable) */
292 endp = find_specific_endpoint(epname, trunk);
293 if (endp) {
294 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
295 "(trunk:%d) found endpoint: %s\n",
296 trunk->trunk_nr, endp->name);
297 endp->wildcarded_req = false;
298 return endp;
299 }
300
301 LOGP(DLMGCP, LOGL_ERROR,
302 "(trunk:%d) Not able to find specified endpoint: %s\n",
303 trunk->trunk_nr, epname);
304 if (cause)
305 *cause = -500;
306
307 return NULL;
308}
309
310/* Check if the domain name, which is supplied with the endpoint name
311 * matches the configuration. */
312static int check_domain_name(const char *epname, struct mgcp_config *cfg)
313{
314 char *domain_to_check;
315
316 domain_to_check = strstr(epname, "@");
317 if (!domain_to_check) {
318 LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
319 epname, cfg->domain);
320 return -EINVAL;
321 }
322
323 /* Accept any domain if configured as "*" */
324 if (!strcmp(cfg->domain, "*"))
325 return 0;
326
327 if (strcmp(domain_to_check+1, cfg->domain) != 0) {
328 LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
329 epname, cfg->domain);
330 return -EINVAL;
331 }
332
333 return 0;
334}
335
336/*! Find an endpoint by its name, search at all trunks.
337 * \param[out] cause, pointer to store cause code, can be NULL.
338 * \param[in] epname, must contain trunk prefix.
339 * \param[in] cfg, mgcp configuration (trunks).
340 * \returns endpoint or NULL if endpoint was not found. */
341struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
342 struct mgcp_config *cfg)
343{
344 struct mgcp_trunk *trunk;
345 struct mgcp_endpoint *endp;
346 char epname_lc[MGCP_ENDPOINT_MAXLEN];
347
348 osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
349 epname = epname_lc;
350
351 if (cause)
352 *cause = -500;
353
354 /* Identify the trunk where the endpoint is located */
355 trunk = mgcp_trunk_by_name(cfg, epname);
356 if (!trunk)
357 return NULL;
358
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200359 /* All endpoint names require a domain as suffix */
360 if (check_domain_name(epname, cfg))
361 return NULL;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200362
363 /* Identify the endpoint on the trunk */
364 endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
365 if (!endp) {
366 return NULL;
367 }
368
369 if (cause)
370 *cause = 0;
371 return endp;
372}
Philipp Maier8d6a1932020-06-18 12:19:31 +0200373
374/* Get the E1 timeslot number from a given E1 endpoint name
375 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
376static uint8_t e1_ts_nr_from_epname(const char *epname)
377{
378 char buf[MGCP_ENDPOINT_MAXLEN + 1];
379 char *save_ptr = NULL;
380 char *buf_ptr = buf;
381 char *token;
382 unsigned long int res = 0;
383
384 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
385
386 while (1) {
387 token = strtok_r(buf_ptr, "/", &save_ptr);
388 buf_ptr = NULL;
389 if (!token)
390 break;
391 if (strncmp(token, "s-", 2) == 0) {
392 errno = 0;
393 res = strtoul(token + 2, NULL, 10);
394 if (errno == ERANGE || res > E1_TIMESLOTS)
395 return 0xff;
396 return (uint8_t) res;
397 }
398 }
399
400 return 0xff;
401}
402
403/* Get the E1 timeslot number from a given E1 endpoint name
404 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
405static uint8_t e1_rate_from_epname(const char *epname)
406{
407 char buf[MGCP_ENDPOINT_MAXLEN + 1];
408 char *save_ptr = NULL;
409 char *buf_ptr = buf;
410 char *token;
411 unsigned long int res = 0;
412 unsigned int i;
413
414 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
415
416 while (1) {
417 token = strtok_r(buf_ptr, "/", &save_ptr);
418 buf_ptr = NULL;
419 if (!token)
420 break;
421 if (strncmp(token, "su", 2) == 0) {
422 errno = 0;
423 res = strtoul(token + 2, NULL, 10);
424 if (errno == ERANGE || res > E1_RATE_MAX)
425 return 0xff;
426 /* Make sure the rate is a valid rate */
427 for (i = 0; i < sizeof(e1_rates); i++) {
428 if (res == e1_rates[i])
429 return (uint8_t) res;
430 }
431 return 0xff;
432 }
433 }
434
435 return 0xff;
436}
437
438/* Get the E1 bitstream offset from a given E1 endpoint name
439 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
440static uint8_t e1_offs_from_epname(const char *epname)
441{
442 char buf[MGCP_ENDPOINT_MAXLEN + 1];
443 char *save_ptr = NULL;
444 char *buf_ptr = buf;
445 char *token;
446 unsigned long int res = 0;
447
448 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
449
450 while (1) {
451 token = strtok_r(buf_ptr, "/", &save_ptr);
452 buf_ptr = NULL;
453 if (!token)
454 break;
455 if (strncmp(token, "su", 2) == 0) {
456 token = strstr(token, "-");
457 if (!token)
458 return 0xff;
459 token += 1;
460 errno = 0;
461 res = strtoul(token, NULL, 10);
462 if (errno == ERANGE || res > E1_OFFS_MAX)
463 return 0xff;
464 return (uint8_t) res;
465 }
466 }
467
468 return 0xff;
469}
470
471/* Get the E1 subslot number (internal) from a given E1 endpoint name
472 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
473static uint8_t e1_ss_nr_from_epname(const char *epname)
474{
475 uint8_t rate;
476 uint8_t offs;
477 unsigned int i;
478
479 rate = e1_rate_from_epname(epname);
480 offs = e1_offs_from_epname(epname);
481
482 osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
483
484 for (i = 0; i < sizeof(e1_rates); i++) {
485 if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
486 return i;
487 }
488
489 return 0xff;
490}
491
492/* Check if the selected E1 endpoint is avalable, which means that none of
493 * the overlapping endpoints are currently serving a call. (if the system
494 * is properly configured such a situation should never ocurr!) */
495static bool endp_avail_e1(struct mgcp_endpoint *endp)
496{
497 /* The following map shows the overlapping of the subslots and their
498 * respective rates. The numbers on the right running from top to bottom
499 * are the bit offsets in the whole 64k timeslot. The numbers inside the
500 * boxes symbolize the internal subslot number (array index) and the
501 * rate in the form: i:r where i is the subslot number and r the
502 * respective rate.
503 *
504 * +--------+--------+--------+--------+ 0
505 * | | | | 7:8k |
506 * | | + 3:16k +--------+ 1
507 * | | | | 8:8k |
508 * | | 1:32k +--------+--------+ 2
509 * | | | | 9:8k |
510 * | | + 4:16k +--------+ 3
511 * | | | | 10:8k |
512 * | 0:64k +--------+--------+--------+ 4
513 * | | | | 11:8k |
514 * | | + 5:16k +--------+ 5
515 * | | | | 12:8k |
516 * | | 2:32k +--------+--------+ 6
517 * | | | | 13:8k |
518 * | | + 6:16k +--------+ 7
519 * | | | | 14:8k |
520 * +--------+--------+--------+--------+ 8
521 *
522 * The following array contains tables with the subslot numbers that must be
523 * unused for each subslot. During this test we do not have to check the
524 * endpoint we need to verify, only the overlaps need to be checked. This is
525 * also the reason why the related subslot number is missing from each each
526 * line. */
Philipp Maierfe67e092020-07-07 21:21:45 +0200527 const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
528 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
Philipp Maier8d6a1932020-06-18 12:19:31 +0200529 { 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
530 { 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
531 { 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
532 { 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
533 { 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
534 { 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
535 { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
536 { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
537 { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
538 { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
539 { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
540 { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
541 { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
542 { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
543
544 const int8_t *interlock;
545 unsigned int i;
546 uint8_t ts_nr = 0;
547 uint8_t ss_nr = 0;
548 char *epname_check;
549 struct mgcp_endpoint *endp_check;
550 bool available = true;
551
552 /* This function must only be used with E1 type endpoints! */
553 OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
554
555 ts_nr = e1_ts_nr_from_epname(endp->name);
556 ss_nr = e1_ss_nr_from_epname(endp->name);
557 if (ts_nr == 0xff || ss_nr == 0xff) {
558 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
559 "cannot check endpoint availability, endpoint name not parseable!\n");
560 return false;
561 }
562
563 interlock = interlock_tab[ss_nr];
564
565 for (i = 0; i < sizeof(interlock_tab[0]); i++) {
566 /* Detect row end */
567 if (interlock[i] == -1)
568 break;
569
570 /* Pick overlapping endpoint to check */
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200571 epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
572 endp->trunk->trunk_nr, ts_nr,
573 interlock[i]);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200574 endp_check = find_specific_endpoint(epname_check, endp->trunk);
575 if (!endp_check) {
576 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
577 "cannot check endpoint availability, overlapping endpoint:%s not found!\n",
578 epname_check);
579 talloc_free(epname_check);
580 continue;
581 }
582 talloc_free(epname_check);
583
584 /* Check if overlapping endpoint currently serves another call
585 * (This is an exceptional situation, that should not occur
586 * in a properly configured environment!) */
587 if (endp_check->callid) {
588 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
589 "endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
590 endp_check->name);
591 available = false;
592 }
593 }
594
595 return available;
596}
597
598/*! check if an endpoint is available for any kind of operation.
599 * \param[in] endp endpoint to check.
600 * \returns true if endpoint is avalable, false it is blocked for any reason. */
601bool mgcp_endp_avail(struct mgcp_endpoint *endp)
602{
603 switch (endp->trunk->trunk_type) {
604 case MGCP_TRUNK_VIRTUAL:
605 /* There are no obstacles that may render a virtual trunk
606 * endpoint unusable, so virtual trunk endpoints are always
607 * available */
608 return true;
609 case MGCP_TRUNK_E1:
610 return endp_avail_e1(endp);
611 default:
612 OSMO_ASSERT(false);
613 }
614
615 return false;
616}