mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-13 16:21:01 +00:00
res_rtp_asterisk: Add a DEVMODE RTP drop packets CLI command
This patch makes it so when Asterisk is compiled in DEVMODE a CLI command is available that allows someone to drop incoming RTP packets. The command allows for dropping of packets once, or on a timed interval (e.g. drop 10 packets every 5 seconds). A user can also specify to drop packets by IP address. Change-Id: I25fa7ae9bad6ed68e273bbcccf0ee51cae6e7024
This commit is contained in:
committed by
George Joseph
parent
623abc2b6a
commit
b86f1ef54c
@@ -63,6 +63,7 @@
|
|||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "asterisk/conversions.h"
|
||||||
#include "asterisk/options.h"
|
#include "asterisk/options.h"
|
||||||
#include "asterisk/logger_category.h"
|
#include "asterisk/logger_category.h"
|
||||||
#include "asterisk/stun.h"
|
#include "asterisk/stun.h"
|
||||||
@@ -7529,6 +7530,113 @@ static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, st
|
|||||||
return AST_LIST_FIRST(&frames);
|
return AST_LIST_FIRST(&frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AST_DEVMODE
|
||||||
|
|
||||||
|
struct rtp_drop_packets_data {
|
||||||
|
/* Whether or not to randomize the number of packets to drop. */
|
||||||
|
unsigned int use_random_num;
|
||||||
|
/* Whether or not to randomize the time interval between packets drops. */
|
||||||
|
unsigned int use_random_interval;
|
||||||
|
/* The total number of packets to drop. If 'use_random_num' is true then this
|
||||||
|
* value becomes the upper bound for a number of random packets to drop. */
|
||||||
|
unsigned int num_to_drop;
|
||||||
|
/* The current number of packets that have been dropped during an interval. */
|
||||||
|
unsigned int num_dropped;
|
||||||
|
/* The optional interval to use between packet drops. If 'use_random_interval'
|
||||||
|
* is true then this values becomes the upper bound for a random interval used. */
|
||||||
|
struct timeval interval;
|
||||||
|
/* The next time a packet drop should be triggered. */
|
||||||
|
struct timeval next;
|
||||||
|
/* An optional IP address from which to drop packets from. */
|
||||||
|
struct ast_sockaddr addr;
|
||||||
|
/* The optional port from which to drop packets from. */
|
||||||
|
unsigned int port;
|
||||||
|
} drop_packets_data;
|
||||||
|
|
||||||
|
static void drop_packets_data_update(struct timeval tv)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* num_dropped keeps up with the number of packets that have been dropped for a
|
||||||
|
* given interval. Once the specified number of packets have been dropped and
|
||||||
|
* the next time interval is ready to trigger then set this number to zero (drop
|
||||||
|
* the next 'n' packets up to 'num_to_drop'), or if 'use_random_num' is set to
|
||||||
|
* true then set to a random number between zero and 'num_to_drop'.
|
||||||
|
*/
|
||||||
|
drop_packets_data.num_dropped = drop_packets_data.use_random_num ?
|
||||||
|
ast_random() % drop_packets_data.num_to_drop : 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A specified number of packets can be dropped at a given interval (e.g every
|
||||||
|
* 30 seconds). If 'use_random_interval' is false simply add the interval to
|
||||||
|
* the given time to get the next trigger point. If set to true, then get a
|
||||||
|
* random time between the given time and up to the specified interval.
|
||||||
|
*/
|
||||||
|
if (drop_packets_data.use_random_interval) {
|
||||||
|
/* Calculate as a percentage of the specified drop packets interval */
|
||||||
|
struct timeval interval = ast_time_create_by_unit(ast_time_tv_to_usec(
|
||||||
|
&drop_packets_data.interval) * ((double)(ast_random() % 100 + 1) / 100),
|
||||||
|
TIME_UNIT_MICROSECOND);
|
||||||
|
|
||||||
|
drop_packets_data.next = ast_tvadd(tv, interval);
|
||||||
|
} else {
|
||||||
|
drop_packets_data.next = ast_tvadd(tv, drop_packets_data.interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int should_drop_packets(struct ast_sockaddr *addr)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
if (!drop_packets_data.num_to_drop) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If an address has been specified then filter on it, and also the port if
|
||||||
|
* it too was included.
|
||||||
|
*/
|
||||||
|
if (!ast_sockaddr_isnull(&drop_packets_data.addr) &&
|
||||||
|
(drop_packets_data.port ?
|
||||||
|
ast_sockaddr_cmp(&drop_packets_data.addr, addr) :
|
||||||
|
ast_sockaddr_cmp_addr(&drop_packets_data.addr, addr)) != 0) {
|
||||||
|
/* Address and/or port does not match */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep dropping packets until we've reached the total to drop */
|
||||||
|
if (drop_packets_data.num_dropped < drop_packets_data.num_to_drop) {
|
||||||
|
++drop_packets_data.num_dropped;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Once the set number of packets has been dropped check to see if it's
|
||||||
|
* time to drop more.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (ast_tvzero(drop_packets_data.interval)) {
|
||||||
|
/* If no interval then drop specified number of packets and be done */
|
||||||
|
drop_packets_data.num_to_drop = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tv = ast_tvnow();
|
||||||
|
if (ast_tvcmp(tv, drop_packets_data.next) == -1) {
|
||||||
|
/* Still waiting for the next time interval to elapse */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The next time interval has elapsed so update the tracking structure
|
||||||
|
* in order to start dropping more packets, and figure out when the next
|
||||||
|
* time interval is.
|
||||||
|
*/
|
||||||
|
drop_packets_data_update(tv);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/*! \pre instance is locked */
|
/*! \pre instance is locked */
|
||||||
static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
|
static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
|
||||||
{
|
{
|
||||||
@@ -7823,6 +7931,14 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
|
|||||||
seqno &= 0xffff;
|
seqno &= 0xffff;
|
||||||
timestamp = ntohl(rtpheader[1]);
|
timestamp = ntohl(rtpheader[1]);
|
||||||
|
|
||||||
|
#ifdef AST_DEVMODE
|
||||||
|
if (should_drop_packets(&addr)) {
|
||||||
|
ast_debug(0, "(%p) RTP: drop received packet from %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6d)\n",
|
||||||
|
instance, ast_sockaddr_stringify(&addr), payloadtype, seqno, timestamp, res - hdrlen);
|
||||||
|
return &ast_null_frame;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (rtp_debug_test_addr(&addr)) {
|
if (rtp_debug_test_addr(&addr)) {
|
||||||
ast_verbose("Got RTP packet from %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6d)\n",
|
ast_verbose("Got RTP packet from %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6d)\n",
|
||||||
ast_sockaddr_stringify(&addr),
|
ast_sockaddr_stringify(&addr),
|
||||||
@@ -9088,11 +9204,145 @@ static char *handle_cli_rtcp_set_stats(struct ast_cli_entry *e, int cmd, struct
|
|||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AST_DEVMODE
|
||||||
|
|
||||||
|
static unsigned int use_random(struct ast_cli_args *a, int pos, unsigned int index)
|
||||||
|
{
|
||||||
|
return pos >= index && !ast_strlen_zero(a->argv[index - 1]) &&
|
||||||
|
!strcasecmp(a->argv[index - 1], "random");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *handle_cli_rtp_drop_incoming_packets(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||||
|
{
|
||||||
|
static const char * const completions_2[] = { "stop", "<N>", NULL };
|
||||||
|
static const char * const completions_3[] = { "random", "incoming packets", NULL };
|
||||||
|
static const char * const completions_5[] = { "on", "every", NULL };
|
||||||
|
static const char * const completions_units[] = { "random", "usec", "msec", "sec", "min", NULL };
|
||||||
|
|
||||||
|
unsigned int use_random_num = 0;
|
||||||
|
unsigned int use_random_interval = 0;
|
||||||
|
unsigned int num_to_drop = 0;
|
||||||
|
unsigned int interval = 0;
|
||||||
|
const char *interval_s = NULL;
|
||||||
|
const char *unit_s = NULL;
|
||||||
|
struct ast_sockaddr addr;
|
||||||
|
const char *addr_s = NULL;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case CLI_INIT:
|
||||||
|
e->command = "rtp drop";
|
||||||
|
e->usage =
|
||||||
|
"Usage: rtp drop [stop|[<N> [random] incoming packets[ every <N> [random] {usec|msec|sec|min}][ on <ip[:port]>]]\n"
|
||||||
|
" Drop RTP incoming packets.\n";
|
||||||
|
return NULL;
|
||||||
|
case CLI_GENERATE:
|
||||||
|
use_random_num = use_random(a, a->pos, 4);
|
||||||
|
use_random_interval = use_random(a, a->pos, 8 + use_random_num) ||
|
||||||
|
use_random(a, a->pos, 10 + use_random_num);
|
||||||
|
|
||||||
|
switch (a->pos - use_random_num - use_random_interval) {
|
||||||
|
case 2:
|
||||||
|
return ast_cli_complete(a->word, completions_2, a->n);
|
||||||
|
case 3:
|
||||||
|
return ast_cli_complete(a->word, completions_3 + use_random_num, a->n);
|
||||||
|
case 5:
|
||||||
|
return ast_cli_complete(a->word, completions_5, a->n);
|
||||||
|
case 7:
|
||||||
|
if (!strcasecmp(a->argv[a->pos - 2], "on")) {
|
||||||
|
ast_cli_completion_add(ast_strdup("every"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Fall through */
|
||||||
|
case 9:
|
||||||
|
if (!strcasecmp(a->argv[a->pos - 2 - use_random_interval], "every")) {
|
||||||
|
return ast_cli_complete(a->word, completions_units + use_random_interval, a->n);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (!strcasecmp(a->argv[a->pos - 3 - use_random_interval], "every")) {
|
||||||
|
ast_cli_completion_add(ast_strdup("on"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a->argc < 3) {
|
||||||
|
return CLI_SHOWUSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
use_random_num = use_random(a, a->argc, 4);
|
||||||
|
use_random_interval = use_random(a, a->argc, 8 + use_random_num) ||
|
||||||
|
use_random(a, a->argc, 10 + use_random_num);
|
||||||
|
|
||||||
|
if (!strcasecmp(a->argv[2], "stop")) {
|
||||||
|
/* rtp drop stop */
|
||||||
|
} else if (a->argc < 5) {
|
||||||
|
return CLI_SHOWUSAGE;
|
||||||
|
} else if (ast_str_to_uint(a->argv[2], &num_to_drop)) {
|
||||||
|
ast_cli(a->fd, "%s is not a valid number of packets to drop\n", a->argv[2]);
|
||||||
|
return CLI_FAILURE;
|
||||||
|
} else if (a->argc - use_random_num == 5) {
|
||||||
|
/* rtp drop <N> [random] incoming packets */
|
||||||
|
} else if (a->argc - use_random_num >= 7 && !strcasecmp(a->argv[5 + use_random_num], "on")) {
|
||||||
|
/* rtp drop <N> [random] incoming packets on <ip[:port]> */
|
||||||
|
addr_s = a->argv[6 + use_random_num];
|
||||||
|
if (a->argc - use_random_num - use_random_interval == 10 &&
|
||||||
|
!strcasecmp(a->argv[7 + use_random_num], "every")) {
|
||||||
|
/* rtp drop <N> [random] incoming packets on <ip[:port]> every <N> [random] {usec|msec|sec|min} */
|
||||||
|
interval_s = a->argv[8 + use_random_num];
|
||||||
|
unit_s = a->argv[9 + use_random_num + use_random_interval];
|
||||||
|
}
|
||||||
|
} else if (a->argc - use_random_num >= 8 && !strcasecmp(a->argv[5 + use_random_num], "every")) {
|
||||||
|
/* rtp drop <N> [random] incoming packets every <N> [random] {usec|msec|sec|min} */
|
||||||
|
interval_s = a->argv[6 + use_random_num];
|
||||||
|
unit_s = a->argv[7 + use_random_num + use_random_interval];
|
||||||
|
if (a->argc == 10 + use_random_num + use_random_interval &&
|
||||||
|
!strcasecmp(a->argv[8 + use_random_num + use_random_interval], "on")) {
|
||||||
|
/* rtp drop <N> [random] incoming packets every <N> [random] {usec|msec|sec|min} on <ip[:port]> */
|
||||||
|
addr_s = a->argv[9 + use_random_num + use_random_interval];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return CLI_SHOWUSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a->argc - use_random_num >= 8 && !interval_s && !addr_s) {
|
||||||
|
return CLI_SHOWUSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval_s && ast_str_to_uint(interval_s, &interval)) {
|
||||||
|
ast_cli(a->fd, "%s is not a valid interval number\n", interval_s);
|
||||||
|
return CLI_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
if (addr_s && !ast_sockaddr_parse(&addr, addr_s, 0)) {
|
||||||
|
ast_cli(a->fd, "%s is not a valid hostname[:port]\n", addr_s);
|
||||||
|
return CLI_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_packets_data.use_random_num = use_random_num;
|
||||||
|
drop_packets_data.use_random_interval = use_random_interval;
|
||||||
|
drop_packets_data.num_to_drop = num_to_drop;
|
||||||
|
drop_packets_data.interval = ast_time_create_by_unit_str(interval, unit_s);
|
||||||
|
ast_sockaddr_copy(&drop_packets_data.addr, &addr);
|
||||||
|
drop_packets_data.port = ast_sockaddr_port(&addr);
|
||||||
|
|
||||||
|
drop_packets_data_update(ast_tvnow());
|
||||||
|
|
||||||
|
return CLI_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct ast_cli_entry cli_rtp[] = {
|
static struct ast_cli_entry cli_rtp[] = {
|
||||||
AST_CLI_DEFINE(handle_cli_rtp_set_debug, "Enable/Disable RTP debugging"),
|
AST_CLI_DEFINE(handle_cli_rtp_set_debug, "Enable/Disable RTP debugging"),
|
||||||
AST_CLI_DEFINE(handle_cli_rtp_settings, "Display RTP settings"),
|
AST_CLI_DEFINE(handle_cli_rtp_settings, "Display RTP settings"),
|
||||||
AST_CLI_DEFINE(handle_cli_rtcp_set_debug, "Enable/Disable RTCP debugging"),
|
AST_CLI_DEFINE(handle_cli_rtcp_set_debug, "Enable/Disable RTCP debugging"),
|
||||||
AST_CLI_DEFINE(handle_cli_rtcp_set_stats, "Enable/Disable RTCP stats"),
|
AST_CLI_DEFINE(handle_cli_rtcp_set_stats, "Enable/Disable RTCP stats"),
|
||||||
|
#ifdef AST_DEVMODE
|
||||||
|
AST_CLI_DEFINE(handle_cli_rtp_drop_incoming_packets, "Drop RTP incoming packets"),
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static int rtp_reload(int reload, int by_external_config)
|
static int rtp_reload(int reload, int by_external_config)
|
||||||
|
Reference in New Issue
Block a user