blob: 94d4083023eabefa96dfc77a7cf6639585fb1ed7 [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
28/* Endpoint typeset definition */
29const struct mgcp_endpoint_typeset ep_typeset = {
30 /* Specify endpoint properties for RTP endpoint */
Philipp Maier0996a1e2020-06-10 15:27:14 +020031 .rtp = {
32 .max_conns = 2,
33 .dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
34 .cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
35 },
36 /* Specify endpoint properties for E1 endpoint */
37 .e1 = {
38 .max_conns = 1,
39 .dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
40 .cleanup_cb = mgcp_cleanup_e1_bridge_cb,
41 },
Philipp Maier87bd9be2017-08-22 16:35:41 +020042};
Philipp Maieredc00f42018-01-24 11:58:56 +010043
Philipp Maier7462b952020-06-10 14:50:34 +020044/* Generate virtual endpoint name from given parameters */
45static char *gen_virtual_epname(void *ctx, const char *domain,
46 unsigned int index)
47{
48 return talloc_asprintf(ctx, "%s%x@%s",
49 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
50}
51
Philipp Maier98c09b32020-06-18 12:17:55 +020052/* Generate E1 endpoint name from given numeric parameters */
53static char *gen_e1_epname(void *ctx, uint8_t trunk_nr, uint8_t ts_nr,
54 uint8_t ss_nr)
55{
56 /* A 64k timeslot on an E1 line can be subdevied into the following
57 * subslot combinations:
58 *
59 * subslot: offset:
60 * [ ][ ][ 16k ][8k_subslot] 0
61 * [ ][ 32k ][_subslot__][8k_subslot] 1
62 * [ ][ subslot ][ 16k ][8k_subslot] 2
63 * [ 64k ][__________][_subslot__][8k_subslot] 3
64 * [ timeslot ][ ][ 16k ][8k_subslot] 4
65 * [ ][ 32K ][_subslot__][8k_subslot] 5
66 * [ ][ subslot ][ 16k ][8k_subslot] 6
67 * [ ][ ][ subslot ][8k_subslot] 7
68 *
69 * Since overlapping assignment of subsolts is not possible there is
70 * a limited set of subsolt assignments possible. The rates array
71 * lists the possible assignments as depicted above. Also each subslot
72 * assignment comes along with a bit offset in the E1 bitstream. The
73 * offsets arrays lists the bit offsets. */
74 static const uint8_t rates[] =
75 { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
76 static const uint8_t offsets[] =
77 { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
78 unsigned int rate;
79 unsigned int offset;
80
81 OSMO_ASSERT(ss_nr < sizeof(rates));
82
83 rate = rates[ss_nr];
84 offset = offsets[ss_nr];
85
86 return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u",
87 MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr, rate, offset);
88}
89
Philipp Maierc66ab2c2020-06-02 20:55:34 +020090/*! allocate an endpoint and set default values.
91 * \param[in] trunk configuration.
Philipp Maier7462b952020-06-10 14:50:34 +020092 * \param[in] name endpoint index.
Philipp Maierc66ab2c2020-06-02 20:55:34 +020093 * \returns endpoint on success, NULL on failure. */
Philipp Maier7462b952020-06-10 14:50:34 +020094struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
95 unsigned int index)
Philipp Maierc66ab2c2020-06-02 20:55:34 +020096{
97 struct mgcp_endpoint *endp;
98
99 endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
100 if (!endp)
101 return NULL;
102
103 INIT_LLIST_HEAD(&endp->conns);
104 endp->cfg = trunk->cfg;
105 endp->trunk = trunk;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200106
107 switch (trunk->trunk_type) {
108 case MGCP_TRUNK_VIRTUAL:
109 endp->type = &ep_typeset.rtp;
Philipp Maier7462b952020-06-10 14:50:34 +0200110 endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200111 break;
112 case MGCP_TRUNK_E1:
Philipp Maier7462b952020-06-10 14:50:34 +0200113 endp->type = &ep_typeset.rtp;
Philipp Maier98c09b32020-06-18 12:17:55 +0200114 endp->name = gen_e1_epname(endp, trunk->trunk_nr, index / 15, index % 15);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200115 break;
116 default:
117 osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
118 trunk->trunk_type, __FILE__, __LINE__);
119 }
120
121 return endp;
122}
123
Philipp Maieredc00f42018-01-24 11:58:56 +0100124/*! release endpoint, all open connections are closed.
125 * \param[in] endp endpoint to release */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100126void mgcp_endp_release(struct mgcp_endpoint *endp)
Philipp Maieredc00f42018-01-24 11:58:56 +0100127{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200128 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
Philipp Maieredc00f42018-01-24 11:58:56 +0100129
130 /* Normally this function should only be called when
131 * all connections have been removed already. In case
132 * that there are still connections open (e.g. when
133 * RSIP is executed), free them all at once. */
134 mgcp_conn_free_all(endp);
135
136 /* Reset endpoint parameters and states */
137 talloc_free(endp->callid);
138 endp->callid = NULL;
139 talloc_free(endp->local_options.string);
140 endp->local_options.string = NULL;
141 talloc_free(endp->local_options.codec);
142 endp->local_options.codec = NULL;
Philipp Maier207ab512018-02-02 14:19:26 +0100143 endp->wildcarded_req = false;
Philipp Maieredc00f42018-01-24 11:58:56 +0100144}
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200145
146/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
147 * "ds/e1-") and write the epname without the prefix back to the memory
148 * pointed at by epname. (per trunk the prefix is the same for all endpoints,
149 * so no ambiguity is introduced) */
150static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
151{
152 size_t prefix_len;
153 switch (trunk->trunk_type) {
154 case MGCP_TRUNK_VIRTUAL:
155 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
156 if (strncmp
157 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
158 prefix_len) == 0)
159 memmove(epname, epname + prefix_len,
160 strlen(epname) - prefix_len + 1);
161 return;
162 case MGCP_TRUNK_E1:
163 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_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 default:
171 OSMO_ASSERT(false);
172 }
173}
174
175/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
176 * epname by writing a '\0' char where the suffix starts. */
177static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
178{
179 char *suffix_begin;
180
181 /* Endpoints on the virtual trunk may have a domain name that is
182 * followed after an @ character, this can be chopped off. All
183 * other supported trunk types do not have any suffixes that may
184 * be chopped off */
185 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
186 suffix_begin = strchr(epname, '@');
187 if (!suffix_begin)
188 return;
189 *suffix_begin = '\0';
190 }
191}
192
193/* Convert all characters in epname to lowercase and strip trunk prefix and
194 * endpoint name suffix (domain name) from epname. The result is written to
195 * to the memory pointed at by epname_stripped. The expected size of the
196 * result is either equal or lower then the length of the input string
197 * (epname) */
198static void strip_epname(char *epname_stripped, const char *epname,
199 const struct mgcp_trunk *trunk)
200{
201 osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
202 chop_epname_prefix(epname_stripped, trunk);
203 chop_epname_suffix(epname_stripped, trunk);
204}
205
206/* Go through the trunk and find a random free (no active calls) endpoint,
207 * this function is called when a wildcarded request is carried out, which
208 * means that it is up to the MGW to choose a random free endpoint. */
209static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
210{
211 struct mgcp_endpoint *endp;
212 unsigned int i;
213
214 for (i = 0; i < trunk->number_endpoints; i++) {
215 endp = trunk->endpoints[i];
216 if (endp->callid == NULL)
217 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
246/*! Find an endpoint by its name on a specified trunk.
247 * \param[out] cause pointer to store cause code, can be NULL.
248 * \param[in] epname endpoint name to lookup.
249 * \param[in] trunk where the endpoint is located.
250 * \returns endpoint or NULL if endpoint was not found. */
251struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
252 const struct mgcp_trunk *trunk)
253{
254 struct mgcp_endpoint *endp;
255
256 if (cause)
257 *cause = 0;
258
259 /* At the moment we only support a primitive ('*'-only) method of
260 * wildcarded endpoint searches that picks the next free endpoint on
261 * a trunk. */
262 if (strstr(epname, "*")) {
263 endp = find_free_endpoint(trunk);
264 if (endp) {
265 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
266 "(trunk:%d) found free endpoint: %s\n",
267 trunk->trunk_nr, endp->name);
268 endp->wildcarded_req = true;
269 return endp;
270 }
271
272 LOGP(DLMGCP, LOGL_ERROR,
273 "(trunk:%d) Not able to find a free endpoint\n",
274 trunk->trunk_nr);
275 if (cause)
276 *cause = -403;
277 return NULL;
278 }
279
280 /* Find an endpoint by its name (if wildcarded request is not
281 * applicable) */
282 endp = find_specific_endpoint(epname, trunk);
283 if (endp) {
284 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
285 "(trunk:%d) found endpoint: %s\n",
286 trunk->trunk_nr, endp->name);
287 endp->wildcarded_req = false;
288 return endp;
289 }
290
291 LOGP(DLMGCP, LOGL_ERROR,
292 "(trunk:%d) Not able to find specified endpoint: %s\n",
293 trunk->trunk_nr, epname);
294 if (cause)
295 *cause = -500;
296
297 return NULL;
298}
299
300/* Check if the domain name, which is supplied with the endpoint name
301 * matches the configuration. */
302static int check_domain_name(const char *epname, struct mgcp_config *cfg)
303{
304 char *domain_to_check;
305
306 domain_to_check = strstr(epname, "@");
307 if (!domain_to_check) {
308 LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
309 epname, cfg->domain);
310 return -EINVAL;
311 }
312
313 /* Accept any domain if configured as "*" */
314 if (!strcmp(cfg->domain, "*"))
315 return 0;
316
317 if (strcmp(domain_to_check+1, cfg->domain) != 0) {
318 LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
319 epname, cfg->domain);
320 return -EINVAL;
321 }
322
323 return 0;
324}
325
326/*! Find an endpoint by its name, search at all trunks.
327 * \param[out] cause, pointer to store cause code, can be NULL.
328 * \param[in] epname, must contain trunk prefix.
329 * \param[in] cfg, mgcp configuration (trunks).
330 * \returns endpoint or NULL if endpoint was not found. */
331struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
332 struct mgcp_config *cfg)
333{
334 struct mgcp_trunk *trunk;
335 struct mgcp_endpoint *endp;
336 char epname_lc[MGCP_ENDPOINT_MAXLEN];
337
338 osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
339 epname = epname_lc;
340
341 if (cause)
342 *cause = -500;
343
344 /* Identify the trunk where the endpoint is located */
345 trunk = mgcp_trunk_by_name(cfg, epname);
346 if (!trunk)
347 return NULL;
348
349 /* Virtual endpoints require a domain name (see RFC3435, section E.3) */
350 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
351 if (check_domain_name(epname, cfg))
352 return NULL;
353 }
354
355 /* Identify the endpoint on the trunk */
356 endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
357 if (!endp) {
358 return NULL;
359 }
360
361 if (cause)
362 *cause = 0;
363 return endp;
364}