blob: b2d1969308c4dc1264bc7cfe7cefc2b7f7d273ce [file] [log] [blame]
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001/* Trunk handling */
2
3/*
4 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2009-2012 by On-Waves
6 * (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
7 * All Rights Reserved
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>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020026#include <osmocom/mgcp/mgcp_endp.h>
27#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier889fe7f2020-07-06 17:44:12 +020028#include <osmocom/mgcp/mgcp_e1.h>
29#include <osmocom/abis/e1_input.h>
Philipp Maier6bad1382021-07-14 17:44:14 +020030#include <osmocom/core/stat_item.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020031
Philipp Maierbd060c32021-07-09 10:24:33 +020032const struct value_string mgcp_trunk_type_strs[] = {
33 { MGCP_TRUNK_VIRTUAL, "virtual" },
34 { MGCP_TRUNK_E1, "e1" },
35 { 0, NULL }
36};
37
Philipp Maierbce5f292020-06-18 11:57:17 +020038/*! allocate trunk and add it (if required) to the trunk list.
39 * (called once at startup by VTY).
40 * \param[in] cfg mgcp configuration.
Philipp Maierbce5f292020-06-18 11:57:17 +020041 * \param[in] ttype trunk type.
Philipp Maier080935a2020-07-01 23:08:50 +020042 * \param[in] nr trunk number.
Philipp Maierbce5f292020-06-18 11:57:17 +020043 * \returns pointer to allocated trunk, NULL on failure. */
Philipp Maierd70eef62021-07-19 13:53:28 +020044struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr)
Philipp Maierc66ab2c2020-06-02 20:55:34 +020045{
46 struct mgcp_trunk *trunk;
47
48 trunk = talloc_zero(cfg, struct mgcp_trunk);
49 if (!trunk) {
50 LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
51 return NULL;
52 }
53
54 trunk->cfg = cfg;
55 trunk->trunk_type = ttype;
56 trunk->trunk_nr = nr;
57
58 trunk->audio_send_ptime = 1;
59 trunk->audio_send_name = 1;
Philipp Maierae6858b2020-09-07 12:07:40 +020060 trunk->v.vty_number_endpoints = 512;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020061 trunk->omit_rtcp = 0;
62
63 mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
64
Philipp Maierd19de2e2020-06-03 13:55:33 +020065 llist_add_tail(&trunk->entry, &cfg->trunks);
Philipp Maierc66ab2c2020-06-02 20:55:34 +020066
Philipp Maiera065e632021-07-09 13:22:42 +020067 mgcp_ratectr_trunk_alloc(trunk);
Philipp Maier6bad1382021-07-14 17:44:14 +020068 mgcp_stat_trunk_alloc(trunk);
Philipp Maierc66ab2c2020-06-02 20:55:34 +020069
70 return trunk;
71}
72
Philipp Maierbce5f292020-06-18 11:57:17 +020073/*! allocate endpoints and set default values
74 * (called once at startup by VTY).
75 * \param[in] trunk trunk configuration.
76 * \returns 0 on success, -1 on failure. */
Philipp Maierc66ab2c2020-06-02 20:55:34 +020077int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
78{
79 int i;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020080 struct mgcp_endpoint *endp;
Philipp Maier869b21c2020-07-03 16:04:16 +020081 unsigned int number_endpoints;
82 unsigned int first_endpoint_nr;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020083
84 /* This function is called once on startup by the VTY to allocate the
85 * endpoints. The number of endpoints must not change througout the
86 * runtime of the MGW */
87 OSMO_ASSERT(trunk->number_endpoints == 0);
88 OSMO_ASSERT(trunk->endpoints == NULL);
89
Philipp Maier869b21c2020-07-03 16:04:16 +020090 switch (trunk->trunk_type) {
91 case MGCP_TRUNK_VIRTUAL:
92 /* Due to historical reasons the endpoints on the virtual
93 * trunk start counting at 1. */
94 first_endpoint_nr = 1;
Philipp Maier889fe7f2020-07-06 17:44:12 +020095 number_endpoints = trunk->v.vty_number_endpoints;
Philipp Maier869b21c2020-07-03 16:04:16 +020096 break;
97 case MGCP_TRUNK_E1:
98 /* The first timeslot on an E1 line is reserved for framing
99 * and alignment and can not be used for audio transport */
100 first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;
Philipp Maier889fe7f2020-07-06 17:44:12 +0200101 number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;
Philipp Maier869b21c2020-07-03 16:04:16 +0200102 break;
103 default:
104 OSMO_ASSERT(false);
105 }
106
107 /* Make sure the amount of requested endpoints does not execeed
108 * sane limits. The VTY already limits the possible amount,
109 * however miss-initialization of the struct or memory corruption
110 * could still lead to an excessive allocation of endpoints, so
111 * better stop early if that is the case. */
112 OSMO_ASSERT(number_endpoints < 65534);
113
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200114 /* allocate pointer array for the endpoints */
Philipp Maier7a755be2020-07-07 15:51:06 +0200115 trunk->endpoints = talloc_zero_array(trunk->cfg, struct mgcp_endpoint*,
116 number_endpoints);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200117 if (!trunk->endpoints)
118 return -1;
119
120 /* create endpoints */
Philipp Maier869b21c2020-07-03 16:04:16 +0200121 for (i = 0; i < number_endpoints; i++) {
122 endp = mgcp_endp_alloc(trunk, i + first_endpoint_nr);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200123 if (!endp) {
124 talloc_free(trunk->endpoints);
125 return -1;
126 }
127 trunk->endpoints[i] = endp;
128 }
129
130 /* make the endpoints we just created available to the MGW code */
Philipp Maier869b21c2020-07-03 16:04:16 +0200131 trunk->number_endpoints = number_endpoints;
Philipp Maier6bad1382021-07-14 17:44:14 +0200132 osmo_stat_item_set(osmo_stat_item_group_get_item(trunk->stats.common, TRUNK_STAT_ENDPOINTS_TOTAL),
133 trunk->number_endpoints);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200134 return 0;
135}
136
Philipp Maier889fe7f2020-07-06 17:44:12 +0200137/*! Equip trunk with endpoints and resources
138 * (called once at startup by VTY).
139 * \param[in] trunk trunk configuration.
140 * \returns 0 on success, -1 on failure. */
141int mgcp_trunk_equip(struct mgcp_trunk *trunk)
142{
143 unsigned int i;
144
145 /* Allocate endpoints */
146 if(mgcp_trunk_alloc_endpts(trunk) != 0)
147 return -1;
148
149 /* Allocate resources */
150 switch (trunk->trunk_type) {
151 case MGCP_TRUNK_VIRTUAL:
152 /* No additional initaliziation required here, virtual
153 * endpoints will open/close network sockets themselves
154 * on demand. */
155 break;
156 case MGCP_TRUNK_E1:
157 /* The TS initalization happens once on startup for all
158 * timeslots. This only affects the i460 multiplexer. Until
159 * now no E1 resources are claimed yet. This happens on demand
160 * when the related endpoint is actually used */
161 memset(trunk->e1.i460_ts, 0, sizeof(trunk->e1.i460_ts));
162 for (i = 0; i < (NUM_E1_TS-1); i++)
163 osmo_i460_ts_init(&trunk->e1.i460_ts[i]);
164 break;
165 default:
166 OSMO_ASSERT(false);
167 }
168
169 return 0;
170}
171
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200172/*! get trunk configuration by trunk number (index).
Philipp Maierbce5f292020-06-18 11:57:17 +0200173 * \param[in] cfg mgcp configuration.
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200174 * \param[in] ttype trunk type.
175 * \param[in] nr trunk number.
Philipp Maierbce5f292020-06-18 11:57:17 +0200176 * \returns pointer to trunk configuration, NULL on error. */
Philipp Maierd70eef62021-07-19 13:53:28 +0200177struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200178{
179 struct mgcp_trunk *trunk;
180
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200181 llist_for_each_entry(trunk, &cfg->trunks, entry) {
182 if (trunk->trunk_nr == nr && trunk->trunk_type == ttype)
183 return trunk;
184 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200185
186 return NULL;
187}
188
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200189/* Made public for unit-testing, do not use from outside this file */
Philipp Maierd70eef62021-07-19 13:53:28 +0200190int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname)
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200191{
Philipp Maierd70eef62021-07-19 13:53:28 +0200192 unsigned long trunk_nr_temp;
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200193 size_t prefix_len;
194 char *str_trunk_nr_end;
195
196 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
197 if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) != 0)
198 return -EINVAL;
199
200 errno = 0;
Philipp Maierd70eef62021-07-19 13:53:28 +0200201 trunk_nr_temp = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
202 if (errno == ERANGE || trunk_nr_temp > 64
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200203 || epname + prefix_len == str_trunk_nr_end
204 || str_trunk_nr_end[0] != '/')
205 return -EINVAL;
Philipp Maierd70eef62021-07-19 13:53:28 +0200206 else {
207 *trunk_nr = (unsigned int)trunk_nr_temp;
208 return 0;
209 }
Philipp Maier7e9ddc92020-06-10 15:22:32 +0200210}
211
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200212/*! Find a trunk by the trunk prefix in the endpoint name.
213 * \param[in] epname endpoint name with trunk prefix to look up.
214 * \param[in] cfg that contains the trunks where the endpoint is located.
215 * \returns trunk or NULL if trunk was not found. */
216struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname)
217{
218 size_t prefix_len;
219 char epname_lc[MGCP_ENDPOINT_MAXLEN];
Philipp Maierd70eef62021-07-19 13:53:28 +0200220 unsigned int trunk_nr;
221 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200222
223 osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
224 epname = epname_lc;
225
226 prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
227 if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200228 return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200229 }
230
Philipp Maierd70eef62021-07-19 13:53:28 +0200231 rc = e1_trunk_nr_from_epname(&trunk_nr, epname);
232 if (rc == 0)
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200233 return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200234
235 /* Earlier versions of osmo-mgw were accepting endpoint names
236 * without trunk prefix. This is normally not allowed, each MGCP
237 * request should supply an endpoint name with trunk prefix.
238 * However in order to stay compatible with old versions of
239 * osmo-bsc and osmo-msc we still accept endpoint names without
240 * trunk prefix and just assume that the virtual trunk should
241 * be selected. There is even a TTCN3 test for this, see also:
242 * MGCP_Test.TC_crcx_noprefix */
243 if ((epname[0] >= '0' && epname[0] <= '9') || (epname[0] >= 'a' && epname[0] <= 'f')) {
244 LOGP(DLMGCP, LOGL_ERROR, "missing trunk prefix in endpoint name \"%s\", assuming trunk \"%s\"!\n", epname,
245 MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200246 return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200247 }
248
249 LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
250 return NULL;
251}
Philipp Maier889fe7f2020-07-06 17:44:12 +0200252
253/*! Find a trunk (E1) by its associated E1 line number.
254 * \param[in] num e1 line number.
255 * \returns trunk or NULL if trunk was not found. */
256struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num)
257{
258 /*! When used on trunks other than E1, the result will always be NULL. */
259 struct mgcp_trunk *trunk;
260
261 llist_for_each_entry(trunk, &cfg->trunks, entry) {
262 if (trunk->trunk_type == MGCP_TRUNK_E1 && trunk->e1.vty_line_nr == num)
263 return trunk;
264 }
265
266 return NULL;
267}