aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2013-11-25 12:53:28 +0100
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2013-11-25 18:07:21 +0100
commit50079a18434836fc6193f7d8ee30aeda6adbf872 (patch)
treecf5244f6d4f3873b00fd5c2b677236f0e377abc8 /openbsc/src
parentec37bb29563c92bf405b732019db4ed3b2cc9477 (diff)
mgcp/rtp: Add counter for invalid RTP timestamp deltas
This patch modifies the patch_and_count() function to check for RTP timestamp inconsistencies. It basically checks, whether dTS/dSeqNo remains constant. If this fails, the corresponding counter is incremented. There are four counter for this: Incoming and outgoing, each for streams from the BTS and the net. Note that this approach presumes, that the per RTP packet duration (in samples) remains the same throughout the entire stream. Changing the number of speech frames per channel and packet will be detected as error. In addition, the VTY command 'show mgcp' is extended by an optional 'stats' to show the counter values, too. Ticket: OW#964 Sponsored-by: On-Waves ehf
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/libmgcp/mgcp_network.c126
-rw-r--r--openbsc/src/libmgcp/mgcp_protocol.c24
-rw-r--r--openbsc/src/libmgcp/mgcp_vty.c23
3 files changed, 146 insertions, 27 deletions
diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c
index 2b55527a..6966be64 100644
--- a/openbsc/src/libmgcp/mgcp_network.c
+++ b/openbsc/src/libmgcp/mgcp_network.c
@@ -134,6 +134,73 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp)
endp->net_end.rtp_port, buf, 1);
}
+static int check_rtp_timestamp(struct mgcp_endpoint *endp,
+ struct mgcp_rtp_stream_state *state,
+ struct mgcp_rtp_end *rtp_end,
+ struct sockaddr_in *addr,
+ uint16_t seq, uint32_t timestamp,
+ const char *text, int32_t *tsdelta_out)
+{
+ int32_t tsdelta;
+
+ /* Not fully intialized, skip */
+ if (state->last_tsdelta == 0 && timestamp == state->last_timestamp)
+ return 0;
+
+ if (seq == state->last_seq) {
+ if (timestamp != state->last_timestamp) {
+ state->err_ts_counter += 1;
+ LOGP(DMGCP, LOGL_ERROR,
+ "The %s timestamp delta is != 0 but the sequence "
+ "number %d is the same"
+ "on 0x%x SSRC: %u timestamp: %u "
+ "from %s:%d in %d\n",
+ text, seq,
+ ENDPOINT_NUMBER(endp), state->ssrc, timestamp,
+ inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+ endp->conn_mode);
+ }
+ return 0;
+ }
+
+ tsdelta =
+ (int32_t)(timestamp - state->last_timestamp) /
+ (int16_t)(seq - state->last_seq);
+
+ if (tsdelta == 0) {
+ state->err_ts_counter += 1;
+ LOGP(DMGCP, LOGL_ERROR,
+ "The %s timestamp delta is %d "
+ "on 0x%x SSRC: %u timestamp: %u "
+ "from %s:%d in %d\n",
+ text, tsdelta,
+ ENDPOINT_NUMBER(endp), state->ssrc, timestamp,
+ inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+ endp->conn_mode);
+
+ return 0;
+ }
+
+ if (state->last_tsdelta != tsdelta) {
+ if (state->last_tsdelta) {
+ state->err_ts_counter += 1;
+ LOGP(DMGCP, LOGL_ERROR,
+ "The %s timestamp delta changes from %d to %d "
+ "on 0x%x SSRC: %u timestamp: %u from %s:%d in %d\n",
+ text, state->last_tsdelta, tsdelta,
+ ENDPOINT_NUMBER(endp), state->ssrc, timestamp,
+ inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+ endp->conn_mode);
+ }
+ }
+
+ if (tsdelta_out)
+ *tsdelta_out = tsdelta;
+
+ return 1;
+}
+
+
/**
* The RFC 3550 Appendix A assumes there are multiple sources but
* some of the supported endpoints (e.g. the nanoBTS) can only handle
@@ -143,13 +210,15 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp)
* 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)
+ struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr,
+ char *data, int len)
{
uint32_t arrival_time;
int32_t transit, d;
uint16_t seq, udelta;
uint32_t timestamp;
struct rtp_hdr *rtp_hdr;
+ int payload = rtp_end->payload_type;
if (len < sizeof(*rtp_hdr))
return;
@@ -160,24 +229,37 @@ static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *s
arrival_time = get_current_ts();
if (!state->initialized) {
+ state->in_stream.last_seq = seq - 1;
+ state->in_stream.ssrc = state->orig_ssrc = rtp_hdr->ssrc;
+ state->in_stream.last_tsdelta = 0;
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->max_seq + 1) - seq;
- state->timestamp_offset = state->last_timestamp - timestamp;
+ state->out_stream = state->in_stream;
+ } else if (state->in_stream.ssrc != rtp_hdr->ssrc) {
+ state->in_stream.ssrc = rtp_hdr->ssrc;
+ state->seq_offset = (state->out_stream.last_seq + 1) - seq;
+ state->timestamp_offset = state->out_stream.last_timestamp - timestamp;
state->patch = endp->allow_patch;
LOGP(DMGCP, LOGL_NOTICE,
"The SSRC changed on 0x%x SSRC: %u offset: %d from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), state->ssrc, state->seq_offset,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode);
+ ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+ state->seq_offset, inet_ntoa(addr->sin_addr),
+ ntohs(addr->sin_port), endp->conn_mode);
+
+ state->in_stream.last_tsdelta = 0;
+ } else {
+ /* Compute current per-packet timestamp delta */
+ check_rtp_timestamp(endp, &state->in_stream, rtp_end, addr,
+ seq, timestamp, "input",
+ &state->in_stream.last_tsdelta);
}
+ /* Save before patching */
+ state->in_stream.last_timestamp = timestamp;
+ state->in_stream.last_seq = seq;
+
/* apply the offset and store it back to the packet */
if (state->patch) {
seq += state->seq_offset;
@@ -188,14 +270,21 @@ static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *s
rtp_hdr->timestamp = htonl(timestamp);
}
+ /* Check again, whether the timestamps are still valid */
+ check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr,
+ seq, timestamp, "output",
+ &state->out_stream.last_tsdelta);
+
/*
* 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.
+ *
+ * Note that last_seq is used where the appendix mentions max_seq.
*/
- udelta = seq - state->max_seq;
+ udelta = seq - state->out_stream.last_seq;
if (udelta < RTP_MAX_DROPOUT) {
- if (seq < state->max_seq)
+ if (seq < state->out_stream.last_seq)
state->cycles += RTP_SEQ_MOD;
} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
LOGP(DMGCP, LOGL_NOTICE,
@@ -215,9 +304,10 @@ static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *s
d = -d;
state->jitter += d - ((state->jitter + 8) >> 4);
-
- state->max_seq = seq;
- state->last_timestamp = timestamp;
+ /* Save output values */
+ state->out_stream.last_seq = seq;
+ state->out_stream.last_timestamp = timestamp;
+ state->out_stream.ssrc = rtp_hdr->ssrc;
if (payload < 0)
return;
@@ -280,7 +370,7 @@ static int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
if (dest == MGCP_DEST_NET) {
if (is_rtp) {
patch_and_count(endp, &endp->bts_state,
- endp->net_end.payload_type,
+ &endp->net_end,
addr, buf, rc);
forward_data(endp->net_end.rtp.fd,
&endp->taps[MGCP_TAP_NET_OUT], buf, rc);
@@ -295,7 +385,7 @@ static int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
} else {
if (is_rtp) {
patch_and_count(endp, &endp->net_state,
- endp->bts_end.payload_type,
+ &endp->bts_end,
addr, buf, rc);
forward_data(endp->bts_end.rtp.fd,
&endp->taps[MGCP_TAP_BTS_OUT], buf, rc);
@@ -684,7 +774,7 @@ 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 = state->cycles + state->out_stream.last_seq;
*expected = *expected - state->base_seq + 1;
if (!state->initialized) {
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index 616e0a94..aec2cb0a 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -1163,14 +1163,30 @@ void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
{
uint32_t expected, jitter;
int ploss;
+ int nchars;
mgcp_state_calc_loss(&endp->net_state, &endp->net_end,
&expected, &ploss);
jitter = mgcp_state_calc_jitter(&endp->net_state);
- snprintf(msg, size, "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
- endp->bts_end.packets, endp->bts_end.octets,
- endp->net_end.packets, endp->net_end.octets,
- ploss, jitter);
+ nchars = snprintf(msg, size,
+ "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
+ endp->bts_end.packets, endp->bts_end.octets,
+ endp->net_end.packets, endp->net_end.octets,
+ ploss, jitter);
+ if (nchars < 0 || nchars >= size)
+ goto truncate;
+
+ msg += nchars;
+ size -= nchars;
+
+ /* Error Counter */
+ snprintf(msg, size,
+ "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
+ endp->net_state.in_stream.err_ts_counter,
+ endp->net_state.out_stream.err_ts_counter,
+ endp->bts_state.in_stream.err_ts_counter,
+ endp->bts_state.out_stream.err_ts_counter);
+truncate:
msg[size - 1] = '\0';
}
diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c
index 3c239d8d..5aeb3930 100644
--- a/openbsc/src/libmgcp/mgcp_vty.c
+++ b/openbsc/src/libmgcp/mgcp_vty.c
@@ -114,7 +114,7 @@ static int config_write_mgcp(struct vty *vty)
return CMD_SUCCESS;
}
-static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg)
+static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int verbose)
{
int i;
@@ -139,18 +139,31 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg)
endp->bts_end.packets, endp->net_end.packets,
endp->trans_net.packets, endp->trans_bts.packets,
VTY_NEWLINE);
+
+ if (verbose)
+ vty_out(vty,
+ " Timestamp Errs: BTS %d->%d, Net %d->%d%s",
+ endp->bts_state.in_stream.err_ts_counter,
+ endp->bts_state.out_stream.err_ts_counter,
+ endp->net_state.in_stream.err_ts_counter,
+ endp->net_state.out_stream.err_ts_counter,
+ VTY_NEWLINE);
}
}
-DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp",
- SHOW_STR "Display information about the MGCP Media Gateway")
+DEFUN(show_mcgp, show_mgcp_cmd,
+ "show mgcp [stats]",
+ SHOW_STR
+ "Display information about the MGCP Media Gateway\n"
+ "Include Statistics\n")
{
struct mgcp_trunk_config *trunk;
+ int show_stats = argc >= 1;
- dump_trunk(vty, &g_cfg->trunk);
+ dump_trunk(vty, &g_cfg->trunk, show_stats);
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
- dump_trunk(vty, trunk);
+ dump_trunk(vty, trunk, show_stats);
return CMD_SUCCESS;
}