blob: 46405bcaa977ba507131d00ba92ed715652897ea [file] [log] [blame]
Neels Hofmeyrc4628a32018-12-07 14:47:34 +01001/* Implementation to manage two RTP streams that make up an MO or MT call leg's RTP forwarding. */
2/*
3 * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
4 * All Rights Reserved
5 *
6 * Author: Neels Hofmeyr
7 *
8 * SPDX-License-Identifier: GPL-2.0+
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 */
24#include <osmocom/core/fsm.h>
25#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
26
27#include <osmocom/msc/debug.h>
28#include <osmocom/msc/gsm_data.h>
29#include <osmocom/msc/msc_a.h>
30
31#include <osmocom/msc/call_leg.h>
32#include <osmocom/msc/rtp_stream.h>
33
34#define LOG_CALL_LEG(cl, level, fmt, args...) \
35 LOGPFSML(cl ? cl->fi : NULL, level, fmt, ##args)
36
37static struct gsm_network *gsmnet = NULL;
38
39enum call_leg_state {
40 CALL_LEG_ST_ESTABLISHING,
41 CALL_LEG_ST_ESTABLISHED,
42 CALL_LEG_ST_RELEASING,
43};
44
45struct osmo_tdef g_mgw_tdefs[] = {
46 { .T=-1, .default_val=4, .desc="MGCP response timeout" },
47 { .T=-2, .default_val=30, .desc="RTP stream establishing timeout" },
48 {}
49};
50
51static const struct osmo_tdef_state_timeout call_leg_fsm_timeouts[32] = {
52 [CALL_LEG_ST_ESTABLISHING] = { .T = -2 },
53 [CALL_LEG_ST_RELEASING] = { .T = -2 },
54};
55
56#define call_leg_state_chg(cl, state) \
57 osmo_tdef_fsm_inst_state_chg((cl)->fi, state, call_leg_fsm_timeouts, g_mgw_tdefs, 10)
58
59static struct osmo_fsm call_leg_fsm;
60
61void call_leg_init(struct gsm_network *net)
62{
63 gsmnet = net;
64 OSMO_ASSERT( osmo_fsm_register(&call_leg_fsm) == 0 );
65}
66
Neels Hofmeyrf50d1302019-05-09 16:23:11 +020067/* Allocate a call leg FSM instance as child of an arbitrary other FSM instance.
68 * The call leg FSM dispatches events to its parent FSM instance on specific events:
69 * - parent_event_term: dispatch this to the parent FI when the call leg terminates (call ended, either planned or by
70 * failure).
71 * - parent_event_rtp_addr_available: one of the rtp_stream instances managed by the call leg has received an RTP
72 * address from the MGW. The struct rtp_stream instance is passed as data argument for the event dispatch.
73 * - parent_event_rtp_complete: one of the rtp_stream instances entered the RTP_STREAM_ST_ESTABLISHED state.
74 */
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010075struct call_leg *call_leg_alloc(struct osmo_fsm_inst *parent_fi,
76 uint32_t parent_event_term,
77 uint32_t parent_event_rtp_addr_available,
Neels Hofmeyr265a4c72019-05-09 16:20:51 +020078 uint32_t parent_event_rtp_complete)
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010079{
80 struct call_leg *cl;
81 struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&call_leg_fsm, parent_fi, parent_event_term);
82
83 OSMO_ASSERT(fi);
84
85 cl = talloc(fi, struct call_leg);
86 OSMO_ASSERT(cl);
87 fi->priv = cl;
88 *cl = (struct call_leg){
89 .fi = fi,
90 .parent_event_rtp_addr_available = parent_event_rtp_addr_available,
91 .parent_event_rtp_complete = parent_event_rtp_complete,
Neels Hofmeyrc4628a32018-12-07 14:47:34 +010092 };
93
94 return cl;
95}
96
97void call_leg_reparent(struct call_leg *cl,
98 struct osmo_fsm_inst *new_parent_fi,
99 uint32_t parent_event_term,
100 uint32_t parent_event_rtp_addr_available,
Neels Hofmeyr265a4c72019-05-09 16:20:51 +0200101 uint32_t parent_event_rtp_complete)
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100102{
103 LOG_CALL_LEG(cl, LOGL_DEBUG, "Reparenting from parent %s to parent %s\n",
104 cl->fi->proc.parent->name, new_parent_fi->name);
105 osmo_fsm_inst_change_parent(cl->fi, new_parent_fi, parent_event_term);
106 talloc_steal(new_parent_fi, cl->fi);
107 cl->parent_event_rtp_addr_available = parent_event_rtp_addr_available;
108 cl->parent_event_rtp_complete = parent_event_rtp_complete;
Neels Hofmeyrc4628a32018-12-07 14:47:34 +0100109}
110
111static int call_leg_fsm_timer_cb(struct osmo_fsm_inst *fi)
112{
113 struct call_leg *cl = fi->priv;
114 call_leg_release(cl);
115 return 0;
116}
117
118void call_leg_release(struct call_leg *cl)
119{
120 if (!cl)
121 return;
122 if (cl->fi->state == CALL_LEG_ST_RELEASING)
123 return;
124 call_leg_state_chg(cl, CALL_LEG_ST_RELEASING);
125}
126
127static void call_leg_mgw_endpoint_gone(struct call_leg *cl)
128{
129 int i;
130 cl->mgw_endpoint = NULL;
131 for (i = 0; i < ARRAY_SIZE(cl->rtp); i++) {
132 if (!cl->rtp[i])
133 continue;
134 cl->rtp[i]->ci = NULL;
135 }
136}
137
138static void call_leg_fsm_establishing_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
139{
140 struct call_leg *cl = fi->priv;
141 struct rtp_stream *rtps;
142 int i;
143 bool established;
144
145 switch (event) {
146
147 case CALL_LEG_EV_RTP_STREAM_ESTABLISHED:
148 /* An rtp_stream says it is established. If all are now established, change to state
149 * CALL_LEG_ST_ESTABLISHED. */
150 established = true;
151 for (i = 0; i < ARRAY_SIZE(cl->rtp); i++) {
152 if (!rtp_stream_is_established(cl->rtp[i])) {
153 established = false;
154 break;
155 }
156 }
157 if (!established)
158 break;
159 call_leg_state_chg(cl, CALL_LEG_ST_ESTABLISHED);
160 break;
161
162 case CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE:
163 rtps = data;
164 osmo_fsm_inst_dispatch(fi->proc.parent, cl->parent_event_rtp_addr_available, rtps);
165 break;
166
167 case CALL_LEG_EV_RTP_STREAM_GONE:
168 call_leg_release(cl);
169 break;
170
171 case CALL_LEG_EV_MGW_ENDPOINT_GONE:
172 call_leg_mgw_endpoint_gone(cl);
173 call_leg_release(cl);
174 break;
175
176 default:
177 OSMO_ASSERT(false);
178 }
179}
180
181void call_leg_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
182{
183 struct call_leg *cl = fi->priv;
184 osmo_fsm_inst_dispatch(fi->proc.parent, cl->parent_event_rtp_complete, cl);
185}
186
187void call_leg_fsm_releasing_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
188{
189 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
190}
191
192static void call_leg_fsm_releasing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
193{
194 struct call_leg *cl = fi->priv;
195
196 switch (event) {
197
198 case CALL_LEG_EV_RTP_STREAM_GONE:
199 /* We're already terminating, child RTP streams will also terminate, there is nothing left to do. */
200 break;
201
202 case CALL_LEG_EV_MGW_ENDPOINT_GONE:
203 call_leg_mgw_endpoint_gone(cl);
204 break;
205
206 default:
207 OSMO_ASSERT(false);
208 }
209}
210
211static const struct value_string call_leg_fsm_event_names[] = {
212 OSMO_VALUE_STRING(CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE),
213 OSMO_VALUE_STRING(CALL_LEG_EV_RTP_STREAM_ESTABLISHED),
214 OSMO_VALUE_STRING(CALL_LEG_EV_RTP_STREAM_GONE),
215 OSMO_VALUE_STRING(CALL_LEG_EV_MGW_ENDPOINT_GONE),
216 {}
217};
218
219#define S(x) (1 << (x))
220
221static const struct osmo_fsm_state call_leg_fsm_states[] = {
222 [CALL_LEG_ST_ESTABLISHING] = {
223 .name = "ESTABLISHING",
224 .in_event_mask = 0
225 | S(CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE)
226 | S(CALL_LEG_EV_RTP_STREAM_ESTABLISHED)
227 | S(CALL_LEG_EV_RTP_STREAM_GONE)
228 | S(CALL_LEG_EV_MGW_ENDPOINT_GONE)
229 ,
230 .out_state_mask = 0
231 | S(CALL_LEG_ST_ESTABLISHED)
232 | S(CALL_LEG_ST_RELEASING)
233 ,
234 .action = call_leg_fsm_establishing_established,
235 },
236 [CALL_LEG_ST_ESTABLISHED] = {
237 .name = "ESTABLISHED",
238 .in_event_mask = 0
239 | S(CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE)
240 | S(CALL_LEG_EV_RTP_STREAM_ESTABLISHED)
241 | S(CALL_LEG_EV_RTP_STREAM_GONE)
242 | S(CALL_LEG_EV_MGW_ENDPOINT_GONE)
243 ,
244 .out_state_mask = 0
245 | S(CALL_LEG_ST_ESTABLISHING)
246 | S(CALL_LEG_ST_RELEASING)
247 ,
248 .onenter = call_leg_fsm_established_onenter,
249 .action = call_leg_fsm_establishing_established, /* same action function as above */
250 },
251 [CALL_LEG_ST_RELEASING] = {
252 .name = "RELEASING",
253 .in_event_mask = 0
254 | S(CALL_LEG_EV_RTP_STREAM_GONE)
255 | S(CALL_LEG_EV_MGW_ENDPOINT_GONE)
256 ,
257 .onenter = call_leg_fsm_releasing_onenter,
258 .action = call_leg_fsm_releasing,
259 },
260};
261
262static struct osmo_fsm call_leg_fsm = {
263 .name = "call_leg",
264 .states = call_leg_fsm_states,
265 .num_states = ARRAY_SIZE(call_leg_fsm_states),
266 .log_subsys = DCC,
267 .event_names = call_leg_fsm_event_names,
268 .timer_cb = call_leg_fsm_timer_cb,
269};
270
271const struct value_string rtp_direction_names[] = {
272 OSMO_VALUE_STRING(RTP_TO_RAN),
273 OSMO_VALUE_STRING(RTP_TO_CN),
274 {}
275};
276
277int call_leg_ensure_rtp_alloc(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans)
278{
279 if (cl->rtp[dir])
280 return 0;
281
282 if (!cl->mgw_endpoint)
283 cl->mgw_endpoint = osmo_mgcpc_ep_alloc(cl->fi, CALL_LEG_EV_MGW_ENDPOINT_GONE,
284 gsmnet->mgw.client, gsmnet->mgw.tdefs, cl->fi->id,
285 "%s", mgcp_client_rtpbridge_wildcard(gsmnet->mgw.client));
286 if (!cl->mgw_endpoint) {
287 LOG_CALL_LEG(cl, LOGL_ERROR, "failed to setup MGW endpoint\n");
288 return -EIO;
289 }
290
291 cl->rtp[dir] = rtp_stream_alloc(cl, dir, call_id, for_trans);
292 OSMO_ASSERT(cl->rtp[dir]);
293 return 0;
294}
295
296struct osmo_sockaddr_str *call_leg_local_ip(struct call_leg *cl, enum rtp_direction dir)
297{
298 struct rtp_stream *rtps;
299 if (!cl)
300 return NULL;
301 rtps = cl->rtp[dir];
302 if (!rtps)
303 return NULL;
304 if (!osmo_sockaddr_str_is_set(&rtps->local))
305 return NULL;
306 return &rtps->local;
307}
308
309/* Make sure an MGW endpoint CI is set up for an RTP connection.
310 * This is the one-stop for all to either completely set up a new endpoint connection, or to modify an existing one.
311 * If not yet present, allocate the rtp_stream for the given direction.
312 * Then, call rtp_stream_set_codec() if codec_if_known is non-NULL, and/or rtp_stream_set_remote_addr() if
313 * remote_addr_if_known is non-NULL.
314 * Finally make sure that a CRCX is sent out for this direction, if this has not already happened.
315 * If the CRCX has already happened but new codec / remote_addr data was passed, call rtp_stream_commit() to trigger an
316 * MDCX.
317 */
318int call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans,
319 const enum mgcp_codecs *codec_if_known, const struct osmo_sockaddr_str *remote_addr_if_known)
320{
321 if (call_leg_ensure_rtp_alloc(cl, dir, call_id, for_trans))
322 return -EIO;
323 cl->rtp[dir]->crcx_conn_mode = cl->crcx_conn_mode[dir];
324 if (codec_if_known)
325 rtp_stream_set_codec(cl->rtp[dir], *codec_if_known);
326 if (remote_addr_if_known && osmo_sockaddr_str_is_set(remote_addr_if_known))
327 rtp_stream_set_remote_addr(cl->rtp[dir], remote_addr_if_known);
328 return rtp_stream_ensure_ci(cl->rtp[dir], cl->mgw_endpoint);
329}
330
331int call_leg_local_bridge(struct call_leg *cl1, uint32_t call_id1, struct gsm_trans *trans1,
332 struct call_leg *cl2, uint32_t call_id2, struct gsm_trans *trans2)
333{
334 enum mgcp_codecs codec;
335
336 cl1->local_bridge = cl2;
337 cl2->local_bridge = cl1;
338
339 /* We may just copy the codec info we have for the RAN side of the first leg to the CN side of both legs. This
340 * also means that if both legs use different codecs the MGW must perform transcoding on the second leg. */
341 if (!cl1->rtp[RTP_TO_RAN] || !cl1->rtp[RTP_TO_RAN]->codec_known) {
342 LOG_CALL_LEG(cl1, LOGL_ERROR, "RAN-side RTP stream codec is not known, not ready for bridging\n");
343 return -EINVAL;
344 }
345 codec = cl1->rtp[RTP_TO_RAN]->codec;
346
347 call_leg_ensure_ci(cl1, RTP_TO_CN, call_id1, trans1,
348 &codec, &cl2->rtp[RTP_TO_CN]->local);
349 call_leg_ensure_ci(cl2, RTP_TO_CN, call_id2, trans2,
350 &codec, &cl1->rtp[RTP_TO_CN]->local);
351 return 0;
352}