aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 d5bd3ddc..8b6a56be 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 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;
}
diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c
index 5565e731..46eb9844 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;