summaryrefslogtreecommitdiffstats
path: root/contrib/ipfilter/perl
diff options
context:
space:
mode:
authordarrenr <darrenr@FreeBSD.org>2000-05-24 02:14:22 +0000
committerdarrenr <darrenr@FreeBSD.org>2000-05-24 02:14:22 +0000
commitdda6755c7b3b3b5d3e9111b7d0c39a7d8b5f3e12 (patch)
tree8658a81b1c1fcbbbec3b1df568cc198dd98d44ee /contrib/ipfilter/perl
parent1fa4f6782a7e92a232e56892d8bce7920d49292e (diff)
downloadFreeBSD-src-dda6755c7b3b3b5d3e9111b7d0c39a7d8b5f3e12.zip
FreeBSD-src-dda6755c7b3b3b5d3e9111b7d0c39a7d8b5f3e12.tar.gz
Import IP Filter 3.4.4 into FreeBSD-current
Diffstat (limited to 'contrib/ipfilter/perl')
-rw-r--r--contrib/ipfilter/perl/ipf-mrtg.pl22
-rw-r--r--contrib/ipfilter/perl/plog1036
2 files changed, 672 insertions, 386 deletions
diff --git a/contrib/ipfilter/perl/ipf-mrtg.pl b/contrib/ipfilter/perl/ipf-mrtg.pl
new file mode 100644
index 0000000..cce30ab
--- /dev/null
+++ b/contrib/ipfilter/perl/ipf-mrtg.pl
@@ -0,0 +1,22 @@
+#!/usr/local/bin/perl
+# reads stats and uptime for ip-filter for mrtg
+# ron@rosie.18james.com, 2 Jan 2000
+
+my $firewall = "IP Filter v3.3.3";
+my($in_pkts,$out_pkts) = (0,0);
+
+open(FW, "/sbin/ipfstat -hi|") || die "cannot open ipfstat -hi\n";
+while (<FW>) {
+ $in_pkts += $1 if (/^(\d+)\s+pass\s+in\s+quick.*group\s+1\d0/);
+}
+close(FW);
+open(FW, "/sbin/ipfstat -ho|") || die "cannot open ipfstat -ho\n";
+while (<FW>) {
+ $out_pkts += $1 if (/^(\d+)\s+pass\s+out\s+quick.*group\s+1\d0/);
+}
+print "$in_pkts\n",
+ "$out_pkts\n";
+my $uptime = `/usr/bin/uptime`;
+$uptime =~ /^\s+(\d{1,2}:\d{2}..)\s+up\s+(\d+)\s+(......),/;
+print "$2 $3\n",
+ "$firewall\n"; \ No newline at end of file
diff --git a/contrib/ipfilter/perl/plog b/contrib/ipfilter/perl/plog
index 8f3f73c..75c733b 100644
--- a/contrib/ipfilter/perl/plog
+++ b/contrib/ipfilter/perl/plog
@@ -1,8 +1,8 @@
#!/usr/bin/perl -wT
#
-# Author: Jefferson Ogata <jogata@nodc.noaa.gov>
-# Date: 1998/11/01
-# Version: 0.4
+# Author: Jefferson Ogata (JO317) <jogata@pobox.com>
+# Date: 2000/04/10
+# Version: 0.8
#
# Please feel free to use or redistribute this program if you find it useful.
# If you have suggestions, or even better, bits of new code, send them to me
@@ -14,17 +14,79 @@
# Parse ipmon output into a coherent form. This program only handles the
# lines regarding filter actions. It does not parse nat and state lines.
#
-# Present lines from ipmon to this program on standard input. One way I
-# often use is:
-# grep ' b ' logfile | plog
-# since a ' b ' sequence indicates a blocked packet.
+# Present lines from ipmon to this program on standard input.
+#
+# EXAMPLES
+#
+# plog -A block,log < /var/log/ipf
+#
+# Generate source and destination reports of all packets logged with
+# block or log actions.
+#
+# plog -S -s ./services www.example.com < /var/log/ipf
+#
+# Generate a source report of traffic to or from www.example.com using
+# the additional services defined in ./services.
+#
+# plog -nSA block < /var/log/ipf
+#
+# Generate a source report of all blocked packets with no hostname
+# lookups. This is handy for an initial pass to identify portscans or
+# other aggressive traffic.
+#
+# TODO
#
-# TODO:
# - Handle output from ipmon -v.
# - Handle timestamps from other locales. Anyone with a timestamp problem
# please email me the format of your timestamps.
+# - It looks as though short TCP or UDP packets will break things, but I
+# haven't seen any yet.
+#
+# CHANGES
+#
+# 2000/04/12 (0.9):
+# - Wasn't handling underscore in hostname,servicename fields; these may be
+# logged using ipmon -n. Observation by <ark@eltex.ru>.
+# - Hadn't properly attributed observation and fix for repetition counter in
+# 0.8 change log. Added John Ladwig to attribution. Thanks, John.
+#
+# 2000/04/10 (0.8):
+# - Service names can also have hyphens, dummy. I wasn't allowing these
+# either. Observation and fix thanks to Taso N. Devetzis
+# <devetzis@snet.net>.
+# - IP Filter now logs a repetition counter. Observation and fixes (changed
+# slightly) from Andy Kreiling <Andy@ntcs-inc.com> and John Ladwig
+# <jladwig@nts.umn.edu>.
+# - Added fix to handle new Solaris log format, e.g.:
+# Nov 30 04:49:37 raoul ipmon[121]: [ID 702911 local0.warning] 04:49:36.420
+541 hme0 @0:34 b 205.152.16.6,58596 -> 204.60.220.24,113 PR tcp len 20 44
+# Fix thanks to Taso N. Devetzis <devetzis@SNET.Net>.
+# - Added services map option.
+# - Added options for generating only source/destination tables.
+# - Added verbosity option.
+# - Added option for reporting traffic for specific hosts.
+# - Added some more ICMP unreachable codes, and made code and type names
+# match the ones in IP Filter parse.c.
+# - Condensed output format somewhat.
+# - Various minor improvements, perhaps slight speed improvements.
+# - Documented new options in usage() and tried to improve wording.
+#
+# 1999/08/02 (0.7):
+# - Hostnames can have hyphens, dummy. I wasn't allowing them in the syslog
+# line. Fix from Antoine Verheijen <antoine.verheijen@ualberta.ca>.
+#
+# 1999/05/05 (0.6):
+# - IRIX syslog prefixes the hostname with a severity code. Handle it. Fix
+# from John Ladwig <jladwig@nts.umn.edu>.
+#
+# 1999/05/05 (0.5):
+# - Protocols other than TCP, UDP, or ICMP have packet lengths reported in
+# parentheses for some reason. The script now handles this. Thanks to
+# Dispatcher <dispatch@blackhelicopters.org>.
+# - I had mixed up info-request and info-reply ICMP codes, and omitted the
+# traceroute code. Sorted this out. I had also missed code 0 for type 6
+# (alternate address for host). Thanks to John Ladwig <jladwig@nts.umn.edu>.
#
-# CHANGES:
# 1999/05/03:
# - Now accepts hostnames in the source and destination address fields, as
# well as port names in the port fields. This allows the people who are
@@ -32,28 +94,33 @@
# hostnames, you are vulnerable to forgery of DNS information, modified
# DNS information, and your log files will be larger also. If you are
# using this program you can have it look up the names for you (still
-# vulnerable to forgery) and keep your addresses all in numeric format,
-# so that packets from the same source will always show the same source
-# address regardless of what's up with DNS. Nevertheless, some people
-# wanted this, so here it is.
+# vulnerable to forgery) and keep your logged addresses all in numeric
+# format, so that packets from the same source will always show the same
+# source address regardless of what's up with DNS. Obviously, I don't
+# favor using ipmon -n. Nevertheless, some people wanted this, so here it
+# is.
# - Added S and n flags to %acts hash. Thanks to Stephen J. Roznowski
# <sjr@home.net>.
# - Stopped reporting host IPs twice when numeric output was requested.
# Thanks, yet again, to Stephen J. Roznowski <sjr@home.net>.
# - Number of minor tweaks that might speed it up a bit, and some comments.
-# - Put the script back up on the web site. I moved the site and forgot to
-# move the tool.
+# - Put the script back up on the web site. I had moved the site and
+# forgotten to move the tool.
+#
# 1999/02/04:
# - Changed log line parser to accept fully-qualified name in the logging
# host field. Thanks to Stephen J. Roznowski <sjr@home.net>.
+#
# 1999/01/22:
# - Changed high port strategy to use 65536 for unknown high ports so that
# they are sorted last.
+#
# 1999/01/21:
# - Moved icmp parsing to output loop.
# - Added parsing of icmp codes, and more types.
# - Changed packet sort routine to sort by port number rather than service
# name.
+#
# 1999/01/20:
# - Fixed problem matching ipmon log lines. Sometimes they have "/ipmon" in
# them, sometimes just "ipmon".
@@ -62,245 +129,540 @@
use strict;
use Socket;
+use IO::File;
-select STDOUT ; $| = 1 ;
+select STDOUT; $| = 1;
my %hosts;
my $me = $0;
-$me =~ s/^([^\/]*\/)*//;
-
-my $numeric = 0;
-
-# Under IPv4 port numbers are unsigned shorts. The value below is higher
-# than the maximum value of an unsigned port, and is used in place of
-# high port numbers that don't correspond to known services. This makes
-# high ports get sorted behind all others.
-my $highPort = 0x10000;
+$me =~ s/^.*\///;
# Map of log codes for various actions. Not all of these can occur, but
# I've included everything in print_ipflog() from ipmon.c.
my %acts = (
- 'p' => 'pass',
- 'P' => 'pass',
- 'b' => 'block',
- 'B' => 'block',
- 'L' => 'log',
+ 'p' => 'pass',
+ 'P' => 'pass',
+ 'b' => 'block',
+ 'B' => 'block',
+ 'L' => 'log',
'S' => 'short',
'n' => 'nomatch',
);
+# Map of ICMP types and their relevant codes.
+my %icmpTypeMap = (
+ 0 => +{
+ name => 'echorep',
+ codes => +{0 => undef},
+ },
+ 3 => +{
+ name => 'unreach',
+ codes => +{
+ 0 => 'net-unr',
+ 1 => 'host-unr',
+ 2 => 'proto-unr',
+ 3 => 'port-unr',
+ 4 => 'needfrag',
+ 5 => 'srcfail',
+ 6 => 'net-unk',
+ 7 => 'host-unk',
+ 8 => 'isolate',
+ 9 => 'net-prohib',
+ 10 => 'host-prohib',
+ 11 => 'net-tos',
+ 12 => 'host-tos',
+ 13 => 'filter-prohib',
+ 14 => 'host-preced',
+ 15 => 'preced-cutoff',
+ },
+ },
+ 4 => +{
+ name => 'squench',
+ codes => +{0 => undef},
+ },
+ 5 => +{
+ name => 'redir',
+ codes => +{
+ 0 => 'net',
+ 1 => 'host',
+ 2 => 'tos',
+ 3 => 'tos-host',
+ },
+ },
+ 6 => +{
+ name => 'alt-host-addr',
+ codes => +{
+ 0 => 'alt-addr'
+ },
+ },
+ 8 => +{
+ name => 'echo',
+ codes => +{0 => undef},
+ },
+ 9 => +{
+ name => 'routerad',
+ codes => +{0 => undef},
+ },
+ 10 => +{
+ name => 'routersol',
+ codes => +{0 => undef},
+ },
+ 11 => +{
+ name => 'timex',
+ codes => +{
+ 0 => 'in-transit',
+ 1 => 'frag-assy',
+ },
+ },
+ 12 => +{
+ name => 'paramprob',
+ codes => +{
+ 0 => 'ptr-err',
+ 1 => 'miss-opt',
+ 2 => 'bad-len',
+ },
+ },
+ 13 => +{
+ name => 'timest',
+ codes => +{0 => undef},
+ },
+ 14 => +{
+ name => 'timestrep',
+ codes => +{0 => undef},
+ },
+ 15 => +{
+ name => 'inforeq',
+ codes => +{0 => undef},
+ },
+ 16 => +{
+ name => 'inforep',
+ codes => +{0 => undef},
+ },
+ 17 => +{
+ name => 'maskreq',
+ codes => +{0 => undef},
+ },
+ 18 => +{
+ name => 'maskrep',
+ codes => +{0 => undef},
+ },
+ 30 => +{
+ name => 'tracert',
+ codes => +{ },
+ },
+ 31 => +{
+ name => 'dgram-conv-err',
+ codes => +{ },
+ },
+ 32 => +{
+ name => 'mbl-host-redir',
+ codes => +{ },
+ },
+ 33 => +{
+ name => 'ipv6-whereru?',
+ codes => +{ },
+ },
+ 34 => +{
+ name => 'ipv6-iamhere',
+ codes => +{ },
+ },
+ 35 => +{
+ name => 'mbl-reg-req',
+ codes => +{ },
+ },
+ 36 => +{
+ name => 'mbl-reg-rep',
+ codes => +{ },
+ },
+);
+
+# Arguments we will parse from argument list.
+my $numeric = 0; # Don't lookup hostnames.
+my $verbosity = 0; # Bla' bla' bla'.
+my $sTable = 0; # Generate source table.
+my $dTable = 0; # Generate destination table.
+my $services = undef; # Preload services table.
+my %selectHosts; # Limit report to these hosts.
+my %selectActs; # Limit report to these actions.
+
+# Parse argument list.
while (defined ($_ = shift))
{
if (s/^-//)
{
- $numeric += s/n//g;
- &usage (0) if (s/[h\?]//g);
- &usage (1) if (length ($_));
- next;
+ while (s/^([nSD\?hsA])//)
+ {
+ my $flag = $1;
+ if ($flag eq 'v')
+ {
+ ++$verbosity;
+ }
+ elsif ($flag eq 'n')
+ {
+ $numeric = 1;
+ }
+ elsif ($flag eq 'S')
+ {
+ $sTable = 1;
+ }
+ elsif ($flag eq 'D')
+ {
+ $dTable = 1;
+ }
+ elsif (($flag eq '?') || ($flag eq 'h'))
+ {
+ &usage (0);
+ }
+ else
+ {
+ my $arg = shift;
+ defined ($arg) || &usage (1, qq{-$flag requires an argument});
+ if ($flag eq 's')
+ {
+ defined ($services) && &usage (1, qq{too many service maps}
+);
+ $services = $arg;
+ }
+ elsif ($flag eq 'A')
+ {
+ my @acts = split (/,/, $arg);
+ my $a;
+ foreach $a (@acts)
+ {
+ my $aa;
+ my $match = 0;
+ foreach $aa (keys (%acts))
+ {
+ if ($acts{$aa} eq $a)
+ {
+ ++$match;
+ $selectActs{$aa} = $a;
+ }
+ }
+ $match || &usage (1, qq{unknown action $a});
+ }
+ }
+ }
+ }
+
+ &usage (1, qq{unknown option: -$_}) if (length);
+
+ next;
}
- &usage (1);
+
+ # Add host to hash of hosts we're interested in.
+ my $addr = &hostNumber ($_);
+ defined ($addr) || &usage (1, qq{cannot resolve hostname $_});
+ $selectHosts{$addr} = undef;
}
+# Which tables will we generate?
+$dTable = $sTable = 1 unless ($dTable || $sTable);
+my @dirs;
+push (@dirs, 'd') if ($dTable);
+push (@dirs, 's') if ($sTable);
+
+# Are we interested in specific hosts?
+my $selectHosts = scalar (keys (%selectHosts));
+
+# Are we interested in specific actions?
+if (scalar (keys (%selectActs)) == 0)
+{
+ %selectActs = %acts;
+}
+
+# We use this hash to cache port name -> number and number -> name mappings.
+# Isn't it cool that we can use the same hash for both?
+my %pn;
+
+# Preload any services map.
+if (defined ($services))
+{
+ my $sf = new IO::File ($services, "r");
+ defined ($sf) || &quit (1, qq{cannot open services file $services});
+
+ while (defined ($_ = $sf->getline ()))
+ {
+ my $text = $_;
+ chomp;
+ s/#.*$//;
+ s/\s+$//;
+ next unless (length);
+ my ($name, $spec, @aliases) = split (/\s+/);
+ ($spec =~ /^([\w\-]+)\/([\w\-]+)$/)
+ || &quit (1, qq{$services:$.: invalid definition: $text});
+ my ($pnum, $proto) = ($1, $2);
+
+ # Enter service definition in pn hash both forwards and backwards.
+ my $port;
+ my $pname;
+ foreach $port ($name, @aliases)
+ {
+ $pname = "$pnum/$proto";
+ $pn{$pname} = $port;
+ }
+ $pname = "$name/$proto";
+ $pn{$pname} = $pnum;
+ }
+
+ $sf->close ();
+}
+
+# Again, we can use the same hash for both host name -> IP mappings and
+# IP -> name mappings.
+my %ip;
+
+# Hash for protocol number <--> name mappings.
+my %pr;
+
+# Under IPv4 port numbers are unsigned shorts. The value below is higher
+# than the maximum value of an unsigned short, and is used in place of
+# high port numbers that don't correspond to known services. This makes
+# high ports get sorted behind all others.
+my $highPort = 0x10000;
+
while (<STDIN>)
{
chomp;
# For ipmon output that came through syslog, we'll have an asctime
- # timestamp, hostname, "ipmon"[process id]: prefixed to the line. For
- # output that was written directly to a file by ipmon, we'll have a date
- # prefix as dd/mm/yyyy (no y2k problem here!). Both formats then have a
- # packet timestamp and the log info.
- my ($time, $log);
- if (/^(\w+\s+\d+\s+\d+:\d+:\d+)\s+([\w\.]+)\s+\S*ipmon\[\d+\]:\s+(\d+:\d+:\d+\.\d+)\s+(.+)/)
+ # timestamp, an optional severity code (IRIX), the hostname,
+ # "ipmon"[process id]: prefixed to the line. For output that was
+ # written directly to a file by ipmon, we'll have a date prefix as
+ # dd/mm/yyyy (no y2k problem here!). Both formats then have a packet
+ # timestamp and the log info.
+ my ($log);
+ if (s/^\w+\s+\d+\s+\d+:\d+:\d+\s+(?:\d\w:)?[\w\.\-]+\s+\S*ipmon\[\d+\]:\s+(
+?:\[ID\s+\d+\s+[\w\.]+\]\s+)?\d+:\d+:\d+\.\d+\s+//)
{
- my ($logtime, $loghost);
- ($logtime, $loghost, $time, $log) = ($1, $2, $3, $4);
+ $log = $_;
}
- elsif (/^(\d+\/\d+\/\d+)\s+(\d+:\d+:\d+\.\d+)\s+(.+)$/)
+ elsif (s/^(?:\d+\/\d+\/\d+)\s+(?:\d+:\d+:\d+\.\d+)\s+//)
{
- my $logdate;
- ($logdate, $time, $log) = ($1, $2, $3);
+ $log = $_;
}
else
{
- # It don't look like no ipmon output to me, baby.
- next;
+ # It don't look like no ipmon output to me, baby.
+ next;
}
next unless (defined ($log));
+ print STDERR "$log\n" if ($verbosity);
+
# Parse the log line. We're expecting interface name, rule group and
# number, an action code, a source host name or IP with possible port
# name or number, a destination host name or IP with possible port
# number, "PR", a protocol name or number, "len", a header length, a
- # packet length, and maybe some additional info.
- $log =~ /^(\w+)\s+@(\d+):(\d+)\s+(\w)\s+([a-zA-Z0-9\-\.,]+)\s+->\s+([a-zA-Z0-9\-\.,]+)\s+PR\s+(\w+)\s+len\s+(\d+)\s+(\d+)\s*(.*)$/;
- my ($if, $group, $rule, $act, $src, $dest, $proto, $hlen, $len, $more)
- = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
- unless (defined ($len))
+ # packet length (which will be in parentheses for protocols other than
+ # TCP, UDP, or ICMP), and maybe some additional info.
+ my @fields = ($log =~ /^(?:(\d+)x)?\s*(\w+)\s+@(\d+):(\d+)\s+(\w)\s+([\w\-\
+..,]+)\s+->\s+([\w\-\.,]+)\s+PR\s+(\w+)\s+len\s+(\d+)\s+\(?(\d+)\)?\s*(.*)$/ox);
+ unless (scalar (@fields))
{
- warn ("Bad input line at $.: \"$_\"");
- next;
+ print STDERR "$me:$.: cannot parse: $_\n";
+ next;
}
+ my ($count, $if, $group, $rule, $act, $src, $dest, $proto, $hlen, $len, $mo
+re) = @fields;
+
+ # Skip actions we're not interested in.
+ next unless (exists ($selectActs{$act}));
+
+ # Packet count defaults to 1.
+ $count = 1 unless (defined ($count));
my ($sport, $dport);
if ($proto eq 'icmp')
{
- if ($more =~ s/^icmp (\d+)\/(\d+)\s*//)
- {
- # We save icmp type and code in both sport and dport.
- $dport = $sport = "$1.$2";
- }
- else
- {
- $sport = '';
- $dport = '';
- }
+ if ($more =~ s/^icmp (\d+)\/(\d+)\s*//)
+ {
+ # We save icmp type and code in both sport and dport. This
+ # allows us to sort icmp packets using the normal port-sorting
+ # code.
+ $dport = $sport = "$1.$2";
+ }
+ else
+ {
+ $sport = '';
+ $dport = '';
+ }
}
else
{
- $sport = (($src =~ s/,(\w+)$//) ? &portSimplify ($1, $proto) : '');
- $dport = (($dest =~ s/,(\w+)$//) ? &portSimplify ($1, $proto) : '');
+ if ($src =~ s/,([\-\w]+)$//)
+ {
+ $sport = &portSimplify ($1, $proto);
+ }
+ else
+ {
+ $sport = '';
+ }
+ if ($dest =~ s/,([\-\w]+)$//)
+ {
+ $dport = &portSimplify ($1, $proto);
+ }
+ else
+ {
+ $dport = '';
+ }
}
# Make sure addresses are numeric at this point. We want to sort by
# IP address later. This has got to do some weird things, but if you
# want to use ipmon -n, be ready for weirdness.
- $src = &hostNumber ($src);
- $dest = &hostNumber ($dest);
+ my $x;
+ $x = &hostNumber ($src);
+ unless (defined ($x))
+ {
+ print STDERR "$me:$.: cannot resolve hostname $src\n";
+ next;
+ }
+ $src = $x;
+ $x = &hostNumber ($dest);
+ unless (defined ($x))
+ {
+ print STDERR "$me:$.: cannot resolve hostname $dest\n";
+ next;
+ }
+ $dest = $x;
+
+ # Skip hosts we're not interested in.
+ next if ($selectHosts && !(exists ($selectHosts{$src}) || exists ($selectHo
+sts{$dest})));
# Convert proto to proto number.
$proto = &protoNumber ($proto);
sub countPacket
{
- my ($host, $dir, $peer, $proto, $packet) = @_;
+ my ($host, $dir, $peer, $proto, $count, $packet) = @_;
- # Make sure host is in the hosts hash.
- $hosts{$host} =
- +{
- 'out' => +{ },
- 'in' => +{ },
- } unless (exists ($hosts{$host}));
+ # Make sure host is in the hosts hash.
+ $hosts{$host} =
+ +{
+ 'd' => +{ },
+ 's' => +{ },
+ } unless (exists ($hosts{$host}));
- # Get the incoming/outgoing traffic hash for the host in question.
- my $trafficHash = $hosts{$host}->{$dir};
+ # Get the source/destination traffic hash for the host in question.
+ my $trafficHash = $hosts{$host}->{$dir};
- # Make sure there's a hash for the peer.
- $trafficHash->{$peer} = +{ } unless (exists ($trafficHash->{$peer}));
+ # Make sure there's a hash for the peer.
+ $trafficHash->{$peer} = +{ } unless (exists ($trafficHash->{$peer}));
- # Make sure the peer hash has a hash for the protocol number.
- my $peerHash = $trafficHash->{$peer};
- $peerHash->{$proto} = +{ } unless (exists ($peerHash->{$proto}));
+ # Make sure the peer hash has a hash for the protocol number.
+ my $peerHash = $trafficHash->{$peer};
+ $peerHash->{$proto} = +{ } unless (exists ($peerHash->{$proto}));
- # Make sure there's a counter for this packet type in the proto hash.
- my $protoHash = $peerHash->{$proto};
- $protoHash->{$packet} = 0 unless (exists ($protoHash->{$packet}));
+ # Make sure there's a counter for this packet type in the proto hash.
+ my $protoHash = $peerHash->{$proto};
+ $protoHash->{$packet} = 0 unless (exists ($protoHash->{$packet}));
- # Increment the counter.
- ++$protoHash->{$packet};
+ # Increment the counter.
+ $protoHash->{$packet} += $count;
}
# Count the packet as outgoing traffic from the source address.
- &countPacket ($src, 'out', $dest, $proto, "$sport:$dport:$if:$act");
+ &countPacket ($src, 's', $dest, $proto, $count, "$sport:$dport:$if:$act") i
+f ($sTable);
# Count the packet as incoming traffic to the destination address.
- &countPacket ($dest, 'in', $src, $proto, "$dport:$sport:$if:$act");
+ &countPacket ($dest, 'd', $src, $proto, $count, "$dport:$sport:$if:$act") i
+f ($dTable);
}
my $dir;
-foreach $dir (qw(out in))
+foreach $dir (@dirs)
{
- my $order = ($dir eq 'out' ? 'source' : 'destination');
- my $arrow = ($dir eq 'out' ? '->' : '<-');
+ my $order = ($dir eq 's' ? 'source' : 'destination');
+ my $arrow = ($dir eq 's' ? '->' : '<-');
+ print "###\n";
print "### Traffic by $order address:\n";
+ print "###\n";
sub ipSort
{
- my @a = split (/\./, $a);
- my @b = split (/\./, $b);
- $a[0] != $b[0] ? $a[0] <=> $b[0]
- : $a[1] != $b[1] ? $a[1] <=> $b[1]
- : $a[2] != $b[2] ? $a[2] <=> $b[2]
- : $a[3] != $b[3] ? $a[3] <=> $b[3]
- : 0;
+ my @a = split (/\./, $a);
+ my @b = split (/\./, $b);
+ $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3
+];
+ }
+
+ sub packetSort
+ {
+ my ($asport, $adport, $aif, $aact) = split (/:/, $a);
+ my ($bsport, $bdport, $bif, $bact) = split (/:/, $b);
+ $bact cmp $aact || $aif cmp $bif || $asport <=> $bsport || $adport <=>
+$bdport;
}
my $host;
foreach $host (sort ipSort (keys %hosts))
{
- my $traffic = $hosts{$host}->{$dir};
-
- # Skip hosts with no traffic.
- next unless (scalar (keys (%{$traffic})));
-
- if ($numeric)
- {
- print " $host\n";
- }
- else
- {
- print " ", &hostName ($host), " \[$host\]\n";
- }
-
- my $peer;
- foreach $peer (sort ipSort (keys %{$traffic}))
- {
- my $peerHash = $traffic->{$peer};
- my $peerName = &hostName ($peer);
- my $proto;
- foreach $proto (sort (keys (%{$peerHash})))
- {
- my $protoHash = $peerHash->{$proto};
- my $protoName = &protoName ($proto);
-
- sub packetSort
- {
- my ($asport, $adport, $aif, $aact) = split (/:/, $a);
- my ($bsport, $bdport, $bif, $bact) = split (/:/, $b);
- return $bact cmp $aact if ($aact ne $bact);
- return $aif cmp $bif if ($aif ne $bif);
- return $asport <=> $bsport if ($asport != $bsport);
- return $adport <=> $bdport if ($adport != $bdport);
- }
-
- my $packet;
- foreach $packet (sort packetSort (keys %{$protoHash}))
- {
- my ($sport, $dport, $if, $act) = split (/:/, $packet);
- my $count = $protoHash->{$packet};
- $act = '?' unless (defined ($act = $acts{$act}));
- if (($protoName eq 'tcp') || ($protoName eq 'udp'))
- {
- printf (" %-6s %7s %5d %6s %14s %2s %s.%s\n", $if, $act, $count, $protoName, &portName ($sport, $protoName), $arrow, $peerName, &portName ($dport, $protoName));
- }
- elsif ($protoName eq 'icmp')
- {
- printf (" %-6s %7s %5d %6s %14s %2s %s\n", $if, $act, $count, $protoName, &icmpType ($sport), $arrow, $peerName);
- }
- else
- {
- printf (" %-6s %7s %5d %6s %14s %2s %s\n", $if, $act, $count, $protoName, '', $arrow, $peerName);
- }
- }
- }
- }
+ my $traffic = $hosts{$host}->{$dir};
+
+ # Skip hosts with no traffic.
+ next unless (scalar (keys (%{$traffic})));
+
+ if ($numeric)
+ {
+ print "$host\n";
+ }
+ else
+ {
+ print &hostName ($host), " \[$host\]\n";
+ }
+
+ my $peer;
+ foreach $peer (sort ipSort (keys %{$traffic}))
+ {
+ my $peerHash = $traffic->{$peer};
+ my $peerName = &hostName ($peer);
+ my $proto;
+ foreach $proto (sort (keys (%{$peerHash})))
+ {
+ my $protoHash = $peerHash->{$proto};
+ my $protoName = &protoName ($proto);
+
+ my $packet;
+ foreach $packet (sort packetSort (keys %{$protoHash}))
+ {
+ my ($sport, $dport, $if, $act) = split (/:/, $packet);
+ my $count = $protoHash->{$packet};
+ $act = '?' unless (defined ($act = $acts{$act}));
+ if (($protoName eq 'tcp') || ($protoName eq 'udp'))
+ {
+ printf (" %-6s %7s %4d %4s %16s %2s %s.%s\n", $if, $
+act, $count, $protoName, &portName ($sport, $protoName), $arrow, $peerName, &po
+rtName ($dport, $protoName));
+ }
+ elsif ($protoName eq 'icmp')
+ {
+ printf (" %-6s %7s %4d %4s %16s %2s %s\n", $if, $act
+, $count, $protoName, &icmpType ($sport), $arrow, $peerName);
+ }
+ else
+ {
+ printf (" %-6s %7s %4d %4s %16s %2s %s\n", $if, $act
+, $count, $protoName, '', $arrow, $peerName);
+ }
+ }
+ }
+ }
}
- print "\n\n";
+ print "\n";
}
exit (0);
-# We use this hash to cache port name -> number and number -> name mappings.
-# Isn't is cool that we can use the same hash for both?
-my %pn;
-
# Translates a numeric port/named protocol to a port name. Reserved ports
-# that do # not have an entry in the services database are left numeric.
-# High ports that do not have an entry in the services database are mapped
+# that do not have an entry in the services database are left numeric. High
+# ports that do not have an entry in the services database are mapped
# to '<high>'.
sub portName
{
@@ -309,8 +671,9 @@ sub portName
my $pname = "$port/$proto";
unless (exists ($pn{$pname}))
{
- my $name = getservbyport ($port, $proto);
- $pn{$pname} = (defined ($name) ? $name : ($port <= 1023 ? $port : '<high>'));
+ my $name = getservbyport ($port, $proto);
+ $pn{$pname} = (defined ($name) ? $name : ($port <= 1023 ? $port : '<hig
+h>'));
}
return $pn{$pname};
}
@@ -323,16 +686,16 @@ sub portNumber
my $pname = "$port/$proto";
unless (exists ($pn{$pname}))
{
- my $number = getservbyname ($port, $proto);
- unless (defined ($number))
- {
- # I don't think we need to recover from this. How did the port
- # name get into the log file if we can't find it? Log file from
- # a different machine? Fix /etc/services on this one if that's
- # your problem.
- die ("Unrecognized port name \"$port\" at $.");
- }
- $pn{$pname} = $number;
+ my $number = getservbyname ($port, $proto);
+ unless (defined ($number))
+ {
+ # I don't think we need to recover from this. How did the port
+ # name get into the log file if we can't find it? Log file from
+ # a different machine? Fix /etc/services on this one if that's
+ # your problem.
+ die ("Unrecognized port name \"$port\" at $.");
+ }
+ $pn{$pname} = $number;
}
return $pn{$pname};
}
@@ -346,7 +709,7 @@ sub portSimplify
# Make sure port is numeric.
$port = &portNumber ($port, $proto)
- unless ($port =~ /^\d+$/);
+ unless ($port =~ /^\d+$/);
# Look up port name.
my $portName = &portName ($port, $proto);
@@ -359,10 +722,6 @@ sub portSimplify
return $port;
}
-# Again, we can use the same hash for both host name -> IP mappings and
-# IP -> name mappings.
-my %ip;
-
# Translates a dotted quad into a hostname. Don't pass names to this
# function.
sub hostName
@@ -371,20 +730,20 @@ sub hostName
return $ip if ($numeric);
unless (exists ($ip{$ip}))
{
- my $addr = inet_aton ($ip);
- my $name = gethostbyaddr ($addr, AF_INET);
- if (defined ($name))
- {
- $ip{$ip} = $name;
-
- # While we're at it, cache the forward lookup.
- $ip{$name} = $ip;
- }
- else
- {
- # Just map the IP address to itself. There's no reverse.
- $ip{$ip} = $ip;
- }
+ my $addr = inet_aton ($ip);
+ my $name = gethostbyaddr ($addr, AF_INET);
+ if (defined ($name))
+ {
+ $ip{$ip} = $name;
+
+ # While we're at it, cache the forward lookup.
+ $ip{$name} = $ip;
+ }
+ else
+ {
+ # Just map the IP address to itself. There's no reverse.
+ $ip{$ip} = $ip;
+ }
}
return $ip{$ip};
}
@@ -395,34 +754,31 @@ sub hostNumber
my $name = shift;
if ($name =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
{
- # Return original value for dotted quads.
- my $or = int ($1) | int ($2) | int ($3) | int ($4);
- return $name if ($or == ($or & 0xff));
+ # Return original value for dotted quads.
+ my $or = int ($1) | int ($2) | int ($3) | int ($4);
+ return $name if ($or == ($or & 0xff));
}
unless (exists ($ip{$name}))
{
- my $addr = inet_aton ($name);
- unless (defined ($addr))
- {
- # Again, I don't think we need to recover from this. If we can't
- # resolve a hostname that ended up in the log file, punt. We
- # want to be able to sort hosts by IP address later, and letting
- # hostnames through will snarl up that code. Users of ipmon -n
- # will have to grin and bear it for now.
- die ("Unable to resolve host \"$name\" at $.");
- }
- my $ip = inet_ntoa ($addr);
- $ip{$name} = $ip;
-
- # While we're at it, cache the reverse lookup.
- $ip{$ip} = $name;
+ my $addr = inet_aton ($name);
+ unless (defined ($addr))
+ {
+ # Again, I don't think we need to recover from this. If we can't
+ # resolve a hostname that ended up in the log file, punt. We
+ # want to be able to sort hosts by IP address later, and letting
+ # hostnames through will snarl up that code. Users of ipmon -n
+ # will have to grin and bear it for now.
+ return undef;
+ }
+ my $ip = inet_ntoa ($addr);
+ $ip{$name} = $ip;
+
+ # While we're at it, cache the reverse lookup.
+ $ip{$ip} = $name;
}
return $ip{$name};
}
-# Hash for protocol number <--> name mappings.
-my %pr;
-
# Translates a protocol number into a protocol name, or a number if no name
# is found in the protocol database.
sub protoName
@@ -431,15 +787,15 @@ sub protoName
return $code if ($code !~ /^\d+$/);
unless (exists ($pr{$code}))
{
- my $name = scalar (getprotobynumber ($code));
- if (defined ($name))
- {
- $pr{$code} = $name;
- }
- else
- {
- $pr{$code} = $code;
- }
+ my $name = scalar (getprotobynumber ($code));
+ if (defined ($name))
+ {
+ $pr{$code} = $name;
+ }
+ else
+ {
+ $pr{$code} = $code;
+ }
}
return $pr{$code};
}
@@ -451,144 +807,27 @@ sub protoNumber
return $name if ($name =~ /^\d+$/);
unless (exists ($pr{$name}))
{
- my $code = scalar (getprotobyname ($name));
- if (defined ($code))
- {
- $pr{$name} = $code;
- }
- else
- {
- $pr{$name} = $name;
- }
+ my $code = scalar (getprotobyname ($name));
+ if (defined ($code))
+ {
+ $pr{$name} = $code;
+ }
+ else
+ {
+ $pr{$name} = $name;
+ }
}
return $pr{$name};
}
sub icmpType
{
- my %icmp = (
- 0 => +{
- name => 'echo-reply',
- codes => +{0 => undef},
- },
- 3 => +{
- name => 'dest-unr',
- codes => +{
- 0 => 'net',
- 1 => 'host',
- 2 => 'proto',
- 3 => 'port',
- 4 => 'need-frag',
- 5 => 'no-sroute',
- 6 => 'net-unk',
- 7 => 'host-unk',
- 8 => 'shost-isol',
- 9 => 'net-proh',
- 10 => 'host-proh',
- 11 => 'net-tos',
- 12 => 'host-tos',
- },
- },
- 4 => +{
- name => 'src-quench',
- codes => +{0 => undef},
- },
- 5 => +{
- name => 'redirect',
- codes => +{
- 0 => 'net',
- 1 => 'host',
- 2 => 'tos',
- 3 => 'tos-host',
- },
- },
- 6 => +{
- name => 'alt-host-addr',
- codes => +{0 => undef},
- },
- 8 => +{
- name => 'echo',
- codes => +{0 => undef},
- },
- 9 => +{
- name => 'rtr-advert',
- codes => +{0 => undef},
- },
- 10 => +{
- name => 'rtr-select',
- codes => +{0 => undef},
- },
- 11 => +{
- name => 'time-excd',
- codes => +{
- 0 => 'in-transit',
- 1 => 'frag-assy',
- },
- },
- 12 => +{
- name => 'param-prob',
- codes => +{
- 0 => 'ptr-err',
- 1 => 'miss-opt',
- 2 => 'bad-len',
- },
- },
- 13 => +{
- name => 'time',
- codes => +{0 => undef},
- },
- 14 => +{
- name => 'time-reply',
- codes => +{0 => undef},
- },
- 15 => +{
- name => 'info',
- codes => +{0 => undef},
- },
- 16 => +{
- name => 'info-req',
- codes => +{0 => undef},
- },
- 17 => +{
- name => 'mask-req',
- codes => +{0 => undef},
- },
- 18 => +{
- name => 'mask-reply',
- codes => +{0 => undef},
- },
- 31 => +{
- name => 'dgram-conv-err',
- codes => +{ },
- },
- 32 => +{
- name => 'mbl-host-redir',
- codes => +{ },
- },
- 33 => +{
- name => 'ipv6-whereru?',
- codes => +{ },
- },
- 34 => +{
- name => 'ipv6-iamhere',
- codes => +{ },
- },
- 35 => +{
- name => 'mbl-reg-req',
- codes => +{ },
- },
- 36 => +{
- name => 'mbl-reg-rep',
- codes => +{ },
- },
- );
-
my $typeCode = shift;
my ($type, $code) = split ('\.', $typeCode);
return "?" unless (defined ($code));
- my $info = $icmp{$type};
+ my $info = $icmpTypeMap{$type};
return "\(type=$type/$code?\)" unless (defined ($info));
@@ -596,58 +835,83 @@ sub icmpType
my $codeName;
if (exists ($info->{codes}->{$code}))
{
- $codeName = $info->{codes}->{$code};
- $codeName = (defined ($codeName) ? "/$codeName" : '');
+ $codeName = $info->{codes}->{$code};
+ $codeName = (defined ($codeName) ? "/$codeName" : '');
}
else
{
- $codeName = "/$code";
+ $codeName = "/$code";
}
return "$typeName$codeName";
}
-sub usage
+sub quit
{
my $ec = shift;
+ my $msg = shift;
- print STDERR <<EOT;
-usage: $me [-n]
-
-Parses logging from ipmon and presents it in a comprehensible format.
-This program generates two tables: one organized by source address and
-another organized by destination address. For the first table, source
-addresses are sorted by IP address. For each address, all packets
-originating at the address are presented in a tabular form, where all
-packets with the same source and destination address and port are counted
-as a single entry. The packet count for each entry is shown as the third
-field. In addition, any port number greater than 1024 that doesn't match
-an entry in the services table is treated as a "high" port, and high ports
-are coalesced into the same entry. The entry fields for the source address
-table are:
+ print STDERR "$me: $msg\n";
+ exit ($ec);
+}
- iface action packet-count proto src-port dest-ip dest-port
+sub usage
+{
+ my $ec = shift;
+ my @msg = @_;
-The entry fields for the destination table are:
+ if (scalar (@msg))
+ {
+ print STDERR "$me: ", join ("\n", @msg), "\n\n";
+ }
+ print STDERR <<EOT;
+usage: $me [-n] [-S] [-D] [-s servicemap] [-A act1,...] host...
+
+Parses logging from ipmon and presents it in a comprehensible format. This
+program generates two reports: one organized by source address and another
+organized by destination address. For the first report, source addresses are
+sorted by IP address. For each address, all packets originating at the address
+are presented in a tabular form, where all packets with the same source and
+destination address and port are counted as a single entry. Any port number
+greater than 1023 that does not match an entry in the services table is treated
+as a "high" port; all high ports are coalesced into the same entry. The fields
+for the source address report are:
+ iface action packet-count proto src-port dest-ip dest-port
+The fields for the destination address report are:
iface action packet-count proto dest-port src-ip src-port
-If the -n option is given, reverse hostname lookups are disabled and all
-hosts are displayed as numeric addresses.
-
-Note: if you are logging traffic with ipmon -n, ipmon will already have
-looked up and logged addresses as hostnames where possible. This has an
-important side effect: this program will translate the hostnames back into
-IP addresses which may not match the original addresses of the logged
-packets because of numerous DNS issues. If you care about where packets
-are really coming from, you simply cannot rely on ipmon -n. An attacker
-with control of his reverse DNS can map the reverse lookup to anything he
-likes. If you haven't logged the numeric IP address, there's no way to
-discover the source of an attack reliably. For this reason, I strongly
-recommend that you run ipmon without the -n option, and use this or a
-similar script to do reverse lookups during analysis, rather than during
-logging.
+Options are:
+-n Disable hostname lookups, and report only IP addresses.
+-S Generate a source address report.
+-D Generate a destination address report.
+-s map Supply an alternate services map to be preloaded. The map should
+ be in the same format as /etc/services. Any service name not found
+ in the map will be looked for in the system services file.
+-A act1,... Limit the report to the specified actions. The possible actions ar
+e
+ pass, block, log, short, and nomatch.
+
+If any hostnames are supplied on the command line, the report is limited to
+these hosts. If a host has multiple addresses, only the first address will be
+considered.
+
+If neither -S nor -D is given, both reports are generated.
+
+Note: if you are logging traffic with ipmon -n, ipmon will already have looked
+up and logged addresses as hostnames where possible. This has an important side
+effect: this program will translate the hostnames back into IP addresses which
+may not match the original addresses of the logged packets because of numerous
+DNS issues. If you care about where packets are really coming from, you simply
+cannot rely on ipmon -n. An attacker with control of his reverse DNS can map
+the reverse lookup to anything he likes. If you haven't logged the numeric IP
+address, there's no way to discover the source of an attack reliably. For this
+reason, I strongly recommend that you run ipmon without the -n option, and use
+this or a similar script to do reverse lookups during analysis, rather than
+during logging.
EOT
exit ($ec);
}
+
+
OpenPOWER on IntegriCloud