1b5ac7c87Sflorian#! /usr/bin/perl 2*bfa684acSbluhm# $OpenBSD: flow.pl,v 1.6 2017/03/03 21:34:14 bluhm Exp $ 3b5ac7c87Sflorian 4b5ac7c87Sflorian# Copyright (c) 2013 Florian Obser <florian@openbsd.org> 5b5ac7c87Sflorian# 6b5ac7c87Sflorian# Permission to use, copy, modify, and distribute this software for any 7b5ac7c87Sflorian# purpose with or without fee is hereby granted, provided that the above 8b5ac7c87Sflorian# copyright notice and this permission notice appear in all copies. 9b5ac7c87Sflorian# 10b5ac7c87Sflorian# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11b5ac7c87Sflorian# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12b5ac7c87Sflorian# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13b5ac7c87Sflorian# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14b5ac7c87Sflorian# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15b5ac7c87Sflorian# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16b5ac7c87Sflorian# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17b5ac7c87Sflorian 18b5ac7c87Sflorian 19b5ac7c87Sflorianuse strict; 20b5ac7c87Sflorianuse warnings; 21b5ac7c87Sflorianuse 5.010; 22b3e4b791Sbluhmuse Config; 23b5ac7c87Sflorian 24b5ac7c87Sflorianuse Data::Dumper; 25b5ac7c87Sflorianuse IO::Socket::INET; 26b5ac7c87Sflorianuse Net::Flow; 27b5ac7c87Sflorian 28b5ac7c87Sflorianmy $port = 9996; 29b5ac7c87Sflorian 30b5ac7c87Sflorian{ 31b5ac7c87Sflorian my $id2name = { 32b5ac7c87Sflorian 1 => 'octetDeltaCount', 33b5ac7c87Sflorian 2 => 'packetDeltaCount', 34b5ac7c87Sflorian 4 => 'protocolIdentifier', 35b5ac7c87Sflorian 5 => 'ipClassOfService', 36b5ac7c87Sflorian 7 => 'sourceTransportPort', 37b5ac7c87Sflorian 8 => 'sourceIPv4Address', 38b5ac7c87Sflorian 10 => 'ingressInterface', 39b5ac7c87Sflorian 11 => 'destinationTransportPort', 40b5ac7c87Sflorian 12 => 'destinationIPv4Address', 41b5ac7c87Sflorian 14 => 'egressInterface', 42b5ac7c87Sflorian 21 => 'flowEndSysUpTime', 43b5ac7c87Sflorian 22 => 'flowStartSysUpTime', 44b5ac7c87Sflorian 27 => 'sourceIPv6Address', 45b5ac7c87Sflorian 28 => 'destinationIPv6Address', 46b5ac7c87Sflorian 150 => 'flowStartSeconds', 47b5ac7c87Sflorian 151 => 'flowEndSeconds', 48b5ac7c87Sflorian 152 => 'flowStartMilliseconds', 49b5ac7c87Sflorian 153 => 'flowEndMilliseconds', 50b5ac7c87Sflorian }; 51b5ac7c87Sflorian my $name2id = {reverse %$id2name}; 52b5ac7c87Sflorian sub id2name { return $id2name->{$_[0]} || $_[0]; } 53b5ac7c87Sflorian sub name2id { return $name2id->{$_[0]} || $_[0]; } 54b5ac7c87Sflorian} 55b5ac7c87Sflorian 56b5ac7c87Sfloriansub get_ifs 57b5ac7c87Sflorian{ 58b5ac7c87Sflorian my (@ifs, $prog); 59b5ac7c87Sflorian open($prog, 'ifconfig |') or die $!; 60b5ac7c87Sflorian while(<$prog>) { 61b5ac7c87Sflorian chomp; 62b5ac7c87Sflorian push(@ifs, $1) if(/^(\w+):/); 63b5ac7c87Sflorian } 64b5ac7c87Sflorian close($prog) or die $!; 65b5ac7c87Sflorian return(grep({$_ ne 'lo0'} @ifs)); 66b5ac7c87Sflorian} 67b5ac7c87Sflorian 68b5ac7c87Sfloriansub gen_pf_conf 69b5ac7c87Sflorian{ 70b5ac7c87Sflorian my @ifs = @_; 71b5ac7c87Sflorian my $skip = 'set skip on {'.join(' ', @ifs).'}'; 72b5ac7c87Sflorian return <<END; 73b5ac7c87Sflorian$skip 74b5ac7c87Sflorianpass on lo0 no state 75b5ac7c87Sflorianpass on lo0 proto tcp from port 12345 to port 12346 keep state (pflow) 76b5ac7c87SflorianEND 77b5ac7c87Sflorian} 78b5ac7c87Sflorian 79b5ac7c87Sflorianif (scalar(@ARGV) != 2 || ($ARGV[0] != 9 && $ARGV[0]!=10)) { 80b5ac7c87Sflorian print STDERR "usage: $0 [9|10] [4|6]\n"; 81b5ac7c87Sflorian exit(1); 82b5ac7c87Sflorian} 83b5ac7c87Sflorian 84b5ac7c87Sflorianif (scalar(@ARGV) != 2 || ($ARGV[1] != 4 && $ARGV[1]!=6)) { 85b5ac7c87Sflorian print STDERR "usage: $0 [9|10] [4|6]\n"; 86b5ac7c87Sflorian exit(1); 87b5ac7c87Sflorian} 88b5ac7c87Sflorian 89b5ac7c87Sflorian 90b5ac7c87Sflorianmy @v94_elem_names = qw (sourceIPv4Address 91b5ac7c87Sflorian destinationIPv4Address 92b5ac7c87Sflorian ingressInterface 93b5ac7c87Sflorian egressInterface 94b5ac7c87Sflorian packetDeltaCount 95b5ac7c87Sflorian octetDeltaCount 96b5ac7c87Sflorian flowStartSysUpTime 97b5ac7c87Sflorian flowEndSysUpTime 98b5ac7c87Sflorian sourceTransportPort 99b5ac7c87Sflorian destinationTransportPort 100b5ac7c87Sflorian ipClassOfService 101b5ac7c87Sflorian protocolIdentifier); 102b5ac7c87Sflorian 103b5ac7c87Sflorianmy @v96_elem_names = qw (sourceIPv6Address 104b5ac7c87Sflorian destinationIPv6Address 105b5ac7c87Sflorian ingressInterface 106b5ac7c87Sflorian egressInterface 107b5ac7c87Sflorian packetDeltaCount 108b5ac7c87Sflorian octetDeltaCount 109b5ac7c87Sflorian flowStartSysUpTime 110b5ac7c87Sflorian flowEndSysUpTime 111b5ac7c87Sflorian sourceTransportPort 112b5ac7c87Sflorian destinationTransportPort 113b5ac7c87Sflorian ipClassOfService 114b5ac7c87Sflorian protocolIdentifier); 115b5ac7c87Sflorian 116b5ac7c87Sflorianmy @v104_elem_names = qw (sourceIPv4Address 117b5ac7c87Sflorian destinationIPv4Address 118b5ac7c87Sflorian ingressInterface 119b5ac7c87Sflorian egressInterface 120b5ac7c87Sflorian packetDeltaCount 121b5ac7c87Sflorian octetDeltaCount 122b5ac7c87Sflorian flowStartMilliseconds 123b5ac7c87Sflorian flowEndMilliseconds 124b5ac7c87Sflorian sourceTransportPort 125b5ac7c87Sflorian destinationTransportPort 126b5ac7c87Sflorian ipClassOfService 127b5ac7c87Sflorian protocolIdentifier); 128b5ac7c87Sflorian 129b5ac7c87Sflorianmy @v106_elem_names = qw (sourceIPv6Address 130b5ac7c87Sflorian destinationIPv6Address 131b5ac7c87Sflorian ingressInterface 132b5ac7c87Sflorian egressInterface 133b5ac7c87Sflorian packetDeltaCount 134b5ac7c87Sflorian octetDeltaCount 135b5ac7c87Sflorian flowStartMilliseconds 136b5ac7c87Sflorian flowEndMilliseconds 137b5ac7c87Sflorian sourceTransportPort 138b5ac7c87Sflorian destinationTransportPort 139b5ac7c87Sflorian ipClassOfService 140b5ac7c87Sflorian protocolIdentifier); 141b5ac7c87Sflorian 142b5ac7c87Sflorianmy ($name, $sock, $packet, $header_ref, $template_ref, $flow_ref, $flows_ref, 143b5ac7c87Sflorian $error_ref, @elem_names, $prog, $line); 144b5ac7c87Sflorian 145b3e4b791Sbluhmsystem('ifconfig', 'lo0', 'inet', '10.11.12.13', 'alias'); 146b5ac7c87Sfloriansystem('ifconfig', 'lo0', 'inet6', '2001:db8::13'); 147b5ac7c87Sflorian 148b5ac7c87Sflorianopen($prog, '|pfctl -f -') or die $!; 149b5ac7c87Sflorianprint $prog gen_pf_conf(get_ifs()); 150b5ac7c87Sflorianclose($prog) or die $!; 151b5ac7c87Sflorian 152b5ac7c87Sflorianif (`ifconfig pflow0 2>&1` ne "pflow0: no such interface\n") { 153b5ac7c87Sflorian system('ifconfig', 'pflow0', 'destroy'); 154b5ac7c87Sflorian} 155b5ac7c87Sflorian 156b5ac7c87Sfloriansystem('ifconfig', 'pflow0', 'flowsrc', '127.0.0.1', 'flowdst', 157b5ac7c87Sflorian '127.0.0.1:9996', 'pflowproto', $ARGV[0]); 158b5ac7c87Sflorian 159b5ac7c87Sfloriansystem('./gen_traffic '.$ARGV[1].' &'); 160b5ac7c87Sflorian 161b5ac7c87Sflorianif ($ARGV[0] == 9 && $ARGV[1] == 4) { 162b5ac7c87Sflorian @elem_names = @v94_elem_names; 163b5ac7c87Sflorian} elsif ($ARGV[0] == 9 && $ARGV[1] == 6) { 164b5ac7c87Sflorian @elem_names = @v96_elem_names; 165b5ac7c87Sflorian} elsif ($ARGV[0] == 10 && $ARGV[1] == 4) { 166b5ac7c87Sflorian @elem_names = @v104_elem_names; 167b5ac7c87Sflorian} elsif ($ARGV[0] == 10 && $ARGV[1] == 6) { 168b5ac7c87Sflorian @elem_names = @v106_elem_names; 169b5ac7c87Sflorian} 170b5ac7c87Sflorian 171b5ac7c87Sflorian$sock = IO::Socket::INET->new(LocalPort =>$port, Proto => 'udp'); 172b5ac7c87Sflorianwhile ($sock->recv($packet,1548)) { 173b5ac7c87Sflorian ($header_ref, $template_ref, $flows_ref, $error_ref) = 174b5ac7c87Sflorian Net::Flow::decode(\$packet, $template_ref); 175b5ac7c87Sflorian if (scalar(@$flows_ref) > 0) { 176b5ac7c87Sflorian say scalar(@$flows_ref),' flows'; 177b5ac7c87Sflorian foreach $flow_ref (@$flows_ref) { 178b5ac7c87Sflorian say scalar(keys %$flow_ref) - 1, ' elements'; 179b5ac7c87Sflorian say 'SetId: ', $flow_ref->{'SetId'}; 180b5ac7c87Sflorian my ($iif, $eif, $start, $end); 181b5ac7c87Sflorian 182b3e4b791Sbluhm my $qpack = $Config{longsize} == 8 ? 'Q>' : 183b3e4b791Sbluhm $Config{byteorder} == 1234 ? 'L>xxxx' : 'xxxxL>'; 184b3e4b791Sbluhm 185b5ac7c87Sflorian foreach $name (@elem_names) { 186b5ac7c87Sflorian if ($name eq 'ingressInterface') { 187b5ac7c87Sflorian $iif = unpack('N', 188b5ac7c87Sflorian $flow_ref->{name2id($name)}); 189b5ac7c87Sflorian } elsif ($name eq 'egressInterface') { 190b5ac7c87Sflorian $eif = unpack('N', 191b5ac7c87Sflorian $flow_ref->{name2id($name)}); 192b5ac7c87Sflorian } elsif ($name eq 'flowStartSysUpTime') { 193b5ac7c87Sflorian $start = unpack('N', 194b5ac7c87Sflorian $flow_ref->{name2id($name)})/1000; 195b5ac7c87Sflorian } elsif ($name eq 'flowEndSysUpTime') { 196b5ac7c87Sflorian $end = unpack('N', 197b5ac7c87Sflorian $flow_ref->{name2id($name)})/1000; 198b5ac7c87Sflorian } elsif ($name eq 'flowStartSeconds') { 199b5ac7c87Sflorian $start = unpack('N', 200b5ac7c87Sflorian $flow_ref->{name2id($name)}); 201b5ac7c87Sflorian } elsif ($name eq 'flowEndSeconds') { 202b5ac7c87Sflorian $end = unpack('N', 203b5ac7c87Sflorian $flow_ref->{name2id($name)}); 204b5ac7c87Sflorian } elsif ($name eq 'flowStartMilliseconds') { 205b3e4b791Sbluhm $start = unpack($qpack, 206b5ac7c87Sflorian $flow_ref->{name2id($name)})/1000; 207b5ac7c87Sflorian } elsif ($name eq 'flowEndMilliseconds') { 208b3e4b791Sbluhm $end = unpack($qpack, 209b5ac7c87Sflorian $flow_ref->{name2id($name)})/1000; 210b5ac7c87Sflorian } else { 211b5ac7c87Sflorian say $name,': ', unpack('H*', 212b5ac7c87Sflorian $flow_ref->{name2id($name)}); 213b5ac7c87Sflorian } 214b5ac7c87Sflorian } 21520d0d40fSflorian 216b5ac7c87Sflorian say 'ingressInterface == egressInterface && '. 217b5ac7c87Sflorian 'egressInterface > 0: ', ($iif == $eif && $eif > 0); 218b5ac7c87Sflorian } 219b5ac7c87Sflorian last; 220b5ac7c87Sflorian } 221b5ac7c87Sflorian} 222b3e4b791Sbluhm 223b3e4b791SbluhmEND { 224b5ac7c87Sflorian system('ifconfig', 'pflow0', 'destroy'); 225b3e4b791Sbluhm system('ifconfig', 'lo0', 'inet', '10.11.12.13', 'delete'); 226b3e4b791Sbluhm system('ifconfig', 'lo0', 'inet6', '2001:db8::13', 'delete'); 227b3e4b791Sbluhm} 228