blob: 4fcddb80fdc3a9dfb06620c52d7e907ba031dfde [file] [log] [blame]
Philipp Maier87bd9be2017-08-22 16:35:41 +02001/* Endpoint types */
2
3/*
Philipp Maierc66ab2c2020-06-02 20:55:34 +02004 * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
Philipp Maier87bd9be2017-08-22 16:35:41 +02005 * All Rights Reserved
6 *
7 * Author: Philipp Maier
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
Philipp Maier993ea6b2020-08-04 18:26:50 +020024#include <osmocom/mgcp/mgcp.h>
25#include <osmocom/mgcp/mgcp_protocol.h>
26#include <osmocom/mgcp/mgcp_conn.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010027#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020028#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020029
Philipp Maier99d4d362020-09-07 11:58:09 +020030#include <osmocom/abis/e1_input.h>
Philipp Maier889fe7f2020-07-06 17:44:12 +020031#include <osmocom/mgcp/mgcp_e1.h>
Philipp Maier124a3e02021-07-26 11:17:15 +020032#include <osmocom/core/stat_item.h>
Philipp Maier889fe7f2020-07-06 17:44:12 +020033
Philipp Maier8d6a1932020-06-18 12:19:31 +020034#define E1_RATE_MAX 64
35#define E1_OFFS_MAX 8
36
Philipp Maier87bd9be2017-08-22 16:35:41 +020037/* Endpoint typeset definition */
38const struct mgcp_endpoint_typeset ep_typeset = {
39 /* Specify endpoint properties for RTP endpoint */
Philipp Maier0996a1e2020-06-10 15:27:14 +020040 .rtp = {
41 .max_conns = 2,
42 .dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
43 .cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
44 },
45 /* Specify endpoint properties for E1 endpoint */
46 .e1 = {
47 .max_conns = 1,
48 .dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
49 .cleanup_cb = mgcp_cleanup_e1_bridge_cb,
50 },
Philipp Maier87bd9be2017-08-22 16:35:41 +020051};
Philipp Maieredc00f42018-01-24 11:58:56 +010052
Philipp Maier7462b952020-06-10 14:50:34 +020053/* Generate virtual endpoint name from given parameters */
54static char *gen_virtual_epname(void *ctx, const char *domain,
55 unsigned int index)
56{
57 return talloc_asprintf(ctx, "%s%x@%s",
58 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
59}
60
Philipp Maier98c09b32020-06-18 12:17:55 +020061/* Generate E1 endpoint name from given numeric parameters */
Philipp Maierd70eef62021-07-19 13:53:28 +020062static char *gen_e1_epname(void *ctx, const char *domain, unsigned int trunk_nr,
Philipp Maier0ffa3bd2020-06-30 16:17:03 +020063 uint8_t ts_nr, uint8_t ss_nr)
Philipp Maier98c09b32020-06-18 12:17:55 +020064{
Philipp Maier98c09b32020-06-18 12:17:55 +020065 unsigned int rate;
66 unsigned int offset;
67
Philipp Maier8d6a1932020-06-18 12:19:31 +020068 OSMO_ASSERT(ss_nr < sizeof(e1_rates));
Philipp Maier98c09b32020-06-18 12:17:55 +020069
Philipp Maier8d6a1932020-06-18 12:19:31 +020070 rate = e1_rates[ss_nr];
71 offset = e1_offsets[ss_nr];
Philipp Maier98c09b32020-06-18 12:17:55 +020072
Philipp Maier0ffa3bd2020-06-30 16:17:03 +020073 return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s",
74 MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr,
75 rate, offset, domain);
Philipp Maier98c09b32020-06-18 12:17:55 +020076}
77
Philipp Maierc66ab2c2020-06-02 20:55:34 +020078/*! allocate an endpoint and set default values.
79 * \param[in] trunk configuration.
Philipp Maier7462b952020-06-10 14:50:34 +020080 * \param[in] name endpoint index.
Philipp Maierc66ab2c2020-06-02 20:55:34 +020081 * \returns endpoint on success, NULL on failure. */
Philipp Maier7462b952020-06-10 14:50:34 +020082struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
83 unsigned int index)
Philipp Maierc66ab2c2020-06-02 20:55:34 +020084{
85 struct mgcp_endpoint *endp;
86
87 endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
88 if (!endp)
89 return NULL;
90
91 INIT_LLIST_HEAD(&endp->conns);
92 endp->cfg = trunk->cfg;
93 endp->trunk = trunk;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020094
95 switch (trunk->trunk_type) {
96 case MGCP_TRUNK_VIRTUAL:
97 endp->type = &ep_typeset.rtp;
Philipp Maier7462b952020-06-10 14:50:34 +020098 endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
Philipp Maierc66ab2c2020-06-02 20:55:34 +020099 break;
100 case MGCP_TRUNK_E1:
Philipp Maier889fe7f2020-07-06 17:44:12 +0200101 endp->type = &ep_typeset.e1;
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200102 endp->name = gen_e1_epname(endp, trunk->cfg->domain,
103 trunk->trunk_nr,
Philipp Maierfbcf3992020-07-02 23:08:37 +0200104 index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200105 break;
106 default:
107 osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
108 trunk->trunk_type, __FILE__, __LINE__);
109 }
110
111 return endp;
112}
113
Philipp Maieredc00f42018-01-24 11:58:56 +0100114/*! release endpoint, all open connections are closed.
115 * \param[in] endp endpoint to release */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100116void mgcp_endp_release(struct mgcp_endpoint *endp)
Philipp Maieredc00f42018-01-24 11:58:56 +0100117{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200118 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
Philipp Maieredc00f42018-01-24 11:58:56 +0100119
120 /* Normally this function should only be called when
121 * all connections have been removed already. In case
122 * that there are still connections open (e.g. when
123 * RSIP is executed), free them all at once. */
124 mgcp_conn_free_all(endp);
125
Philipp Maier124a3e02021-07-26 11:17:15 +0200126 /* We must only decrement the stat item when the endpoint as actually
127 * claimed. An endpoint is claimed when a call-id is set */
128 if (endp->callid)
129 osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
130 TRUNK_STAT_ENDPOINTS_USED), 1);
131
Philipp Maieredc00f42018-01-24 11:58:56 +0100132 /* Reset endpoint parameters and states */
133 talloc_free(endp->callid);
134 endp->callid = NULL;
135 talloc_free(endp->local_options.string);
136 endp->local_options.string = NULL;
137 talloc_free(endp->local_options.codec);
138 endp->local_options.codec = NULL;
Philipp Maier889fe7f2020-07-06 17:44:12 +0200139
140 if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
141 mgcp_e1_endp_release(endp);
Philipp Maieredc00f42018-01-24 11:58:56 +0100142}
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200143
144/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
145 * "ds/e1-") and write the epname without the prefix back to the memory
146 * pointed at by epname. (per trunk the prefix is the same for all endpoints,
147 * so no ambiguity is introduced) */
148static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
149{
150 size_t prefix_len;
151 switch (trunk->trunk_type) {
152 case MGCP_TRUNK_VIRTUAL:
153 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_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 case MGCP_TRUNK_E1:
161 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
162 if (strncmp
163 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
164 prefix_len) == 0)
165 memmove(epname, epname + prefix_len,
166 strlen(epname) - prefix_len + 1);
167 return;
168 default:
169 OSMO_ASSERT(false);
170 }
171}
172
173/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
174 * epname by writing a '\0' char where the suffix starts. */
175static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
176{
177 char *suffix_begin;
178
179 /* Endpoints on the virtual trunk may have a domain name that is
180 * followed after an @ character, this can be chopped off. All
181 * other supported trunk types do not have any suffixes that may
182 * be chopped off */
183 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
184 suffix_begin = strchr(epname, '@');
185 if (!suffix_begin)
186 return;
187 *suffix_begin = '\0';
188 }
189}
190
191/* Convert all characters in epname to lowercase and strip trunk prefix and
192 * endpoint name suffix (domain name) from epname. The result is written to
193 * to the memory pointed at by epname_stripped. The expected size of the
194 * result is either equal or lower then the length of the input string
195 * (epname) */
196static void strip_epname(char *epname_stripped, const char *epname,
197 const struct mgcp_trunk *trunk)
198{
199 osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
200 chop_epname_prefix(epname_stripped, trunk);
201 chop_epname_suffix(epname_stripped, trunk);
202}
203
204/* Go through the trunk and find a random free (no active calls) endpoint,
205 * this function is called when a wildcarded request is carried out, which
206 * means that it is up to the MGW to choose a random free endpoint. */
207static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
208{
209 struct mgcp_endpoint *endp;
210 unsigned int i;
211
212 for (i = 0; i < trunk->number_endpoints; i++) {
213 endp = trunk->endpoints[i];
Philipp Maier8d6a1932020-06-18 12:19:31 +0200214 /* A free endpoint must not serve a call already and it must
215 * be available. */
216 if (endp->callid == NULL && mgcp_endp_avail(endp))
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200217 return endp;
218 }
219
220 return NULL;
221}
222
223/* Find an endpoint specified by its name. If the endpoint can not be found,
224 * return NULL */
225static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
226 const struct mgcp_trunk *trunk)
227{
228 char epname_stripped[MGCP_ENDPOINT_MAXLEN];
229 char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
230 struct mgcp_endpoint *endp;
231 unsigned int i;
232
233 /* Strip irrelevant information from the endpoint name */
234 strip_epname(epname_stripped, epname, trunk);
235
236 for (i = 0; i < trunk->number_endpoints; i++) {
237 endp = trunk->endpoints[i];
238 strip_epname(epname_stripped_endp, endp->name, trunk);
239 if (strcmp(epname_stripped_endp, epname_stripped) == 0)
240 return endp;
241 }
242
243 return NULL;
244}
245
Philipp Maierd64c0412021-07-14 11:53:49 +0200246/*! Check if the given epname refers to a wildcarded request or to a specific
247 * endpoint.
248 * \param[in] epname endpoint name to check
249 * \returns true if epname refers to wildcarded request, else false. */
250bool mgcp_endp_is_wildcarded(const char *epname)
251{
252 if (strstr(epname, "*"))
253 return true;
254
255 return false;
256}
257
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200258/*! Find an endpoint by its name on a specified trunk.
259 * \param[out] cause pointer to store cause code, can be NULL.
260 * \param[in] epname endpoint name to lookup.
261 * \param[in] trunk where the endpoint is located.
262 * \returns endpoint or NULL if endpoint was not found. */
263struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
264 const struct mgcp_trunk *trunk)
265{
266 struct mgcp_endpoint *endp;
267
268 if (cause)
269 *cause = 0;
270
271 /* At the moment we only support a primitive ('*'-only) method of
272 * wildcarded endpoint searches that picks the next free endpoint on
273 * a trunk. */
Philipp Maierd64c0412021-07-14 11:53:49 +0200274 if (mgcp_endp_is_wildcarded(epname)) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200275 endp = find_free_endpoint(trunk);
276 if (endp) {
277 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
278 "(trunk:%d) found free endpoint: %s\n",
279 trunk->trunk_nr, endp->name);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200280 return endp;
281 }
282
283 LOGP(DLMGCP, LOGL_ERROR,
284 "(trunk:%d) Not able to find a free endpoint\n",
285 trunk->trunk_nr);
286 if (cause)
287 *cause = -403;
288 return NULL;
289 }
290
291 /* Find an endpoint by its name (if wildcarded request is not
292 * applicable) */
293 endp = find_specific_endpoint(epname, trunk);
294 if (endp) {
295 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
296 "(trunk:%d) found endpoint: %s\n",
297 trunk->trunk_nr, endp->name);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200298 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
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200310/*! Find an endpoint by its name, search at all trunks.
311 * \param[out] cause, pointer to store cause code, can be NULL.
312 * \param[in] epname, must contain trunk prefix.
313 * \param[in] cfg, mgcp configuration (trunks).
314 * \returns endpoint or NULL if endpoint was not found. */
315struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
316 struct mgcp_config *cfg)
317{
318 struct mgcp_trunk *trunk;
319 struct mgcp_endpoint *endp;
320 char epname_lc[MGCP_ENDPOINT_MAXLEN];
321
322 osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
323 epname = epname_lc;
324
325 if (cause)
326 *cause = -500;
327
328 /* Identify the trunk where the endpoint is located */
329 trunk = mgcp_trunk_by_name(cfg, epname);
330 if (!trunk)
331 return NULL;
332
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200333 /* Identify the endpoint on the trunk */
334 endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
335 if (!endp) {
336 return NULL;
337 }
338
339 if (cause)
340 *cause = 0;
341 return endp;
342}
Philipp Maier8d6a1932020-06-18 12:19:31 +0200343
344/* Get the E1 timeslot number from a given E1 endpoint name
345 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
346static uint8_t e1_ts_nr_from_epname(const char *epname)
347{
348 char buf[MGCP_ENDPOINT_MAXLEN + 1];
349 char *save_ptr = NULL;
350 char *buf_ptr = buf;
351 char *token;
352 unsigned long int res = 0;
353
354 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
355
356 while (1) {
357 token = strtok_r(buf_ptr, "/", &save_ptr);
358 buf_ptr = NULL;
359 if (!token)
360 break;
361 if (strncmp(token, "s-", 2) == 0) {
362 errno = 0;
363 res = strtoul(token + 2, NULL, 10);
Philipp Maier99d4d362020-09-07 11:58:09 +0200364 if (errno == ERANGE || res > NUM_E1_TS)
Philipp Maier8d6a1932020-06-18 12:19:31 +0200365 return 0xff;
366 return (uint8_t) res;
367 }
368 }
369
370 return 0xff;
371}
372
373/* Get the E1 timeslot number from a given E1 endpoint name
374 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
375static uint8_t e1_rate_from_epname(const char *epname)
376{
377 char buf[MGCP_ENDPOINT_MAXLEN + 1];
378 char *save_ptr = NULL;
379 char *buf_ptr = buf;
380 char *token;
381 unsigned long int res = 0;
382 unsigned int i;
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, "su", 2) == 0) {
392 errno = 0;
393 res = strtoul(token + 2, NULL, 10);
394 if (errno == ERANGE || res > E1_RATE_MAX)
395 return 0xff;
396 /* Make sure the rate is a valid rate */
397 for (i = 0; i < sizeof(e1_rates); i++) {
398 if (res == e1_rates[i])
399 return (uint8_t) res;
400 }
401 return 0xff;
402 }
403 }
404
405 return 0xff;
406}
407
408/* Get the E1 bitstream offset from a given E1 endpoint name
409 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
410static uint8_t e1_offs_from_epname(const char *epname)
411{
412 char buf[MGCP_ENDPOINT_MAXLEN + 1];
413 char *save_ptr = NULL;
414 char *buf_ptr = buf;
415 char *token;
416 unsigned long int res = 0;
417
418 strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
419
420 while (1) {
421 token = strtok_r(buf_ptr, "/", &save_ptr);
422 buf_ptr = NULL;
423 if (!token)
424 break;
425 if (strncmp(token, "su", 2) == 0) {
426 token = strstr(token, "-");
427 if (!token)
428 return 0xff;
429 token += 1;
430 errno = 0;
431 res = strtoul(token, NULL, 10);
432 if (errno == ERANGE || res > E1_OFFS_MAX)
433 return 0xff;
434 return (uint8_t) res;
435 }
436 }
437
438 return 0xff;
439}
440
441/* Get the E1 subslot number (internal) from a given E1 endpoint name
442 * (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
443static uint8_t e1_ss_nr_from_epname(const char *epname)
444{
445 uint8_t rate;
446 uint8_t offs;
447 unsigned int i;
448
449 rate = e1_rate_from_epname(epname);
450 offs = e1_offs_from_epname(epname);
451
452 osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
453
454 for (i = 0; i < sizeof(e1_rates); i++) {
455 if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
456 return i;
457 }
458
459 return 0xff;
460}
461
462/* Check if the selected E1 endpoint is avalable, which means that none of
463 * the overlapping endpoints are currently serving a call. (if the system
464 * is properly configured such a situation should never ocurr!) */
465static bool endp_avail_e1(struct mgcp_endpoint *endp)
466{
467 /* The following map shows the overlapping of the subslots and their
468 * respective rates. The numbers on the right running from top to bottom
469 * are the bit offsets in the whole 64k timeslot. The numbers inside the
470 * boxes symbolize the internal subslot number (array index) and the
471 * rate in the form: i:r where i is the subslot number and r the
472 * respective rate.
473 *
474 * +--------+--------+--------+--------+ 0
475 * | | | | 7:8k |
476 * | | + 3:16k +--------+ 1
477 * | | | | 8:8k |
478 * | | 1:32k +--------+--------+ 2
479 * | | | | 9:8k |
480 * | | + 4:16k +--------+ 3
481 * | | | | 10:8k |
482 * | 0:64k +--------+--------+--------+ 4
483 * | | | | 11:8k |
484 * | | + 5:16k +--------+ 5
485 * | | | | 12:8k |
486 * | | 2:32k +--------+--------+ 6
487 * | | | | 13:8k |
488 * | | + 6:16k +--------+ 7
489 * | | | | 14:8k |
490 * +--------+--------+--------+--------+ 8
491 *
492 * The following array contains tables with the subslot numbers that must be
493 * unused for each subslot. During this test we do not have to check the
494 * endpoint we need to verify, only the overlaps need to be checked. This is
495 * also the reason why the related subslot number is missing from each each
496 * line. */
Philipp Maierfe67e092020-07-07 21:21:45 +0200497 const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
498 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
Philipp Maier8d6a1932020-06-18 12:19:31 +0200499 { 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
500 { 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
501 { 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
502 { 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
503 { 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
504 { 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
505 { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
506 { 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
507 { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
508 { 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
509 { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
510 { 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
511 { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
512 { 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
513
514 const int8_t *interlock;
515 unsigned int i;
516 uint8_t ts_nr = 0;
517 uint8_t ss_nr = 0;
518 char *epname_check;
519 struct mgcp_endpoint *endp_check;
520 bool available = true;
521
522 /* This function must only be used with E1 type endpoints! */
523 OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
524
525 ts_nr = e1_ts_nr_from_epname(endp->name);
526 ss_nr = e1_ss_nr_from_epname(endp->name);
527 if (ts_nr == 0xff || ss_nr == 0xff) {
528 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
529 "cannot check endpoint availability, endpoint name not parseable!\n");
530 return false;
531 }
532
533 interlock = interlock_tab[ss_nr];
534
535 for (i = 0; i < sizeof(interlock_tab[0]); i++) {
536 /* Detect row end */
537 if (interlock[i] == -1)
538 break;
539
540 /* Pick overlapping endpoint to check */
Philipp Maier0ffa3bd2020-06-30 16:17:03 +0200541 epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
542 endp->trunk->trunk_nr, ts_nr,
543 interlock[i]);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200544 endp_check = find_specific_endpoint(epname_check, endp->trunk);
545 if (!endp_check) {
546 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
547 "cannot check endpoint availability, overlapping endpoint:%s not found!\n",
548 epname_check);
549 talloc_free(epname_check);
550 continue;
551 }
552 talloc_free(epname_check);
553
554 /* Check if overlapping endpoint currently serves another call
555 * (This is an exceptional situation, that should not occur
556 * in a properly configured environment!) */
557 if (endp_check->callid) {
558 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
559 "endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
560 endp_check->name);
561 available = false;
562 }
563 }
564
565 return available;
566}
567
568/*! check if an endpoint is available for any kind of operation.
569 * \param[in] endp endpoint to check.
570 * \returns true if endpoint is avalable, false it is blocked for any reason. */
571bool mgcp_endp_avail(struct mgcp_endpoint *endp)
572{
573 switch (endp->trunk->trunk_type) {
574 case MGCP_TRUNK_VIRTUAL:
575 /* There are no obstacles that may render a virtual trunk
576 * endpoint unusable, so virtual trunk endpoints are always
577 * available */
578 return true;
579 case MGCP_TRUNK_E1:
580 return endp_avail_e1(endp);
581 default:
582 OSMO_ASSERT(false);
583 }
584
585 return false;
586}
Philipp Maier889fe7f2020-07-06 17:44:12 +0200587
588/*! claim endpoint, sets callid and activates endpoint, should be called at the
589 * beginning of the CRCX procedure when it is clear that a new call should be
590 * created.
591 * \param[in] endp endpoint to claim.
592 * \param[in] callid that is assingned to this endpoint. */
593int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
594{
595 int rc = 0;
596 uint8_t ts;
597 uint8_t ss;
598 uint8_t offs;
599
600 /* TODO: Make this function more intelligent, it should run the
601 * call id checks we currently have in protocol.c directly here. */
602
603 /* Set the callid, creation of another connection will only be possible
604 * when the callid matches up. (Connections are distinguished by their
605 * connection ids) */
606 endp->callid = talloc_strdup(endp, callid);
607 OSMO_ASSERT(endp->callid);
Philipp Maier124a3e02021-07-26 11:17:15 +0200608 osmo_stat_item_inc(osmo_stat_item_group_get_item(endp->trunk->stats.common,
609 TRUNK_STAT_ENDPOINTS_USED), 1);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200610
611 /* Allocate resources */
612 switch (endp->trunk->trunk_type) {
613 case MGCP_TRUNK_VIRTUAL:
614 /* No additional initaliziation required here, virtual
615 * endpoints will open/close network sockets themselves
616 * on demand. */
617 break;
618 case MGCP_TRUNK_E1:
619 ts = e1_ts_nr_from_epname(endp->name);
620 ss = e1_ss_nr_from_epname(endp->name);
621 offs = e1_offs_from_epname(endp->name);
622 OSMO_ASSERT(ts != 0xFF);
623 OSMO_ASSERT(ts != 0);
624 OSMO_ASSERT(ss != 0xFF);
625 OSMO_ASSERT(offs != 0xFF);
626 rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
627 break;
628 default:
629 OSMO_ASSERT(false);
630 }
631
Pau Espin Pedrol30e01352020-09-21 12:29:41 +0200632 /* Make sure the endpoint is released when claiming the endpoint fails. */
Philipp Maier889fe7f2020-07-06 17:44:12 +0200633 if (rc < 0)
634 mgcp_endp_release(endp);
635
636 return rc;
637}
638
639/*! update endpoint, updates internal endpoint specific data, should be
640 * after when MDCX or CRCX has been executed successuflly.
641 * \param[in] endp endpoint to update. */
642void mgcp_endp_update(struct mgcp_endpoint *endp)
643{
644 /* Allocate resources */
645 switch (endp->trunk->trunk_type) {
646 case MGCP_TRUNK_VIRTUAL:
647 /* No updating initaliziation required for virtual endpoints. */
648 break;
649 case MGCP_TRUNK_E1:
650 mgcp_e1_endp_update(endp);
651 break;
652 default:
653 OSMO_ASSERT(false);
654 }
655}
Pau Espin Pedrol6d0a59a2020-09-08 16:50:22 +0200656
657void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
658{
659 llist_add(&conn->entry, &endp->conns);
660}
661
662void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
663{
664 /* Run endpoint cleanup action. By this we inform the endpoint about
665 * the removal of the connection and allow it to clean up its inner
666 * state accordingly */
667 if (endp->type->cleanup_cb)
668 endp->type->cleanup_cb(endp, conn);
669 llist_del(&conn->entry);
670 if (llist_empty(&endp->conns))
671 mgcp_endp_release(endp);
672}