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