diff options
Diffstat (limited to 'src/mgcp/mgcp_network.c')
-rw-r--r-- | src/mgcp/mgcp_network.c | 152 |
1 files changed, 132 insertions, 20 deletions
diff --git a/src/mgcp/mgcp_network.c b/src/mgcp/mgcp_network.c index f930238..3bbc600 100644 --- a/src/mgcp/mgcp_network.c +++ b/src/mgcp/mgcp_network.c @@ -2,8 +2,8 @@ /* The protocol implementation */ /* - * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2009-2011 by On-Waves + * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2012 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify @@ -38,12 +38,18 @@ #warning "Make use of the rtp proxy code" /* attempt to determine byte order */ -#include <sys/types.h> #include <sys/param.h> #include <limits.h> +#include <time.h> #ifndef __BYTE_ORDER -#error "__BYTE_ORDER should be defined by someone" +# ifdef __APPLE__ +# define __BYTE_ORDER __DARWIN_BYTE_ORDER +# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN +# else +# error "__BYTE_ORDER should be defined by someone" +# endif #endif /* according to rtp_proxy.c RFC 3550 */ @@ -68,6 +74,10 @@ struct rtp_hdr { uint32_t ssrc; } __attribute__((packed)); +#define RTP_SEQ_MOD (1 << 16) +#define RTP_MAX_DROPOUT 3000 +#define RTP_MAX_MISORDER 100 + enum { DEST_NETWORK = 0, @@ -82,6 +92,29 @@ enum { #define DUMMY_LOAD 0x23 +/** + * This does not need to be a precision timestamp and + * is allowed to wrap quite fast. The returned value is + * milli seconds now. + */ +uint32_t get_current_ts(void) +{ + struct timespec tp; + uint64_t ret; + + memset(&tp, 0, sizeof(tp)); + if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) + LOGP(DMGCP, LOGL_NOTICE, + "Getting the clock failed.\n"); + + /* convert it to useconds */ + ret = tp.tv_sec; + ret *= 1000; + ret += tp.tv_nsec / 1000 / 1000; + + return ret; +} + static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) { struct sockaddr_in out; @@ -100,10 +133,20 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp) endp->net_end.rtp_port, buf, 1); } +/** + * The RFC 3550 Appendix A assumes there are multiple sources but + * some of the supported endpoints (e.g. the nanoBTS) can only handle + * one source and this code will patch packages to appear as if there + * is only one source. + * There is also no probation period for new sources. Every package + * we receive will be seen as a switch in streams. + */ static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, int payload, struct sockaddr_in *addr, char *data, int len) { - uint16_t seq; + uint32_t arrival_time; + int32_t transit, d; + uint16_t seq, udelta; uint32_t timestamp; struct rtp_hdr *rtp_hdr; @@ -113,15 +156,19 @@ static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *s rtp_hdr = (struct rtp_hdr *) data; seq = ntohs(rtp_hdr->sequence); timestamp = ntohl(rtp_hdr->timestamp); + arrival_time = get_current_ts(); if (!state->initialized) { - state->seq_no = seq - 1; + state->base_seq = seq; + state->max_seq = seq - 1; state->ssrc = state->orig_ssrc = rtp_hdr->ssrc; state->initialized = 1; state->last_timestamp = timestamp; + state->jitter = 0; + state->transit = arrival_time - timestamp; } else if (state->ssrc != rtp_hdr->ssrc) { state->ssrc = rtp_hdr->ssrc; - state->seq_offset = (state->seq_no + 1) - seq; + state->seq_offset = (state->max_seq + 1) - seq; state->timestamp_offset = state->last_timestamp - timestamp; #warning "Always allow to patch the SSRC" state->patch = 1; @@ -141,11 +188,35 @@ static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *s rtp_hdr->timestamp = htonl(timestamp); } - /* seq changed, now compare if we have lost something */ - if (state->seq_no + 1u != seq) - state->lost_no = abs(seq - (state->seq_no + 1)); - state->seq_no = seq; + /* + * The below takes the shape of the validation from Appendix A. Check + * if there is something weird with the sequence number, otherwise check + * for a wrap around in the sequence number. + */ + udelta = seq - state->max_seq; + if (udelta < RTP_MAX_DROPOUT) { + if (seq < state->max_seq) + state->cycles += RTP_SEQ_MOD; + } else if (udelta <= RTP_SEQ_MOD + RTP_MAX_MISORDER) { + LOGP(DMGCP, LOGL_NOTICE, + "RTP seqno made a very large jump on 0x%x delta: %u\n", + ENDPOINT_NUMBER(endp), udelta); + } + + /* + * calculate the jitter between the two packages. The TS should be + * taken closer to the read function. This was taken from the + * Appendix A of RFC 3550. The local timestamp has a usec resolution. + */ + transit = arrival_time - timestamp; + d = transit - state->transit; + state->transit = transit; + if (d < 0) + d = -d; + state->jitter += d - ((state->jitter + 8) >> 4); + + state->max_seq = seq; state->last_timestamp = timestamp; if (payload < 0) @@ -214,7 +285,7 @@ static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp, &endp->taps[MGCP_TAP_NET_OUT], buf, rc); return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, endp->net_end.rtp_port, buf, rc); - } else { + } else if (!tcfg->omit_rtcp) { return udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr, endp->net_end.rtcp_port, buf, rc); } @@ -227,15 +298,17 @@ static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp, &endp->taps[MGCP_TAP_BTS_OUT], buf, rc); return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr, endp->bts_end.rtp_port, buf, rc); - } else { + } else if (!tcfg->omit_rtcp) { return udp_send(endp->bts_end.rtcp.fd, &endp->bts_end.addr, endp->bts_end.rtcp_port, buf, rc); } } + + return 0; } -static int recevice_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *addr, - char *buf, int bufsize) +static int receive_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *addr, + char *buf, int bufsize) { int rc; socklen_t slen = sizeof(*addr); @@ -266,14 +339,16 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) endp = (struct mgcp_endpoint *) fd->data; - rc = recevice_from(endp, fd->fd, &addr, buf, sizeof(buf)); + rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf)); if (rc <= 0) return -1; if (memcmp(&addr.sin_addr, &endp->net_end.addr, sizeof(addr.sin_addr)) != 0) { LOGP(DMGCP, LOGL_ERROR, - "Data from wrong address %s on 0x%x\n", - inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp)); + "Endpoint 0x%x data from wrong address %s vs. ", + ENDPOINT_NUMBER(endp), inet_ntoa(addr.sin_addr)); + LOGPC(DMGCP, LOGL_ERROR, + "%s\n", inet_ntoa(endp->net_end.addr)); return -1; } @@ -294,6 +369,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) proto = fd == &endp->net_end.rtp ? PROTO_RTP : PROTO_RTCP; endp->net_end.packets += 1; + endp->net_end.octets += rc; forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc); if (endp->is_transcoded) @@ -338,7 +414,7 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) endp = (struct mgcp_endpoint *) fd->data; - rc = recevice_from(endp, fd->fd, &addr, buf, sizeof(buf)); + rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf)); if (rc <= 0) return -1; @@ -372,6 +448,7 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) /* do this before the loop handling */ endp->bts_end.packets += 1; + endp->bts_end.octets += rc; forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc); if (endp->is_transcoded) @@ -389,7 +466,7 @@ static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_ int rc, proto; cfg = _endp->cfg; - rc = recevice_from(_endp, fd->fd, &addr, buf, sizeof(buf)); + rc = receive_from(_endp, fd->fd, &addr, buf, sizeof(buf)); if (rc <= 0) return -1; @@ -575,3 +652,38 @@ int mgcp_free_rtp_port(struct mgcp_rtp_end *end) return 0; } + + +void mgcp_state_calc_loss(struct mgcp_rtp_state *state, + struct mgcp_rtp_end *end, uint32_t *expected, + int *loss) +{ + *expected = state->cycles + state->max_seq; + *expected = *expected - state->base_seq + 1; + + if (!state->initialized) { + *expected = 0; + *loss = 0; + return; + } + + /* + * Make sure the sign is correct and use the biggest + * positive/negative number that fits. + */ + *loss = *expected - end->packets; + if (*expected < end->packets) { + if (*loss > 0) + *loss = INT_MIN; + } else { + if (*loss < 0) + *loss = INT_MAX; + } +} + +uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *state) +{ + if (!state->initialized) + return 0; + return state->jitter >> 4; +} |