Skip to content
This repository has been archived by the owner on Nov 12, 2021. It is now read-only.

For upstream2 #4

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 103 additions & 31 deletions dhcp0f.pl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ =head1 SYNOPSIS
dhcp0f.pl [options]

Options:
-k Fingerbank API key (mandatory)
-k Fingerbank API key
-i Interface (default: "eth0")
-f Filter (eg. "host 128.103.1.1")
-c CHADDR (show requests from specific client)
Expand All @@ -25,6 +25,7 @@ =head1 SYNOPSIS
7 DHCPRELEASE
8 DHCPINFORM
-v verbose
-j output unique identifiers in json format
-h Help

=cut
Expand All @@ -49,10 +50,9 @@ =head1 SYNOPSIS

use Util qw(clean_mac);
use pf::util::dhcp;
use fingerbank::api;

my %args;
getopts( 'k:t:i:f:c:o:huv', \%args );
getopts( 'k:t:i:f:c:o:jhuv', \%args );

my $verbose = $INFO;
if ( $args{v} ) {
Expand All @@ -62,9 +62,17 @@ =head1 SYNOPSIS
Log::Log4perl->easy_init({ level => $verbose, layout => '%m%n' });
my $logger = Log::Log4perl->get_logger('');

unless($args{'k'}){
$logger->fatal("No key specified");
pod2usage( -verbose => 1 );
my $fingerbank = 0;
if ( $args{k} ) {
require fingerbank::api;
$fingerbank = 1;
}

my $output_type = 'plain';
if ( $args{j} ) {
require JSON;
JSON->import();
$output_type = 'json';
}

my $interface = $args{i} || "eth0";
Expand Down Expand Up @@ -134,7 +142,9 @@ =head1 SYNOPSIS

Net::Pcap::setfilter( $pcap_t, $filter_t );

$logger->info("Starting to listen on $interface with filter: $filter");
if ( $output_type eq 'plain' ) {
$logger->info("Starting to listen on $interface with filter: $filter");
}
Net::Pcap::loop( $pcap_t, -1, \&process_pkt, $interface );

sub process_pkt {
Expand Down Expand Up @@ -167,14 +177,39 @@ sub listen_dhcp {
# DHCP Message Type filter
return if ( $type && $type ne $dhcp->{'options'}{'53'} );

$logger->info(POSIX::strftime( "%Y-%m-%d %H:%M:%S", localtime ));
$logger->info("-" x 80);
$logger->info(sprintf("Ethernet\tsrc:\t%s\tdst:\t%s", clean_mac($l2->{'src_mac'}), clean_mac($l2->{'dest_mac'})));
$logger->info(sprintf("IP\t\tsrc: %20s\tdst: %20s", $l3->{'src_ip'}, $l3->{'dest_ip'}));
$logger->info(sprintf("UDP\t\tsrc port: %15s\tdst port: %15s", $l4->{'src_port'}, $l4->{'dest_port'}));
$logger->info("-" x 80);
$logger->info(dhcp_summary($dhcp));
$logger->debug(Dumper($dhcp));
#past all the returns, the real work starts
my %dhcp_hash; #this could be conditional but doesn't seem worth it
$dhcp_hash{'msg_type'} = $dhcp->{'options'}{'53'};
$dhcp_hash{'hostname'} = ( defined($dhcp->{'options'}{'12'}) ? $dhcp->{'options'}{'12'} : '');

#https://en.wikipedia.org/wiki/EtherType
#33024 is 0x8100 802.1q
#33280 is 0x8200 Cisco inter switch link https://www.cisco.com/c/en/us/support/docs/lan-switching/8021q/17056-741-4.html
#37120 is 0x9100 Vlan-tagged with double tagging
my $vlan = "untagged";
if ( $l2->{'tpid'} ) {
if (($l2->{'tpid'} == 33024) || ($l2->{'tpid'} == 33280) || ($l2->{'tpid'} == 37120)) {
$vlan = $l2->{'vid'};
}
}
$dhcp_hash{'vlan'} = $vlan;

if ( $output_type eq 'plain' ) {
$logger->info(POSIX::strftime( "%Y-%m-%d %H:%M:%S", localtime ));
$logger->info("-" x 80);
$logger->info(sprintf("Ethernet\tsrc:\t%s\tdst:\t%s", clean_mac($l2->{'src_mac'}), clean_mac($l2->{'dest_mac'})));
$logger->info(sprintf("IP\t\tsrc: %20s\tdst: %20s", $l3->{'src_ip'}, $l3->{'dest_ip'}));
$logger->info(sprintf("UDP\t\tsrc port: %15s\tdst port: %15s", $l4->{'src_port'}, $l4->{'dest_port'}));
$logger->info("vlan: " . $vlan);
$logger->info("-" x 80);
$logger->info(dhcp_summary($dhcp));
$logger->debug(Dumper($dhcp));
} elsif ( $output_type eq 'json' ) {
$dhcp_hash{'src_mac'} = clean_mac($l2->{'src_mac'});
$dhcp_hash{'dest_mac'} = clean_mac($l2->{'dest_mac'});
$dhcp_hash{'src_ip'} = $l3->{'src_ip'};
$dhcp_hash{'dest_ip'} = $l3->{'dest_ip'};
}

foreach my $key ( keys(%{ $dhcp->{'options'} }) ) {
my $tmpkey = $key;
Expand All @@ -194,30 +229,67 @@ sub listen_dhcp {
$output = $dhcp->{'options'}{$key};
}
unless ( !$output ) {
$logger->info( "$tmpkey: $output" );
if ( $output_type eq 'plain' ) {
$logger->info( "$tmpkey: $output" );
}
}
}
$logger->info("TTL: $l3->{'ttl'}");
if ( $output_type eq 'plain' ) {
$logger->info("TTL: $l3->{'ttl'}");
} elsif ( $output_type eq 'json' ) {
$dhcp_hash{'ttl'} = $l3->{'ttl'};
}

my $dhcp_fingerprint = $dhcp->{'options'}{'55'};
my $dhcp_vendor = $dhcp->{'options'}{'60'};
$logger->info("DHCP fingerprint: " . ( defined($dhcp_fingerprint) ? $dhcp_fingerprint : 'None' ));
$logger->info("DHCP vendor: " . ( defined($dhcp_vendor) ? $dhcp_vendor : 'None' ));

my $fingerbank_result = fingerbank::api::query($args{k}, {dhcp_fingerprint => $dhcp_fingerprint, dhcp_vendor => $dhcp_vendor});
if ( $output_type eq 'plain' ) {
$logger->info("DHCP fingerprint: " . ( defined($dhcp_fingerprint) ? $dhcp_fingerprint : 'None' ));
$logger->info("DHCP vendor: " . ( defined($dhcp_vendor) ? $dhcp_vendor : 'None' ));
} elsif ( $output_type eq 'json' ) {
$dhcp_hash{'dhcp_fingerprint'} = ( defined($dhcp_fingerprint) ? $dhcp_fingerprint : 'None' );
$dhcp_hash{'dhcp_vendor'} = ( defined($dhcp_vendor) ? $dhcp_vendor : 'None' );
}

if(defined($fingerbank_result)) {
my $fingerbank_device = join('/', reverse(map {$_->{name}} @{$fingerbank_result->{device}->{parents}})) . '/' . $fingerbank_result->{device}->{name};
$logger->info("Fingerbank device : $fingerbank_device (".$fingerbank_result->{device}->{id}.")");
my $fingerbank_version = $fingerbank_result->{version} // 'Unknown';
$logger->info("Fingerbank device version : ".$fingerbank_version);
$logger->info("Fingerbank device score : ".$fingerbank_result->{score});
if ( $fingerbank ) {
my $fingerbank_result = fingerbank::api::query($args{k}, {dhcp_fingerprint => $dhcp_fingerprint, dhcp_vendor => $dhcp_vendor});

if(defined($fingerbank_result)) {
my $fingerbank_device = join('/', reverse(map {$_->{name}} @{$fingerbank_result->{device}->{parents}})) . '/' . $fingerbank_result->{device}->{name};
$logger->info("Fingerbank device : $fingerbank_device (".$fingerbank_result->{device}->{id}.")");
my $fingerbank_version = $fingerbank_result->{version} // 'Unknown';
if ( $output_type eq 'plain' ) {
$logger->info("Fingerbank device version : ".$fingerbank_version);
$logger->info("Fingerbank device score : ".$fingerbank_result->{score});
} elsif ( $output_type eq 'json' ) {
$dhcp_hash{'fingerbank_version'} = $fingerbank_version;
$dhcp_hash{'fingerbank_score'} = $fingerbank_result->{score};
}
}
else {
if ( $output_type eq 'plain' ) {
$logger->info("Fingerbank device unknown");
} elsif ( $output_type eq 'json' ) {
$dhcp_hash{'fingerbank_version'} = "unknown";
$dhcp_hash{'fingerbank_score'} = -1;
}
}
}
else {
$logger->info("Fingerbank device unknown");
if ( $output_type eq 'plain' ) {
$logger->info("=" x 80);
} elsif ( $output_type eq 'json' ) {
$logger->debug("-" x 80);
$logger->debug("src_mac: " . clean_mac($l2->{'src_mac'}));
$logger->debug("dest_mac: " . clean_mac($l2->{'dest_mac'}));
$logger->debug("src_ip: " . $l3->{'src_ip'});
$logger->debug("dest_ip: " . $l3->{'dest_ip'});
$logger->debug("hostname: " . ( defined($dhcp->{'options'}{'12'}) ? $dhcp->{'options'}{'12'} : ''));
$logger->debug("vlan: " . $vlan);
$logger->debug("dhcp option 53 (message type): " . ( defined($dhcp->{'options'}{'53'}) ? $dhcp->{'options'}{'53'} : '' ));
$logger->debug("TTL: $l3->{'ttl'}");
$logger->debug("dhcp fingerprint: " . ( defined($dhcp_fingerprint) ? $dhcp_fingerprint : 'None' ));
$logger->debug("dhcp vendor: " . ( defined($dhcp_vendor) ? $dhcp_vendor : 'None' ));
$logger->info(encode_json(\%dhcp_hash));
}

$logger->info("=" x 80);
}

=head1 AUTHOR
Expand Down