229 lines
8.2 KiB
229 lines
8.2 KiB
package NGCP::Panel::Utils::Callflow;
|
|
use strict;
|
|
use warnings;
|
|
|
|
use NGCP::Panel::Utils::GD::Simple;
|
|
|
|
sub generate_pcap {
|
|
my $packets = shift;
|
|
|
|
my $pcap = pack("LSSlLLL",
|
|
0xa1b2c3d4, # magic number
|
|
2, 4, # major/minor version number
|
|
0, 0, # gmt offset and timestamp accuracy
|
|
0xffff, # snap length
|
|
1, # data link type (http://www.tcpdump.org/linktypes.html)
|
|
);
|
|
|
|
foreach my $pkg(@{$packets}) {
|
|
my($ts_sec, $ts_usec) = $pkg->timestamp =~ /^(\d+)\.(\d+)$/;
|
|
my $len = length($pkg->header) + length($pkg->payload) + length($pkg->trailer);
|
|
|
|
$pcap .= pack("LLLLa*a*a*",
|
|
$ts_sec, $ts_usec, # timestamp
|
|
$len, $len, # bytes on-wire/off-wire
|
|
$pkg->header,
|
|
$pkg->payload,
|
|
$pkg->trailer,
|
|
);
|
|
}
|
|
return $pcap;
|
|
}
|
|
|
|
sub draw_line {
|
|
my ($c, $from_x, $from_y, $to_x, $to_y, $width, $color) = @_;
|
|
$c->fgcolor($color);
|
|
$c->moveTo($from_x, $from_y);
|
|
$c->penSize($width, $width);
|
|
$c->lineTo($to_x, $to_y);
|
|
}
|
|
|
|
sub draw_arrow {
|
|
my ($c, $from_x, $from_y, $to_x, $to_y, $width, $color) = @_;
|
|
$c->fgcolor($color);
|
|
$c->moveTo($from_x, $from_y);
|
|
$c->penSize($width, $width);
|
|
$c->lineTo($to_x, $to_y);
|
|
my $poly = GD::Polygon->new;
|
|
$poly->addPt($to_x, $to_y);
|
|
my $dir = ($to_x > $from_x) ? -1 : 1;
|
|
$poly->addPt($to_x + 4*$width*$dir, $to_y - 2*$width-(($width%2)?0:1));
|
|
$poly->addPt($to_x + 4*$width*$dir, $to_y + 2*$width);
|
|
my $oldbgcolor = $c->bgcolor();
|
|
$c->bgcolor($color);
|
|
$c->penSize(1,1);
|
|
$c->polygon($poly);
|
|
$c->bgcolor($oldbgcolor);
|
|
}
|
|
|
|
sub draw_text {
|
|
my ($c, $x, $y, $ftype, $fsize, $fcolor, $txt) = @_;
|
|
$c->font($ftype);
|
|
$c->fontsize($fsize);
|
|
$c->fgcolor($fcolor);
|
|
$c->moveTo($x, $y);
|
|
$c->string($txt);
|
|
my @b = $c->stringBounds($txt);
|
|
my %bounds = ('x', $x, 'y', $y, 'dx', $b[0], 'dy', $b[1]);
|
|
return %bounds;
|
|
}
|
|
|
|
sub process_callmap {
|
|
my $c = shift;
|
|
my $packets = shift;
|
|
my $r_png = shift;
|
|
my $r_info = shift;
|
|
my $i = 0;
|
|
|
|
my %int_uas = (
|
|
$c->config->{callflow}->{lb_int}, 'lb',
|
|
$c->config->{callflow}->{lb_ext}, 'lb',
|
|
$c->config->{callflow}->{proxy}, 'proxy',
|
|
$c->config->{callflow}->{sbc}, 'sbc',
|
|
$c->config->{callflow}->{app}, 'app',
|
|
$c->config->{callflow}->{pbx}, 'pbx',
|
|
$c->config->{callflow}->{b2b}, 'b2b',
|
|
);
|
|
|
|
my $canvas_margin = 100; # enough free space around diagram for text etc
|
|
my $canvas_elem_distance = 220; # horizontal distance between element lines
|
|
my $canvas_pkg_distance = 30; # vertical distance between packet arrows
|
|
|
|
my $canvas_elem_line_width = 2;
|
|
my $canvas_elem_line_color = 'darkgray';
|
|
my $canvas_elem_font = 'Courier:bold';
|
|
my $canvas_elem_font_size = 8;
|
|
my $canvas_elem_font_color = 'darkgray';
|
|
|
|
my $canvas_pkg_line_width = 2;
|
|
my $canvas_pkg_line_color = 'green';
|
|
my %canvas_pkg_line_colors = (TCP => 'blue');
|
|
my $canvas_pkg_font = 'Courier:bold';
|
|
my $canvas_pkg_font_size = 8;
|
|
my $canvas_pkg_font_color = 'dimgray';
|
|
|
|
my $html_padding = 5;
|
|
|
|
my %ext_uas = ();
|
|
my @uas = ();
|
|
|
|
### gather all involved elements
|
|
foreach my $packet(@{$packets}) {
|
|
if(exists($int_uas{$packet->src_ip.':'.$packet->src_port})) {
|
|
#print "skipping internal elem ".$packet->src_ip.':'.$packet->src_port." (".$int_uas{$packet->src_ip.':'.$packet->src_port}.")\n";
|
|
my $ua = $int_uas{$packet->src_ip.':'.$packet->src_port};
|
|
push (@uas, $ua) unless grep {$_ eq $ua} @uas;
|
|
}
|
|
elsif(exists($ext_uas{$packet->src_ip.':'.$packet->src_port})) {
|
|
#print "skipping known external elem ".$packet->src_ip.':'.$packet->src_port."\n";
|
|
}
|
|
else {
|
|
#print "adding new src elem ".$packet->src_ip.':'.$packet->src_port."\n";
|
|
$ext_uas{$packet->src_ip.':'.$packet->src_port} = 1;
|
|
# TODO: prefix "proto:" as well
|
|
push @uas, $packet->src_ip.':'.$packet->src_port;
|
|
}
|
|
|
|
if(exists($int_uas{$packet->dst_ip.':'.$packet->dst_port})) {
|
|
#print "skipping internal elem ".$packet->dst_ip.':'.$packet->dst_port." (".$int_uas{$packet->dst_ip.':'.$packet->dst_port}.")\n";
|
|
my $ua = $int_uas{$packet->dst_ip.':'.$packet->dst_port};
|
|
push (@uas, $ua) unless grep {$_ eq $ua} @uas;
|
|
}
|
|
elsif(exists($ext_uas{$packet->dst_ip.':'.$packet->dst_port})) {
|
|
#print "skipping known external elem ".$packet->dst_ip.':'.$packet->dst_port."\n";
|
|
}
|
|
else {
|
|
#print "adding new dst elem ".$packet->dst_ip.':'.$packet->dst_port."\n";
|
|
$ext_uas{$packet->dst_ip.':'.$packet->dst_port} = 1;
|
|
# TODO: prefix "proto:" as well
|
|
push @uas, $packet->dst_ip.':'.$packet->dst_port;
|
|
}
|
|
}
|
|
|
|
### calculate x position of all uas
|
|
my %uas_pos_x = ();
|
|
$i = 0;
|
|
foreach my $ua(@uas) {
|
|
my $name = $ua;
|
|
foreach my $k(keys %int_uas) {
|
|
if($ua eq $int_uas{$k}) {
|
|
$uas_pos_x{$k} = $canvas_margin + $canvas_elem_distance*$i;
|
|
}
|
|
}
|
|
$uas_pos_x{$ua} = $canvas_margin + $canvas_elem_distance*$i;
|
|
++$i;
|
|
}
|
|
|
|
### calculate canvas size
|
|
# TODO: take into account length of "proto:[ipv6]:port"
|
|
my $canvas_width = 2*$canvas_margin + $canvas_elem_distance*(@uas - 1);
|
|
my $canvas_height = 2*$canvas_margin + $canvas_pkg_distance*(@{$packets} + 1); # leave one pkg_distance free at begin and end
|
|
my $canvas = NGCP::Panel::Utils::GD::Simple->new($canvas_width, $canvas_height);
|
|
$canvas->bgcolor('white');
|
|
|
|
### prepare html
|
|
$r_info->{width} = $canvas_width;
|
|
$r_info->{height} = $canvas_height;
|
|
$r_info->{areas} = ();
|
|
|
|
### draw vertical lines
|
|
my $offset = $canvas_margin;
|
|
foreach my $ua(@uas) {
|
|
draw_line($canvas, $offset, $canvas_margin, $offset, $canvas_height-$canvas_margin, $canvas_elem_line_width, $canvas_elem_line_color);
|
|
my @bounds = $canvas->stringBounds($ua); # get bounds for text centering
|
|
draw_text($canvas, $offset-int(abs($bounds[0])/2), $canvas_margin-abs($bounds[1]), $canvas_elem_font, $canvas_elem_font_size, $canvas_elem_font_color, $ua);
|
|
$offset += $canvas_elem_distance;
|
|
}
|
|
|
|
### draw arrows
|
|
my $last_timestamp = undef;
|
|
my $y_offset = $canvas_margin + $canvas_pkg_distance;
|
|
$i = 1;
|
|
foreach my $packet(@{$packets}) {
|
|
my $time_offset = defined $last_timestamp ? ($packet->timestamp->hires_epoch - $last_timestamp->hires_epoch) : 0;
|
|
$last_timestamp = $packet->timestamp;
|
|
my $from_x = $uas_pos_x{$packet->src_ip.':'.$packet->src_port};
|
|
my $to_x = $uas_pos_x{$packet->dst_ip.':'.$packet->dst_port};
|
|
#print "arrow from ".$packet->src_ip.':'.$packet->src_port." to ".$packet->dst_ip.':'.$packet->dst_port.": $from_x - $to_x\n";
|
|
draw_arrow($canvas, $from_x, $y_offset, $to_x, $y_offset, $canvas_pkg_line_width,
|
|
$canvas_pkg_line_colors{$packet->transport} || $canvas_pkg_line_color);
|
|
$packet->payload =~ /\ncseq:\s*(\d+)\s+[a-zA-Z]+/i;
|
|
my $cseq = $1 ? $1 : '?';
|
|
my $txt = sprintf($i.'. '.$packet->get_column('method').' ('.$cseq.', +%0.3fs)', $time_offset);
|
|
my @bounds = $canvas->stringBounds($txt); # get bounds for text centering
|
|
if($from_x < $to_x) {
|
|
$from_x = $from_x+int($canvas_elem_distance/2)-int($bounds[0]/2);
|
|
} elsif($from_x > $to_x) {
|
|
$from_x = $from_x-int($canvas_elem_distance/2)-int($bounds[0]/2);
|
|
} else {
|
|
$from_x += 10; # call to itself, e.g. in cf loop
|
|
}
|
|
draw_text($canvas, $from_x, $y_offset-int(abs($bounds[1])/2), $canvas_pkg_font, $canvas_pkg_font_size, $canvas_pkg_font_color, $txt);
|
|
|
|
push @{$r_info->{areas}}, {"id", $packet->id, "coords", ($from_x-$html_padding).','.($y_offset-abs($bounds[1])-$html_padding).','.($from_x+abs($bounds[0])+$html_padding).','.($y_offset)};
|
|
|
|
$y_offset += $canvas_pkg_distance;
|
|
++$i;
|
|
}
|
|
$$r_png = $canvas->png;
|
|
}
|
|
|
|
|
|
sub generate_callmap {
|
|
my $c = shift;
|
|
my $packets = shift;
|
|
my $png; my %info;
|
|
process_callmap($c, $packets, \$png, \%info);
|
|
return \%info;
|
|
}
|
|
|
|
sub generate_callmap_png {
|
|
my $c = shift;
|
|
my $packets = shift;
|
|
my $png; my %info;
|
|
process_callmap($c, $packets, \$png, \%info);
|
|
return $png;
|
|
}
|
|
|
|
1;
|