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