blob: 3d0191892d35acdae3cc6aad1ecf76230905e117 [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* The protocol implementation */
3
4/*
5 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009-2012 by On-Waves
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
24#include <string.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <errno.h>
28#include <time.h>
29#include <limits.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020030#include <arpa/inet.h>
31
32#include <osmocom/core/msgb.h>
33#include <osmocom/core/select.h>
Philipp Maier1cb1e382017-11-02 17:16:04 +010034#include <osmocom/core/socket.h>
Philipp Maier4dba7692018-08-03 12:20:52 +020035#include <osmocom/core/byteswap.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020036#include <osmocom/netif/rtp.h>
Philipp Maier228e5912019-03-05 13:56:59 +010037#include <osmocom/netif/amr.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020038#include <osmocom/mgcp/mgcp.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020039#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020040#include <osmocom/mgcp/mgcp_internal.h>
41#include <osmocom/mgcp/mgcp_stat.h>
42#include <osmocom/mgcp/osmux.h>
43#include <osmocom/mgcp/mgcp_conn.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010044#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020045#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier6931f9a2018-07-26 09:29:31 +020046#include <osmocom/mgcp/mgcp_codec.h>
Philipp Maierc3413882017-10-27 12:26:54 +020047#include <osmocom/mgcp/debug.h>
Philipp Maier9fc8a022019-02-20 12:26:52 +010048#include <osmocom/codec/codec.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020049
Philipp Maier6931f9a2018-07-26 09:29:31 +020050
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020051#define RTP_SEQ_MOD (1 << 16)
52#define RTP_MAX_DROPOUT 3000
53#define RTP_MAX_MISORDER 100
54#define RTP_BUF_SIZE 4096
55
56enum {
57 MGCP_PROTO_RTP,
58 MGCP_PROTO_RTCP,
59};
60
Harald Weltea48ff4a2020-03-08 14:45:08 +010061static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
62 int id, int inc)
63{
64 struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
Philipp Maierc66ab2c2020-06-02 20:55:34 +020065 struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats;
Harald Weltea48ff4a2020-03-08 14:45:08 +010066
Philipp Maierc66ab2c2020-06-02 20:55:34 +020067 /* add to both the per-connection and the global stats */
Harald Weltea48ff4a2020-03-08 14:45:08 +010068 rate_ctr_add(&conn_stats->ctr[id], inc);
Philipp Maierc66ab2c2020-06-02 20:55:34 +020069 rate_ctr_add(&mgw_stats->ctr[id], inc);
Harald Weltea48ff4a2020-03-08 14:45:08 +010070}
71
72static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
73{
74 rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
75}
76
Philipp Maier1cb1e382017-11-02 17:16:04 +010077/*! Determine the local rtp bind IP-address.
Philipp Maier0b79d212020-06-18 12:02:49 +020078 * \param[out] addr caller provided memory to store the resulting IP-Address.
79 * \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters.
Philipp Maier1cb1e382017-11-02 17:16:04 +010080 *
81 * The local bind IP-address is automatically selected by probing the
82 * IP-Address of the interface that is pointing towards the remote IP-Address,
83 * if no remote IP-Address is known yet, the statically configured
84 * IP-Addresses are used as fallback. */
85void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
86{
87
88 struct mgcp_endpoint *endp;
89 int rc;
90 endp = conn->conn->endp;
91
92 /* Try probing the local IP-Address */
93 if (endp->cfg->net_ports.bind_addr_probe && conn->end.addr.s_addr != 0) {
94 rc = osmo_sock_local_ip(addr, inet_ntoa(conn->end.addr));
95 if (rc < 0)
Pau Espin Pedrol3239f622019-04-24 18:47:46 +020096 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
97 "local interface auto detection failed, using configured addresses...\n");
Philipp Maier1cb1e382017-11-02 17:16:04 +010098 else {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +020099 LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
100 "selected local rtp bind ip %s by probing using remote ip %s\n",
101 addr, inet_ntoa(conn->end.addr));
Philipp Maier1cb1e382017-11-02 17:16:04 +0100102 return;
103 }
104 }
105
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200106 /* Select from preconfigured IP-Addresses. We don't have bind_addr for Osmux (yet?). */
Philipp Maier1cb1e382017-11-02 17:16:04 +0100107 if (endp->cfg->net_ports.bind_addr) {
108 /* Check there is a bind IP for the RTP traffic configured,
109 * if so, use that IP-Address */
Philipp Maierf8bfbe82017-11-23 19:32:31 +0100110 osmo_strlcpy(addr, endp->cfg->net_ports.bind_addr, INET_ADDRSTRLEN);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200111 LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
112 "using configured rtp bind ip as local bind ip %s\n",
113 addr);
Philipp Maier1cb1e382017-11-02 17:16:04 +0100114 } else {
115 /* No specific bind IP is configured for the RTP traffic, so
116 * assume the IP where we listen for incoming MGCP messages
117 * as bind IP */
Philipp Maierf8bfbe82017-11-23 19:32:31 +0100118 osmo_strlcpy(addr, endp->cfg->source_addr, INET_ADDRSTRLEN);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200119 LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
120 "using mgcp bind ip as local rtp bind ip: %s\n", addr);
Philipp Maier1cb1e382017-11-02 17:16:04 +0100121 }
122}
123
Philipp Maier87bd9be2017-08-22 16:35:41 +0200124/* This does not need to be a precision timestamp and
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200125 * is allowed to wrap quite fast. The returned value is
Philipp Maier87bd9be2017-08-22 16:35:41 +0200126 * 1/codec_rate seconds. */
127static uint32_t get_current_ts(unsigned codec_rate)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200128{
129 struct timespec tp;
130 uint64_t ret;
131
Philipp Maier87bd9be2017-08-22 16:35:41 +0200132 if (!codec_rate)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200133 return 0;
134
135 memset(&tp, 0, sizeof(tp));
136 if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
Philipp Maierc3413882017-10-27 12:26:54 +0200137 LOGP(DRTP, LOGL_NOTICE, "Getting the clock failed.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200138
139 /* convert it to 1/unit seconds */
140 ret = tp.tv_sec;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200141 ret *= codec_rate;
142 ret += (int64_t) tp.tv_nsec * codec_rate / 1000 / 1000 / 1000;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200143
144 return ret;
145}
146
Philipp Maier87bd9be2017-08-22 16:35:41 +0200147/*! send udp packet.
Philipp Maier0b79d212020-06-18 12:02:49 +0200148 * \param[in] fd associated file descriptor.
149 * \param[in] addr destination ip-address.
150 * \param[in] port destination UDP port (network byte order).
151 * \param[in] buf buffer that holds the data to be send.
152 * \param[in] len length of the data to be sent.
153 * \returns bytes sent, -1 on error. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200154int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
155{
156 struct sockaddr_in out;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200157
Philipp Maierc3413882017-10-27 12:26:54 +0200158 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200159 "sending %i bytes length packet to %s:%u ...\n",
160 len, inet_ntoa(*addr), ntohs(port));
161
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200162 out.sin_family = AF_INET;
163 out.sin_port = port;
164 memcpy(&out.sin_addr, addr, sizeof(*addr));
165
166 return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
167}
168
Philipp Maier87bd9be2017-08-22 16:35:41 +0200169/*! send RTP dummy packet (to keep NAT connection open).
Philipp Maier0b79d212020-06-18 12:02:49 +0200170 * \param[in] endp mcgp endpoint that holds the RTP connection.
171 * \param[in] conn associated RTP connection.
172 * \returns bytes sent, -1 on error. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200173int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200174{
175 static char buf[] = { MGCP_DUMMY_LOAD };
176 int rc;
177 int was_rtcp = 0;
178
Philipp Maier87bd9be2017-08-22 16:35:41 +0200179 OSMO_ASSERT(endp);
180 OSMO_ASSERT(conn);
181
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200182 LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,"sending dummy packet... %s\n",
183 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200184
185 rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
186 conn->end.rtp_port, buf, 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200187
188 if (rc == -1)
189 goto failed;
190
Philipp Maier14b27a82020-06-02 20:15:30 +0200191 if (endp->trunk->omit_rtcp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200192 return rc;
193
194 was_rtcp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200195 rc = mgcp_udp_send(conn->end.rtcp.fd, &conn->end.addr,
196 conn->end.rtcp_port, buf, 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200197
198 if (rc >= 0)
199 return rc;
200
201failed:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200202 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
203 "Failed to send dummy %s packet.\n",
204 was_rtcp ? "RTCP" : "RTP");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200205
206 return -1;
207}
208
Philipp Maier87bd9be2017-08-22 16:35:41 +0200209/* Compute timestamp alignment error */
210static int32_t ts_alignment_error(struct mgcp_rtp_stream_state *sstate,
211 int ptime, uint32_t timestamp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200212{
213 int32_t timestamp_delta;
214
215 if (ptime == 0)
216 return 0;
217
218 /* Align according to: T - Tlast = k * Tptime */
219 timestamp_delta = timestamp - sstate->last_timestamp;
220
221 return timestamp_delta % ptime;
222}
223
Philipp Maier87bd9be2017-08-22 16:35:41 +0200224/* Check timestamp and sequence number for plausibility */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200225static int check_rtp_timestamp(struct mgcp_endpoint *endp,
226 struct mgcp_rtp_state *state,
227 struct mgcp_rtp_stream_state *sstate,
228 struct mgcp_rtp_end *rtp_end,
229 struct sockaddr_in *addr,
230 uint16_t seq, uint32_t timestamp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200231 const char *text, int32_t * tsdelta_out)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200232{
233 int32_t tsdelta;
234 int32_t timestamp_error;
235
236 /* Not fully intialized, skip */
237 if (sstate->last_tsdelta == 0 && timestamp == sstate->last_timestamp)
238 return 0;
239
240 if (seq == sstate->last_seq) {
241 if (timestamp != sstate->last_timestamp) {
Philipp Maier9e1d1642018-05-09 16:26:34 +0200242 rate_ctr_inc(sstate->err_ts_ctr);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200243 LOGPENDP(endp, DRTP, LOGL_ERROR,
244 "The %s timestamp delta is != 0 but the sequence "
245 "number %d is the same, "
246 "TS offset: %d, SeqNo offset: %d "
247 "on SSRC: %u timestamp: %u "
248 "from %s:%d\n",
249 text, seq,
250 state->patch.timestamp_offset, state->patch.seq_offset,
251 sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr),
252 ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200253 }
254 return 0;
255 }
256
257 tsdelta =
Philipp Maier87bd9be2017-08-22 16:35:41 +0200258 (int32_t)(timestamp - sstate->last_timestamp) /
259 (int16_t)(seq - sstate->last_seq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200260
261 if (tsdelta == 0) {
262 /* Don't update *tsdelta_out */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200263 LOGPENDP(endp, DRTP, LOGL_NOTICE,
264 "The %s timestamp delta is %d "
265 "on SSRC: %u timestamp: %u "
266 "from %s:%d\n",
267 text, tsdelta,
268 sstate->ssrc, timestamp,
269 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200270
271 return 0;
272 }
273
274 if (sstate->last_tsdelta != tsdelta) {
275 if (sstate->last_tsdelta) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200276 LOGPENDP(endp, DRTP, LOGL_INFO,
277 "The %s timestamp delta changes from %d to %d "
278 "on SSRC: %u timestamp: %u from %s:%d\n",
279 text, sstate->last_tsdelta, tsdelta,
280 sstate->ssrc, timestamp,
281 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200282 }
283 }
284
285 if (tsdelta_out)
286 *tsdelta_out = tsdelta;
287
288 timestamp_error =
Philipp Maier87bd9be2017-08-22 16:35:41 +0200289 ts_alignment_error(sstate, state->packet_duration, timestamp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200290
291 if (timestamp_error) {
Philipp Maier9e1d1642018-05-09 16:26:34 +0200292 rate_ctr_inc(sstate->err_ts_ctr);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200293 LOGPENDP(endp, DRTP, LOGL_NOTICE,
294 "The %s timestamp has an alignment error of %d "
295 "on SSRC: %u "
296 "SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d "
297 "from %s:%d. ptime: %d\n",
298 text, timestamp_error,
299 sstate->ssrc,
300 (int16_t)(seq - sstate->last_seq),
301 (int32_t)(timestamp - sstate->last_timestamp),
302 tsdelta,
303 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
304 state->packet_duration);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200305 }
306 return 1;
307}
308
309/* Set the timestamp offset according to the packet duration. */
310static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
311 struct mgcp_rtp_state *state,
312 struct mgcp_rtp_end *rtp_end,
313 struct sockaddr_in *addr,
314 int16_t delta_seq, uint32_t in_timestamp)
315{
316 int32_t tsdelta = state->packet_duration;
317 int timestamp_offset;
318 uint32_t out_timestamp;
319
320 if (tsdelta == 0) {
321 tsdelta = state->out_stream.last_tsdelta;
322 if (tsdelta != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200323 LOGPENDP(endp, DRTP, LOGL_NOTICE,
324 "A fixed packet duration is not available, "
325 "using last output timestamp delta instead: %d "
326 "from %s:%d\n", tsdelta,
327 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200328 } else {
Philipp Maierbc0346e2018-06-07 09:52:16 +0200329 tsdelta = rtp_end->codec->rate * 20 / 1000;
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200330 LOGPENDP(endp, DRTP, LOGL_NOTICE,
331 "Fixed packet duration and last timestamp delta "
332 "are not available, "
333 "using fixed 20ms instead: %d "
334 "from %s:%d\n", tsdelta,
335 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200336 }
337 }
338
339 out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta;
340 timestamp_offset = out_timestamp - in_timestamp;
341
Harald Welte33381352017-12-25 09:44:26 +0100342 if (state->patch.timestamp_offset != timestamp_offset) {
343 state->patch.timestamp_offset = timestamp_offset;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200344
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200345 LOGPENDP(endp, DRTP, LOGL_NOTICE,
346 "Timestamp offset change on SSRC: %u "
347 "SeqNo delta: %d, TS offset: %d, "
348 "from %s:%d\n", state->in_stream.ssrc,
349 delta_seq, state->patch.timestamp_offset,
350 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200351 }
352
353 return timestamp_offset;
354}
355
356/* Set the timestamp offset according to the packet duration. */
357static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
358 struct mgcp_rtp_state *state,
359 struct mgcp_rtp_end *rtp_end,
360 struct sockaddr_in *addr,
361 uint32_t timestamp)
362{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200363 int ts_error = 0;
364 int ts_check = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200365 int ptime = state->packet_duration;
366
367 /* Align according to: T + Toffs - Tlast = k * Tptime */
368
Philipp Maier87bd9be2017-08-22 16:35:41 +0200369 ts_error = ts_alignment_error(&state->out_stream, ptime,
Harald Welte33381352017-12-25 09:44:26 +0100370 timestamp + state->patch.timestamp_offset);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200371
Philipp Maier87bd9be2017-08-22 16:35:41 +0200372 /* If there is an alignment error, we have to compensate it */
373 if (ts_error) {
Harald Welte33381352017-12-25 09:44:26 +0100374 state->patch.timestamp_offset += ptime - ts_error;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200375
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200376 LOGPENDP(endp, DRTP, LOGL_NOTICE,
377 "Corrected timestamp alignment error of %d on SSRC: %u "
378 "new TS offset: %d, "
379 "from %s:%d\n",
380 ts_error, state->in_stream.ssrc,
381 state->patch.timestamp_offset, inet_ntoa(addr->sin_addr),
382 ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200383 }
384
Philipp Maier87bd9be2017-08-22 16:35:41 +0200385 /* Check we really managed to compensate the timestamp
386 * offset. There should not be any remaining error, failing
Harald Welte1d1b98f2017-12-25 10:03:40 +0100387 * here would point to a serous problem with the alignment
388 * error computation function */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200389 ts_check = ts_alignment_error(&state->out_stream, ptime,
Harald Welte33381352017-12-25 09:44:26 +0100390 timestamp + state->patch.timestamp_offset);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200391 OSMO_ASSERT(ts_check == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200392
Philipp Maier87bd9be2017-08-22 16:35:41 +0200393 /* Return alignment error before compensation */
394 return ts_error;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200395}
396
Philipp Maier87bd9be2017-08-22 16:35:41 +0200397/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
Philipp Maier0b79d212020-06-18 12:02:49 +0200398 * \param[in] associated endpoint.
399 * \param[in] destination RTP end.
400 * \param[in,out] pointer to buffer with voice data.
401 * \param[in] voice data length.
402 * \param[in] maximum size of caller provided voice data buffer.
403 * \returns ignores input parameters, return always 0. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200404int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
405 struct mgcp_rtp_end *dst_end,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200406 char *data, int *len, int buf_size)
407{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200408 LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200409 return 0;
410}
411
Philipp Maier87bd9be2017-08-22 16:35:41 +0200412/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
Philipp Maier0b79d212020-06-18 12:02:49 +0200413 * \param[in] associated endpoint.
414 * \param[in] destination RTP connnection.
415 * \param[in] source RTP connection.
416 * \returns ignores input parameters, return always 0. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200417int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
Philipp Maieracc10352018-07-19 18:07:57 +0200418 struct mgcp_conn_rtp *conn_dst,
419 struct mgcp_conn_rtp *conn_src)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200420{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200421 LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200422 return 0;
423}
424
425void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
Philipp Maier58128252019-03-06 11:28:18 +0100426 const struct mgcp_rtp_codec **codec,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200427 const char **fmtp_extra,
428 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200429{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200430 LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s using format defaults\n",
431 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200432
Philipp Maier58128252019-03-06 11:28:18 +0100433 *codec = conn->end.codec;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200434 *fmtp_extra = conn->end.fmtp_extra;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200435}
436
Philipp Maier87bd9be2017-08-22 16:35:41 +0200437void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
438 struct mgcp_rtp_state *state, const uint16_t seq,
439 const int32_t transit, const uint32_t ssrc)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200440{
441 int32_t d;
442
443 /* initialize or re-initialize */
Harald Welte49e3d5a2017-12-25 09:47:57 +0100444 if (!state->stats.initialized || state->stats.ssrc != ssrc) {
445 state->stats.initialized = 1;
446 state->stats.base_seq = seq;
447 state->stats.max_seq = seq - 1;
448 state->stats.ssrc = ssrc;
449 state->stats.jitter = 0;
450 state->stats.transit = transit;
451 state->stats.cycles = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200452 } else {
453 uint16_t udelta;
454
Philipp Maier87bd9be2017-08-22 16:35:41 +0200455 /* The below takes the shape of the validation of
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200456 * Appendix A. Check if there is something weird with
457 * the sequence number, otherwise check for a wrap
458 * around in the sequence number.
459 * It can't wrap during the initialization so let's
460 * skip it here. The Appendix A probably doesn't have
Philipp Maier87bd9be2017-08-22 16:35:41 +0200461 * this issue because of the probation. */
Harald Welte49e3d5a2017-12-25 09:47:57 +0100462 udelta = seq - state->stats.max_seq;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200463 if (udelta < RTP_MAX_DROPOUT) {
Harald Welte49e3d5a2017-12-25 09:47:57 +0100464 if (seq < state->stats.max_seq)
465 state->stats.cycles += RTP_SEQ_MOD;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200466 } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200467 LOGPENDP(endp, DRTP, LOGL_NOTICE,
468 "RTP seqno made a very large jump on delta: %u\n",
469 udelta);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200470 }
471 }
472
Philipp Maier87bd9be2017-08-22 16:35:41 +0200473 /* Calculate the jitter between the two packages. The TS should be
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200474 * taken closer to the read function. This was taken from the
475 * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate
Philipp Maier87bd9be2017-08-22 16:35:41 +0200476 * resolution. */
Harald Welte49e3d5a2017-12-25 09:47:57 +0100477 d = transit - state->stats.transit;
478 state->stats.transit = transit;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200479 if (d < 0)
480 d = -d;
Harald Welte49e3d5a2017-12-25 09:47:57 +0100481 state->stats.jitter += d - ((state->stats.jitter + 8) >> 4);
482 state->stats.max_seq = seq;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200483}
484
Philipp Maier6931f9a2018-07-26 09:29:31 +0200485/* There may be different payload type numbers negotiated for two connections.
486 * Patch the payload type of an RTP packet so that it uses the payload type
487 * that is valid for the destination connection (conn_dst) */
488static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
489 struct mgcp_conn_rtp *conn_dst, char *data, int len)
490{
491 struct rtp_hdr *rtp_hdr;
492 uint8_t pt_in;
493 int pt_out;
494
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200495 if (len < sizeof(struct rtp_hdr))
496 return -EINVAL;
497
Philipp Maier6931f9a2018-07-26 09:29:31 +0200498 rtp_hdr = (struct rtp_hdr *)data;
499
500 pt_in = rtp_hdr->payload_type;
501 pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
502 if (pt_out < 0)
503 return -EINVAL;
504
505 rtp_hdr->payload_type = (uint8_t) pt_out;
506 return 0;
507}
508
Philipp Maier87bd9be2017-08-22 16:35:41 +0200509/* The RFC 3550 Appendix A assumes there are multiple sources but
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200510 * some of the supported endpoints (e.g. the nanoBTS) can only handle
511 * one source and this code will patch RTP header to appear as if there
512 * is only one source.
513 * There is also no probation period for new sources. Every RTP header
Philipp Maier87bd9be2017-08-22 16:35:41 +0200514 * we receive will be seen as a switch in streams. */
515void mgcp_patch_and_count(struct mgcp_endpoint *endp,
516 struct mgcp_rtp_state *state,
517 struct mgcp_rtp_end *rtp_end,
518 struct sockaddr_in *addr, char *data, int len)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200519{
520 uint32_t arrival_time;
521 int32_t transit;
522 uint16_t seq;
523 uint32_t timestamp, ssrc;
524 struct rtp_hdr *rtp_hdr;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200525 int payload = rtp_end->codec->payload_type;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200526
527 if (len < sizeof(*rtp_hdr))
528 return;
529
Philipp Maier87bd9be2017-08-22 16:35:41 +0200530 rtp_hdr = (struct rtp_hdr *)data;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200531 seq = ntohs(rtp_hdr->sequence);
532 timestamp = ntohl(rtp_hdr->timestamp);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200533 arrival_time = get_current_ts(rtp_end->codec->rate);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200534 ssrc = ntohl(rtp_hdr->ssrc);
535 transit = arrival_time - timestamp;
536
537 mgcp_rtp_annex_count(endp, state, seq, transit, ssrc);
538
539 if (!state->initialized) {
540 state->initialized = 1;
541 state->in_stream.last_seq = seq - 1;
Harald Welte33381352017-12-25 09:44:26 +0100542 state->in_stream.ssrc = state->patch.orig_ssrc = ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200543 state->in_stream.last_tsdelta = 0;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200544 state->packet_duration =
545 mgcp_rtp_packet_duration(endp, rtp_end);
Philipp Maier0ec1d4e2018-05-16 11:09:42 +0200546 state->out_stream.last_seq = seq - 1;
547 state->out_stream.ssrc = state->patch.orig_ssrc = ssrc;
548 state->out_stream.last_tsdelta = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200549 state->out_stream.last_timestamp = timestamp;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200550 state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200551 LOGPENDP(endp, DRTP, LOGL_INFO,
552 "initializing stream, SSRC: %u timestamp: %u "
553 "pkt-duration: %d, from %s:%d\n",
554 state->in_stream.ssrc,
555 state->patch.seq_offset, state->packet_duration,
556 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200557 if (state->packet_duration == 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200558 state->packet_duration =
Philipp Maierbc0346e2018-06-07 09:52:16 +0200559 rtp_end->codec->rate * 20 / 1000;
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200560 LOGPENDP(endp, DRTP, LOGL_NOTICE,
561 "fixed packet duration is not available, "
562 "using fixed 20ms instead: %d from %s:%d\n",
563 state->packet_duration,
564 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200565 }
566 } else if (state->in_stream.ssrc != ssrc) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200567 LOGPENDP(endp, DRTP, LOGL_NOTICE,
568 "SSRC changed: %u -> %u "
569 "from %s:%d\n",
570 state->in_stream.ssrc, rtp_hdr->ssrc,
571 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200572
573 state->in_stream.ssrc = ssrc;
574 if (rtp_end->force_constant_ssrc) {
575 int16_t delta_seq;
576
577 /* Always increment seqno by 1 */
Harald Welte33381352017-12-25 09:44:26 +0100578 state->patch.seq_offset =
Philipp Maier87bd9be2017-08-22 16:35:41 +0200579 (state->out_stream.last_seq + 1) - seq;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200580
581 /* Estimate number of packets that would have been sent */
582 delta_seq =
Philipp Maier87bd9be2017-08-22 16:35:41 +0200583 (arrival_time - state->in_stream.last_arrival_time
584 + state->packet_duration / 2) /
585 state->packet_duration;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200586
587 adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
588 delta_seq, timestamp);
589
Harald Welte33381352017-12-25 09:44:26 +0100590 state->patch.patch_ssrc = 1;
591 ssrc = state->patch.orig_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200592 if (rtp_end->force_constant_ssrc != -1)
593 rtp_end->force_constant_ssrc -= 1;
594
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200595 LOGPENDP(endp, DRTP, LOGL_NOTICE,
596 "SSRC patching enabled, SSRC: %u "
597 "SeqNo offset: %d, TS offset: %d "
598 "from %s:%d\n", state->in_stream.ssrc,
599 state->patch.seq_offset, state->patch.timestamp_offset,
600 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200601 }
602
603 state->in_stream.last_tsdelta = 0;
604 } else {
605 /* Compute current per-packet timestamp delta */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200606 check_rtp_timestamp(endp, state, &state->in_stream, rtp_end,
607 addr, seq, timestamp, "input",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200608 &state->in_stream.last_tsdelta);
609
Harald Welte33381352017-12-25 09:44:26 +0100610 if (state->patch.patch_ssrc)
611 ssrc = state->patch.orig_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200612 }
613
614 /* Save before patching */
615 state->in_stream.last_timestamp = timestamp;
616 state->in_stream.last_seq = seq;
617 state->in_stream.last_arrival_time = arrival_time;
618
619 if (rtp_end->force_aligned_timing &&
620 state->out_stream.ssrc == ssrc && state->packet_duration)
621 /* Align the timestamp offset */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200622 align_rtp_timestamp_offset(endp, state, rtp_end, addr,
623 timestamp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200624
625 /* Store the updated SSRC back to the packet */
Harald Welte33381352017-12-25 09:44:26 +0100626 if (state->patch.patch_ssrc)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200627 rtp_hdr->ssrc = htonl(ssrc);
628
629 /* Apply the offset and store it back to the packet.
630 * This won't change anything if the offset is 0, so the conditional is
631 * omitted. */
Harald Welte33381352017-12-25 09:44:26 +0100632 seq += state->patch.seq_offset;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200633 rtp_hdr->sequence = htons(seq);
Harald Welte33381352017-12-25 09:44:26 +0100634 timestamp += state->patch.timestamp_offset;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200635 rtp_hdr->timestamp = htonl(timestamp);
636
637 /* Check again, whether the timestamps are still valid */
638 if (state->out_stream.ssrc == ssrc)
639 check_rtp_timestamp(endp, state, &state->out_stream, rtp_end,
640 addr, seq, timestamp, "output",
641 &state->out_stream.last_tsdelta);
642
643 /* Save output values */
644 state->out_stream.last_seq = seq;
645 state->out_stream.last_timestamp = timestamp;
646 state->out_stream.ssrc = ssrc;
647
648 if (payload < 0)
649 return;
650
651#if 0
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200652 LOGPENDP(endp, DRTP, LOGL_DEBUG, "payload hdr payload %u -> endp payload %u\n",
653 rtp_hdr->payload_type, payload);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200654 rtp_hdr->payload_type = payload;
655#endif
656}
657
Philipp Maier9fc8a022019-02-20 12:26:52 +0100658/* There are different dialects used to format and transfer voice data. When
659 * the receiving end expects GSM-HR data to be formated after RFC 5993, this
660 * function is used to convert between RFC 5993 and TS 101318, which we normally
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200661 * use.
662 * Return 0 on sucess, negative on errors like invalid data length. */
663static int rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
Philipp Maier9fc8a022019-02-20 12:26:52 +0100664{
665 /* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
666 * plenty of space available to store the slightly larger, converted
667 * data */
668
669 struct rtp_hdr *rtp_hdr;
670
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200671 if (*len < sizeof(struct rtp_hdr)) {
672 LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n",
673 *len, sizeof(struct rtp_hdr));
674 return -EINVAL;
675 }
676
Philipp Maier9fc8a022019-02-20 12:26:52 +0100677 rtp_hdr = (struct rtp_hdr *)data;
678
679 if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
680 /* TS 101318 encoding => RFC 5993 encoding */
681 memmove(rtp_hdr->data + 1, rtp_hdr->data, GSM_HR_BYTES);
682 rtp_hdr->data[0] = 0x00;
683 (*len) += 1;
684
685 } else if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr) + 1) {
686 /* RFC 5993 encoding => TS 101318 encoding */
687 memmove(rtp_hdr->data, rtp_hdr->data + 1, GSM_HR_BYTES);
688 (*len) -= 1;
689 } else {
690 /* It is possible that multiple payloads occur in one RTP
691 * packet. This is not supported yet. */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200692 LOGPENDP(endp, DRTP, LOGL_ERROR,
693 "cannot figure out how to convert RTP packet\n");
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200694 return -ENOTSUP;
Philipp Maier9fc8a022019-02-20 12:26:52 +0100695 }
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200696 return 0;
Philipp Maier9fc8a022019-02-20 12:26:52 +0100697}
698
Philipp Maier228e5912019-03-05 13:56:59 +0100699/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
700 * efficient encoding scheme where all fields are packed together one after
701 * another and an octet aligned mode where all fields are aligned to octet
702 * boundaries. This function is used to convert between the two modes */
703static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
704 bool target_is_oa)
705{
706 /* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
707 * plenty of space available to store the slightly larger, converted
708 * data */
709
710 struct rtp_hdr *rtp_hdr;
711 unsigned int payload_len;
712 int rc;
713
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200714 if (*len < sizeof(struct rtp_hdr)) {
715 LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", *len, sizeof(struct rtp_hdr));
716 return -EINVAL;
717 }
718
Philipp Maier228e5912019-03-05 13:56:59 +0100719 rtp_hdr = (struct rtp_hdr *)data;
720
721 payload_len = *len - sizeof(struct rtp_hdr);
722
723 if (osmo_amr_is_oa(rtp_hdr->data, payload_len)) {
724 if (!target_is_oa)
725 /* Input data is oa an target format is bwe
726 * ==> convert */
727 rc = osmo_amr_oa_to_bwe(rtp_hdr->data, payload_len);
728 else
729 /* Input data is already bew, but we accept it anyway
730 * ==> no conversion needed */
731 rc = payload_len;
732 } else {
733 if (target_is_oa)
734 /* Input data is bwe an target format is oa
735 * ==> convert */
736 rc = osmo_amr_bwe_to_oa(rtp_hdr->data, payload_len,
737 RTP_BUF_SIZE);
738 else
739 /* Input data is already oa, but we accept it anyway
740 * ==> no conversion needed */
741 rc = payload_len;
742 }
743 if (rc < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200744 LOGPENDP(endp, DRTP, LOGL_ERROR,
745 "AMR RTP packet conversion failed\n");
Philipp Maier228e5912019-03-05 13:56:59 +0100746 return -EINVAL;
747 }
748
749 *len = rc + sizeof(struct rtp_hdr);
750
751 return 0;
752}
753
754/* Check if a conversion between octet-aligned and bandwith-efficient mode is
755 * indicated. */
756static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
757{
758 if (codec->param_present == false)
759 return false;
760 if (!codec->param.amr_octet_aligned_present)
761 return false;
762 if (strcmp(codec->subtype_name, "AMR") != 0)
763 return false;
764 return true;
765}
766
767
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200768/* Return whether an RTP packet with AMR payload is in octet-aligned mode.
769 * Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */
770static int amr_oa_check(char *data, int len)
Philipp Maier228e5912019-03-05 13:56:59 +0100771{
772 struct rtp_hdr *rtp_hdr;
773 unsigned int payload_len;
774
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200775 if (len < sizeof(struct rtp_hdr))
776 return -EINVAL;
777
Philipp Maier228e5912019-03-05 13:56:59 +0100778 rtp_hdr = (struct rtp_hdr *)data;
779
780 payload_len = len - sizeof(struct rtp_hdr);
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200781 if (payload_len < sizeof(struct amr_hdr))
782 return -EINVAL;
Philipp Maier228e5912019-03-05 13:56:59 +0100783
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +0200784 return osmo_amr_is_oa(rtp_hdr->data, payload_len) ? 1 : 0;
Philipp Maier228e5912019-03-05 13:56:59 +0100785}
786
Philipp Maier87bd9be2017-08-22 16:35:41 +0200787/* Forward data to a debug tap. This is debug function that is intended for
788 * debugging the voice traffic with tools like gstreamer */
Philipp Maiere6f172d2017-11-07 12:00:01 +0100789static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
790 int len)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200791{
Philipp Maiere6f172d2017-11-07 12:00:01 +0100792 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200793
Philipp Maiere6f172d2017-11-07 12:00:01 +0100794 if (!tap->enabled)
795 return;
796
797 rc = sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward,
798 sizeof(tap->forward));
799
800 if (rc < 0)
801 LOGP(DRTP, LOGL_ERROR,
802 "Forwarding tapped (debug) voice data failed.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200803}
804
Philipp Maier87bd9be2017-08-22 16:35:41 +0200805/*! Send RTP/RTCP data to a specified destination connection.
Philipp Maier0b79d212020-06-18 12:02:49 +0200806 * \param[in] endp associated endpoint (for configuration, logging).
807 * \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
808 * \param[in] spoofed source address (set to NULL to disable).
809 * \param[in] buf buffer that contains the RTP/RTCP data.
810 * \param[in] len length of the buffer that contains the RTP/RTCP data.
811 * \param[in] conn_src associated source connection.
812 * \param[in] conn_dst associated destination connection.
813 * \returns 0 on success, -1 on ERROR. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200814int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
815 char *buf, int len, struct mgcp_conn_rtp *conn_src,
816 struct mgcp_conn_rtp *conn_dst)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200817{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200818 /*! When no destination connection is available (e.g. when only one
819 * connection in loopback mode exists), then the source connection
820 * shall be specified as destination connection */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200821
Philipp Maier14b27a82020-06-02 20:15:30 +0200822 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200823 struct mgcp_rtp_end *rtp_end;
824 struct mgcp_rtp_state *rtp_state;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200825 char *dest_name;
Philipp Maier6931f9a2018-07-26 09:29:31 +0200826 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200827
Philipp Maier87bd9be2017-08-22 16:35:41 +0200828 OSMO_ASSERT(conn_src);
829 OSMO_ASSERT(conn_dst);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200830
Philipp Maier87bd9be2017-08-22 16:35:41 +0200831 if (is_rtp) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200832 LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTP packet...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200833 } else {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200834 LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTCP packet...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200835 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200836
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200837 LOGPENDP(endp, DRTP, LOGL_DEBUG, "loop:%d, mode:%d%s\n",
Philipp Maier14b27a82020-06-02 20:15:30 +0200838 trunk->audio_loop, conn_src->conn->mode,
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200839 conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200840
Philipp Maier6931f9a2018-07-26 09:29:31 +0200841 /* FIXME: It is legal that the payload type on the egress connection is
842 * different from the payload type that has been negotiated on the
843 * ingress connection. Essentially the codecs are the same so we can
844 * match them and patch the payload type. However, if we can not find
845 * the codec pendant (everything ist equal except the PT), we are of
846 * course unable to patch the payload type. A situation like this
847 * should not occur if transcoding is consequently avoided. Until
848 * we have transcoding support in osmo-mgw we can not resolve this. */
Philipp Maierda895b12018-08-03 12:16:37 +0200849 if (is_rtp) {
850 rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
851 if (rc < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200852 LOGPENDP(endp, DRTP, LOGL_DEBUG,
853 "can not patch PT because no suitable egress codec was found.\n");
Philipp Maierda895b12018-08-03 12:16:37 +0200854 }
Philipp Maier6931f9a2018-07-26 09:29:31 +0200855 }
856
Philipp Maier87bd9be2017-08-22 16:35:41 +0200857 /* Note: In case of loopback configuration, both, the source and the
858 * destination will point to the same connection. */
859 rtp_end = &conn_dst->end;
860 rtp_state = &conn_src->state;
861 dest_name = conn_dst->conn->name;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200862
863 if (!rtp_end->output_enabled) {
Harald Weltea48ff4a2020-03-08 14:45:08 +0100864 rtpconn_rate_ctr_inc(conn_dst, endp, RTP_DROPPED_PACKETS_CTR);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200865 LOGPENDP(endp, DRTP, LOGL_DEBUG,
866 "output disabled, drop to %s %s "
867 "rtp_port:%u rtcp_port:%u\n",
868 dest_name,
869 inet_ntoa(rtp_end->addr),
870 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200871 );
872 } else if (is_rtp) {
873 int cont;
874 int nbytes = 0;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200875 int buflen = len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200876 do {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200877 /* Run transcoder */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200878 cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200879 buf, &buflen,
880 RTP_BUF_SIZE);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200881 if (cont < 0)
882 break;
883
Philipp Maier87bd9be2017-08-22 16:35:41 +0200884 if (addr)
885 mgcp_patch_and_count(endp, rtp_state, rtp_end,
886 addr, buf, buflen);
Philipp Maier9fc8a022019-02-20 12:26:52 +0100887
Philipp Maier228e5912019-03-05 13:56:59 +0100888 if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
Neels Hofmeyr5a6220f2019-08-20 03:09:04 +0200889 rc = amr_oa_bwe_convert(endp, buf, &buflen,
890 conn_dst->end.codec->param.amr_octet_aligned);
891 if (rc < 0) {
892 LOGPENDP(endp, DRTP, LOGL_ERROR,
893 "Error in AMR octet-aligned <-> bandwidth-efficient mode conversion\n");
894 break;
895 }
Philipp Maier228e5912019-03-05 13:56:59 +0100896 }
897 else if (rtp_end->rfc5993_hr_convert
Philipp Maier9fc8a022019-02-20 12:26:52 +0100898 && strcmp(conn_src->end.codec->subtype_name,
Neels Hofmeyr5a6220f2019-08-20 03:09:04 +0200899 "GSM-HR-08") == 0) {
900 rc = rfc5993_hr_convert(endp, buf, &buflen);
901 if (rc < 0) {
902 LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
903 break;
904 }
905 }
Philipp Maier9fc8a022019-02-20 12:26:52 +0100906
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200907 LOGPENDP(endp, DRTP, LOGL_DEBUG,
908 "process/send to %s %s "
909 "rtp_port:%u rtcp_port:%u\n",
910 dest_name, inet_ntoa(rtp_end->addr),
911 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
912 );
Philipp Maier87bd9be2017-08-22 16:35:41 +0200913
914 /* Forward a copy of the RTP data to a debug ip/port */
915 forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
916 buf, buflen);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200917
918 /* FIXME: HACK HACK HACK. See OS#2459.
919 * The ip.access nano3G needs the first RTP payload's first two bytes to read hex
920 * 'e400', or it will reject the RAB assignment. It seems to not harm other femto
921 * cells (as long as we patch only the first RTP payload in each stream).
922 */
Neels Hofmeyr35a38292018-07-23 18:29:04 +0200923 if (!rtp_state->patched_first_rtp_payload
924 && conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200925 uint8_t *data = (uint8_t *) & buf[12];
Neels Hofmeyr35a38292018-07-23 18:29:04 +0200926 if (data[0] == 0xe0) {
927 data[0] = 0xe4;
928 data[1] = 0x00;
929 rtp_state->patched_first_rtp_payload = true;
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200930 LOGPENDP(endp, DRTP, LOGL_DEBUG,
931 "Patching over first two bytes"
932 " to fake an IuUP Initialization Ack\n");
Neels Hofmeyr35a38292018-07-23 18:29:04 +0200933 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200934 }
935
Philipp Maier87bd9be2017-08-22 16:35:41 +0200936 len = mgcp_udp_send(rtp_end->rtp.fd,
937 &rtp_end->addr,
938 rtp_end->rtp_port, buf, buflen);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200939
Philipp Maier87bd9be2017-08-22 16:35:41 +0200940 if (len <= 0)
941 return len;
942
Harald Weltea48ff4a2020-03-08 14:45:08 +0100943 rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
944 rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200945
946 nbytes += len;
947 buflen = cont;
948 } while (buflen > 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200949 return nbytes;
Philipp Maier14b27a82020-06-02 20:15:30 +0200950 } else if (!trunk->omit_rtcp) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200951 LOGPENDP(endp, DRTP, LOGL_DEBUG,
952 "send to %s %s rtp_port:%u rtcp_port:%u\n",
953 dest_name, inet_ntoa(rtp_end->addr),
954 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
955 );
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200956
Philipp Maier87bd9be2017-08-22 16:35:41 +0200957 len = mgcp_udp_send(rtp_end->rtcp.fd,
958 &rtp_end->addr,
959 rtp_end->rtcp_port, buf, len);
960
Harald Weltea48ff4a2020-03-08 14:45:08 +0100961 rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
962 rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200963
964 return len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200965 }
966
967 return 0;
968}
969
Philipp Maier87bd9be2017-08-22 16:35:41 +0200970/* Helper function for mgcp_recv(),
971 Receive one RTP Packet + Originating address from file descriptor */
972static int receive_from(struct mgcp_endpoint *endp, int fd,
973 struct sockaddr_in *addr, char *buf, int bufsize)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200974{
975 int rc;
976 socklen_t slen = sizeof(*addr);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200977 struct sockaddr_in addr_sink;
978 char buf_sink[RTP_BUF_SIZE];
979 bool tossed = false;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200980
Philipp Maier87bd9be2017-08-22 16:35:41 +0200981 if (!addr)
982 addr = &addr_sink;
983 if (!buf) {
984 tossed = true;
985 buf = buf_sink;
986 bufsize = sizeof(buf_sink);
987 }
988
989 rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
990
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200991 LOGPENDP(endp, DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200992 "receiving %u bytes length packet from %s:%u ...\n",
993 rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
994
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200995 if (rc < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200996 LOGPENDP(endp, DRTP, LOGL_ERROR,
997 "failed to receive packet, errno: %d/%s\n",
998 errno, strerror(errno));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200999 return -1;
1000 }
1001
Philipp Maier87bd9be2017-08-22 16:35:41 +02001002 if (tossed) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001003 LOGPENDP(endp, DRTP, LOGL_ERROR, "packet tossed\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001004 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001005
1006 return rc;
1007}
1008
Philipp Maier87bd9be2017-08-22 16:35:41 +02001009/* Check if the origin (addr) matches the address/port data of the RTP
1010 * connections. */
1011static int check_rtp_origin(struct mgcp_conn_rtp *conn,
1012 struct sockaddr_in *addr)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001013{
Harald Weltec26b6652018-10-21 12:01:04 +02001014 if (conn->end.addr.s_addr == 0) {
Neels Hofmeyrefd645e2018-09-10 13:14:50 +02001015 switch (conn->conn->mode) {
1016 case MGCP_CONN_LOOPBACK:
1017 /* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
1018 * message received. We currently hackishly accomplish that by putting the endpoint in
1019 * loopback mode and patching over the looped back RTP message to make it look like an
1020 * ack. We don't know the femto cell's IP address and port until the RAB Assignment
1021 * Response is received, but the nano3G expects an IuUP Initialization Ack before it even
1022 * sends the RAB Assignment Response. Hence, if the remote address is 0.0.0.0 and the
1023 * MGCP port is in loopback mode, allow looping back the packet to any source. */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001024 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
1025 "In loopback mode and remote address not set:"
1026 " allowing data from address: %s\n", inet_ntoa(addr->sin_addr));
Neels Hofmeyrefd645e2018-09-10 13:14:50 +02001027 return 0;
1028
1029 default:
1030 /* Receiving early media before the endpoint is configured. Instead of logging
1031 * this as an error that occurs on every call, keep it more low profile to not
1032 * confuse humans with expected errors. */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001033 LOGPCONN(conn->conn, DRTP, LOGL_INFO,
1034 "Rx RTP from %s, but remote address not set:"
1035 " dropping early media\n", inet_ntoa(addr->sin_addr));
Neels Hofmeyrefd645e2018-09-10 13:14:50 +02001036 return -1;
1037 }
Neels Hofmeyr0063ca22018-07-23 18:12:16 +02001038 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001039
Philipp Maier87bd9be2017-08-22 16:35:41 +02001040 /* Note: Check if the inbound RTP data comes from the same host to
1041 * which we send our outgoing RTP traffic. */
Harald Weltec26b6652018-10-21 12:01:04 +02001042 if (conn->end.addr.s_addr != addr->sin_addr.s_addr) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001043 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
1044 "data from wrong address: %s, ", inet_ntoa(addr->sin_addr));
Philipp Maierc3413882017-10-27 12:26:54 +02001045 LOGPC(DRTP, LOGL_ERROR, "expected: %s\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +02001046 inet_ntoa(conn->end.addr));
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001047 LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001048 return -1;
1049 }
1050
Philipp Maier87bd9be2017-08-22 16:35:41 +02001051 /* Note: Usually the remote remote port of the data we receive will be
1052 * the same as the remote port where we transmit outgoing RTP traffic
1053 * to (set by MDCX). We use this to check the origin of the data for
1054 * plausibility. */
1055 if (conn->end.rtp_port != addr->sin_port &&
1056 conn->end.rtcp_port != addr->sin_port) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001057 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
1058 "data from wrong source port: %d, ", ntohs(addr->sin_port));
Philipp Maierc3413882017-10-27 12:26:54 +02001059 LOGPC(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001060 "expected: %d for RTP or %d for RTCP\n",
1061 ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port));
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001062 LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001063 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001064 }
1065
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001066 return 0;
1067}
1068
Philipp Maier87bd9be2017-08-22 16:35:41 +02001069/* Check the if the destination address configuration of an RTP connection
1070 * makes sense */
1071static int check_rtp_destin(struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001072{
Philipp Maiere6df0e42018-05-29 14:03:06 +02001073 /* Note: it is legal to create a connection but never setting a port
1074 * and IP-address for outgoing data. */
1075 if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001076 LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
1077 "destination IP-address and rtp port is (not yet) known (%s:%u)\n",
1078 inet_ntoa(conn->end.addr), conn->end.rtp_port);
Philipp Maiere6df0e42018-05-29 14:03:06 +02001079 return -1;
1080 }
1081
Philipp Maier87bd9be2017-08-22 16:35:41 +02001082 if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001083 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
1084 "destination IP-address is invalid (%s:%u)\n",
1085 inet_ntoa(conn->end.addr), conn->end.rtp_port);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001086 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001087 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001088
1089 if (conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001090 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
1091 "destination rtp port is invalid (%s:%u)\n",
1092 inet_ntoa(conn->end.addr), conn->end.rtp_port);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001093 return -1;
1094 }
1095
1096 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001097}
1098
Philipp Maier4dba7692018-08-03 12:20:52 +02001099/* Do some basic checks to make sure that the RTCP packets we are going to
1100 * process are not complete garbage */
1101static int check_rtcp(char *buf, unsigned int buf_size)
1102{
1103 struct rtcp_hdr *hdr;
1104 unsigned int len;
1105 uint8_t type;
1106
1107 /* RTPC packets that are just a header without data do not make
1108 * any sense. */
1109 if (buf_size < sizeof(struct rtcp_hdr))
1110 return -EINVAL;
1111
1112 /* Make sure that the length of the received packet does not exceed
1113 * the available buffer size */
1114 hdr = (struct rtcp_hdr *)buf;
1115 len = (osmo_ntohs(hdr->length) + 1) * 4;
1116 if (len > buf_size)
1117 return -EINVAL;
1118
1119 /* Make sure we accept only packets that have a proper packet type set
1120 * See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
1121 type = hdr->type;
1122 if ((type < 192 || type > 195) && (type < 200 || type > 213))
1123 return -EINVAL;
1124
1125 return 0;
1126}
1127
1128/* Do some basic checks to make sure that the RTP packets we are going to
1129 * process are not complete garbage */
1130static int check_rtp(char *buf, unsigned int buf_size)
1131{
1132 /* RTP packets that are just a header without data do not make
1133 * any sense. */
1134 if (buf_size < sizeof(struct rtp_hdr))
1135 return -EINVAL;
1136
1137 /* FIXME: Add more checks, the reason why we do not check more than
1138 * the length is because we currently handle IUUP packets as RTP
1139 * packets, so they must pass this check, if we weould be more
1140 * strict here, we would possibly break 3G. (see also FIXME note
1141 * below */
1142
1143 return 0;
1144}
1145
Philipp Maier87bd9be2017-08-22 16:35:41 +02001146/* Receive RTP data from a specified source connection and dispatch it to a
1147 * destination connection. */
1148static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
1149 unsigned int buf_size, struct osmo_fd *fd)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001150{
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001151 struct mgcp_endpoint *endp;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001152 struct mgcp_conn_rtp *conn;
Philipp Maier14b27a82020-06-02 20:15:30 +02001153 struct mgcp_trunk *trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001154 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001155
Philipp Maier87bd9be2017-08-22 16:35:41 +02001156 conn = (struct mgcp_conn_rtp*) fd->data;
1157 endp = conn->conn->endp;
Philipp Maier14b27a82020-06-02 20:15:30 +02001158 trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001159
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001160 LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001161
1162 rc = receive_from(endp, fd->fd, addr, buf, buf_size);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001163 if (rc <= 0)
1164 return -1;
Philipp Maier4dba7692018-08-03 12:20:52 +02001165
1166 /* FIXME: The way how we detect the protocol looks odd. We should look
1167 * into the packet header. Also we should introduce a packet type
1168 * MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
1169 * packets which is problematic. */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001170 *proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001171
Philipp Maier4dba7692018-08-03 12:20:52 +02001172 if (*proto == MGCP_PROTO_RTP) {
1173 if (check_rtp(buf, rc) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001174 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
1175 "invalid RTP packet received -- packet tossed\n");
Philipp Maier4dba7692018-08-03 12:20:52 +02001176 return -1;
1177 }
1178 } else if (*proto == MGCP_PROTO_RTCP) {
1179 if (check_rtcp(buf, rc) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001180 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
1181 "invalid RTCP packet received -- packet tossed\n");
Philipp Maier4dba7692018-08-03 12:20:52 +02001182 return -1;
1183 }
1184 }
1185
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001186 LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "");
Neels Hofmeyr56725632018-01-15 15:15:07 +01001187 LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +02001188 conn->conn->name, inet_ntoa(addr->sin_addr),
1189 ntohs(addr->sin_port));
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001190 LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001191
Philipp Maier87bd9be2017-08-22 16:35:41 +02001192 /* Check if the origin of the RTP packet seems plausible */
Philipp Maier14b27a82020-06-02 20:15:30 +02001193 if (trunk->rtp_accept_all == 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001194 if (check_rtp_origin(conn, addr) != 0)
1195 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001196 }
1197
Philipp Maier87bd9be2017-08-22 16:35:41 +02001198 /* Filter out dummy message */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001199 if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001200 LOGPCONN(conn->conn, DRTP, LOGL_NOTICE,
1201 "dummy message received\n");
1202 LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
1203 "packet tossed\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001204 return 0;
1205 }
1206
Philipp Maier87bd9be2017-08-22 16:35:41 +02001207 /* Increment RX statistics */
Harald Weltea48ff4a2020-03-08 14:45:08 +01001208 rtpconn_rate_ctr_inc(conn, endp, RTP_PACKETS_RX_CTR);
1209 rtpconn_rate_ctr_add(conn, endp, RTP_OCTETS_RX_CTR, rc);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001210
Philipp Maier87bd9be2017-08-22 16:35:41 +02001211 /* Forward a copy of the RTP data to a debug ip/port */
1212 forward_data(fd->fd, &conn->tap_in, buf, rc);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001213
Philipp Maier87bd9be2017-08-22 16:35:41 +02001214 return rc;
1215}
1216
1217/* Send RTP data. Possible options are standard RTP packet
1218 * transmission or trsmission via an osmux connection */
1219static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
1220 unsigned int buf_size,
1221 struct mgcp_conn_rtp *conn_src,
1222 struct mgcp_conn_rtp *conn_dst)
1223{
1224 struct mgcp_endpoint *endp;
1225 endp = conn_src->conn->endp;
1226
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001227 LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
1228 mgcp_conn_dump(conn_dst->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001229
1230 /* Before we try to deliver the packet, we check if the destination
1231 * port and IP-Address make sense at all. If not, we will be unable
1232 * to deliver the packet. */
1233 if (check_rtp_destin(conn_dst) != 0)
1234 return -1;
1235
1236 /* Depending on the RTP connection type, deliver the RTP packet to the
1237 * destination connection. */
1238 switch (conn_dst->type) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001239 case MGCP_RTP_DEFAULT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001240 LOGPENDP(endp, DRTP, LOGL_DEBUG,
1241 "endpoint type is MGCP_RTP_DEFAULT, "
1242 "using mgcp_send() to forward data directly\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001243 return mgcp_send(endp, proto == MGCP_PROTO_RTP,
1244 addr, buf, buf_size, conn_src, conn_dst);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001245 case MGCP_OSMUX_BSC_NAT:
Philipp Maier87bd9be2017-08-22 16:35:41 +02001246 case MGCP_OSMUX_BSC:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001247 LOGPENDP(endp, DRTP, LOGL_DEBUG,
1248 "endpoint type is MGCP_OSMUX_BSC_NAT, "
1249 "using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001250 return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001251 }
1252
Philipp Maier87bd9be2017-08-22 16:35:41 +02001253 /* If the data has not been handled/forwarded until here, it will
1254 * be discarded, this should not happen, normally the MGCP type
1255 * should be properly set */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001256 LOGPENDP(endp, DRTP, LOGL_ERROR, "bad MGCP type -- data discarded!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001257
1258 return -1;
1259}
1260
1261/*! dispatch incoming RTP packet to opposite RTP connection.
Philipp Maier0b79d212020-06-18 12:02:49 +02001262 * \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP).
1263 * \param[in] addr socket address where the RTP packet has been received from.
1264 * \param[in] buf buffer that hold the RTP payload.
1265 * \param[in] buf_size size data length of buf.
1266 * \param[in] conn originating connection.
1267 * \returns 0 on success, -1 on ERROR. */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001268int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
1269 unsigned int buf_size, struct mgcp_conn *conn)
1270{
1271 struct mgcp_conn *conn_dst;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001272
1273 /*! NOTE: This callback function implements the endpoint specific
Alexander Chemeris61cf9bb2020-05-11 18:13:53 +03001274 * dispatch behaviour of an rtp bridge/proxy endpoint. It is assumed
Philipp Maier87bd9be2017-08-22 16:35:41 +02001275 * that the endpoint will hold only two connections. This premise
1276 * is used to determine the opposite connection (it is always the
1277 * connection that is not the originating connection). Once the
1278 * destination connection is known the RTP packet is sent via
1279 * the destination connection. */
1280
Pau Espin Pedrol9aaaab62019-05-15 23:23:27 +02001281
1282 /* Check if the connection is in loopback mode, if yes, just send the
1283 * incoming data back to the origin */
1284 if (conn->mode == MGCP_CONN_LOOPBACK) {
1285 /* When we are in loopback mode, we loop back all incoming
1286 * packets back to their origin. We will use the originating
1287 * address data from the UDP packet header to patch the
1288 * outgoing address in connection on the fly */
1289 if (conn->u.rtp.end.rtp_port == 0) {
1290 conn->u.rtp.end.addr = addr->sin_addr;
1291 conn->u.rtp.end.rtp_port = addr->sin_port;
1292 }
1293 return mgcp_send_rtp(proto, addr, buf,
1294 buf_size, &conn->u.rtp, &conn->u.rtp);
1295 }
1296
Philipp Maier87bd9be2017-08-22 16:35:41 +02001297 /* Find a destination connection. */
1298 /* NOTE: This code path runs every time an RTP packet is received. The
1299 * function mgcp_find_dst_conn() we use to determine the detination
1300 * connection will iterate the connection list inside the endpoint.
1301 * Since list iterations are quite costly, we will figure out the
1302 * destination only once and use the optional private data pointer of
1303 * the connection to cache the destination connection pointer. */
1304 if (!conn->priv) {
1305 conn_dst = mgcp_find_dst_conn(conn);
1306 conn->priv = conn_dst;
1307 } else {
1308 conn_dst = (struct mgcp_conn *)conn->priv;
1309 }
1310
1311 /* There is no destination conn, stop here */
1312 if (!conn_dst) {
Alexander Chemerisebb9bf32020-05-11 18:14:31 +03001313 LOGPCONN(conn, DRTP, LOGL_DEBUG,
1314 "no connection to forward an incoming RTP packet to\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001315 return -1;
1316 }
1317
1318 /* The destination conn is not an RTP connection */
1319 if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001320 LOGPCONN(conn, DRTP, LOGL_ERROR,
1321 "unable to find suitable destination conn\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001322 return -1;
1323 }
1324
1325 /* Dispatch RTP packet to destination RTP connection */
1326 return mgcp_send_rtp(proto, addr, buf,
1327 buf_size, &conn->u.rtp, &conn_dst->u.rtp);
1328
1329}
1330
Philipp Maierdf5d2192018-01-24 11:39:32 +01001331/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
1332 * \param[in] endp Endpoint on which the connection resides.
Philipp Maier08eb9352020-06-18 11:55:35 +02001333 * \param[in] conn Connection that is about to be removed (ignored). */
Philipp Maierdf5d2192018-01-24 11:39:32 +01001334void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
1335{
1336 struct mgcp_conn *conn_cleanup;
1337
1338 /* In mgcp_dispatch_rtp_bridge_cb() we use conn->priv to cache the
1339 * pointer to the destination connection, so that we do not have
1340 * to go through the list every time an RTP packet arrives. To prevent
1341 * a use-after-free situation we invalidate this information for all
1342 * connections present when one connection is removed from the
1343 * endpoint. */
1344 llist_for_each_entry(conn_cleanup, &endp->conns, entry) {
1345 conn_cleanup->priv = NULL;
1346 }
1347}
1348
Philipp Maier87bd9be2017-08-22 16:35:41 +02001349/* Handle incoming RTP data from NET */
1350static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
1351{
1352 /* NOTE: This is a generic implementation. RTP data is received. In
1353 * case of loopback the data is just sent back to its origin. All
1354 * other cases implement endpoint specific behaviour (e.g. how is the
1355 * destination connection determined?). That specific behaviour is
1356 * implemented by the callback function that is called at the end of
1357 * the function */
1358
1359 struct mgcp_conn_rtp *conn_src;
1360 struct mgcp_endpoint *endp;
1361 struct sockaddr_in addr;
1362
1363 char buf[RTP_BUF_SIZE];
1364 int proto;
Philipp Maierb9694552017-11-08 16:53:57 +01001365 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001366
1367 conn_src = (struct mgcp_conn_rtp *)fd->data;
1368 OSMO_ASSERT(conn_src);
1369 endp = conn_src->conn->endp;
1370 OSMO_ASSERT(endp);
1371
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001372 LOGPENDP(endp, DRTP, LOGL_DEBUG, "source conn:%s\n",
1373 mgcp_conn_dump(conn_src->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001374
1375 /* Receive packet */
Philipp Maierb9694552017-11-08 16:53:57 +01001376 len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
1377 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001378 return -1;
1379
Oliver Smithe36b7752019-01-22 16:31:36 +01001380 mgcp_conn_watchdog_kick(conn_src->conn);
1381
Philipp Maier228e5912019-03-05 13:56:59 +01001382 /* If AMR is configured for the ingress connection a conversion of the
1383 * framing mode (octet-aligned vs. bandwith-efficient is explicitly
1384 * define, then we check if the incoming payload matches that
1385 * expectation. */
1386 if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
Neels Hofmeyr7c6dd3c2019-08-20 03:06:17 +02001387 int oa = amr_oa_check(buf, len);
1388 if (oa < 0)
1389 return -1;
1390 if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned)
Philipp Maier228e5912019-03-05 13:56:59 +01001391 return -1;
1392 }
1393
Philipp Maier87bd9be2017-08-22 16:35:41 +02001394 /* Execute endpoint specific implementation that handles the
1395 * dispatching of the RTP data */
Philipp Maierb9694552017-11-08 16:53:57 +01001396 return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001397 conn_src->conn);
1398}
1399
1400/*! set IP Type of Service parameter.
Philipp Maier0b79d212020-06-18 12:02:49 +02001401 * \param[in] fd associated file descriptor.
1402 * \param[in] tos dscp value.
1403 * \returns 0 on success, -1 on ERROR. */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001404int mgcp_set_ip_tos(int fd, int tos)
1405{
1406 int ret;
1407 ret = setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
1408
1409 if (ret < 0)
1410 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001411 return 0;
1412}
1413
Philipp Maier87bd9be2017-08-22 16:35:41 +02001414/*! bind RTP port to osmo_fd.
Philipp Maier0b79d212020-06-18 12:02:49 +02001415 * \param[in] source_addr source (local) address to bind on.
1416 * \param[in] fd associated file descriptor.
1417 * \param[in] port to bind on.
1418 * \returns 0 on success, -1 on ERROR. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001419int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
1420{
Harald Welte8890dfa2017-11-17 15:09:30 +01001421 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001422
Harald Welte8890dfa2017-11-17 15:09:30 +01001423 rc = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, source_addr, port,
1424 NULL, 0, OSMO_SOCK_F_BIND);
1425 if (rc < 0) {
Philipp Maierc3413882017-10-27 12:26:54 +02001426 LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +02001427 source_addr, port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001428 return -1;
1429 }
Harald Welte8890dfa2017-11-17 15:09:30 +01001430 fd->fd = rc;
1431 LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001432
1433 return 0;
1434}
1435
Philipp Maier87bd9be2017-08-22 16:35:41 +02001436/* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001437static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001438 struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001439{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001440 /* NOTE: The port that is used for RTCP is the RTP port incremented by one
1441 * (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
1442
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001443 if (mgcp_create_bind(source_addr, &rtp_end->rtp,
1444 rtp_end->local_port) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001445 LOGPENDP(endp, DRTP, LOGL_ERROR,
1446 "failed to create RTP port: %s:%d\n",
1447 source_addr, rtp_end->local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001448 goto cleanup0;
1449 }
1450
1451 if (mgcp_create_bind(source_addr, &rtp_end->rtcp,
1452 rtp_end->local_port + 1) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001453 LOGPENDP(endp, DRTP, LOGL_ERROR,
1454 "failed to create RTCP port: %s:%d\n",
1455 source_addr, rtp_end->local_port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001456 goto cleanup1;
1457 }
1458
Philipp Maier87bd9be2017-08-22 16:35:41 +02001459 /* Set Type of Service (DSCP-Value) as configured via VTY */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001460 mgcp_set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
1461 mgcp_set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp);
1462
Pau Espin Pedrola7152e02020-05-09 19:15:50 +02001463 rtp_end->rtp.when = OSMO_FD_READ;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001464 if (osmo_fd_register(&rtp_end->rtp) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001465 LOGPENDP(endp, DRTP, LOGL_ERROR,
1466 "failed to register RTP port %d\n",
1467 rtp_end->local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001468 goto cleanup2;
1469 }
1470
Pau Espin Pedrola7152e02020-05-09 19:15:50 +02001471 rtp_end->rtcp.when = OSMO_FD_READ;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001472 if (osmo_fd_register(&rtp_end->rtcp) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001473 LOGPENDP(endp, DRTP, LOGL_ERROR,
1474 "failed to register RTCP port %d\n",
1475 rtp_end->local_port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001476 goto cleanup3;
1477 }
1478
1479 return 0;
1480
1481cleanup3:
1482 osmo_fd_unregister(&rtp_end->rtp);
1483cleanup2:
1484 close(rtp_end->rtcp.fd);
1485 rtp_end->rtcp.fd = -1;
1486cleanup1:
1487 close(rtp_end->rtp.fd);
1488 rtp_end->rtp.fd = -1;
1489cleanup0:
1490 return -1;
1491}
1492
Philipp Maier87bd9be2017-08-22 16:35:41 +02001493/*! bind RTP port to endpoint/connection.
Philipp Maier0b79d212020-06-18 12:02:49 +02001494 * \param[in] endp endpoint that holds the RTP connection.
1495 * \param[in] rtp_port port number to bind on.
1496 * \param[in] conn associated RTP connection.
1497 * \returns 0 on success, -1 on ERROR. */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001498int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
1499 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001500{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001501 char name[512];
1502 struct mgcp_rtp_end *end;
Philipp Maier1cb1e382017-11-02 17:16:04 +01001503 char local_ip_addr[INET_ADDRSTRLEN];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001504
Philipp Maier01d24a32017-11-21 17:26:09 +01001505 snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001506 end = &conn->end;
1507
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001508 if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001509 LOGPENDP(endp, DRTP, LOGL_ERROR, "%u was already bound on conn:%s\n",
1510 rtp_port, mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001511
1512 /* Double bindings should never occour! Since we always allocate
1513 * connections dynamically and free them when they are not
1514 * needed anymore, there must be no previous binding leftover.
1515 * Should there be a connection bound twice, we have a serious
1516 * problem and must exit immediately! */
1517 OSMO_ASSERT(false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001518 }
1519
1520 end->local_port = rtp_port;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001521 end->rtp.cb = rtp_data_net;
1522 end->rtp.data = conn;
1523 end->rtcp.data = conn;
1524 end->rtcp.cb = rtp_data_net;
1525
Philipp Maier1cb1e382017-11-02 17:16:04 +01001526 mgcp_get_local_addr(local_ip_addr, conn);
1527
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001528 return bind_rtp(endp->cfg, local_ip_addr, end, endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001529}
1530
Philipp Maier87bd9be2017-08-22 16:35:41 +02001531/*! free allocated RTP and RTCP ports.
1532 * \param[in] end RTP end */
1533void mgcp_free_rtp_port(struct mgcp_rtp_end *end)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001534{
1535 if (end->rtp.fd != -1) {
1536 close(end->rtp.fd);
1537 end->rtp.fd = -1;
1538 osmo_fd_unregister(&end->rtp);
1539 }
1540
1541 if (end->rtcp.fd != -1) {
1542 close(end->rtcp.fd);
1543 end->rtcp.fd = -1;
1544 osmo_fd_unregister(&end->rtcp);
1545 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001546}