in reply to Getting ethernet type using NetPacket

I wrote this stuff in 2003-2004, and no longer remember the intricate details .. You may find plagiarizing this useful:

#!/usr/bin/perl -w # # Network Traffic Analyzer # ------------------------- # May 2003, by Netwallah # # This program analyzes network traffic, and reports on packets captur +ed. # It uses the "pcap" interface (winpcap for Win32 : http://winpcap.pol +ito.it). # It uses Net::pcap. Win32 version of this is at http://www.bribes.org +/perl/wnetpcap.html # To use this, you may also need to do the following commands: # ppm install Data-HexDump # ppm install NetPacket # ppm install http://www.bribes.org/perl/ppm/Net-Pcap.ppd # ppm install http://www.bribes.org/perl/ppm/Net-PcapUtils.ppd ###################################################################### +### use strict; use English; use Net::Pcap; use NetPacket::Ethernet qw(:types); use NetPacket::IP qw(:protos); use NetPacket::ARP qw(:opcodes); use NetPacket::TCP; use NetPacket::UDP; use NetPacket::ICMP qw(:types); use Data::HexDump; my( $pcap_desc, $err, $result); my $verbose = 1; my %pcap_parameters = ( SNAPLEN => 124, # Num bytes to capture from packet PROMISCUOUS_MODE => 1, # Operate in promiscuous mode? TIMEOUT => 1000, # Read timeout (ms) NUMPACKETS => 500, # Pkts to read (-1 = loop forever) #FILTER => 'ip proto \icmp', # Filter string FILTER => 'arp or udp dst port 161', # Filter string USERDATA => '', # Passed as first arg to callback fn SAVEFILE => '', # Default save file # Items below are RETURNED values from PCap calls. # Do not attempt to change them in the declaration. FILTER_HANDLE => 0, # Reference to compiled filter NETWORK_INTERFACE => 'intel',# Network interface to open NETWORK_ADDR =>0, # Network Address (32 bit number) NETWORK_MASK =>0, # Mask (32-bit number) mode => '', # Internal variable ); # Partial list from http://www.iana.org/assignments/ethernet-numbers my %Ethernet_Type_Name = ( (ETH_TYPE_IP) =>{NAME=>'IP', DECODER => \&Decode_IP} +, (ETH_TYPE_ARP) =>{NAME=>'ARP', DECODER => \&Decode_AR +P}, (ETH_TYPE_APPLETALK) =>{NAME=>'APPLETALK', DECODER => 0}, 0x8035 =>{NAME=>'RARP', DECODER => \&Decode_ARP}, # (E +TH_TYPE_RARP is NOT exported!!!) (ETH_TYPE_SNMP) =>{NAME=>'SNMP', DECODER => 0}, (ETH_TYPE_IPv6) =>{NAME=>'IPv6', DECODER => 0}, (ETH_TYPE_PPP) =>{NAME=>'PPP' ,DECODER => 0} ); # Partial list From http://www.iana.org/assignments/protocol-numbers my %IP_Type_Name = ( (IP_PROTO_IP) =>{NAME=>'IP', DECODER=>0}, (IP_PROTO_ICMP) =>{NAME=>'ICMP', DECODER=>\&Decode_IP_ICMP}, (IP_PROTO_IGMP) =>{NAME=>'IGMP', DECODER=>0}, (IP_PROTO_IPIP) =>{NAME=>'IP', DECODER=>0}, # Encapsulate IP ins +ide IP (IP_PROTO_TCP) =>{NAME=>'TCP', DECODER=> \&Decode_IP_TCP}, (IP_PROTO_UDP) =>{NAME=>'UDP', DECODER=> \&Decode_IP_UDP}, 41 =>{NAME=>'IPv6', DECODER=>0}, 46 =>{NAME=>'RSVP', DECODER=>0}, 47 =>{NAME=>'GRE', DECODER=>0}, 88 =>{NAME=>'EIGRP', DECODER=>0}, 112 =>{NAME=>'VRRP', DECODER=>0}, 115 =>{NAME=>'L2TP' , DECODER=>0} ); my %ARP_Type_Name = ( (ARP_OPCODE_REQUEST) =>{NAME=>'ArpRequest', DECODER=>0}, (ARP_OPCODE_REPLY) =>{NAME=>'ArpReply', DECODER=>0}, (RARP_OPCODE_REQUEST) =>{NAME=>'RArpRequest', DECODER=>0}, (RARP_OPCODE_REPLY) =>{NAME=>'RArpReply', DECODER=>0}, 8 =>{NAME=>'InArpRequest', DECODER=>0}, 9 =>{NAME=>'InArpReply', DECODER=>0}, 10 =>{NAME=>'ArpNAK', DECODER=>0} ); my %Hex_Decoder; # Some Well-known ports .. my %TCP_UDP_Ports =( 7 => {NAME=>'Echo', TCP=>1,UDP=>1, DECOD +ER=>0}, 20 => {NAME=>'ftp-data', TCP=>1,UDP=>1, DECOD +ER=>0}, 21 => {NAME=>'ftp-control', TCP=>1,UDP=>1, DECOD +ER=>0}, 22 => {NAME=>'SSH', TCP=>1,UDP=>1, DECOD +ER=>0}, 23 => {NAME=>'Telnet', TCP=>1,UDP=>1, DECOD +ER=>0}, 25 => {NAME=>'SMTP', TCP=>1,UDP=>1, DECOD +ER=>0}, 53 => {NAME=>'DNS', TCP=>1,UDP=>1, DECOD +ER=>0}, 67 => {NAME=>'BOOTP Client', TCP=>0,UDP=>1, DECOD +ER=>\&Decode_TCPUDP_BOOTP}, 68 => {NAME=>'BOOTP Server', TCP=>0,UDP=>1, DECOD +ER=>\&Decode_TCPUDP_BOOTP}, 69 => {NAME=>'TFTP', TCP=>0,UDP=>1, DECOD +ER=>0}, 80 => {NAME=>'HTTP', TCP=>1,UDP=>0, DECOD +ER=>0}, 119 => {NAME=>'NNTP', TCP=>1,UDP=>1, DECOD +ER=>0}, 123 => {NAME=>'NTP', TCP=>1,UDP=>1, DECOD +ER=>0}, 137 => {NAME=>'NBName', TCP=>1,UDP=>1, DECOD +ER=>\&Decode_IP_UDP_NETBIOS_Name}, 138 => {NAME=>'NETBIOS DGram' , TCP=>1,UDP=>1, DECOD +ER=>0}, 139 => {NAME=>'NETBIOS Sess', TCP=>1,UDP=>1, DECOD +ER=>0}, 161 => {NAME=>'SNMP', TCP=>1,UDP=>1, DECOD +ER=>\&SNMP_Special_Handler}, 443 => {NAME=>'HTTPS', TCP=>1,UDP=>0, DECOD +ER=>0}, 520 => {NAME=>'RIP', TCP=>0,UDP=>1, DECOD +ER=>0}, 1433 => {NAME=>'mssql-srv', TCP=>1,UDP=>1, DECOD +ER=>0}, 1434 => {NAME=>'mssql-monitor', TCP=>1,UDP=>1, DECOD +ER=>0}, 10000 => {NAME=>'WEBMIN', TCP=>1,UDP=>0, DECOD +ER=>0}, ); my %ICMP_TYPES=( 0 => "ECHOREPLY" , 3 => "UNREACH" , 4 => "SOURCEQUENCH" , 5 => "REDIRECT" , 8 => "ECHO" , 9 => "ROUTERADVERT" , 10 => "ROUTERSOLICIT" , 11 => "TIMXCEED" , 12 => "PARAMPROB" , 13 => "TSTAMP" , 14 => "TSTAMPREPLY" , 15 => "IREQ" , 16 => "IREQREPLY" , 17 => "MASKREQ" , 18 => "MASKREPLY" , ); ###################################################################### +## #---- Action Starts ----- &Initialize(); my $count = 0; Net::Pcap::loop($pcap_desc, $pcap_parameters{NUMPACKETS}, \&process_pk +t, "abc"); Net::Pcap::close($pcap_desc); if ($count != $pcap_parameters{NUMPACKETS}) { print("Bad pkt count - expected $pcap_parameters{NUMPACKETS} but +got $count.\n"); exit; } ########################################## sub process_pkt { my($user, $hdr, $pkt) = @_; print("Bad user data\n"), if ($user ne "abc"); print("Bad pkthdr\n"), if (!defined($hdr)); print("Bad pkt data\n"), if (!defined($pkt)); my ($sec,$min,$hour) =localtime($hdr->{tv_sec}); my $len= $hdr->{len}; my $buf; #print("RcvPkt Totlen(PacketLen) $hdr->{len}($hdr->{caplen})" . # "\t Time.Usec=$hour:$min:$sec.$hdr->{tv_usec}\n"); my $eth_obj = NetPacket::Ethernet->decode($pkt); #print("$eth_obj->{src_mac}:$eth_obj->{dest_mac} " . # "$Ethernet_Type_Name{$eth_obj->{type}} \n"); $buf = sprintf("%02d:%02d:%02d.%03d[%4d] ", $hour,$min,$sec, $hdr->{tv_usec} / 1000,$hdr->{len}); # Call the appropriate decoder, depending on pkt type if (&Dispach_Decoder_If_Any(\%Ethernet_Type_Name, $eth_obj->{type} +, $eth_obj,\$buf)){ # Decoder call failed.. $buf .= "Ether " . $eth_obj->{src_mac} . "-> $eth_obj->{dest_mac} " . &get_TypeName(\%Ethernet_Type_Name,$eth_obj->{type}) ; $verbose > 1 and $buf .= "\n" . &GetHex($eth_obj->{data}); } $verbose and print "$buf\n"; if ($verbose > 2){ my $HexData = HexDump($eth_obj->{data}) ; $HexData =~s/^.+\n\n//; # Delete first line print "$HexData\n"; } $count++; } ####################################################### sub Decode_IP(){ my ($eth_obj ,$bufref) = @_; my $ip_obj = NetPacket::IP->decode($eth_obj->{data},$eth_obj); $$bufref .= "IP:$ip_obj->{src_ip}\->$ip_obj->{dest_ip} Len=$ip_ +obj->{len} " ##. " Proto=$ip_obj->{proto} " . &get_TypeName(\%IP_Type_Name,$ip_obj->{proto}); # Call the appropriate decoder, depending on pkt type if (&Dispach_Decoder_If_Any(\%IP_Type_Name, $ip_obj->{proto}, $ip_obj,$bufref)){ # Decoder call failed.. (Ignore) } } ####################################################### sub Decode_IP_TCP(){ my ($ip_obj ,$bufref) = @_; my $tcp_obj = NetPacket::TCP->decode($ip_obj->{data},$ip_obj); # $$bufref .= " Port $tcp_obj->{src_port}\->$tcp_obj->{dest_port}"; $$bufref .= " " . &get_TypeName(\%TCP_UDP_Ports ,$tcp_obj->{src_por +t},1) ."->" . &get_TypeName(\%TCP_UDP_Ports ,$tcp_obj->{dest_po +rt},1); } ####################################################### sub Decode_IP_UDP(){ my ($ip_obj ,$bufref) = @_; my $udp_obj = NetPacket::UDP->decode($ip_obj->{data}, $ip_obj); $$bufref .= " " . &get_TypeName(\%TCP_UDP_Ports ,$udp_obj->{src_por +t},1) ."->" . &get_TypeName(\%TCP_UDP_Ports ,$udp_obj->{dest_po +rt},1); # Call the appropriate decoder, depending on pkt type if (&Dispach_Decoder_If_Any(\%TCP_UDP_Ports, $udp_obj->{dest_port}, $udp_obj,$bufref)){ # Decoder call failed.. (Ignore) } } ####################################################### sub Decode_IP_UDP_NETBIOS_Name(){ my ($udp_obj ,$bufref) = @_; my ($R_Flag, $OPCODE,$NM_FLAGS,$RCODE,$Q_Name); my ($NAME_TRN_ID,$temp, $QDCOUNT,$ANCOUNT,$NSCOUNT,$ARCOUNT,$NameInfo) = unpack("nnnnnnA*", $udp_obj->{data}); # Extract bit fields $R_Flag = ($temp & 0x8000) >> 15; # Top bit $OPCODE = ($temp & 0x7800) >> 11; # Next 4 bits $NM_FLAGS = ($temp & 0x07f0) >> 4; # Next 7 bits $RCODE = ($temp & 0x000f) ; # Bottom 4 bits $$bufref .= " Op,Rsp,Qcnt=$OPCODE,$R_Flag,$QDCOUNT" . "[" . &Decode_DNS_Name(\$NameInfo) . "]" . &GetHex($NameInfo); } ####################################################### sub Decode_TCPUDP_BOOTP(){ my ($udp_obj ,$bufref) = @_; my ($opcode, $hw_type, $hw_addr_len, $hop_count, $tranid,$seconds_since_boot, $flags, $client_ip,$your_ip,$server_ip,$gw_ip, $server_hostname,$boot_filename) = unpack("CCCCNnnNNNNA16", $udp_obj->{data}); $$bufref .= "Op=$opcode, hwty=$hw_type, hwlen=$hw_addr_len, hop=$ho +p_count" . "trans=$tranid,Sec=$seconds_since_boot, Flg=$flags," . "clientIP=$client_ip,OtherIPs=$your_ip,$server_ip,$gw_ip"; my %DHCP_Info = &Decode_UDP_DHCP($udp_obj ,$bufref); $$bufref .= "\n" . &DHCP_toString(%DHCP_Info) ; # void BOOTP; # LABL 0 0 0 b1 BootP - Bootstrap Protocol; # DBYT 0 2 b0 c2 Operation:; # CEQU 1 2 0 14 Boot Request; # CEQU 2 2 0 14 Boot Reply; # DBYT 0 2 b0 c2 Hardware Address Type:; # CST# 0 2 0 14 HW Types; # DBYT 0 3 b0 c2 Hardware Address Length:; # LABL 0 0 0 10 bytes; # DBYT 0 0 b0 c2 Hops:; # DLNG 0 0 b0 c2 Transaction ID:; # DWRD 0 0 b0 c2 Seconds Since Boot Start:; # HWRD 0 4 b0 c2 Flags:; # CBIT f 4 0 14 Broadcast; # IPLG 0 4 b0 c2 IP Address Known By Client:; # CEQU 0 4 0 14 IP Address Not Known By Client; # IPLG 0 4 b0 c2 Client IP Addr Given By Srvr:; # IPLG 0 0 b0 c2 Server IP Address:; # IPLG 0 4 b0 c2 Gateway IP Address:; # TEQU 6 3 0 0 HWAddr Len 6; # TLSE 0 0 0 0 Other Adds; # # void HWAddr Len 6; # ETHR 0 0 b0 c2 Client Hardware Addr:; # TNXT -BootP End; # # void Other Adds; # HEX# 0 3 b0 c2 Client Hardware Addr:; # TNXT -BootP End; # # void BootP End; # SUBG 10 3 ; # MULG ffffffff 3; # HEX# 0 3 b0 c2 Unused:; # MOVE 80 2 ; # MOVE 40 3 ; # DUMP 0 3 b0 c2 Server Host Name:; # DUMP 0 2 b0 c2 Boot File Name:; # GLNG 0 3 ; # TEQU 63825363 3 0 0 DHCP Start; # DUMP 0 3 b0 c2 Vendor Specified Data:; # } ########################################################## sub Decode_DNS_Name(){ #DNS/Netbios Name decoder # This homegrown crazy bit of code developed by packet observation + only. my $nameref = shift; my ($retval, $namelen, $b1, $b2, $lastchar); $namelen = ord(substr($$nameref,0,1)); for (my $i=1; $i <= $namelen; $i+=2){ $b1=(ord(substr($$nameref,$i,1))) - 0x41; #Don't ask me why . +. $b2=(ord(substr($$nameref,$i+1,1))) - 0x41; # It just works thi +s way!! $retval .= chr( ($b1 << 4)+$b2); } # Last byte of NB name is the "Type". chop it if it is .. if (($lastchar = chop($retval)) gt ' '){ # Ooops ... That was a real part of the name ..Need to restore i +t.. $retval .= $lastchar; }; #Trim trailing blanks $retval =~s/\s*$//; return $retval ; #. } ########################################################### sub Decode_ARP(){ my ($eth_obj ,$bufref) = @_; my $arp_obj = NetPacket::ARP->decode($eth_obj->{data}, $eth_obj); $$bufref .= &get_TypeName(\%ARP_Type_Name,$arp_obj->{opcode}) . " srcEth=$arp_obj->{sha} " . "dest=$arp_obj->{tha} " . # Protocol addr(tpa)=$arp_obj->{tp +a} &get_TypeName(\%Ethernet_Type_Name, $arp_obj->{proto},1) . &get_dotquad_FromHex($arp_obj->{spa}) . "->" . &get_dotquad_FromHex($arp_obj->{tpa}) ; } ###################################### sub Decode_UDP_DHCP { #Converts a string, presumably a #received UDP packet, into a Net::DHCP::Packet. use bytes; my ($udp_obj,$bufref) = @_; my $pos = 0; my %self; $self{op} = unpack('C',substr($udp_obj->{data},$pos++,1)); $self{htype} = unpack('C',substr($udp_obj->{data},$pos++,1)); $self{hlen} = unpack('C',substr($udp_obj->{data},$pos++,1)); $self{hops} = unpack('C',substr($udp_obj->{data},$pos++,1)); $self{xid} = substr($udp_obj->{data},$pos,4); $pos+=4; $self{secs} = substr($udp_obj->{data},$pos,2); $pos+=2; $self{flags} = substr($udp_obj->{data},$pos,2); $pos+=2; $self{ciaddr} = inet_ntoa(substr($udp_obj->{data},$pos,4)); $pos+= +4; $self{yiaddr} = inet_ntoa(substr($udp_obj->{data},$pos,4)); $pos+= +4; $self{siaddr} = inet_ntoa(substr($udp_obj->{data},$pos,4)); $pos+= +4; $self{giaddr} = inet_ntoa(substr($udp_obj->{data},$pos,4)); $pos+= +4; $self{chaddr} = mac2str($self{hlen},substr($udp_obj->{data},$pos,1 +6)); $pos+=16; $self{sname} = substr($udp_obj->{data},$pos,64); $pos+=64; $self{sname} = substr($udp_obj->{data},$pos,128); $pos+=128; #$self{options} = new Net::DHCP::Options()->marshall(substr($udp_o +bj->{data},$pos)); return %self; } ############################################################ sub Decode_IP_ICMP{ my ($ip_obj ,$bufref) = @_; my $icmp_obj = NetPacket::ICMP->decode($ip_obj->{data}, $ip_obj); my %icmp = (type=>0); ($icmp{type}, $icmp{code}, $icmp{cksum}, $icmp{identifier},$icmp{sequence}, $icmp{data}) = unpack("CCnnna*", $ip_obj->{data}); $$bufref .= " type=" . $ICMP_TYPES{$icmp_obj->{type}} . " Data=" . HexDump($icmp{data}) ; $verbose == 0 and print qq($$bufref\n); # Force print } ############################################################ sub mac2str($$) { my $nibbles = shift(@_) * 2; my $str = unpack("H$nibbles", shift); return $str; } sub DHCP_toString { # Returns a textual representation of the packet, for debug +ging. my ($self) = @_; my $s = ""; while ( my ($key, $value) = each (%$self) ) { #next if ($key eq 'options'); $s .= sprintf(" %s=%s;",$key,$value); } #$s .= sprintf("options =\n %s\n", $self->{options}->toString()); return $s; } ########################################### sub GetHex(){ my $Data = shift; my $HexData = HexDump($Data) ; $HexData =~s/^.+\n\n//; # Delete first line (Heading) $HexData =~s/\n$//; # Zap trailing newline. return $HexData; } ###################################### sub Initialize(){ #Get the name of a network device that can be used my %description; foreach(Net::Pcap::findalldevs(\$err, \%description)) { print "$_\n $description{$_}\n\n"; # Try to match the Network Interface param, if specified. if ( length($pcap_parameters{NETWORK_INTERFACE}) >1 ){ # Something there - see if desc matches user spec ; bail if not +. next unless $description{$_} =~m /$pcap_parameters{NETWORK_INTE +RFACE}/i ; } m/Generic/i and next; # Ignore "Generic" adapters # Found a NON-Generic adapter .. Use it $pcap_parameters{NETWORK_INTERFACE} = $_; } print $err if defined $err; # $pcap_parameters{NETWORK_INTERFACE} = Net::Pcap::lookupdev(\$err) o +r die "No Network device found:$err\n"; $verbose and print "Found device \t$pcap_parameters{NETWORK_INTERFAC +E}\n"; $result = Net::Pcap::lookupnet($pcap_parameters{NETWORK_INTERFACE}, \$pcap_parameters{NETWORK_ADDR}, \$pcap_parameters{NETWORK_MASK}, \$err); $verbose and print "Found Net \tnet " . NetPacket::IP::to_dotquad($p +cap_parameters{NETWORK_ADDR}) . " mask " . NetPacket::IP::to_dotquad($pcap_parameters{NETWORK +_MASK}) . "\n"; $pcap_desc = Net::Pcap::open_live($pcap_parameters{NETWORK_INTERFACE +}, $pcap_parameters{SNAPLEN}, $pcap_parameters{PROMISCUOUS_MODE}, $pcap_parameters{TIMEOUT}, \$err) or die("Net::Pcap::open_live returned error $err\n"); # Set up filter, if defined if ($pcap_parameters{FILTER} ne '') { die(Net::Pcap::geterr($pcap_desc)), if ((Net::Pcap::compile($pcap_desc, \$pcap_parameters{FILTER_H +ANDLE}, $pcap_parameters{FILTER}, 0, $pcap_parameters{NETWORK_MASK}) == + -1) || (Net::Pcap::setfilter($pcap_desc, $pcap_parameters{FILTER_ +HANDLE}) == -1)); } # Set 0000-05DC Ether types to be= IEEE802.3 Length Field [XEROX +] for (my $i=0; $i<256; $i++){ $Ethernet_Type_Name{$i} = {NAME=>"Len=$i" , DECODER =>0}; $Hex_Decoder{sprintf('%02x',$i)} = $i; $Hex_Decoder{sprintf('%02X',$i)} = $i; # Handle Upper-case hex al +so.. } } ############################################################### sub get_dotquad_FromHex(){ my $hex = shift; # Contains ASCII text like "0A0C0030" my $result = ''; for (my $i=0; $i < length($hex); $i+=2){ $i > 0 and $result .= "."; $result .= $Hex_Decoder{substr($hex,$i,2)}; } return $result; } ############################################################### sub get_TypeName(){ my ($NameHashRef, $lookup,$DontAddText) = @_; my $retval = $NameHashRef->{$lookup}; if (ref($retval) eq "HASH"){ $retval = $retval->{NAME}; } $retval and return $retval; $DontAddText and return $lookup; # Caller does not want crap return "*Unknown=$lookup*"; } ############################################################### ############################################################### sub Dispach_Decoder_If_Any(){ # Call the appropriate decoder, depending on pkt type my ($NameRef, $index, $Parent_obj, $bufref) = @_; my $Decode_Subroutine = $NameRef->{$index}{DECODER}; if ($Decode_Subroutine){ # Decoder for this proto has been specified !!, call it &$Decode_Subroutine($Parent_obj, $bufref); return 0; # Found the decoder & called it }; return 1; # Did not find a decoder.. } ################################################## sub SNMP_Special_Handler{ # Check src & Dest IP addresses for Interest # Change Dest IP addr & Port number # Send packet out }

            "XML is like violence: if it doesn't solve your problem, use more."

Replies are listed 'Best First'.
Re^2: Getting ethernet type using NetPacket
by merrittr (Novice) on Jul 21, 2011 at 16:05 UTC
    Wow... I will take a look , should be something to help me

      I couldn't get that code running

      just wondering if I need to dig into eth_obj->(data) more to pull out what this packets and its roll is at the application layer?

      my $ip_obj = NetPacket::IP->decode($eth_obj->{data})
        What part did you have trouble with ? Any error messages ?

        If you un-comment the lines below, it will print packets as they arrive.

        #print("RcvPkt Totlen(PacketLen) $hdr->{len}($hdr->{caplen})" . # "\t Time.Usec=$hour:$min:$sec.$hdr->{tv_usec}\n");
        If you capture packets, you can control-feed them into the program (read pcap docs), and run the "perl -d <program>" debugger, and examine data as you go.

                    "XML is like violence: if it doesn't solve your problem, use more."