blob: db8898bd48acb5bc2831b877cbb4400a5b8b38b1 [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 */
31 .rtp.max_conns = 2,
Philipp Maierdf5d2192018-01-24 11:39:32 +010032 .rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
33 .rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
Philipp Maier87bd9be2017-08-22 16:35:41 +020034};
Philipp Maieredc00f42018-01-24 11:58:56 +010035
Philipp Maier7462b952020-06-10 14:50:34 +020036/* Generate virtual endpoint name from given parameters */
37static char *gen_virtual_epname(void *ctx, const char *domain,
38 unsigned int index)
39{
40 return talloc_asprintf(ctx, "%s%x@%s",
41 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
42}
43
Philipp Maier98c09b32020-06-18 12:17:55 +020044/* Generate E1 endpoint name from given numeric parameters */
45static char *gen_e1_epname(void *ctx, uint8_t trunk_nr, uint8_t ts_nr,
46 uint8_t ss_nr)
47{
48 /* A 64k timeslot on an E1 line can be subdevied into the following
49 * subslot combinations:
50 *
51 * subslot: offset:
52 * [ ][ ][ 16k ][8k_subslot] 0
53 * [ ][ 32k ][_subslot__][8k_subslot] 1
54 * [ ][ subslot ][ 16k ][8k_subslot] 2
55 * [ 64k ][__________][_subslot__][8k_subslot] 3
56 * [ timeslot ][ ][ 16k ][8k_subslot] 4
57 * [ ][ 32K ][_subslot__][8k_subslot] 5
58 * [ ][ subslot ][ 16k ][8k_subslot] 6
59 * [ ][ ][ subslot ][8k_subslot] 7
60 *
61 * Since overlapping assignment of subsolts is not possible there is
62 * a limited set of subsolt assignments possible. The rates array
63 * lists the possible assignments as depicted above. Also each subslot
64 * assignment comes along with a bit offset in the E1 bitstream. The
65 * offsets arrays lists the bit offsets. */
66 static const uint8_t rates[] =
67 { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
68 static const uint8_t offsets[] =
69 { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
70 unsigned int rate;
71 unsigned int offset;
72
73 OSMO_ASSERT(ss_nr < sizeof(rates));
74
75 rate = rates[ss_nr];
76 offset = offsets[ss_nr];
77
78 return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u",
79 MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr, rate, offset);
80}
81
Philipp Maierc66ab2c2020-06-02 20:55:34 +020082/*! allocate an endpoint and set default values.
83 * \param[in] trunk configuration.
Philipp Maier7462b952020-06-10 14:50:34 +020084 * \param[in] name endpoint index.
Philipp Maierc66ab2c2020-06-02 20:55:34 +020085 * \returns endpoint on success, NULL on failure. */
Philipp Maier7462b952020-06-10 14:50:34 +020086struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
87 unsigned int index)
Philipp Maierc66ab2c2020-06-02 20:55:34 +020088{
89 struct mgcp_endpoint *endp;
90
91 endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
92 if (!endp)
93 return NULL;
94
95 INIT_LLIST_HEAD(&endp->conns);
96 endp->cfg = trunk->cfg;
97 endp->trunk = trunk;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020098
99 switch (trunk->trunk_type) {
100 case MGCP_TRUNK_VIRTUAL:
101 endp->type = &ep_typeset.rtp;
Philipp Maier7462b952020-06-10 14:50:34 +0200102 endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200103 break;
104 case MGCP_TRUNK_E1:
Philipp Maier7462b952020-06-10 14:50:34 +0200105 endp->type = &ep_typeset.rtp;
Philipp Maier98c09b32020-06-18 12:17:55 +0200106 endp->name = gen_e1_epname(endp, trunk->trunk_nr, index / 15, index % 15);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200107 break;
108 default:
109 osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
110 trunk->trunk_type, __FILE__, __LINE__);
111 }
112
113 return endp;
114}
115
Philipp Maieredc00f42018-01-24 11:58:56 +0100116/*! release endpoint, all open connections are closed.
117 * \param[in] endp endpoint to release */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100118void mgcp_endp_release(struct mgcp_endpoint *endp)
Philipp Maieredc00f42018-01-24 11:58:56 +0100119{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200120 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
Philipp Maieredc00f42018-01-24 11:58:56 +0100121
122 /* Normally this function should only be called when
123 * all connections have been removed already. In case
124 * that there are still connections open (e.g. when
125 * RSIP is executed), free them all at once. */
126 mgcp_conn_free_all(endp);
127
128 /* Reset endpoint parameters and states */
129 talloc_free(endp->callid);
130 endp->callid = NULL;
131 talloc_free(endp->local_options.string);
132 endp->local_options.string = NULL;
133 talloc_free(endp->local_options.codec);
134 endp->local_options.codec = NULL;
Philipp Maier207ab512018-02-02 14:19:26 +0100135 endp->wildcarded_req = false;
Philipp Maieredc00f42018-01-24 11:58:56 +0100136}
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200137
138/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
139 * "ds/e1-") and write the epname without the prefix back to the memory
140 * pointed at by epname. (per trunk the prefix is the same for all endpoints,
141 * so no ambiguity is introduced) */
142static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
143{
144 size_t prefix_len;
145 switch (trunk->trunk_type) {
146 case MGCP_TRUNK_VIRTUAL:
147 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
148 if (strncmp
149 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
150 prefix_len) == 0)
151 memmove(epname, epname + prefix_len,
152 strlen(epname) - prefix_len + 1);
153 return;
154 case MGCP_TRUNK_E1:
155 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_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 default:
163 OSMO_ASSERT(false);
164 }
165}
166
167/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
168 * epname by writing a '\0' char where the suffix starts. */
169static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
170{
171 char *suffix_begin;
172
173 /* Endpoints on the virtual trunk may have a domain name that is
174 * followed after an @ character, this can be chopped off. All
175 * other supported trunk types do not have any suffixes that may
176 * be chopped off */
177 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
178 suffix_begin = strchr(epname, '@');
179 if (!suffix_begin)
180 return;
181 *suffix_begin = '\0';
182 }
183}
184
185/* Convert all characters in epname to lowercase and strip trunk prefix and
186 * endpoint name suffix (domain name) from epname. The result is written to
187 * to the memory pointed at by epname_stripped. The expected size of the
188 * result is either equal or lower then the length of the input string
189 * (epname) */
190static void strip_epname(char *epname_stripped, const char *epname,
191 const struct mgcp_trunk *trunk)
192{
193 osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
194 chop_epname_prefix(epname_stripped, trunk);
195 chop_epname_suffix(epname_stripped, trunk);
196}
197
198/* Go through the trunk and find a random free (no active calls) endpoint,
199 * this function is called when a wildcarded request is carried out, which
200 * means that it is up to the MGW to choose a random free endpoint. */
201static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
202{
203 struct mgcp_endpoint *endp;
204 unsigned int i;
205
206 for (i = 0; i < trunk->number_endpoints; i++) {
207 endp = trunk->endpoints[i];
208 if (endp->callid == NULL)
209 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
341 /* Virtual endpoints require a domain name (see RFC3435, section E.3) */
342 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
343 if (check_domain_name(epname, cfg))
344 return NULL;
345 }
346
347 /* Identify the endpoint on the trunk */
348 endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
349 if (!endp) {
350 return NULL;
351 }
352
353 if (cause)
354 *cause = 0;
355 return endp;
356}