You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

153 lines
3.6 KiB

#!/usr/bin/perl
use strict;
use warnings;
use Socket;
use UUID;
use BSD::Resource;
use Getopt::Long;
use Socket6;
my ($NUM, $RUNTIME) = (1000, 30);
my ($NODEL, $IP, $IPV6, $KEEPGOING);
GetOptions(
'no-delete' => \$NODEL,
'num-calls=i' => \$NUM,
'local-ip=s' => \$IP,
'local-ipv6=s' => \$IPV6,
'runtime=i' => \$RUNTIME,
'keep-going' => \$KEEPGOING, # don't stop sending rtp if a packet doesn't go through
) or die;
($IP || $IPV6) or die("at least one of --local-ip or --local-ipv6 must be given");
$SIG{ALRM} = sub { print "alarm!\n"; };
setrlimit(RLIMIT_NOFILE, 8000, 8000);
my @chrs = ('a' .. 'z', 'A' .. 'Z', '0' .. '9');
sub rand_str {
my ($len) = @_;
return join('', (map {$chrs[rand(@chrs)]} (1 .. $len)));
}
my $fd;
sub msg {
my ($l) = @_;
my $cookie = $$ . '_' . rand_str(10);
my $r;
while (1) {
send($fd, "$cookie $l", 0) or die $!;
my $err = '';
alarm(1);
recv($fd, $r, 0xffff, 0) or $err = "$!";
alarm(0);
$err =~ /interrupt/i and next;
$err and die $err;
last;
}
$r =~ s/^\Q$cookie\E +//s or die $r;
$r =~ s/[\r\n]+$//s;
return $r;
}
socket($fd, AF_INET, SOCK_DGRAM, 0) or die $!;
connect($fd, sockaddr_in(12222, inet_aton("127.0.0.1"))) or die $!;
msg('V') eq '20040107' or die;
my @calls;
sub do_rtp {
print("sending rtp\n");
for my $c (@calls) {
$c or next;
my ($fds,$outputs,$protos) = @$c[0,4,6];
for my $i ([0,1],[1,0]) {
my ($a, $b) = @$i;
my $pr = $$protos[$a];
my $payload = rand_str(100);
send($$fds[$a], $payload, 0, $$pr{sockaddr}($$outputs[$b][0],
inet_pton($$pr{family}, $$outputs[$b][1]))) or die $!;
my $x;
my $err = '';
alarm(1);
recv($$fds[$b], $x, 0xffff, 0) or $err = "$!";
alarm(0);
$err && $err !~ /interrupt/i and die $err;
if (($x || '') ne $payload) {
warn("no rtp reply received, ports $$outputs[$b][0] and $$outputs[$a][0]");
$KEEPGOING or undef($c);
}
}
}
}
my %proto_defs = (
ipv4 => {
code => 'I',
family => AF_INET,
reply => '4',
address => $IP,
sockaddr => \&sockaddr_in,
},
ipv6 => {
code => 'E',
family => AF_INET6,
reply => '6',
address => $IPV6,
sockaddr => \&sockaddr_in6,
},
);
my @protos_avail;
$IP and push(@protos_avail, $proto_defs{ipv4});
$IPV6 and push(@protos_avail, $proto_defs{ipv6});
for my $iter (1 .. $NUM) {
($iter % 10 == 0) and print("$iter\n"), do_rtp();
my $callid = rand_str(50);
my @protos = map {$protos_avail[int(rand(@protos_avail))]} (0,0);
my @prefixes = qw(US LS);
$prefixes[0] .= join('', (map {$_->{code}} @protos));
my (@fds,@ports,@ips,@tags,@outputs);
for my $ix ([0,1],[1,0]) {
my ($i,$j) = @$ix;
my ($pr,$pr_o) = @protos[@$ix];
socket($fds[$i], $$pr{family}, SOCK_DGRAM, 0) or die $!;
while (1) {
my $port = rand(0x7000) << 1 + 1024;
bind($fds[$i], $$pr{sockaddr}($port, inet_pton($$pr{family}, $$pr{address}))) and last;
}
my $addr = getsockname($fds[$i]);
my $ip;
($ports[$i], $ip) = $$pr{sockaddr}($addr);
$ips[$i] = inet_ntop($$pr{family}, $ip);
$tags[$i] = rand_str(15);
my $tagstr = ($i == 1 ? "$tags[0];1 " : '') . "$tags[$i];1";
my $o = msg("$prefixes[$i] $callid $ips[$i] $ports[$i] $tagstr");
$o =~ /^(\d+) ([\d.a-f:]+) ([46])[\r\n]*$/is or die $o;
$1 == 0 and die "mediaproxy ran out of ports";
$3 ne $$pr_o{reply} and die "incorrect address family reply code";
$outputs[$i] = [$1,$2];
}
push(@calls, [\(@fds,@ports,@ips,@tags,@outputs), $callid, \@protos]);
}
my $end = time() + $RUNTIME;
while (time() < $end) {
sleep(1);
do_rtp();
}
if (!$NODEL) {
print("deleting\n");
@calls = sort {rand() < .5} @calls;
for my $c (@calls) {
$c or next;
my ($tags, $callid) = @$c[3,5];
msg("D $callid $$tags[0] $$tags[1]");
}
}
print("done\n");