summaryrefslogtreecommitdiffstats
path: root/contrib/ipfilter/perl/plog
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ipfilter/perl/plog')
-rw-r--r--contrib/ipfilter/perl/plog1061
1 files changed, 0 insertions, 1061 deletions
diff --git a/contrib/ipfilter/perl/plog b/contrib/ipfilter/perl/plog
deleted file mode 100644
index 208c6ea..0000000
--- a/contrib/ipfilter/perl/plog
+++ /dev/null
@@ -1,1061 +0,0 @@
-#!/usr/bin/perl -wT
-#
-# Author: Jefferson Ogata (JO317) <jogata@pobox.com>
-# Date: 2000/04/22
-# Version: 0.10
-#
-# 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
-# and I will add them when I have time. The current version of this script
-# can always be found at the URL:
-#
-# http://www.antibozo.net/ogata/webtools/plog.pl
-# http://pobox.com/~ogata/webtools/plog.txt
-#
-# 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.
-#
-# EXAMPLES
-#
-# plog -AF block,log < /var/log/ipf
-#
-# Generate source and destination reports of all packets logged with
-# block or log actions, and report TCP flags and keep state 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.
-#
-# plog -SFp 192.168.0.0/24 www.example.com/24 < /var/log/ipf
-#
-# Generate a source report of all packets whose source or destination
-# address is either in 192.168.0.0/24 or an address associated with
-# the host www.example.com, report packet flags and perform paranoid
-# hostname lookups. This is a handy usage for examining traffic more
-# closely after identifying a potential attack.
-#
-# 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/22 (0.10):
-# - Restructured host name and address caches. Hosts are now cached using
-# packed addresses as keys. Conversion to IPv6 should be simple now.
-# - Added paranoid hostname lookups.
-# - Added netmask qualifications for address arguments.
-# - Tweaked usage info.
-# 2000/04/20:
-# - Added parsing and tracking of TCP and state flags.
-# 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.420541 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>.
-#
-# 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
-# using ipmon -n to still use plog. Note that if you are logging
-# 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 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 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".
-# - Added numeric parse option to turn off hostname lookups.
-# - Moved summary to usage() sub.
-
-use strict;
-use Socket;
-use IO::File;
-
-select STDOUT; $| = 1;
-
-my %hosts;
-
-my $me = $0;
-$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',
- '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 $paranoid = 0; # Do paranoid hostname lookups.
-my $verbosity = 0; # Bla' bla' bla'.
-my $sTable = 0; # Generate source table.
-my $dTable = 0; # Generate destination table.
-my @services = (); # Preload services tables.
-my $showFlags = 0; # Show TCP flag combinations.
-my %selectAddrs; # Limit report to these hosts.
-my %selectActs; # Limit report to these actions.
-
-# Parse argument list.
-while (defined ($_ = shift))
-{
- if (s/^-//)
- {
- while (s/^([vnpSD\?hsAF])//)
- {
- my $flag = $1;
- if ($flag eq 'v')
- {
- ++$verbosity;
- }
- elsif ($flag eq 'n')
- {
- $numeric = 1;
- }
- elsif ($flag eq 'p')
- {
- $paranoid = 1;
- }
- elsif ($flag eq 'S')
- {
- $sTable = 1;
- }
- elsif ($flag eq 'D')
- {
- $dTable = 1;
- }
- elsif ($flag eq 'F')
- {
- $showFlags = 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')
- {
- push (@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;
- }
-
- # Add host to hash of hosts we're interested in.
- (/^(.+)\/([\d+\.]+)$/) || (/^(.+)$/) || &usage (1, qq{invalid CIDR address $_});
- my ($addr, $mask) = ($1, $2);
- my @addr = &hostAddrs ($addr);
- (scalar (@addr)) || &usage (1, qq{cannot resolve hostname $_});
- if (!defined ($mask))
- {
- $mask = (2 ** 32) - 1;
- }
- elsif (($mask =~ /^\d+$/) && ($mask <= 32))
- {
- $mask = (2 ** 32) - 1 - ((2 ** (32 - $mask)) - 1);
- }
- elsif (defined ($mask = &isDottedAddr ($mask)))
- {
- $mask = &integerAddr ($mask);
- }
- else
- {
- &usage (1, qq{invalid CIDR address $_});
- }
- foreach $addr (@addr)
- {
- # Save mask unless we already have a less specific one for this address.
- my $a = &integerAddr ($addr) & $mask;
- $selectAddrs{$a} = $mask unless (exists ($selectAddrs{$a}) && ($selectAddrs{$a} < $mask));
- }
-}
-
-# 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 $selectAddrs = scalar (keys (%selectAddrs));
-
-# 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 maps.
-my $sm;
-foreach $sm (@services)
-{
- my $sf = new IO::File ($sm, "r");
- defined ($sf) || &quit (1, qq{cannot open services file $sm});
-
- 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{$sm:$.: 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 ();
-}
-
-# Cache for host name -> addr mappings.
-my %ipAddr;
-
-# Cache for host addr -> name mappings.
-my %ipName;
-
-# 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, 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+//)
- {
- $log = $_;
- }
- elsif (s/^(?:\d+\/\d+\/\d+)\s+(?:\d+:\d+:\d+\.\d+)\s+//)
- {
- $log = $_;
- }
- else
- {
- # 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 (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))
- {
- print STDERR "$me:$.: cannot parse: $_\n";
- next;
- }
- my ($count, $if, $group, $rule, $act, $src, $dest, $proto, $hlen, $len, $more) = @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, @flags);
-
- if ($proto eq 'icmp')
- {
- 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
- {
- if ($showFlags)
- {
- if (($proto eq 'tcp') && ($more =~ s/^\-([A-Z]+)\s*//))
- {
- push (@flags, $1);
- }
- if ($more =~ s/^K\-S\s*//)
- {
- push (@flags, 'state');
- }
- }
- 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. If the hostname doesn't resolve, punt. If you
- # must use ipmon -n, be ready for weirdness. Use only the first
- # address returned.
- my $x;
- $x = (&hostAddrs ($src))[0];
- unless (defined ($x))
- {
- print STDERR "$me:$.: cannot resolve hostname $src\n";
- next;
- }
- $src = $x;
- $x = (&hostAddrs ($dest))[0];
- unless (defined ($x))
- {
- print STDERR "$me:$.: cannot resolve hostname $dest\n";
- next;
- }
- $dest = $x;
-
- # Skip hosts we're not interested in.
- if ($selectAddrs)
- {
- my ($a, $m);
- my $s = &integerAddr ($src);
- my $d = &integerAddr ($dest);
- my $cute = 0;
- while (($a, $m) = each (%selectAddrs))
- {
- if ((($s & $m) == $a) || (($d & $m) == $a))
- {
- $cute = 1;
- last;
- }
- }
- next unless ($cute);
- }
-
- # Convert proto to proto number.
- $proto = &protoNumber ($proto);
-
- sub countPacket
- {
- my ($host, $dir, $peer, $proto, $count, $packet, @flags) = @_;
-
- # Make sure host is in the hosts hash.
- $hosts{$host} =
- +{
- 'd' => +{ },
- 's' => +{ },
- } unless (exists ($hosts{$host}));
-
- # 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 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}));
-
- # Increment the counter and mark flags.
- my $packetHash = $protoHash->{$packet};
- $packetHash->{''} += $count;
- map { $packetHash->{$_} = undef; } (@flags);
- }
-
- # Count the packet as outgoing traffic from the source address.
- &countPacket ($src, 's', $dest, $proto, $count, "$sport:$dport:$if:$act", @flags) if ($sTable);
-
- # Count the packet as incoming traffic to the destination address.
- &countPacket ($dest, 'd', $src, $proto, $count, "$dport:$sport:$if:$act", @flags) if ($dTable);
-}
-
-my $dir;
-foreach $dir (@dirs)
-{
- my $order = ($dir eq 's' ? 'source' : 'destination');
- my $arrow = ($dir eq 's' ? '->' : '<-');
-
- print "###\n";
- print "### Traffic by $order address:\n";
- print "###\n";
-
- sub ipSort
- {
- &integerAddr ($a) <=> &integerAddr ($b);
- }
-
- 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 &dottedAddr ($host), "\n";
- }
- else
- {
- print &hostName ($host), " \[", &dottedAddr ($host), "\]\n";
- }
-
- my $peer;
- foreach $peer (sort ipSort (keys %{$traffic}))
- {
- my $peerHash = $traffic->{$peer};
- my $peerName = ($numeric ? &dottedAddr ($peer) : &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 $packetHash = $protoHash->{$packet};
- my $count = $packetHash->{''};
- $act = '?' unless (defined ($act = $acts{$act}));
- if (($protoName eq 'tcp') || ($protoName eq 'udp'))
- {
- printf (" %-6s %7s %4d %4s %16s %2s %s.%s", $if, $act, $count, $protoName, &portName ($sport, $protoName), $arrow, $peerName, &portName ($dport, $protoName));
- }
- elsif ($protoName eq 'icmp')
- {
- printf (" %-6s %7s %4d %4s %16s %2s %s", $if, $act, $count, $protoName, &icmpType ($sport), $arrow, $peerName);
- }
- else
- {
- printf (" %-6s %7s %4d %4s %16s %2s %s", $if, $act, $count, $protoName, '', $arrow, $peerName);
- }
- if ($showFlags)
- {
- my @flags = sort (keys (%{$packetHash}));
- if (scalar (@flags))
- {
- shift (@flags);
- print ' (', join (',', @flags), ')' if (scalar (@flags));
- }
- }
- print "\n";
- }
- }
- }
- }
-
- print "\n";
-}
-
-exit (0);
-
-# 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
-# to '<high>'.
-sub portName
-{
- my $port = shift;
- my $proto = shift;
- my $pname = "$port/$proto";
- unless (exists ($pn{$pname}))
- {
- my $name = getservbyport ($port, $proto);
- $pn{$pname} = (defined ($name) ? $name : ($port <= 1023 ? $port : '<high>'));
- }
- return $pn{$pname};
-}
-
-# Translates a named port/protocol to a port number.
-sub portNumber
-{
- my $port = shift;
- my $proto = shift;
- 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;
- }
- return $pn{$pname};
-}
-
-# Convert all unrecognized high ports to the same value so they are treated
-# identically. The protocol should be by name.
-sub portSimplify
-{
- my $port = shift;
- my $proto = shift;
-
- # Make sure port is numeric.
- $port = &portNumber ($port, $proto)
- unless ($port =~ /^\d+$/);
-
- # Look up port name.
- my $portName = &portName ($port, $proto);
-
- # Port is an unknown high port. Return a value that is too high for a
- # port number, so that high ports get sorted last.
- return $highPort if ($portName eq '<high>');
-
- # Return original port number.
- return $port;
-}
-
-# Translates a numeric address into a hostname. Pass only packed numeric
-# addresses to this routine.
-sub hostName
-{
- my $ip = shift;
- return $ipName{$ip} if (exists ($ipName{$ip}));
-
- # Do an inverse lookup on the address.
- my $name = gethostbyaddr ($ip, AF_INET);
- unless (defined ($name))
- {
- # Inverse lookup failed, so map the IP address to its dotted
- # representation and cache that.
- $ipName{$ip} = &dottedAddr ($ip);
- return $ipName{$ip};
- }
-
- # For paranoid hostname lookups.
- if ($paranoid)
- {
- # If this address already matches, we're happy.
- unless (exists ($ipName{$ip}) && (lc ($ipName{$ip}) eq lc ($name)))
- {
- # Do a forward lookup on the resulting name.
- my @addr = &hostAddrs ($name);
- my $match = 0;
-
- # Cache the forward lookup results for future inverse lookups,
- # but don't stomp on inverses we've already cached, even if they
- # are questionable. We want to generate consistent output, and
- # the cache is growing incrementally.
- foreach (@addr)
- {
- $ipName{$_} = $name unless (exists ($ipName{$_}));
- $match = 1 if ($_ eq $ip);
- }
-
- # Was this one of the addresses? If not, tack on a ?.
- $name .= '?' unless ($match);
- }
- }
- else
- {
- # Just believe it and cache it.
- $ipName{$ip} = $name;
- }
-
- return $name;
-}
-
-# Translates a hostname or dotted address into a list of packed numeric
-# addresses.
-sub hostAddrs
-{
- my $name = shift;
- my $ip;
-
- # Check if it's a dotted representation.
- return ($ip) if (defined ($ip = &isDottedAddr ($name)));
-
- # Return result from cache.
- $name = lc ($name);
- return @{$ipAddr{$name}} if (exists ($ipAddr{$name}));
-
- # Look up the addresses.
- my @addr = gethostbyname ($name);
- splice (@addr, 0, 4);
-
- unless (scalar (@addr))
- {
- # Again, I don't think we need to recover from this gracefully.
- # 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. The
- # functions that get undef back should treat it as an error or
- # as some default address, e.g. 0 just to make things work.
- return ();
- }
-
- $ipAddr{$name} = [ @addr ];
- return @{$ipAddr{$name}};
-}
-
-# If the argument is a valid dotted address, returns the corresponding
-# packed numeric address, otherwise returns undef.
-sub isDottedAddr
-{
- my $addr = shift;
- if ($addr =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
- {
- my @a = (int ($1), int ($2), int ($3), int ($4));
- foreach (@a)
- {
- return undef if ($_ >= 256);
- }
- return pack ('C*', @a);
- }
- return undef;
-}
-
-# Unpacks a packed numeric address and returns an integer representation.
-sub integerAddr
-{
- my $addr = shift;
- return unpack ('N', $addr);
-
- # The following is for generalized IPv4/IPv6 stuff. For now, it's a
- # lot faster to assume IPv4.
- my @a = unpack ('C*', $addr);
- my $a = 0;
- while (scalar (@a))
- {
- $a = ($a << 8) | shift (@a);
- }
- return $a;
-}
-
-# Unpacks a packed numeric address into a dotted representation.
-sub dottedAddr
-{
- my $addr = shift;
- my @a = unpack ('C*', $addr);
- return join ('.', @a);
-}
-
-# Translates a protocol number into a protocol name, or a number if no name
-# is found in the protocol database.
-sub protoName
-{
- my $code = shift;
- return $code if ($code !~ /^\d+$/);
- unless (exists ($pr{$code}))
- {
- my $name = scalar (getprotobynumber ($code));
- if (defined ($name))
- {
- $pr{$code} = $name;
- }
- else
- {
- $pr{$code} = $code;
- }
- }
- return $pr{$code};
-}
-
-# Translates a protocol name or number into a protocol number.
-sub protoNumber
-{
- my $name = shift;
- return $name if ($name =~ /^\d+$/);
- unless (exists ($pr{$name}))
- {
- my $code = scalar (getprotobyname ($name));
- if (defined ($code))
- {
- $pr{$name} = $code;
- }
- else
- {
- $pr{$name} = $name;
- }
- }
- return $pr{$name};
-}
-
-sub icmpType
-{
- my $typeCode = shift;
- my ($type, $code) = split ('\.', $typeCode);
-
- return "?" unless (defined ($code));
-
- my $info = $icmpTypeMap{$type};
-
- return "\(type=$type/$code?\)" unless (defined ($info));
-
- my $typeName = $info->{name};
- my $codeName;
- if (exists ($info->{codes}->{$code}))
- {
- $codeName = $info->{codes}->{$code};
- $codeName = (defined ($codeName) ? "/$codeName" : '');
- }
- else
- {
- $codeName = "/$code";
- }
- return "$typeName$codeName";
-}
-
-sub quit
-{
- my $ec = shift;
- my $msg = shift;
-
- print STDERR "$me: $msg\n";
- exit ($ec);
-}
-
-sub usage
-{
- my $ec = shift;
- my @msg = @_;
-
- if (scalar (@msg))
- {
- print STDERR "$me: ", join ("\n", @msg), "\n\n";
- }
-
- print <<EOT;
-usage: $me [-nSDF] [-s servicemap] [-A act1,...] [address...]
-
-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-host.dest-port \[\(flags\)\]
-The fields for the destination address report are:
- iface action packet-count proto dest-port src-host.src-port \[\(flags\)\]
-
-Options are:
--n Disable hostname lookups, and report only IP addresses.
--p Perform paranoid hostname lookups.
--S Generate a source address report.
--D Generate a destination address report.
--F Show all flag combinations associated with packets.
--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
- are pass, block, log, short, and nomatch.
-
-If any addresses are supplied on the command line, the report is limited to
-these hosts. Addresses may be given as dotted IP addresses or hostnames, and
-may be qualified with netmasks in CIDR \(/24\) or dotted \(/255.255.255.0\) format.
-If a hostname resolves to multiple addresses, all addresses are used.
-
-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