|
|
|
@ -838,7 +838,7 @@ static void stream_fd_closed(int fd, void *p, uintptr_t u) { |
|
|
|
|
|
|
|
|
|
|
|
/* returns: 0 = not a muxed stream, 1 = muxed, RTP, 2 = muxed, RTCP */ |
|
|
|
static int rtcp_demux(str *s, struct call_media *media) { |
|
|
|
static int rtcp_demux(const str *s, struct call_media *media) { |
|
|
|
if (!MEDIA_ISSET(media, RTCP_MUX)) |
|
|
|
return 0; |
|
|
|
return rtcp_demux_is_rtcp(s) ? 2 : 1; |
|
|
|
@ -1167,223 +1167,207 @@ static void __stream_ssrc(struct packet_stream *in_srtp, struct packet_stream *o |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* XXX split this function into pieces */ |
|
|
|
/* called lock-free */ |
|
|
|
static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, const struct timeval *tv) { |
|
|
|
/** |
|
|
|
* Incoming packets: |
|
|
|
* - sfd->socket.local: the local IP/port on which the packet arrived |
|
|
|
* - sfd->stream->endpoint: adjusted/learned IP/port from where the packet |
|
|
|
* was sent |
|
|
|
* - sfd->stream->advertised_endpoint: the unadjusted IP/port from where the |
|
|
|
* packet was sent. These are the values present in the SDP |
|
|
|
* |
|
|
|
* Outgoing packets: |
|
|
|
* - sfd->stream->rtp_sink->endpoint: the destination IP/port |
|
|
|
* - sfd->stream->selected_sfd->socket.local: the local source IP/port for the |
|
|
|
* outgoing packet |
|
|
|
* |
|
|
|
* If the rtpengine runs behind a NAT and local addresses are configured with |
|
|
|
* different advertised endpoints, the SDP would not contain the address from |
|
|
|
* `...->socket.local`, but rather from `sfd->local_intf->spec->address.advertised` |
|
|
|
* (of type `sockaddr_t`). The port will be the same. |
|
|
|
*/ |
|
|
|
/* TODO move the above comments to the data structure definitions, if the above |
|
|
|
* always holds true */ |
|
|
|
struct packet_stream *stream, |
|
|
|
*sink = NULL, |
|
|
|
*in_srtp, *out_srtp; |
|
|
|
struct call_media *media; |
|
|
|
int ret = 0, update = 0, stun_ret = 0, handler_ret = 0, muxed_rtcp = 0, rtcp = 0, |
|
|
|
unk = 0; |
|
|
|
int i; |
|
|
|
struct call *call; |
|
|
|
/*unsigned char cc;*/ |
|
|
|
struct endpoint endpoint; |
|
|
|
rewrite_func rwf_in, rwf_out; |
|
|
|
//struct local_intf *loc_addr; |
|
|
|
struct rtp_header *rtp_h; |
|
|
|
struct rtcp_packet *rtcp_h; |
|
|
|
struct rtp_stats *rtp_s; |
|
|
|
struct ssrc_ctx *ssrc_in = NULL, *ssrc_out = NULL; |
|
|
|
|
|
|
|
call = sfd->call; |
|
|
|
|
|
|
|
rwlock_lock_r(&call->master_lock); |
|
|
|
|
|
|
|
stream = sfd->stream; |
|
|
|
if (!stream) |
|
|
|
goto unlock_out; |
|
|
|
__C_DBG("Try to Kernelizing media stream: %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
|
|
|
|
|
|
|
|
media = stream->media; |
|
|
|
|
|
|
|
if (!stream->selected_sfd) |
|
|
|
goto unlock_out; |
|
|
|
|
|
|
|
|
|
|
|
/* demux other protocols running on this port */ |
|
|
|
// returns: 0 = packet processed by other protocol hander; -1 = packet not handled, proceed; |
|
|
|
// 1 = same as 0, but stream can be kernelized |
|
|
|
static int media_demux_protocols(struct stream_fd *sfd, const str *s, const endpoint_t *fsin) { |
|
|
|
struct packet_stream *stream = sfd->stream; |
|
|
|
struct call_media *media = stream->media; |
|
|
|
|
|
|
|
if (MEDIA_ISSET(media, DTLS) && is_dtls(s)) { |
|
|
|
mutex_lock(&stream->in_lock); |
|
|
|
ret = dtls(stream, s, fsin); |
|
|
|
int ret = dtls(stream, s, fsin); |
|
|
|
mutex_unlock(&stream->in_lock); |
|
|
|
if (!ret) |
|
|
|
goto unlock_out; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
if (media->ice_agent && is_stun(s)) { |
|
|
|
stun_ret = stun(s, sfd, fsin); |
|
|
|
int stun_ret = stun(s, sfd, fsin); |
|
|
|
if (!stun_ret) |
|
|
|
goto unlock_out; |
|
|
|
return 0; |
|
|
|
if (stun_ret == 1) { |
|
|
|
call_media_state_machine(media); |
|
|
|
mutex_lock(&stream->in_lock); /* for the jump */ |
|
|
|
goto kernel_check; |
|
|
|
return 1; |
|
|
|
} |
|
|
|
else /* not an stun packet */ |
|
|
|
stun_ret = 0; |
|
|
|
; |
|
|
|
} |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
#if RTP_LOOP_PROTECT |
|
|
|
if (MEDIA_ISSET(media, LOOP_CHECK)) { |
|
|
|
mutex_lock(&stream->in_lock); |
|
|
|
|
|
|
|
for (i = 0; i < RTP_LOOP_PACKETS; i++) { |
|
|
|
if (stream->lp_buf[i].len != s->len) |
|
|
|
continue; |
|
|
|
if (memcmp(stream->lp_buf[i].buf, s->s, MIN(s->len, RTP_LOOP_PROTECT))) |
|
|
|
continue; |
|
|
|
|
|
|
|
__C_DBG("packet dupe"); |
|
|
|
if (stream->lp_count >= RTP_LOOP_MAX_COUNT) { |
|
|
|
ilog(LOG_WARNING, "More than %d duplicate packets detected, dropping packet " |
|
|
|
"to avoid potential loop", RTP_LOOP_MAX_COUNT); |
|
|
|
goto done; |
|
|
|
} |
|
|
|
#if RTP_LOOP_PROTECT |
|
|
|
// returns: 0 = ok, proceed; -1 = duplicate detected, drop packet |
|
|
|
static int media_loop_detect(struct packet_stream *stream, const str *s) { |
|
|
|
mutex_lock(&stream->in_lock); |
|
|
|
|
|
|
|
stream->lp_count++; |
|
|
|
goto loop_ok; |
|
|
|
for (int i = 0; i < RTP_LOOP_PACKETS; i++) { |
|
|
|
if (stream->lp_buf[i].len != s->len) |
|
|
|
continue; |
|
|
|
if (memcmp(stream->lp_buf[i].buf, s->s, MIN(s->len, RTP_LOOP_PROTECT))) |
|
|
|
continue; |
|
|
|
|
|
|
|
__C_DBG("packet dupe"); |
|
|
|
if (stream->lp_count >= RTP_LOOP_MAX_COUNT) { |
|
|
|
ilog(LOG_WARNING, "More than %d duplicate packets detected, dropping packet " |
|
|
|
"to avoid potential loop", RTP_LOOP_MAX_COUNT); |
|
|
|
mutex_unlock(&stream->in_lock); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
/* not a dupe */ |
|
|
|
stream->lp_count = 0; |
|
|
|
stream->lp_buf[stream->lp_idx].len = s->len; |
|
|
|
memcpy(stream->lp_buf[stream->lp_idx].buf, s->s, MIN(s->len, RTP_LOOP_PROTECT)); |
|
|
|
stream->lp_idx = (stream->lp_idx + 1) % RTP_LOOP_PACKETS; |
|
|
|
loop_ok: |
|
|
|
mutex_unlock(&stream->in_lock); |
|
|
|
stream->lp_count++; |
|
|
|
goto loop_ok; |
|
|
|
} |
|
|
|
|
|
|
|
/* not a dupe */ |
|
|
|
stream->lp_count = 0; |
|
|
|
stream->lp_buf[stream->lp_idx].len = s->len; |
|
|
|
memcpy(stream->lp_buf[stream->lp_idx].buf, s->s, MIN(s->len, RTP_LOOP_PROTECT)); |
|
|
|
stream->lp_idx = (stream->lp_idx + 1) % RTP_LOOP_PACKETS; |
|
|
|
loop_ok: |
|
|
|
mutex_unlock(&stream->in_lock); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
/* demux RTCP */ |
|
|
|
|
|
|
|
in_srtp = stream; |
|
|
|
sink = stream->rtp_sink; |
|
|
|
if (!sink && PS_ISSET(stream, RTCP)) { |
|
|
|
sink = stream->rtcp_sink; |
|
|
|
// returns: true/false (is RTCP or not) |
|
|
|
// in_srtp and out_srtp are set to point to the SRTP contexts to use |
|
|
|
// sink_p is set to where to forward the packet to |
|
|
|
static int media_packet_rtcp_demux(const str *s, struct packet_stream *stream, |
|
|
|
struct packet_stream **in_srtp_p, struct packet_stream **out_srtp_p, |
|
|
|
struct packet_stream **sink_p) |
|
|
|
{ |
|
|
|
struct call_media *media = stream->media; |
|
|
|
int rtcp = 0; |
|
|
|
|
|
|
|
*in_srtp_p = stream; |
|
|
|
*sink_p = stream->rtp_sink; |
|
|
|
if (!*sink_p && PS_ISSET(stream, RTCP)) { |
|
|
|
*sink_p = stream->rtcp_sink; |
|
|
|
rtcp = 1; |
|
|
|
} |
|
|
|
else if (stream->rtcp_sink) { |
|
|
|
muxed_rtcp = rtcp_demux(s, media); |
|
|
|
int muxed_rtcp = rtcp_demux(s, media); |
|
|
|
if (muxed_rtcp == 2) { |
|
|
|
sink = stream->rtcp_sink; |
|
|
|
*sink_p = stream->rtcp_sink; |
|
|
|
rtcp = 1; |
|
|
|
in_srtp = stream->rtcp_sibling; |
|
|
|
*in_srtp_p = stream->rtcp_sibling; // use RTCP SRTP context |
|
|
|
} |
|
|
|
} |
|
|
|
out_srtp = sink; |
|
|
|
if (rtcp && sink && sink->rtcp_sibling) |
|
|
|
out_srtp = sink->rtcp_sibling; |
|
|
|
|
|
|
|
*out_srtp_p = *sink_p; |
|
|
|
if (rtcp && *sink_p && (*sink_p)->rtcp_sibling) |
|
|
|
*out_srtp_p = (*sink_p)->rtcp_sibling; // use RTCP SRTP context |
|
|
|
|
|
|
|
/* RTP/RTCP specifics */ |
|
|
|
return rtcp; |
|
|
|
} |
|
|
|
|
|
|
|
if (G_LIKELY(media->protocol && media->protocol->rtp)) { |
|
|
|
if (G_LIKELY(!rtcp && !rtp_payload(&rtp_h, NULL, s))) { |
|
|
|
if (G_LIKELY(out_srtp != NULL)) |
|
|
|
__stream_ssrc(in_srtp, out_srtp, rtp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash); |
|
|
|
|
|
|
|
// check the payload type |
|
|
|
i = (rtp_h->m_pt & 0x7f); |
|
|
|
if (G_LIKELY(ssrc_in)) |
|
|
|
ssrc_in->parent->payload_type = i; |
|
|
|
static void media_packet_rtp(const str *s, struct packet_stream *stream, struct packet_stream *in_srtp, |
|
|
|
struct packet_stream *out_srtp, int rtcp, |
|
|
|
struct ssrc_ctx **ssrc_in_p, struct ssrc_ctx **ssrc_out_p) |
|
|
|
{ |
|
|
|
struct call_media *media = stream->media; |
|
|
|
struct call *call = media->call; |
|
|
|
struct rtp_header *rtp_h; |
|
|
|
struct rtcp_packet *rtcp_h; |
|
|
|
|
|
|
|
// XXX convert to array? or keep last pointer? |
|
|
|
rtp_s = g_hash_table_lookup(stream->rtp_stats, &i); |
|
|
|
if (!rtp_s) { |
|
|
|
ilog(LOG_WARNING | LOG_FLAG_LIMIT, |
|
|
|
"RTP packet with unknown payload type %u received", i); |
|
|
|
atomic64_inc(&stream->stats.errors); |
|
|
|
atomic64_inc(&rtpe_statsps.errors); |
|
|
|
} |
|
|
|
if (G_UNLIKELY(!media->protocol)) |
|
|
|
return; |
|
|
|
if (G_UNLIKELY(!media->protocol->rtp)) |
|
|
|
return; |
|
|
|
|
|
|
|
else { |
|
|
|
atomic64_inc(&rtp_s->packets); |
|
|
|
atomic64_add(&rtp_s->bytes, s->len); |
|
|
|
} |
|
|
|
if (G_LIKELY(!rtcp && !rtp_payload(&rtp_h, NULL, s))) { |
|
|
|
if (G_LIKELY(out_srtp != NULL)) |
|
|
|
__stream_ssrc(in_srtp, out_srtp, rtp_h->ssrc, ssrc_in_p, ssrc_out_p, call->ssrc_hash); |
|
|
|
|
|
|
|
// check the payload type |
|
|
|
int i = (rtp_h->m_pt & 0x7f); |
|
|
|
if (G_LIKELY(*ssrc_in_p)) |
|
|
|
(*ssrc_in_p)->parent->payload_type = i; |
|
|
|
|
|
|
|
// XXX convert to array? or keep last pointer? |
|
|
|
struct rtp_stats *rtp_s = g_hash_table_lookup(stream->rtp_stats, &i); |
|
|
|
if (!rtp_s) { |
|
|
|
ilog(LOG_WARNING | LOG_FLAG_LIMIT, |
|
|
|
"RTP packet with unknown payload type %u received", i); |
|
|
|
atomic64_inc(&stream->stats.errors); |
|
|
|
atomic64_inc(&rtpe_statsps.errors); |
|
|
|
} |
|
|
|
else if (rtcp && !rtcp_payload(&rtcp_h, NULL, s)) { |
|
|
|
if (G_LIKELY(out_srtp != NULL)) |
|
|
|
__stream_ssrc(in_srtp, out_srtp, rtcp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash); |
|
|
|
|
|
|
|
else { |
|
|
|
atomic64_inc(&rtp_s->packets); |
|
|
|
atomic64_add(&rtp_s->bytes, s->len); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* do we have somewhere to forward it to? */ |
|
|
|
|
|
|
|
if (G_UNLIKELY(!sink || !sink->selected_sfd || !out_srtp || !out_srtp->selected_sfd || !in_srtp->selected_sfd)) { |
|
|
|
ilog(LOG_WARNING, "RTP packet from %s discarded", endpoint_print_buf(fsin)); |
|
|
|
atomic64_inc(&stream->stats.errors); |
|
|
|
atomic64_inc(&rtpe_statsps.errors); |
|
|
|
goto unlock_out; |
|
|
|
else if (rtcp && !rtcp_payload(&rtcp_h, NULL, s)) { |
|
|
|
if (G_LIKELY(out_srtp != NULL)) |
|
|
|
__stream_ssrc(in_srtp, out_srtp, rtcp_h->ssrc, ssrc_in_p, ssrc_out_p, call->ssrc_hash); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* transcoding stuff, in and out */ |
|
|
|
static int media_packet_decrypt(str *s, int rtcp, struct packet_stream *in_srtp, struct packet_stream *sink, |
|
|
|
struct stream_fd *sfd, const endpoint_t *fsin, const struct timeval *tv, |
|
|
|
struct ssrc_ctx *ssrc_in, rewrite_func *rwf_out_p) |
|
|
|
{ |
|
|
|
rewrite_func rwf_in; |
|
|
|
|
|
|
|
mutex_lock(&in_srtp->in_lock); |
|
|
|
|
|
|
|
determine_handler(in_srtp, sink); |
|
|
|
|
|
|
|
// XXX use an array with index instead of if/else |
|
|
|
if (G_LIKELY(!rtcp)) { |
|
|
|
rwf_in = in_srtp->handler->in->rtp; |
|
|
|
rwf_out = in_srtp->handler->out->rtp; |
|
|
|
*rwf_out_p = in_srtp->handler->out->rtp; |
|
|
|
} |
|
|
|
else { |
|
|
|
rwf_in = in_srtp->handler->in->rtcp; |
|
|
|
rwf_out = in_srtp->handler->out->rtcp; |
|
|
|
*rwf_out_p = in_srtp->handler->out->rtcp; |
|
|
|
} |
|
|
|
|
|
|
|
mutex_lock(&out_srtp->out_lock); |
|
|
|
|
|
|
|
/* return values are: 0 = forward packet, -1 = error/dont forward, |
|
|
|
* 1 = forward and push update to redis */ |
|
|
|
if (rwf_in) { |
|
|
|
handler_ret = rwf_in(s, in_srtp, sfd, fsin, tv, ssrc_in); |
|
|
|
} |
|
|
|
int ret = 0; |
|
|
|
if (rwf_in) |
|
|
|
ret = rwf_in(s, in_srtp, sfd, fsin, tv, ssrc_in); |
|
|
|
|
|
|
|
// If recording pcap dumper is set, then we record the call. |
|
|
|
if (call->recording) { |
|
|
|
dump_packet(call->recording, stream, s); |
|
|
|
} |
|
|
|
mutex_unlock(&in_srtp->in_lock); |
|
|
|
|
|
|
|
if (G_LIKELY(handler_ret >= 0)) { |
|
|
|
if (rwf_out) |
|
|
|
handler_ret += rwf_out(s, out_srtp, NULL, NULL, NULL, ssrc_out); |
|
|
|
} |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
if (handler_ret > 0) { |
|
|
|
__unkernelize(stream); |
|
|
|
update = 1; |
|
|
|
} |
|
|
|
static int media_packet_encrypt(str *s, int rtcp, struct packet_stream *out_srtp, struct packet_stream *sink, |
|
|
|
struct ssrc_ctx *ssrc_out, rewrite_func rwf_out) |
|
|
|
{ |
|
|
|
if (!rwf_out) |
|
|
|
return 0; |
|
|
|
|
|
|
|
mutex_lock(&out_srtp->out_lock); |
|
|
|
|
|
|
|
int ret = 0; |
|
|
|
if (rwf_out) |
|
|
|
ret = rwf_out(s, out_srtp, NULL, NULL, NULL, ssrc_out); |
|
|
|
|
|
|
|
mutex_unlock(&out_srtp->out_lock); |
|
|
|
mutex_unlock(&in_srtp->in_lock); |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* endpoint address handling */ |
|
|
|
// returns: 0 = OK, forward packet; -1 = drop packet; 1 = OK, forward, but also update info |
|
|
|
// 2 = OK, we are ready to kernelize; 3 = same as 1, but also update info |
|
|
|
static int media_packet_address_check(struct packet_stream *stream, struct packet_stream *sink, |
|
|
|
struct stream_fd *sfd, const endpoint_t *fsin) |
|
|
|
{ |
|
|
|
struct call_media *media = stream->media; |
|
|
|
struct call *call = stream->call; |
|
|
|
struct endpoint endpoint; |
|
|
|
int unk = 0, ret; |
|
|
|
|
|
|
|
mutex_lock(&stream->in_lock); |
|
|
|
|
|
|
|
@ -1392,7 +1376,8 @@ loop_ok: |
|
|
|
/* if the other side hasn't been signalled yet, just forward the packet */ |
|
|
|
if (!PS_ISSET(stream, FILLED)) { |
|
|
|
__C_DBG("stream %s:%d not FILLED", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
goto forward; |
|
|
|
ret = 0; |
|
|
|
goto out; |
|
|
|
} |
|
|
|
|
|
|
|
/* do not pay attention to source addresses of incoming packets for asymmetric streams */ |
|
|
|
@ -1416,6 +1401,8 @@ loop_ok: |
|
|
|
/* out_lock remains locked */ |
|
|
|
ilog(LOG_INFO, "Peer address changed to %s", endpoint_print_buf(fsin)); |
|
|
|
unk = 1; |
|
|
|
ret = 1; |
|
|
|
stream->endpoint = *fsin; |
|
|
|
goto update_addr; |
|
|
|
} |
|
|
|
|
|
|
|
@ -1426,29 +1413,33 @@ loop_ok: |
|
|
|
sockaddr_print_buf(&endpoint.address), endpoint.port, |
|
|
|
sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
atomic64_inc(&stream->stats.errors); |
|
|
|
goto drop; |
|
|
|
ret = -1; |
|
|
|
goto out; |
|
|
|
} |
|
|
|
} |
|
|
|
goto kernel_check; |
|
|
|
ret = 1; |
|
|
|
goto out; |
|
|
|
} |
|
|
|
|
|
|
|
/* wait at least 3 seconds after last signal before committing to a particular |
|
|
|
* endpoint address */ |
|
|
|
ret = 0; |
|
|
|
if (!call->last_signal || rtpe_now.tv_sec <= call->last_signal + 3) |
|
|
|
goto update_peerinfo; |
|
|
|
|
|
|
|
ret = 2; |
|
|
|
|
|
|
|
ilog(LOG_INFO, "Confirmed peer address as %s", endpoint_print_buf(fsin)); |
|
|
|
|
|
|
|
PS_SET(stream, CONFIRMED); |
|
|
|
update = 1; |
|
|
|
|
|
|
|
update_peerinfo: |
|
|
|
mutex_lock(&stream->out_lock); |
|
|
|
update_addr: |
|
|
|
endpoint = stream->endpoint; |
|
|
|
stream->endpoint = *fsin; |
|
|
|
if (memcmp(&endpoint, &stream->endpoint, sizeof(endpoint))) |
|
|
|
update = 1; |
|
|
|
ret |= 1; // either 0 or 2 -> makes it 1 or 3 |
|
|
|
update_addr: |
|
|
|
mutex_unlock(&stream->out_lock); |
|
|
|
|
|
|
|
/* check the destination address of the received packet against what we think our |
|
|
|
@ -1456,50 +1447,168 @@ update_addr: |
|
|
|
if (stream->selected_sfd && sfd != stream->selected_sfd) { |
|
|
|
ilog(LOG_INFO, "Switching local interface to %s", endpoint_print_buf(&sfd->socket.local)); |
|
|
|
stream->selected_sfd = sfd; |
|
|
|
update = 1; |
|
|
|
ret |= 1; // 0 or 2 -> 1 or 3 |
|
|
|
} |
|
|
|
|
|
|
|
out: |
|
|
|
if (unk) |
|
|
|
__stream_unconfirm(stream); |
|
|
|
|
|
|
|
mutex_unlock(&stream->in_lock); |
|
|
|
|
|
|
|
kernel_check: |
|
|
|
if (unk) { |
|
|
|
stream_unconfirm(stream->rtp_sink); |
|
|
|
stream_unconfirm(stream->rtcp_sink); |
|
|
|
} |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void media_packet_kernel_check(struct packet_stream *stream, struct packet_stream *sink) { |
|
|
|
if (PS_ISSET(stream, NO_KERNEL_SUPPORT)) { |
|
|
|
__C_DBG("stream %s:%d NO_KERNEL_SUPPORT", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
goto forward; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!PS_ISSET(stream, CONFIRMED)) { |
|
|
|
__C_DBG("stream %s:%d not CONFIRMED", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
goto forward; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!sink) { |
|
|
|
__C_DBG("sink is NULL for stream %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
goto forward; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!PS_ISSET(sink, CONFIRMED)) { |
|
|
|
__C_DBG("sink not CONFIRMED for stream %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
goto forward; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!PS_ISSET(sink, FILLED)) { |
|
|
|
__C_DBG("sink not FILLED for stream %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
goto forward; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
kernelize(stream); |
|
|
|
} |
|
|
|
|
|
|
|
forward: |
|
|
|
if (sink) |
|
|
|
mutex_lock(&sink->out_lock); |
|
|
|
|
|
|
|
if (!sink |
|
|
|
|| !sink->advertised_endpoint.port |
|
|
|
/* called lock-free */ |
|
|
|
static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, const struct timeval *tv) { |
|
|
|
/** |
|
|
|
* Incoming packets: |
|
|
|
* - sfd->socket.local: the local IP/port on which the packet arrived |
|
|
|
* - sfd->stream->endpoint: adjusted/learned IP/port from where the packet |
|
|
|
* was sent |
|
|
|
* - sfd->stream->advertised_endpoint: the unadjusted IP/port from where the |
|
|
|
* packet was sent. These are the values present in the SDP |
|
|
|
* |
|
|
|
* Outgoing packets: |
|
|
|
* - sfd->stream->rtp_sink->endpoint: the destination IP/port |
|
|
|
* - sfd->stream->selected_sfd->socket.local: the local source IP/port for the |
|
|
|
* outgoing packet |
|
|
|
* |
|
|
|
* If the rtpengine runs behind a NAT and local addresses are configured with |
|
|
|
* different advertised endpoints, the SDP would not contain the address from |
|
|
|
* `...->socket.local`, but rather from `sfd->local_intf->spec->address.advertised` |
|
|
|
* (of type `sockaddr_t`). The port will be the same. |
|
|
|
*/ |
|
|
|
/* TODO move the above comments to the data structure definitions, if the above |
|
|
|
* always holds true */ |
|
|
|
// XXX collect all these vars in a struct to pass around |
|
|
|
struct packet_stream *stream, |
|
|
|
*sink = NULL, |
|
|
|
*in_srtp, *out_srtp; |
|
|
|
struct call_media *media; |
|
|
|
int ret = 0, update = 0, handler_ret = 0, rtcp = 0; |
|
|
|
struct call *call; |
|
|
|
rewrite_func rwf_out; |
|
|
|
struct ssrc_ctx *ssrc_in = NULL, *ssrc_out = NULL; |
|
|
|
|
|
|
|
call = sfd->call; |
|
|
|
|
|
|
|
rwlock_lock_r(&call->master_lock); |
|
|
|
|
|
|
|
stream = sfd->stream; |
|
|
|
if (G_UNLIKELY(!stream)) |
|
|
|
goto unlock_out; |
|
|
|
__C_DBG("Handling packet on: %s:%d", sockaddr_print_buf(&stream->endpoint.address), stream->endpoint.port); |
|
|
|
|
|
|
|
|
|
|
|
media = stream->media; |
|
|
|
|
|
|
|
if (!stream->selected_sfd) |
|
|
|
goto unlock_out; |
|
|
|
|
|
|
|
|
|
|
|
int stun_ret = media_demux_protocols(sfd, s, fsin); |
|
|
|
if (stun_ret == 0) // packet processed |
|
|
|
goto unlock_out; |
|
|
|
if (stun_ret == 1) { |
|
|
|
media_packet_kernel_check(stream, sink); |
|
|
|
goto drop; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#if RTP_LOOP_PROTECT |
|
|
|
if (MEDIA_ISSET(media, LOOP_CHECK)) { |
|
|
|
if (media_loop_detect(stream, s)) |
|
|
|
goto unlock_out; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
// this sets in_srtp, out_srtp, and sink |
|
|
|
rtcp = media_packet_rtcp_demux(s, stream, &in_srtp, &out_srtp, &sink); |
|
|
|
|
|
|
|
// this set ssrc_in and ssrc_out |
|
|
|
media_packet_rtp(s, stream, in_srtp, out_srtp, rtcp, &ssrc_in, &ssrc_out); |
|
|
|
|
|
|
|
|
|
|
|
/* do we have somewhere to forward it to? */ |
|
|
|
|
|
|
|
if (G_UNLIKELY(!sink || !sink->selected_sfd || !out_srtp || !out_srtp->selected_sfd || !in_srtp->selected_sfd)) { |
|
|
|
ilog(LOG_WARNING, "RTP packet from %s discarded", endpoint_print_buf(fsin)); |
|
|
|
atomic64_inc(&stream->stats.errors); |
|
|
|
atomic64_inc(&rtpe_statsps.errors); |
|
|
|
goto unlock_out; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
handler_ret = media_packet_decrypt(s, rtcp, in_srtp, sink, sfd, fsin, tv, ssrc_in, &rwf_out); |
|
|
|
|
|
|
|
// If recording pcap dumper is set, then we record the call. |
|
|
|
if (call->recording) |
|
|
|
dump_packet(call->recording, stream, s); |
|
|
|
|
|
|
|
if (G_LIKELY(handler_ret >= 0)) |
|
|
|
handler_ret = media_packet_encrypt(s, rtcp, out_srtp, in_srtp, ssrc_out, rwf_out); |
|
|
|
|
|
|
|
if (handler_ret > 0) { |
|
|
|
unkernelize(stream); |
|
|
|
update = 1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int address_check = media_packet_address_check(stream, sink, sfd, fsin); |
|
|
|
if (address_check == -1) |
|
|
|
goto drop; |
|
|
|
if ((address_check & 1)) |
|
|
|
update = 1; |
|
|
|
if ((address_check & 2)) |
|
|
|
media_packet_kernel_check(stream, sink); |
|
|
|
|
|
|
|
|
|
|
|
mutex_lock(&sink->out_lock); |
|
|
|
|
|
|
|
if (!sink->advertised_endpoint.port |
|
|
|
|| (is_addr_unspecified(&sink->advertised_endpoint.address) |
|
|
|
&& !is_trickle_ice_address(&sink->advertised_endpoint)) |
|
|
|
|| stun_ret || handler_ret < 0) |
|
|
|
|| handler_ret < 0) |
|
|
|
goto drop; |
|
|
|
|
|
|
|
// s is my packet? |
|
|
|
ret = socket_sendto(&sink->selected_sfd->socket, s->s, s->len, &sink->endpoint); |
|
|
|
__C_DBG("Forward to sink endpoint: %s:%d", sockaddr_print_buf(&sink->endpoint.address), sink->endpoint.port); |
|
|
|
|
|
|
|
@ -1529,16 +1638,6 @@ out: |
|
|
|
if (ret == 0 && update) |
|
|
|
ret = 1; |
|
|
|
|
|
|
|
#if RTP_LOOP_PROTECT |
|
|
|
done: |
|
|
|
#endif |
|
|
|
if (unk) |
|
|
|
__stream_unconfirm(stream); |
|
|
|
mutex_unlock(&stream->in_lock); |
|
|
|
if (unk) { |
|
|
|
stream_unconfirm(stream->rtp_sink); |
|
|
|
stream_unconfirm(stream->rtcp_sink); |
|
|
|
} |
|
|
|
unlock_out: |
|
|
|
rwlock_unlock_r(&call->master_lock); |
|
|
|
|
|
|
|
|