blob: 6c78de240940fc60a7b5ab26cd119c515844f7d6 [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 Maierc66ab2c2020-06-02 20:55:34 +020036/*! allocate an endpoint and set default values.
37 * \param[in] trunk configuration.
38 * \param[in] name endpoint name.
39 * \returns endpoint on success, NULL on failure. */
40struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, char *name)
41{
42 struct mgcp_endpoint *endp;
43
44 endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
45 if (!endp)
46 return NULL;
47
48 INIT_LLIST_HEAD(&endp->conns);
49 endp->cfg = trunk->cfg;
50 endp->trunk = trunk;
51 endp->name = talloc_strdup(endp, name);
52
53 switch (trunk->trunk_type) {
54 case MGCP_TRUNK_VIRTUAL:
55 endp->type = &ep_typeset.rtp;
56 break;
57 case MGCP_TRUNK_E1:
58 /* FIXME: Implement E1 allocation */
59 LOGP(DLMGCP, LOGL_FATAL, "E1 trunks not implemented!\n");
60 break;
61 default:
62 osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
63 trunk->trunk_type, __FILE__, __LINE__);
64 }
65
66 return endp;
67}
68
Philipp Maieredc00f42018-01-24 11:58:56 +010069/*! release endpoint, all open connections are closed.
70 * \param[in] endp endpoint to release */
Philipp Maier1355d7e2018-02-01 14:30:06 +010071void mgcp_endp_release(struct mgcp_endpoint *endp)
Philipp Maieredc00f42018-01-24 11:58:56 +010072{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +020073 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
Philipp Maieredc00f42018-01-24 11:58:56 +010074
75 /* Normally this function should only be called when
76 * all connections have been removed already. In case
77 * that there are still connections open (e.g. when
78 * RSIP is executed), free them all at once. */
79 mgcp_conn_free_all(endp);
80
81 /* Reset endpoint parameters and states */
82 talloc_free(endp->callid);
83 endp->callid = NULL;
84 talloc_free(endp->local_options.string);
85 endp->local_options.string = NULL;
86 talloc_free(endp->local_options.codec);
87 endp->local_options.codec = NULL;
Philipp Maier207ab512018-02-02 14:19:26 +010088 endp->wildcarded_req = false;
Philipp Maieredc00f42018-01-24 11:58:56 +010089}
Philipp Maierc66ab2c2020-06-02 20:55:34 +020090
91/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
92 * "ds/e1-") and write the epname without the prefix back to the memory
93 * pointed at by epname. (per trunk the prefix is the same for all endpoints,
94 * so no ambiguity is introduced) */
95static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
96{
97 size_t prefix_len;
98 switch (trunk->trunk_type) {
99 case MGCP_TRUNK_VIRTUAL:
100 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
101 if (strncmp
102 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
103 prefix_len) == 0)
104 memmove(epname, epname + prefix_len,
105 strlen(epname) - prefix_len + 1);
106 return;
107 case MGCP_TRUNK_E1:
108 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
109 if (strncmp
110 (epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
111 prefix_len) == 0)
112 memmove(epname, epname + prefix_len,
113 strlen(epname) - prefix_len + 1);
114 return;
115 default:
116 OSMO_ASSERT(false);
117 }
118}
119
120/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
121 * epname by writing a '\0' char where the suffix starts. */
122static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
123{
124 char *suffix_begin;
125
126 /* Endpoints on the virtual trunk may have a domain name that is
127 * followed after an @ character, this can be chopped off. All
128 * other supported trunk types do not have any suffixes that may
129 * be chopped off */
130 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
131 suffix_begin = strchr(epname, '@');
132 if (!suffix_begin)
133 return;
134 *suffix_begin = '\0';
135 }
136}
137
138/* Convert all characters in epname to lowercase and strip trunk prefix and
139 * endpoint name suffix (domain name) from epname. The result is written to
140 * to the memory pointed at by epname_stripped. The expected size of the
141 * result is either equal or lower then the length of the input string
142 * (epname) */
143static void strip_epname(char *epname_stripped, const char *epname,
144 const struct mgcp_trunk *trunk)
145{
146 osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
147 chop_epname_prefix(epname_stripped, trunk);
148 chop_epname_suffix(epname_stripped, trunk);
149}
150
151/* Go through the trunk and find a random free (no active calls) endpoint,
152 * this function is called when a wildcarded request is carried out, which
153 * means that it is up to the MGW to choose a random free endpoint. */
154static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
155{
156 struct mgcp_endpoint *endp;
157 unsigned int i;
158
159 for (i = 0; i < trunk->number_endpoints; i++) {
160 endp = trunk->endpoints[i];
161 if (endp->callid == NULL)
162 return endp;
163 }
164
165 return NULL;
166}
167
168/* Find an endpoint specified by its name. If the endpoint can not be found,
169 * return NULL */
170static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
171 const struct mgcp_trunk *trunk)
172{
173 char epname_stripped[MGCP_ENDPOINT_MAXLEN];
174 char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
175 struct mgcp_endpoint *endp;
176 unsigned int i;
177
178 /* Strip irrelevant information from the endpoint name */
179 strip_epname(epname_stripped, epname, trunk);
180
181 for (i = 0; i < trunk->number_endpoints; i++) {
182 endp = trunk->endpoints[i];
183 strip_epname(epname_stripped_endp, endp->name, trunk);
184 if (strcmp(epname_stripped_endp, epname_stripped) == 0)
185 return endp;
186 }
187
188 return NULL;
189}
190
191/*! Find an endpoint by its name on a specified trunk.
192 * \param[out] cause pointer to store cause code, can be NULL.
193 * \param[in] epname endpoint name to lookup.
194 * \param[in] trunk where the endpoint is located.
195 * \returns endpoint or NULL if endpoint was not found. */
196struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
197 const struct mgcp_trunk *trunk)
198{
199 struct mgcp_endpoint *endp;
200
201 if (cause)
202 *cause = 0;
203
204 /* At the moment we only support a primitive ('*'-only) method of
205 * wildcarded endpoint searches that picks the next free endpoint on
206 * a trunk. */
207 if (strstr(epname, "*")) {
208 endp = find_free_endpoint(trunk);
209 if (endp) {
210 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
211 "(trunk:%d) found free endpoint: %s\n",
212 trunk->trunk_nr, endp->name);
213 endp->wildcarded_req = true;
214 return endp;
215 }
216
217 LOGP(DLMGCP, LOGL_ERROR,
218 "(trunk:%d) Not able to find a free endpoint\n",
219 trunk->trunk_nr);
220 if (cause)
221 *cause = -403;
222 return NULL;
223 }
224
225 /* Find an endpoint by its name (if wildcarded request is not
226 * applicable) */
227 endp = find_specific_endpoint(epname, trunk);
228 if (endp) {
229 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
230 "(trunk:%d) found endpoint: %s\n",
231 trunk->trunk_nr, endp->name);
232 endp->wildcarded_req = false;
233 return endp;
234 }
235
236 LOGP(DLMGCP, LOGL_ERROR,
237 "(trunk:%d) Not able to find specified endpoint: %s\n",
238 trunk->trunk_nr, epname);
239 if (cause)
240 *cause = -500;
241
242 return NULL;
243}
244
245/* Check if the domain name, which is supplied with the endpoint name
246 * matches the configuration. */
247static int check_domain_name(const char *epname, struct mgcp_config *cfg)
248{
249 char *domain_to_check;
250
251 domain_to_check = strstr(epname, "@");
252 if (!domain_to_check) {
253 LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
254 epname, cfg->domain);
255 return -EINVAL;
256 }
257
258 /* Accept any domain if configured as "*" */
259 if (!strcmp(cfg->domain, "*"))
260 return 0;
261
262 if (strcmp(domain_to_check+1, cfg->domain) != 0) {
263 LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
264 epname, cfg->domain);
265 return -EINVAL;
266 }
267
268 return 0;
269}
270
271/*! Find an endpoint by its name, search at all trunks.
272 * \param[out] cause, pointer to store cause code, can be NULL.
273 * \param[in] epname, must contain trunk prefix.
274 * \param[in] cfg, mgcp configuration (trunks).
275 * \returns endpoint or NULL if endpoint was not found. */
276struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
277 struct mgcp_config *cfg)
278{
279 struct mgcp_trunk *trunk;
280 struct mgcp_endpoint *endp;
281 char epname_lc[MGCP_ENDPOINT_MAXLEN];
282
283 osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
284 epname = epname_lc;
285
286 if (cause)
287 *cause = -500;
288
289 /* Identify the trunk where the endpoint is located */
290 trunk = mgcp_trunk_by_name(cfg, epname);
291 if (!trunk)
292 return NULL;
293
294 /* Virtual endpoints require a domain name (see RFC3435, section E.3) */
295 if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
296 if (check_domain_name(epname, cfg))
297 return NULL;
298 }
299
300 /* Identify the endpoint on the trunk */
301 endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
302 if (!endp) {
303 return NULL;
304 }
305
306 if (cause)
307 *cause = 0;
308 return endp;
309}