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
This commit is contained in:
Jacob Erlbeck 2013-11-25 12:53:28 +01:00 committed by Holger Hans Peter Freyther
parent ec37bb2956
commit 50079a1843
5 changed files with 160 additions and 32 deletions

View File

@ -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 {

View File

@ -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) {

View File

@ -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';
}

View File

@ -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;
}

View File

@ -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;