This is crazy use of perl. This work as pure network bridge which routing based on mac-adress. Performance is realy very good 1.2ms for flood ping. I test it only on linux with 2 NICs :-)
1 - receive packet from lo, filtering baseon mac or ip, do route based on mac and learned mac-interface table, and finaly show some statistics.Writing of statistics is very slow and Idu it every 10s :-) but performance is going down while writing to screen :-)
#!/usr/bin/perl
# program which send data from eth0 to lo
# I call it eth0
use Socket 1.3;
use Net::Pcap;
use strict;
no utf8;
socket(RAW,17, 3, 255) || die $!;
binmode (RAW);
my $iface = 'eth0';
my $ifaceid = pack ('n',2);
my $flags = pack("SniC12",17,0,1,0);
my ($err);
my $pcap_eth = Net::Pcap::open_live($iface, 1524, 1, 0, \$err);
if (!defined($pcap_eth)) {
die ("Error opening device eth0 returned error $err\n");
}
Net::Pcap::loop ($pcap_eth,-1,\&process,undef);
sub process{
send(RAW,$ifaceid.$_[2],0,$flags) || die "Send :$!\n";
}
this is same as previous
#!/usr/bin/perl
# This one sends packet from eth1 to lo
# I call it eth1
use Socket 1.3;
use Net::Pcap;
use strict;
no utf8;
socket(RAW,17, 3, 255) || die $!;
binmode (RAW);
my $iface = 'eth1';
my $ifaceid = pack ('n',3);
my $flags = pack("SniC12",17,0,1,0);
my ($err);
my $pcap_eth = Net::Pcap::open_live($iface, 1524, 1, 0, \$err);
if (!defined($pcap_eth)) {
die ("Error opening device eth0 returned error $err\n");
}
Net::Pcap::loop ($pcap_eth,-1,\&process,undef);
sub process{
#I add interface id to packe to identify source interface
send(RAW,$ifaceid.$_[2],0,$flags) || die "Send :$!\n";
}
Finaly the main bridge software :-)))
#!/usr/bin/perl
use Socket 1.3;
#use Data::Dumper;
#use Data::Hexdumper;
#use NetPacket;
use NetPacket::IP;
use NetPacket::ARP;
use Net::Pcap;
use Time::HiRes qw( gettimeofday tv_interval);
use strict;
no utf8;
#define SOCKET_PACKET
socket(RAW,17, 3, 255) || die $!;
binmode (RAW);
#setup sniffer
my ($err);
my $pcap_eth = Net::Pcap::open_live('lo', 1524, 1, 0, \$err);
if (!defined($pcap_eth)) {
die ("Error opening device eth0 returned error $err\n");
}
#load block maps
my $blockip={};
loadip();
my $blockmac = {};
loadmac();
sleep 1;
my $clear = `clear`;
#statistic
my $stat = {
eth0 => 0,
eth1 => 0,
'eth1->eth0' => 0,
'eth0->eth1' => 0,
macdrop => 0,
ipdrop => 0,
total => 0,
sum => 0,
};
#define global vars
my $debug=1;
my $tabletime=10;
my $delta = 5;
my $arptable = {};
my $last = 0;
my $protoname = {
2048 => 'ip',
2054 => 'arp',
};
#start working
Net::Pcap::loop ($pcap_eth,-1,\&process,undef);
######################################################################
### Process sniffed packet
######################################################################
sub process {
my $t0 = [gettimeofday];
my (undef,$hdr,$ipkt) = @_;
#decode source interface
my ($srciface,$pkt)=unpack('na*',$ipkt);
return if ($srciface > 3 || $srciface<2);
$stat->{total}++;
#decode Layer-2
my ($dstmac,$srcmac,$proto,$data)=unpack('H12H12na*',$pkt);
#block on Layer-2
if ($blockmac->{$dstmac} == 1 || $blockmac->{$srcmac} == 1) {
$stat->{macdrop}++;
$stat->{sum}+= tv_interval ($t0);
return;
}
#block on Layer-3 proto IP,ARP
if (blockip($proto,$data) == 1) {
$stat->{ipdrop}++;
$stat->{sum}+= tv_interval ($t0);
return;
}
#do route and send
my $ret = route ($dstmac,$srcmac,$srciface,$pkt);
$stat->{eth0}+= $ret if ($srciface == 2);
$stat->{eth1}+= $ret if ($srciface == 3);
#elapsed time
$stat->{sum}+= tv_interval ($t0);
#cleanup arp tables every 10sec
my $time = time;
if ($last<$time) {
cleanup($time);
showstat();
}
}
######################################################################
### Route based on ETH - MAC
######################################################################
sub route ($$$) {
my ($dstmac,$srcmac,$srciface,$pkt) = @_;
#temoprary time to don't nedd call function time
my $time=time;
#check for duplicity of packet
if (defined ($arptable->{$srcmac}->{iface}) && $arptable->{$srcmac
+}->{iface} != $srciface) {
return 0;
}
$arptable->{'ffffffffffff'} = undef;
$arptable->{$srcmac}={
'time' => ($time+$tabletime),
iface => $srciface,
};
my $dstiface;
#check route in table and validity
if ($arptable->{$dstmac}->{'time'} > $time) {
$dstiface = $arptable->{$dstmac}->{iface};
}else{ #if not longer valid remove
$dstiface=undef;
$arptable->{$dstmac}=undef;
}
#return if same interface do nothing
return 1 if ($dstiface == $srciface);
#if valid send to destination
if ($dstiface == 2 || $dstiface == 3){
send(RAW,$pkt,0,pack("SniC12",17,0,$dstiface,0)) || die "Send
+:$!\n";
$stat->{'eth0->eth1'}++ if ($srciface == 2);
$stat->{'eth1->eth0'}++ if ($srciface == 3);
#else send to all except src;
}else{
if ($srciface == 2) {
$stat->{'eth0->eth1'}++;
send(RAW,$pkt,0,pack("SniC12",17,0,3,0)) || die "Send :$!\
+n";
}elsif ($srciface == 3) {
$stat->{'eth1->eth0'}++;
send(RAW,$pkt,0,pack("SniC12",17,0,2,0)) || die "Send :$!\
+n";
};
}
return 1;
}
######################################################################
### Block IP adress
######################################################################
sub blockip {
my ($proto,$data) = @_;
#decode Layer-3
if ($proto eq 0x0806) {
my $arp = NetPacket::ARP->decode($data);
return 1 if ($blockip->{$arp->{spa}} == 1);
return 1 if ($blockip->{$arp->{tpa}} == 1);
}elsif ($proto eq 0x0800) {
my $ip = NetPacket::IP->decode($data);
return 1 if ($blockip->{$ip->{src_ip}} == 1);
return 1 if ($blockip->{$ip->{dest_ip}} == 1);
}
}
######################################################################
### Visual & Cleanup Utils
######################################################################
sub show {
print STDERR Data::Hexdumper::hexdump (data => $_[0]);
}
sub showarp {
print "Mac Interface validity\n";
print "-------------------------------\n";
my $time = time;
my $ifacename = {
2 => 'eth0',
3 => 'eth1',
};
foreach my $key(sort (keys %{$arptable})){
my $valid = $arptable->{$key}->{'time'}-$time;
next if ($valid<0);
print "$key\t".$ifacename->{$arptable->{$key}->{iface}}."\t$va
+lid s\n";
}
}
sub cleanup {
my $time = shift;
foreach my $key( keys %{$arptable}){
$arptable->{$key}= undef if ($arptable->{$key}->{'time'}<$time
+);
}
$last = $time+$delta;
}
sub showstat {
print $clear."\n";
print "Network stats\n";
print "eth0\teth1\teth0->eth1\teth1->eth0\tdropmac\tdropip\n";
print $stat->{eth0}."\t".$stat->{eth1}."\t".$stat->{'eth0->eth1'}.
+"\t\t".$stat->{'eth1->eth0'}."\t\t".$stat->{macdrop}."\t".$stat->{ipd
+rop}."\n\n";
print "Average proces time: ".(($stat->{sum}/$stat->{total})*1000)
+." ms\n\n";
showarp();
}
######################################################################
### Loading policy files
######################################################################
sub loadmac {
#open file with mac adresses
open(MAC, './block.mac') || die "Open block.mac: $!\n";
my $block={};
#read echa line from file
print "Blocked MACs\n";
while (<MAC>) {
#normalize to network type
lc $_;
s/://gsi;
s/\s//gsi;
s/\.//gsi;
#skip all not valid mac adresses
next if ( length ($_)< 12);
#add to deny list
$blockmac->{$_}=1;
print $_."\n";
}
close MAC;
return;
}
sub loadip {
#open file with mac adresses
open(IP, './block.ip') || die "Open block.ip: $!\n";
print "Blocked IPs\n";
#read echa line from file
while (<IP>) {
#next if to short
next if ( length ($_)< 7);
#parse IP from file
my (@ip) = m/(\d*)\.(\d*)\.(\d*)\.(\d*)/;
#convert to binary
my $pip = pack ('C4',@ip);
$blockip->{$pip}=1;
$blockip->{$_}=1;
print "$_ \n";
}
close IP;
return;
}
Crazy :-) but works nearly at same speed like C program :-)
PS: Thank's to all monks which helpme with some problems (binmode ;-)
-=- MamuT -=-