aboutsummaryrefslogtreecommitdiffstats
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
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
-rw-r--r--openbsc/include/openbsc/mgcp_internal.h14
-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
-rw-r--r--openbsc/tests/mgcp/mgcp_test.c5
5 files changed, 160 insertions, 32 deletions
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h
index d5bd3ddc6..8b6a56be8 100644
--- a/openbsc/include/openbsc/mgcp_internal.h
+++ b/openbsc/include/openbsc/mgcp_internal.h
@@ -40,22 +40,30 @@ enum mgcp_trunk_type {
MGCP_TRUNK_E1,
};
+struct mgcp_rtp_stream_state {
+ uint32_t ssrc;
+ uint16_t last_seq;
+ uint32_t last_timestamp;
+ uint32_t err_ts_counter;
+ int32_t last_tsdelta;
+};
+
struct mgcp_rtp_state {
int initialized;
int patch;
uint32_t orig_ssrc;
- uint32_t ssrc;
uint16_t base_seq;
- uint16_t max_seq;
int seq_offset;
int cycles;
- uint32_t last_timestamp;
int32_t timestamp_offset;
uint32_t jitter;
int32_t transit;
+
+ struct mgcp_rtp_stream_state in_stream;
+ struct mgcp_rtp_stream_state out_stream;
};
struct mgcp_rtp_end {
diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c
index 2b55527aa..6966be64e 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 616e0a94c..aec2cb0a4 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 3c239d8df..5aeb3930e 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;
}
diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c
index 5565e7316..46eb98440 100644
--- a/openbsc/tests/mgcp/mgcp_test.c
+++ b/openbsc/tests/mgcp/mgcp_test.c
@@ -97,7 +97,8 @@
"C: 2\r\n"
#define DLCX_RET "250 7 OK\r\n" \
- "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n"
+ "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \
+ "X-Osmo-CP: EC TIS=0, TOS=0, TIR=0, TOR=0\r\n"
#define RQNT "RQNT 186908780 1@mgw MGCP 1.0\r\n" \
"X: B244F267488\r\n" \
@@ -309,7 +310,7 @@ static void test_packet_loss_calc(void)
state.initialized = 1;
state.base_seq = pl_test_dat[i].base_seq;
- state.max_seq = pl_test_dat[i].max_seq;
+ state.out_stream.last_seq = pl_test_dat[i].max_seq;
state.cycles = pl_test_dat[i].cycles;
rtp.packets = pl_test_dat[i].packets;