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