blob: 3d52d07e2b48d1fb64de48b671b8b45455e3d9eb [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 <sys/socket.h>
31#include <arpa/inet.h>
32
33#include <osmocom/core/msgb.h>
34#include <osmocom/core/select.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020035#include <osmocom/netif/rtp.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020036#include <osmocom/mgcp/mgcp.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020037#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020038#include <osmocom/mgcp/mgcp_internal.h>
39#include <osmocom/mgcp/mgcp_stat.h>
40#include <osmocom/mgcp/osmux.h>
41#include <osmocom/mgcp/mgcp_conn.h>
42#include <osmocom/mgcp/mgcp_ep.h>
Philipp Maierc3413882017-10-27 12:26:54 +020043#include <osmocom/mgcp/debug.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020044
45#define RTP_SEQ_MOD (1 << 16)
46#define RTP_MAX_DROPOUT 3000
47#define RTP_MAX_MISORDER 100
48#define RTP_BUF_SIZE 4096
49
50enum {
51 MGCP_PROTO_RTP,
52 MGCP_PROTO_RTCP,
53};
54
Philipp Maier87bd9be2017-08-22 16:35:41 +020055/* This does not need to be a precision timestamp and
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020056 * is allowed to wrap quite fast. The returned value is
Philipp Maier87bd9be2017-08-22 16:35:41 +020057 * 1/codec_rate seconds. */
58static uint32_t get_current_ts(unsigned codec_rate)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020059{
60 struct timespec tp;
61 uint64_t ret;
62
Philipp Maier87bd9be2017-08-22 16:35:41 +020063 if (!codec_rate)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020064 return 0;
65
66 memset(&tp, 0, sizeof(tp));
67 if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
Philipp Maierc3413882017-10-27 12:26:54 +020068 LOGP(DRTP, LOGL_NOTICE, "Getting the clock failed.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020069
70 /* convert it to 1/unit seconds */
71 ret = tp.tv_sec;
Philipp Maier87bd9be2017-08-22 16:35:41 +020072 ret *= codec_rate;
73 ret += (int64_t) tp.tv_nsec * codec_rate / 1000 / 1000 / 1000;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020074
75 return ret;
76}
77
Philipp Maier87bd9be2017-08-22 16:35:41 +020078/*! send udp packet.
79 * \param[in] fd associated file descriptor
80 * \param[in] addr destination ip-address
81 * \param[in] port destination UDP port
82 * \param[in] buf buffer that holds the data to be send
83 * \param[in] len length of the data to be sent
84 * \returns bytes sent, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020085int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
86{
87 struct sockaddr_in out;
Philipp Maier87bd9be2017-08-22 16:35:41 +020088
Philipp Maierc3413882017-10-27 12:26:54 +020089 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +020090 "sending %i bytes length packet to %s:%u ...\n",
91 len, inet_ntoa(*addr), ntohs(port));
92
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020093 out.sin_family = AF_INET;
94 out.sin_port = port;
95 memcpy(&out.sin_addr, addr, sizeof(*addr));
96
97 return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
98}
99
Philipp Maier87bd9be2017-08-22 16:35:41 +0200100/*! send RTP dummy packet (to keep NAT connection open).
101 * \param[in] endp mcgp endpoint that holds the RTP connection
102 * \param[in] conn associated RTP connection
103 * \returns bytes sent, -1 on error */
104int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200105{
106 static char buf[] = { MGCP_DUMMY_LOAD };
107 int rc;
108 int was_rtcp = 0;
109
Philipp Maier87bd9be2017-08-22 16:35:41 +0200110 OSMO_ASSERT(endp);
111 OSMO_ASSERT(conn);
112
Philipp Maierc3413882017-10-27 12:26:54 +0200113 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200114 "endpoint:%x sending dummy packet...\n", ENDPOINT_NUMBER(endp));
Philipp Maierc3413882017-10-27 12:26:54 +0200115 LOGP(DRTP, LOGL_DEBUG, "endpoint:%x conn:%s\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200116 ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
117
118 rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
119 conn->end.rtp_port, buf, 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200120
121 if (rc == -1)
122 goto failed;
123
124 if (endp->tcfg->omit_rtcp)
125 return rc;
126
127 was_rtcp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200128 rc = mgcp_udp_send(conn->end.rtcp.fd, &conn->end.addr,
129 conn->end.rtcp_port, buf, 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200130
131 if (rc >= 0)
132 return rc;
133
134failed:
Philipp Maierc3413882017-10-27 12:26:54 +0200135 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200136 "endpoint:%x Failed to send dummy %s packet.\n",
137 ENDPOINT_NUMBER(endp), was_rtcp ? "RTCP" : "RTP");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200138
139 return -1;
140}
141
Philipp Maier87bd9be2017-08-22 16:35:41 +0200142/* Compute timestamp alignment error */
143static int32_t ts_alignment_error(struct mgcp_rtp_stream_state *sstate,
144 int ptime, uint32_t timestamp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200145{
146 int32_t timestamp_delta;
147
148 if (ptime == 0)
149 return 0;
150
151 /* Align according to: T - Tlast = k * Tptime */
152 timestamp_delta = timestamp - sstate->last_timestamp;
153
154 return timestamp_delta % ptime;
155}
156
Philipp Maier87bd9be2017-08-22 16:35:41 +0200157/* Check timestamp and sequence number for plausibility */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200158static int check_rtp_timestamp(struct mgcp_endpoint *endp,
159 struct mgcp_rtp_state *state,
160 struct mgcp_rtp_stream_state *sstate,
161 struct mgcp_rtp_end *rtp_end,
162 struct sockaddr_in *addr,
163 uint16_t seq, uint32_t timestamp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200164 const char *text, int32_t * tsdelta_out)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200165{
166 int32_t tsdelta;
167 int32_t timestamp_error;
168
169 /* Not fully intialized, skip */
170 if (sstate->last_tsdelta == 0 && timestamp == sstate->last_timestamp)
171 return 0;
172
173 if (seq == sstate->last_seq) {
174 if (timestamp != sstate->last_timestamp) {
175 sstate->err_ts_counter += 1;
Philipp Maierc3413882017-10-27 12:26:54 +0200176 LOGP(DRTP, LOGL_ERROR,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200177 "The %s timestamp delta is != 0 but the sequence "
178 "number %d is the same, "
179 "TS offset: %d, SeqNo offset: %d "
180 "on 0x%x SSRC: %u timestamp: %u "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200181 "from %s:%d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200182 text, seq,
183 state->timestamp_offset, state->seq_offset,
184 ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200185 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200186 }
187 return 0;
188 }
189
190 tsdelta =
Philipp Maier87bd9be2017-08-22 16:35:41 +0200191 (int32_t)(timestamp - sstate->last_timestamp) /
192 (int16_t)(seq - sstate->last_seq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200193
194 if (tsdelta == 0) {
195 /* Don't update *tsdelta_out */
Philipp Maierc3413882017-10-27 12:26:54 +0200196 LOGP(DRTP, LOGL_NOTICE,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200197 "The %s timestamp delta is %d "
198 "on 0x%x SSRC: %u timestamp: %u "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200199 "from %s:%d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200200 text, tsdelta,
201 ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200202 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200203
204 return 0;
205 }
206
207 if (sstate->last_tsdelta != tsdelta) {
208 if (sstate->last_tsdelta) {
Philipp Maierc3413882017-10-27 12:26:54 +0200209 LOGP(DRTP, LOGL_INFO,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200210 "The %s timestamp delta changes from %d to %d "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200211 "on 0x%x SSRC: %u timestamp: %u from %s:%d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200212 text, sstate->last_tsdelta, tsdelta,
213 ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200214 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200215 }
216 }
217
218 if (tsdelta_out)
219 *tsdelta_out = tsdelta;
220
221 timestamp_error =
Philipp Maier87bd9be2017-08-22 16:35:41 +0200222 ts_alignment_error(sstate, state->packet_duration, timestamp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200223
224 if (timestamp_error) {
225 sstate->err_ts_counter += 1;
Philipp Maierc3413882017-10-27 12:26:54 +0200226 LOGP(DRTP, LOGL_NOTICE,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200227 "The %s timestamp has an alignment error of %d "
228 "on 0x%x SSRC: %u "
229 "SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200230 "from %s:%d. ptime: %d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200231 text, timestamp_error,
232 ENDPOINT_NUMBER(endp), sstate->ssrc,
233 (int16_t)(seq - sstate->last_seq),
234 (int32_t)(timestamp - sstate->last_timestamp),
235 tsdelta,
236 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200237 state->packet_duration);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200238 }
239 return 1;
240}
241
242/* Set the timestamp offset according to the packet duration. */
243static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
244 struct mgcp_rtp_state *state,
245 struct mgcp_rtp_end *rtp_end,
246 struct sockaddr_in *addr,
247 int16_t delta_seq, uint32_t in_timestamp)
248{
249 int32_t tsdelta = state->packet_duration;
250 int timestamp_offset;
251 uint32_t out_timestamp;
252
253 if (tsdelta == 0) {
254 tsdelta = state->out_stream.last_tsdelta;
255 if (tsdelta != 0) {
Philipp Maierc3413882017-10-27 12:26:54 +0200256 LOGP(DRTP, LOGL_NOTICE,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200257 "A fixed packet duration is not available on 0x%x, "
258 "using last output timestamp delta instead: %d "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200259 "from %s:%d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200260 ENDPOINT_NUMBER(endp), tsdelta,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200261 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200262 } else {
263 tsdelta = rtp_end->codec.rate * 20 / 1000;
Philipp Maierc3413882017-10-27 12:26:54 +0200264 LOGP(DRTP, LOGL_NOTICE,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200265 "Fixed packet duration and last timestamp delta "
266 "are not available on 0x%x, "
267 "using fixed 20ms instead: %d "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200268 "from %s:%d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200269 ENDPOINT_NUMBER(endp), tsdelta,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200270 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200271 }
272 }
273
274 out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta;
275 timestamp_offset = out_timestamp - in_timestamp;
276
277 if (state->timestamp_offset != timestamp_offset) {
278 state->timestamp_offset = timestamp_offset;
279
Philipp Maierc3413882017-10-27 12:26:54 +0200280 LOGP(DRTP, LOGL_NOTICE,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200281 "Timestamp offset change on 0x%x SSRC: %u "
282 "SeqNo delta: %d, TS offset: %d, "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200283 "from %s:%d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200284 ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
285 delta_seq, state->timestamp_offset,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200286 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200287 }
288
289 return timestamp_offset;
290}
291
292/* Set the timestamp offset according to the packet duration. */
293static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
294 struct mgcp_rtp_state *state,
295 struct mgcp_rtp_end *rtp_end,
296 struct sockaddr_in *addr,
297 uint32_t timestamp)
298{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200299 int ts_error = 0;
300 int ts_check = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200301 int ptime = state->packet_duration;
302
303 /* Align according to: T + Toffs - Tlast = k * Tptime */
304
Philipp Maier87bd9be2017-08-22 16:35:41 +0200305 ts_error = ts_alignment_error(&state->out_stream, ptime,
306 timestamp + state->timestamp_offset);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200307
Philipp Maier87bd9be2017-08-22 16:35:41 +0200308 /* If there is an alignment error, we have to compensate it */
309 if (ts_error) {
310 state->timestamp_offset += ptime - ts_error;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200311
Philipp Maierc3413882017-10-27 12:26:54 +0200312 LOGP(DRTP, LOGL_NOTICE,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200313 "Corrected timestamp alignment error of %d on 0x%x SSRC: %u "
314 "new TS offset: %d, "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200315 "from %s:%d\n",
316 ts_error,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200317 ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
318 state->timestamp_offset, inet_ntoa(addr->sin_addr),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200319 ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200320 }
321
Philipp Maier87bd9be2017-08-22 16:35:41 +0200322 /* Check we really managed to compensate the timestamp
323 * offset. There should not be any remaining error, failing
324 * here would point to a serous problem with the alingnment
325 * error computation fuction */
326 ts_check = ts_alignment_error(&state->out_stream, ptime,
327 timestamp + state->timestamp_offset);
328 OSMO_ASSERT(ts_check == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200329
Philipp Maier87bd9be2017-08-22 16:35:41 +0200330 /* Return alignment error before compensation */
331 return ts_error;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200332}
333
Philipp Maier87bd9be2017-08-22 16:35:41 +0200334/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
335 * \param[in] associated endpoint
336 * \param[in] destination RTP end
337 * \param[in,out] pointer to buffer with voice data
338 * \param[in] voice data length
339 * \param[in] maxmimum size of caller provided voice data buffer
340 * \returns ignores input parameters, return always 0 */
341int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
342 struct mgcp_rtp_end *dst_end,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200343 char *data, int *len, int buf_size)
344{
Philipp Maierc3413882017-10-27 12:26:54 +0200345 LOGP(DRTP, LOGL_DEBUG, "endpoint:%x transcoding disabled\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200346 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200347 return 0;
348}
349
Philipp Maier87bd9be2017-08-22 16:35:41 +0200350/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
351 * \param[in] associated endpoint
352 * \param[in] destination RTP end
353 * \param[in] source RTP end
354 * \returns ignores input parameters, return always 0 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200355int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
356 struct mgcp_rtp_end *dst_end,
357 struct mgcp_rtp_end *src_end)
358{
Philipp Maierc3413882017-10-27 12:26:54 +0200359 LOGP(DRTP, LOGL_DEBUG, "endpoint:%x transcoding disabled\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200360 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200361 return 0;
362}
363
364void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
365 int *payload_type,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200366 const char **audio_name,
367 const char **fmtp_extra,
368 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200369{
Philipp Maierc3413882017-10-27 12:26:54 +0200370 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200371 "endpoint:%x conn:%s using format defaults\n",
372 ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
373
374 *payload_type = conn->end.codec.payload_type;
375 *audio_name = conn->end.codec.audio_name;
376 *fmtp_extra = conn->end.fmtp_extra;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200377}
378
Philipp Maier87bd9be2017-08-22 16:35:41 +0200379void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
380 struct mgcp_rtp_state *state, const uint16_t seq,
381 const int32_t transit, const uint32_t ssrc)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200382{
383 int32_t d;
384
385 /* initialize or re-initialize */
386 if (!state->stats_initialized || state->stats_ssrc != ssrc) {
387 state->stats_initialized = 1;
388 state->stats_base_seq = seq;
389 state->stats_max_seq = seq - 1;
390 state->stats_ssrc = ssrc;
391 state->stats_jitter = 0;
392 state->stats_transit = transit;
393 state->stats_cycles = 0;
394 } else {
395 uint16_t udelta;
396
Philipp Maier87bd9be2017-08-22 16:35:41 +0200397 /* The below takes the shape of the validation of
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200398 * Appendix A. Check if there is something weird with
399 * the sequence number, otherwise check for a wrap
400 * around in the sequence number.
401 * It can't wrap during the initialization so let's
402 * skip it here. The Appendix A probably doesn't have
Philipp Maier87bd9be2017-08-22 16:35:41 +0200403 * this issue because of the probation. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200404 udelta = seq - state->stats_max_seq;
405 if (udelta < RTP_MAX_DROPOUT) {
406 if (seq < state->stats_max_seq)
407 state->stats_cycles += RTP_SEQ_MOD;
408 } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
Philipp Maierc3413882017-10-27 12:26:54 +0200409 LOGP(DRTP, LOGL_NOTICE,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200410 "RTP seqno made a very large jump on 0x%x delta: %u\n",
411 ENDPOINT_NUMBER(endp), udelta);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200412 }
413 }
414
Philipp Maier87bd9be2017-08-22 16:35:41 +0200415 /* Calculate the jitter between the two packages. The TS should be
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200416 * taken closer to the read function. This was taken from the
417 * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate
Philipp Maier87bd9be2017-08-22 16:35:41 +0200418 * resolution. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200419 d = transit - state->stats_transit;
420 state->stats_transit = transit;
421 if (d < 0)
422 d = -d;
423 state->stats_jitter += d - ((state->stats_jitter + 8) >> 4);
424 state->stats_max_seq = seq;
425}
426
Philipp Maier87bd9be2017-08-22 16:35:41 +0200427/* The RFC 3550 Appendix A assumes there are multiple sources but
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200428 * some of the supported endpoints (e.g. the nanoBTS) can only handle
429 * one source and this code will patch RTP header to appear as if there
430 * is only one source.
431 * There is also no probation period for new sources. Every RTP header
Philipp Maier87bd9be2017-08-22 16:35:41 +0200432 * we receive will be seen as a switch in streams. */
433void mgcp_patch_and_count(struct mgcp_endpoint *endp,
434 struct mgcp_rtp_state *state,
435 struct mgcp_rtp_end *rtp_end,
436 struct sockaddr_in *addr, char *data, int len)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200437{
438 uint32_t arrival_time;
439 int32_t transit;
440 uint16_t seq;
441 uint32_t timestamp, ssrc;
442 struct rtp_hdr *rtp_hdr;
443 int payload = rtp_end->codec.payload_type;
444
445 if (len < sizeof(*rtp_hdr))
446 return;
447
Philipp Maier87bd9be2017-08-22 16:35:41 +0200448 rtp_hdr = (struct rtp_hdr *)data;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200449 seq = ntohs(rtp_hdr->sequence);
450 timestamp = ntohl(rtp_hdr->timestamp);
451 arrival_time = get_current_ts(rtp_end->codec.rate);
452 ssrc = ntohl(rtp_hdr->ssrc);
453 transit = arrival_time - timestamp;
454
455 mgcp_rtp_annex_count(endp, state, seq, transit, ssrc);
456
457 if (!state->initialized) {
458 state->initialized = 1;
459 state->in_stream.last_seq = seq - 1;
460 state->in_stream.ssrc = state->orig_ssrc = ssrc;
461 state->in_stream.last_tsdelta = 0;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200462 state->packet_duration =
463 mgcp_rtp_packet_duration(endp, rtp_end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200464 state->out_stream = state->in_stream;
465 state->out_stream.last_timestamp = timestamp;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200466 state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
Philipp Maierc3413882017-10-27 12:26:54 +0200467 LOGP(DRTP, LOGL_INFO,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200468 "endpoint:%x initializing stream, SSRC: %u timestamp: %u "
469 "pkt-duration: %d, from %s:%d\n",
470 ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
471 state->seq_offset, state->packet_duration,
472 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200473 if (state->packet_duration == 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200474 state->packet_duration =
475 rtp_end->codec.rate * 20 / 1000;
Philipp Maierc3413882017-10-27 12:26:54 +0200476 LOGP(DRTP, LOGL_NOTICE,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200477 "endpoint:%x fixed packet duration is not available, "
478 "using fixed 20ms instead: %d from %s:%d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200479 ENDPOINT_NUMBER(endp), state->packet_duration,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200480 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200481 }
482 } else if (state->in_stream.ssrc != ssrc) {
Philipp Maierc3413882017-10-27 12:26:54 +0200483 LOGP(DRTP, LOGL_NOTICE,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200484 "endpoint:%x SSRC changed: %u -> %u "
485 "from %s:%d\n",
486 ENDPOINT_NUMBER(endp),
487 state->in_stream.ssrc, rtp_hdr->ssrc,
488 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200489
490 state->in_stream.ssrc = ssrc;
491 if (rtp_end->force_constant_ssrc) {
492 int16_t delta_seq;
493
494 /* Always increment seqno by 1 */
495 state->seq_offset =
Philipp Maier87bd9be2017-08-22 16:35:41 +0200496 (state->out_stream.last_seq + 1) - seq;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200497
498 /* Estimate number of packets that would have been sent */
499 delta_seq =
Philipp Maier87bd9be2017-08-22 16:35:41 +0200500 (arrival_time - state->in_stream.last_arrival_time
501 + state->packet_duration / 2) /
502 state->packet_duration;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200503
504 adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
505 delta_seq, timestamp);
506
507 state->patch_ssrc = 1;
508 ssrc = state->orig_ssrc;
509 if (rtp_end->force_constant_ssrc != -1)
510 rtp_end->force_constant_ssrc -= 1;
511
Philipp Maierc3413882017-10-27 12:26:54 +0200512 LOGP(DRTP, LOGL_NOTICE,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200513 "endpoint:%x SSRC patching enabled, SSRC: %u "
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200514 "SeqNo offset: %d, TS offset: %d "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200515 "from %s:%d\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200516 ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
517 state->seq_offset, state->timestamp_offset,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200518 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200519 }
520
521 state->in_stream.last_tsdelta = 0;
522 } else {
523 /* Compute current per-packet timestamp delta */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200524 check_rtp_timestamp(endp, state, &state->in_stream, rtp_end,
525 addr, seq, timestamp, "input",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200526 &state->in_stream.last_tsdelta);
527
528 if (state->patch_ssrc)
529 ssrc = state->orig_ssrc;
530 }
531
532 /* Save before patching */
533 state->in_stream.last_timestamp = timestamp;
534 state->in_stream.last_seq = seq;
535 state->in_stream.last_arrival_time = arrival_time;
536
537 if (rtp_end->force_aligned_timing &&
538 state->out_stream.ssrc == ssrc && state->packet_duration)
539 /* Align the timestamp offset */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200540 align_rtp_timestamp_offset(endp, state, rtp_end, addr,
541 timestamp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200542
543 /* Store the updated SSRC back to the packet */
544 if (state->patch_ssrc)
545 rtp_hdr->ssrc = htonl(ssrc);
546
547 /* Apply the offset and store it back to the packet.
548 * This won't change anything if the offset is 0, so the conditional is
549 * omitted. */
550 seq += state->seq_offset;
551 rtp_hdr->sequence = htons(seq);
552 timestamp += state->timestamp_offset;
553 rtp_hdr->timestamp = htonl(timestamp);
554
555 /* Check again, whether the timestamps are still valid */
556 if (state->out_stream.ssrc == ssrc)
557 check_rtp_timestamp(endp, state, &state->out_stream, rtp_end,
558 addr, seq, timestamp, "output",
559 &state->out_stream.last_tsdelta);
560
561 /* Save output values */
562 state->out_stream.last_seq = seq;
563 state->out_stream.last_timestamp = timestamp;
564 state->out_stream.ssrc = ssrc;
565
566 if (payload < 0)
567 return;
568
569#if 0
Philipp Maierc3413882017-10-27 12:26:54 +0200570 DEBUGP(DRTP,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200571 "endpoint:%x payload hdr payload %u -> endp payload %u\n",
572 ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200573 rtp_hdr->payload_type = payload;
574#endif
575}
576
Philipp Maier87bd9be2017-08-22 16:35:41 +0200577/* Forward data to a debug tap. This is debug function that is intended for
578 * debugging the voice traffic with tools like gstreamer */
579static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
580 int len)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200581{
582 if (!tap->enabled)
583 return 0;
584
585 return sendto(fd, buf, len, 0,
586 (struct sockaddr *)&tap->forward, sizeof(tap->forward));
587}
588
Philipp Maier87bd9be2017-08-22 16:35:41 +0200589/*! Send RTP/RTCP data to a specified destination connection.
590 * \param[in] endp associated endpoint (for configuration, logging)
591 * \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP
592 * \param[in] spoofed source address (set to NULL to disable)
593 * \param[in] buf buffer that contains the RTP/RTCP data
594 * \param[in] len length of the buffer that contains the RTP/RTCP data
595 * \param[in] conn_src associated source connection
596 * \param[in] conn_dst associated destination connection
597 * \returns 0 on success, -1 on ERROR */
598int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
599 char *buf, int len, struct mgcp_conn_rtp *conn_src,
600 struct mgcp_conn_rtp *conn_dst)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200601{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200602 /*! When no destination connection is available (e.g. when only one
603 * connection in loopback mode exists), then the source connection
604 * shall be specified as destination connection */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200605
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200606 struct mgcp_trunk_config *tcfg = endp->tcfg;
607 struct mgcp_rtp_end *rtp_end;
608 struct mgcp_rtp_state *rtp_state;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200609 char *dest_name;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200610
Philipp Maier87bd9be2017-08-22 16:35:41 +0200611 OSMO_ASSERT(conn_src);
612 OSMO_ASSERT(conn_dst);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200613
Philipp Maier87bd9be2017-08-22 16:35:41 +0200614 if (is_rtp) {
Philipp Maierc3413882017-10-27 12:26:54 +0200615 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200616 "endpoint:%x delivering RTP packet...\n",
617 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200618 } else {
Philipp Maierc3413882017-10-27 12:26:54 +0200619 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200620 "endpoint:%x delivering RTCP packet...\n",
621 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200622 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200623
Philipp Maierc3413882017-10-27 12:26:54 +0200624 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200625 "endpoint:%x loop:%d, mode:%d ",
626 ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode);
627 if (conn_src->conn->mode == MGCP_CONN_LOOPBACK)
Philipp Maierc3413882017-10-27 12:26:54 +0200628 LOGPC(DRTP, LOGL_DEBUG, "(loopback)\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200629 else
Philipp Maierc3413882017-10-27 12:26:54 +0200630 LOGPC(DRTP, LOGL_DEBUG, "\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200631
Philipp Maier87bd9be2017-08-22 16:35:41 +0200632 /* Note: In case of loopback configuration, both, the source and the
633 * destination will point to the same connection. */
634 rtp_end = &conn_dst->end;
635 rtp_state = &conn_src->state;
636 dest_name = conn_dst->conn->name;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200637
638 if (!rtp_end->output_enabled) {
639 rtp_end->dropped_packets += 1;
Philipp Maierc3413882017-10-27 12:26:54 +0200640 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200641 "endpoint:%x output disabled, drop to %s %s "
642 "rtp_port:%u rtcp_port:%u\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200643 ENDPOINT_NUMBER(endp),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200644 dest_name,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200645 inet_ntoa(rtp_end->addr),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200646 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200647 );
648 } else if (is_rtp) {
649 int cont;
650 int nbytes = 0;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200651 int buflen = len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200652 do {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200653 /* Run transcoder */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200654 cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200655 buf, &buflen,
656 RTP_BUF_SIZE);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200657 if (cont < 0)
658 break;
659
Philipp Maier87bd9be2017-08-22 16:35:41 +0200660 if (addr)
661 mgcp_patch_and_count(endp, rtp_state, rtp_end,
662 addr, buf, buflen);
Philipp Maierc3413882017-10-27 12:26:54 +0200663 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200664 "endpoint:%x process/send to %s %s "
665 "rtp_port:%u rtcp_port:%u\n",
666 ENDPOINT_NUMBER(endp), dest_name,
667 inet_ntoa(rtp_end->addr), ntohs(rtp_end->rtp_port),
668 ntohs(rtp_end->rtcp_port)
669 );
670
671 /* Forward a copy of the RTP data to a debug ip/port */
672 forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
673 buf, buflen);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200674
675 /* FIXME: HACK HACK HACK. See OS#2459.
676 * The ip.access nano3G needs the first RTP payload's first two bytes to read hex
677 * 'e400', or it will reject the RAB assignment. It seems to not harm other femto
678 * cells (as long as we patch only the first RTP payload in each stream).
679 */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200680 if (!rtp_state->patched_first_rtp_payload) {
681 uint8_t *data = (uint8_t *) & buf[12];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200682 data[0] = 0xe4;
683 data[1] = 0x00;
684 rtp_state->patched_first_rtp_payload = true;
685 }
686
Philipp Maier87bd9be2017-08-22 16:35:41 +0200687 len = mgcp_udp_send(rtp_end->rtp.fd,
688 &rtp_end->addr,
689 rtp_end->rtp_port, buf, buflen);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200690
Philipp Maier87bd9be2017-08-22 16:35:41 +0200691 if (len <= 0)
692 return len;
693
694 conn_dst->end.packets_tx += 1;
695 conn_dst->end.octets_tx += len;
696
697 nbytes += len;
698 buflen = cont;
699 } while (buflen > 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200700 return nbytes;
701 } else if (!tcfg->omit_rtcp) {
Philipp Maierc3413882017-10-27 12:26:54 +0200702 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200703 "endpoint:%x send to %s %s rtp_port:%u rtcp_port:%u\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200704 ENDPOINT_NUMBER(endp),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200705 dest_name,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200706 inet_ntoa(rtp_end->addr),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200707 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200708 );
709
Philipp Maier87bd9be2017-08-22 16:35:41 +0200710 len = mgcp_udp_send(rtp_end->rtcp.fd,
711 &rtp_end->addr,
712 rtp_end->rtcp_port, buf, len);
713
714 conn_dst->end.packets_tx += 1;
715 conn_dst->end.octets_tx += len;
716
717 return len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200718 }
719
720 return 0;
721}
722
Philipp Maier87bd9be2017-08-22 16:35:41 +0200723/* Helper function for mgcp_recv(),
724 Receive one RTP Packet + Originating address from file descriptor */
725static int receive_from(struct mgcp_endpoint *endp, int fd,
726 struct sockaddr_in *addr, char *buf, int bufsize)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200727{
728 int rc;
729 socklen_t slen = sizeof(*addr);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200730 struct sockaddr_in addr_sink;
731 char buf_sink[RTP_BUF_SIZE];
732 bool tossed = false;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200733
Philipp Maier87bd9be2017-08-22 16:35:41 +0200734 if (!addr)
735 addr = &addr_sink;
736 if (!buf) {
737 tossed = true;
738 buf = buf_sink;
739 bufsize = sizeof(buf_sink);
740 }
741
742 rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
743
Philipp Maierc3413882017-10-27 12:26:54 +0200744 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200745 "receiving %u bytes length packet from %s:%u ...\n",
746 rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
747
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200748 if (rc < 0) {
Philipp Maierc3413882017-10-27 12:26:54 +0200749 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200750 "endpoint:%x failed to receive packet, errno: %d/%s\n",
751 ENDPOINT_NUMBER(endp), errno, strerror(errno));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200752 return -1;
753 }
754
Philipp Maier87bd9be2017-08-22 16:35:41 +0200755 if (tossed) {
Philipp Maierc3413882017-10-27 12:26:54 +0200756 LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200757 ENDPOINT_NUMBER(endp));
758 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200759
760 return rc;
761}
762
Philipp Maier87bd9be2017-08-22 16:35:41 +0200763/* Check if the origin (addr) matches the address/port data of the RTP
764 * connections. */
765static int check_rtp_origin(struct mgcp_conn_rtp *conn,
766 struct sockaddr_in *addr)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200767{
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200768 struct mgcp_endpoint *endp;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200769 endp = conn->conn->endp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200770
Philipp Maier87bd9be2017-08-22 16:35:41 +0200771 /* Note: Check if the inbound RTP data comes from the same host to
772 * which we send our outgoing RTP traffic. */
773 if (memcmp(&addr->sin_addr, &conn->end.addr, sizeof(addr->sin_addr))
774 != 0) {
Philipp Maierc3413882017-10-27 12:26:54 +0200775 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200776 "endpoint:%x data from wrong address: %s, ",
777 ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr));
Philipp Maierc3413882017-10-27 12:26:54 +0200778 LOGPC(DRTP, LOGL_ERROR, "expected: %s\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200779 inet_ntoa(conn->end.addr));
Philipp Maierc3413882017-10-27 12:26:54 +0200780 LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200781 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200782 return -1;
783 }
784
Philipp Maier87bd9be2017-08-22 16:35:41 +0200785 /* Note: Usually the remote remote port of the data we receive will be
786 * the same as the remote port where we transmit outgoing RTP traffic
787 * to (set by MDCX). We use this to check the origin of the data for
788 * plausibility. */
789 if (conn->end.rtp_port != addr->sin_port &&
790 conn->end.rtcp_port != addr->sin_port) {
Philipp Maierc3413882017-10-27 12:26:54 +0200791 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200792 "endpoint:%x data from wrong source port: %d, ",
793 ENDPOINT_NUMBER(endp), ntohs(addr->sin_port));
Philipp Maierc3413882017-10-27 12:26:54 +0200794 LOGPC(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200795 "expected: %d for RTP or %d for RTCP\n",
796 ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port));
Philipp Maierc3413882017-10-27 12:26:54 +0200797 LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200798 ENDPOINT_NUMBER(endp));
799 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200800 }
801
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200802 return 0;
803}
804
Philipp Maier87bd9be2017-08-22 16:35:41 +0200805/* Check the if the destination address configuration of an RTP connection
806 * makes sense */
807static int check_rtp_destin(struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200808{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200809 struct mgcp_endpoint *endp;
810 endp = conn->conn->endp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200811
Philipp Maier87bd9be2017-08-22 16:35:41 +0200812 if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
Philipp Maierc3413882017-10-27 12:26:54 +0200813 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200814 "endpoint:%x destination IP-address is invalid\n",
815 ENDPOINT_NUMBER(endp));
816 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200817 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200818
819 if (conn->end.rtp_port == 0) {
Philipp Maierc3413882017-10-27 12:26:54 +0200820 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200821 "endpoint:%x destination rtp port is invalid\n",
822 ENDPOINT_NUMBER(endp));
823 return -1;
824 }
825
826 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200827}
828
Philipp Maier87bd9be2017-08-22 16:35:41 +0200829/* Receive RTP data from a specified source connection and dispatch it to a
830 * destination connection. */
831static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
832 unsigned int buf_size, struct osmo_fd *fd)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200833{
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200834 struct mgcp_endpoint *endp;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200835 struct mgcp_conn_rtp *conn;
836 struct mgcp_trunk_config *tcfg;
837 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200838
Philipp Maier87bd9be2017-08-22 16:35:41 +0200839 conn = (struct mgcp_conn_rtp*) fd->data;
840 endp = conn->conn->endp;
841 tcfg = endp->tcfg;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200842
Philipp Maierc3413882017-10-27 12:26:54 +0200843 LOGP(DRTP, LOGL_DEBUG, "endpoint:%x receiving RTP/RTCP packet...\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200844 ENDPOINT_NUMBER(endp));
845
846 rc = receive_from(endp, fd->fd, addr, buf, buf_size);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200847 if (rc <= 0)
848 return -1;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200849 *proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200850
Philipp Maierc3413882017-10-27 12:26:54 +0200851 LOGP(DRTP, LOGL_DEBUG, "endpoint:%x ", ENDPOINT_NUMBER(endp));
852 LOGPC(DRTP, LOGL_DEBUG, "receiveing from %s %s %d\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200853 conn->conn->name, inet_ntoa(addr->sin_addr),
854 ntohs(addr->sin_port));
Philipp Maierc3413882017-10-27 12:26:54 +0200855 LOGP(DRTP, LOGL_DEBUG, "endpoint:%x conn:%s\n", ENDPOINT_NUMBER(endp),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200856 mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200857
Philipp Maier87bd9be2017-08-22 16:35:41 +0200858 /* Check if the origin of the RTP packet seems plausible */
859 if (tcfg->rtp_accept_all == 0) {
860 if (check_rtp_origin(conn, addr) != 0)
861 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200862 }
863
Philipp Maier87bd9be2017-08-22 16:35:41 +0200864 /* Filter out dummy message */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200865 if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
Philipp Maierc3413882017-10-27 12:26:54 +0200866 LOGP(DRTP, LOGL_NOTICE,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200867 "endpoint:%x dummy message received\n",
868 ENDPOINT_NUMBER(endp));
Philipp Maierc3413882017-10-27 12:26:54 +0200869 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200870 "endpoint:%x packet tossed\n", ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200871 return 0;
872 }
873
Philipp Maier87bd9be2017-08-22 16:35:41 +0200874 /* Increment RX statistics */
875 conn->end.packets_rx += 1;
876 conn->end.octets_rx += rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200877
Philipp Maier87bd9be2017-08-22 16:35:41 +0200878 /* Forward a copy of the RTP data to a debug ip/port */
879 forward_data(fd->fd, &conn->tap_in, buf, rc);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200880
Philipp Maier87bd9be2017-08-22 16:35:41 +0200881 return rc;
882}
883
884/* Send RTP data. Possible options are standard RTP packet
885 * transmission or trsmission via an osmux connection */
886static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
887 unsigned int buf_size,
888 struct mgcp_conn_rtp *conn_src,
889 struct mgcp_conn_rtp *conn_dst)
890{
891 struct mgcp_endpoint *endp;
892 endp = conn_src->conn->endp;
893
Philipp Maierc3413882017-10-27 12:26:54 +0200894 LOGP(DRTP, LOGL_DEBUG, "endpoint:%x destin conn:%s\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200895 ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_dst->conn));
896
897 /* Before we try to deliver the packet, we check if the destination
898 * port and IP-Address make sense at all. If not, we will be unable
899 * to deliver the packet. */
900 if (check_rtp_destin(conn_dst) != 0)
901 return -1;
902
903 /* Depending on the RTP connection type, deliver the RTP packet to the
904 * destination connection. */
905 switch (conn_dst->type) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200906 case MGCP_RTP_DEFAULT:
Philipp Maierc3413882017-10-27 12:26:54 +0200907 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200908 "endpoint:%x endpoint type is MGCP_RTP_DEFAULT, "
909 "using mgcp_send() to forward data directly\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200910 ENDPOINT_NUMBER(endp));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200911 return mgcp_send(endp, proto == MGCP_PROTO_RTP,
912 addr, buf, buf_size, conn_src, conn_dst);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200913 case MGCP_OSMUX_BSC_NAT:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200914 case MGCP_OSMUX_BSC:
Philipp Maierc3413882017-10-27 12:26:54 +0200915 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200916 "endpoint:%x endpoint type is MGCP_OSMUX_BSC_NAT, "
917 "using osmux_xfrm_to_osmux() to forward data through OSMUX\n",
918 ENDPOINT_NUMBER(endp));
919 return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200920 }
921
Philipp Maier87bd9be2017-08-22 16:35:41 +0200922 /* If the data has not been handled/forwarded until here, it will
923 * be discarded, this should not happen, normally the MGCP type
924 * should be properly set */
Philipp Maierc3413882017-10-27 12:26:54 +0200925 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200926 "endpoint:%x bad MGCP type -- data discarded!\n",
927 ENDPOINT_NUMBER(endp));
928
929 return -1;
930}
931
932/*! dispatch incoming RTP packet to opposite RTP connection.
933 * \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP)
934 * \param[in] addr socket address where the RTP packet has been received from
935 * \param[in] buf buffer that hold the RTP payload
936 * \param[in] buf_size size data length of buf
937 * \param[in] conn originating connection
938 * \returns 0 on success, -1 on ERROR */
939int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
940 unsigned int buf_size, struct mgcp_conn *conn)
941{
942 struct mgcp_conn *conn_dst;
943 struct mgcp_endpoint *endp;
944 endp = conn->endp;
945
946 /*! NOTE: This callback function implements the endpoint specific
947 * dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
948 * that the endpoint will hold only two connections. This premise
949 * is used to determine the opposite connection (it is always the
950 * connection that is not the originating connection). Once the
951 * destination connection is known the RTP packet is sent via
952 * the destination connection. */
953
954 /* Find a destination connection. */
955 /* NOTE: This code path runs every time an RTP packet is received. The
956 * function mgcp_find_dst_conn() we use to determine the detination
957 * connection will iterate the connection list inside the endpoint.
958 * Since list iterations are quite costly, we will figure out the
959 * destination only once and use the optional private data pointer of
960 * the connection to cache the destination connection pointer. */
961 if (!conn->priv) {
962 conn_dst = mgcp_find_dst_conn(conn);
963 conn->priv = conn_dst;
964 } else {
965 conn_dst = (struct mgcp_conn *)conn->priv;
966 }
967
968 /* There is no destination conn, stop here */
969 if (!conn_dst) {
Philipp Maierc3413882017-10-27 12:26:54 +0200970 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200971 "endpoint:%x unable to find destination conn\n",
972 ENDPOINT_NUMBER(endp));
973 return -1;
974 }
975
976 /* The destination conn is not an RTP connection */
977 if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
Philipp Maierc3413882017-10-27 12:26:54 +0200978 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200979 "endpoint:%x unable to find suitable destination conn\n",
980 ENDPOINT_NUMBER(endp));
981 return -1;
982 }
983
984 /* Dispatch RTP packet to destination RTP connection */
985 return mgcp_send_rtp(proto, addr, buf,
986 buf_size, &conn->u.rtp, &conn_dst->u.rtp);
987
988}
989
990/* Handle incoming RTP data from NET */
991static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
992{
993 /* NOTE: This is a generic implementation. RTP data is received. In
994 * case of loopback the data is just sent back to its origin. All
995 * other cases implement endpoint specific behaviour (e.g. how is the
996 * destination connection determined?). That specific behaviour is
997 * implemented by the callback function that is called at the end of
998 * the function */
999
1000 struct mgcp_conn_rtp *conn_src;
1001 struct mgcp_endpoint *endp;
1002 struct sockaddr_in addr;
1003
1004 char buf[RTP_BUF_SIZE];
1005 int proto;
1006 int rc;
1007
1008 conn_src = (struct mgcp_conn_rtp *)fd->data;
1009 OSMO_ASSERT(conn_src);
1010 endp = conn_src->conn->endp;
1011 OSMO_ASSERT(endp);
1012
Philipp Maierc3413882017-10-27 12:26:54 +02001013 LOGP(DRTP, LOGL_DEBUG, "endpoint:%x source conn:%s\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +02001014 ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_src->conn));
1015
1016 /* Receive packet */
1017 rc = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
1018 if (rc < 0)
1019 return -1;
1020
1021 /* Check if the connection is in loopback mode, if yes, just send the
1022 * incoming data back to the origin */
1023 if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
1024 return mgcp_send_rtp(proto, &addr, buf,
1025 sizeof(buf), conn_src, conn_src);
1026 }
1027
1028 /* Execute endpoint specific implementation that handles the
1029 * dispatching of the RTP data */
1030 return endp->type->dispatch_rtp_cb(proto, &addr, buf, sizeof(buf),
1031 conn_src->conn);
1032}
1033
1034/*! set IP Type of Service parameter.
1035 * \param[in] fd associated file descriptor
1036 * \param[in] tos dscp value
1037 * \returns 0 on success, -1 on ERROR */
1038int mgcp_set_ip_tos(int fd, int tos)
1039{
1040 int ret;
1041 ret = setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
1042
1043 if (ret < 0)
1044 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001045 return 0;
1046}
1047
Philipp Maier87bd9be2017-08-22 16:35:41 +02001048/*! bind RTP port to osmo_fd.
1049 * \param[in] source_addr source (local) address to bind on
1050 * \param[in] fd associated file descriptor
1051 * \param[in] port to bind on
1052 * \returns 0 on success, -1 on ERROR */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001053int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
1054{
1055 struct sockaddr_in addr;
1056 int on = 1;
1057
1058 fd->fd = socket(AF_INET, SOCK_DGRAM, 0);
1059 if (fd->fd < 0) {
Philipp Maierc3413882017-10-27 12:26:54 +02001060 LOGP(DRTP, LOGL_ERROR, "failed to create UDP port (%s:%i).\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +02001061 source_addr, port);
1062 return -1;
1063 } else {
Philipp Maierc3413882017-10-27 12:26:54 +02001064 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001065 "created UDP port (%s:%i).\n", source_addr, port);
1066 }
1067
1068 if (setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) {
Philipp Maierc3413882017-10-27 12:26:54 +02001069 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001070 "failed to set socket options (%s:%i).\n", source_addr,
1071 port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001072 return -1;
1073 }
1074
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001075 memset(&addr, 0, sizeof(addr));
1076 addr.sin_family = AF_INET;
1077 addr.sin_port = htons(port);
1078 inet_aton(source_addr, &addr.sin_addr);
1079
Philipp Maier87bd9be2017-08-22 16:35:41 +02001080 if (bind(fd->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001081 close(fd->fd);
1082 fd->fd = -1;
Philipp Maierc3413882017-10-27 12:26:54 +02001083 LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +02001084 source_addr, port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001085 return -1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001086 } else {
Philipp Maierc3413882017-10-27 12:26:54 +02001087 LOGP(DRTP, LOGL_DEBUG,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001088 "bound UDP port (%s:%i).\n", source_addr, port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001089 }
1090
1091 return 0;
1092}
1093
Philipp Maier87bd9be2017-08-22 16:35:41 +02001094/* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001095static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001096 struct mgcp_rtp_end *rtp_end, int endpno)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001097{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001098 /* NOTE: The port that is used for RTCP is the RTP port incremented by one
1099 * (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
1100
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001101 if (mgcp_create_bind(source_addr, &rtp_end->rtp,
1102 rtp_end->local_port) != 0) {
Philipp Maierc3413882017-10-27 12:26:54 +02001103 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001104 "endpoint:%x failed to create RTP port: %s:%d\n", endpno,
1105 source_addr, rtp_end->local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001106 goto cleanup0;
1107 }
1108
1109 if (mgcp_create_bind(source_addr, &rtp_end->rtcp,
1110 rtp_end->local_port + 1) != 0) {
Philipp Maierc3413882017-10-27 12:26:54 +02001111 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001112 "endpoint:%x failed to create RTCP port: %s:%d\n", endpno,
1113 source_addr, rtp_end->local_port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001114 goto cleanup1;
1115 }
1116
Philipp Maier87bd9be2017-08-22 16:35:41 +02001117 /* Set Type of Service (DSCP-Value) as configured via VTY */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001118 mgcp_set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
1119 mgcp_set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp);
1120
1121 rtp_end->rtp.when = BSC_FD_READ;
1122 if (osmo_fd_register(&rtp_end->rtp) != 0) {
Philipp Maierc3413882017-10-27 12:26:54 +02001123 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001124 "endpoint:%x failed to register RTP port %d\n", endpno,
1125 rtp_end->local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001126 goto cleanup2;
1127 }
1128
1129 rtp_end->rtcp.when = BSC_FD_READ;
1130 if (osmo_fd_register(&rtp_end->rtcp) != 0) {
Philipp Maierc3413882017-10-27 12:26:54 +02001131 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001132 "endpoint:%x failed to register RTCP port %d\n", endpno,
1133 rtp_end->local_port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001134 goto cleanup3;
1135 }
1136
1137 return 0;
1138
1139cleanup3:
1140 osmo_fd_unregister(&rtp_end->rtp);
1141cleanup2:
1142 close(rtp_end->rtcp.fd);
1143 rtp_end->rtcp.fd = -1;
1144cleanup1:
1145 close(rtp_end->rtp.fd);
1146 rtp_end->rtp.fd = -1;
1147cleanup0:
1148 return -1;
1149}
1150
Philipp Maier87bd9be2017-08-22 16:35:41 +02001151/*! bind RTP port to endpoint/connection.
1152 * \param[in] endp endpoint that holds the RTP connection
1153 * \param[in] rtp_port port number to bind on
1154 * \param[in] conn associated RTP connection
1155 * \returns 0 on success, -1 on ERROR */
1156int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
1157 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001158{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001159 char name[512];
1160 struct mgcp_rtp_end *end;
1161
1162 snprintf(name, sizeof(name), "%s-%u", conn->conn->name, conn->conn->id);
1163 end = &conn->end;
1164
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001165 if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
Philipp Maierc3413882017-10-27 12:26:54 +02001166 LOGP(DRTP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001167 "endpoint:%x %u was already bound on conn:%s\n",
1168 ENDPOINT_NUMBER(endp), rtp_port,
1169 mgcp_conn_dump(conn->conn));
1170
1171 /* Double bindings should never occour! Since we always allocate
1172 * connections dynamically and free them when they are not
1173 * needed anymore, there must be no previous binding leftover.
1174 * Should there be a connection bound twice, we have a serious
1175 * problem and must exit immediately! */
1176 OSMO_ASSERT(false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001177 }
1178
1179 end->local_port = rtp_port;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001180 end->rtp.cb = rtp_data_net;
1181 end->rtp.data = conn;
1182 end->rtcp.data = conn;
1183 end->rtcp.cb = rtp_data_net;
1184
1185 return bind_rtp(endp->cfg, mgcp_net_src_addr(endp), end,
1186 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001187}
1188
Philipp Maier87bd9be2017-08-22 16:35:41 +02001189/*! free allocated RTP and RTCP ports.
1190 * \param[in] end RTP end */
1191void mgcp_free_rtp_port(struct mgcp_rtp_end *end)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001192{
1193 if (end->rtp.fd != -1) {
1194 close(end->rtp.fd);
1195 end->rtp.fd = -1;
1196 osmo_fd_unregister(&end->rtp);
1197 }
1198
1199 if (end->rtcp.fd != -1) {
1200 close(end->rtcp.fd);
1201 end->rtcp.fd = -1;
1202 osmo_fd_unregister(&end->rtcp);
1203 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001204}