Perl Pcap Module not capturing over 1500 Bytes - perl

I have a small pcap listener i made in perl. And whenever i get anything over 1500 Bytes, it just prints as 1500.
66.0.X.X 1500
Now i get that reply when i sent a 2000 byte packet, i also tried sending 1600, and 10k.
No matter what i get 1500 for pcap.
How can i fix this i looked at SNAPLEN but when i set it under or over, i get the same results.
I have also tried tcpdump and i get weird results, as you can see the first "length" is 1500, but the second a line under is 5000 which is what i sent.
tcpdump: listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
21:59:06.142530 IP (tos 0x0, ttl 58, id 45206, offset 0, flags [+], proto UDP (17), length 1500)
37.X.X.X.48254 > XXX.54: UDP, length 5000
my code:
use Net::Pcap;
use NetPacket::Ethernet;
use NetPacket::Ethernet qw(:strip);
use NetPacket::IP;
use NetPacket::IP;
use NetPacket::TCP;
use strict;
my $err;
# Use network device passed in program arguments or if no
# argument is passed, determine an appropriate network
# device for packet sniffing using the
# Net::Pcap::lookupdev method
my $dev = $ARGV[0];
unless (defined $dev) {
$dev = Net::Pcap::lookupdev(\$err);
if (defined $err) {
die 'Unable to determine network device for monitoring - ', $err;
}
}
# Look up network address information about network
# device using Net::Pcap::lookupnet - This also acts as a
# check on bogus network device arguments that may be
# passed to the program as an argument
my ($address, $netmask);
if (Net::Pcap::lookupnet($dev, \$address, \$netmask, \$err)) {
die 'Unable to look up device information for ', $dev, ' - ', $err;
}
# Create packet capture object on device
my $object;
$object = Net::Pcap::open_live($dev, 65535, 1, 0, \$err);
unless (defined $object) {
die 'Unable to create packet capture on device ', $dev, ' - ', $err;
}
# Compile and set packet filter for packet capture
# object - For the capture of TCP packets with the SYN
# header flag set directed at the external interface of
# the local host, the packet filter of '(dst IP) && (tcp
# [13] & 2 != 0)' is used where IP is the IP address of
# the external interface of the machine. For
# illustrative purposes, the IP address of 127.0.0.1 is
# used in this example.
my $filter;
Net::Pcap::compile(
$object,
\$filter,
'(port 111)',
0,
$netmask
) && die 'Unable to compile packet capture filter';
Net::Pcap::setfilter($object, $filter) &&
die 'Unable to set packet capture filter';
# Set callback function and initiate packet capture loop
Net::Pcap::loop($object, -1, \&process_packet, '') ||
die 'Unable to perform packet capture';
Net::Pcap::close($object);
sub process_packet {
my ($user_data, $hdr, $pkt) = #_;
my $ip_obj = NetPacket::IP->decode(eth_strip($pkt));
#print("$ip_obj->{src_ip} -> $ip_obj->{dest_ip} $ip_obj->{caplen}\n");
warn "packet!\n";
my %header = %$hdr;
#process_packet(\%header, $pkt);
my $len = length $pkt;
my $fag = length $user_data;
my $fag2 = length $hdr;
warn "$header{len} $header{caplen} $len $fag $fag2\n";
}

From "listening on eth1" I infer you're capturing on an Ethernet. The largest packet size on Ethernet is 1518 bytes (except for non-standard "jumbo frames"), which is:
14 bytes of Ethernet header;
1500 bytes of Ethernet payload;
4 bytes of frame check sequence at the end.
This means that (unless the network is using jumbo frames) the largest IP packet you can send on an Ethernet is 1500 bytes.
So, if you try to send 5000 bytes of data over UDP-over-IPv4 on an Ethernet, that will become a 5008-byte UDP packet when the 8-byte UDP header is added, and that will become a 5028-byte or larger IPv4 packet when the IPv4 header is added (the minimum size of an IPv4 header is 20 bytes, and it can be bigger if there are options in the packet). That's too big for Ethernet, so the IP layer in the protocol stack on your machine will "fragment" that packet into multiple smaller IP packets, and, if all of those fragments arrive at the destination machine, its IP layer will reassemble them into a larger IP packet and then hand the IP payload of the reassembled packet to the UDP layer, so the program receiving the UDP packet will see all 5000 bytes.
Your tcpdump output is the output for the first of the fragments; the IP-layer total length of the packet, extracted from the IP header, is 1500 (IP's length field includes the length of the IP headers and the payload), but the UDP-layer length, extracted from the UDP header (which will normally fit in the first fragment), is 5000.
This has nothing to do with the snapshot length; it has to do with the way IP works, and the maximum packet size on Ethernet.

Upon advice from the comments, I decided to try Net::Pcap::Easy instead of Net::Pcap. As stated in the pod for the former module: "Net::Pcap is awesome, but it's difficult to bootstrap".
The following code solves my problem:
use strict;
use warnings;
use Net::Pcap::Easy;
# all arguments to new are optoinal
my $npe = Net::Pcap::Easy->new(
dev => "eth1",
filter => "port 111",
packets_per_loop => 10,
bytes_to_capture => 1024,
timeout_in_ms => 0, # 0ms means forever
promiscuous => 0, # true or false
udp_callback => sub {
my ($npe, $ether, $ip, $udp, $header ) = #_;
print "UDP: $ip->{src_ip}:$udp->{src_port}"
. " -> $ip->{dest_ip}:$udp->{dest_port} $udp->{len}\n";
},
# tcp_callback => sub {
# my ($npe, $ether, $ip, $tcp, $header ) = #_;
# my $xmit = localtime( $header->{tv_sec} );
#
# print "$xmit TCP: $ip->{src_ip}:$tcp->{src_port}"
# . " -> $ip->{dest_ip}:$tcp->{dest_port}\n";
#
# },
#
# icmp_callback => sub {
# my ($npe, $ether, $ip, $icmp, $header ) = #_;
# my $xmit = localtime( $header->{tv_sec} );
#
# print "$xmit ICMP: $ether->{src_mac}:$ip->{src_ip}"
# . " -> $ether->{dest_mac}:$ip->{dest_ip}\n";
# },
);
1 while $npe->loop;

Related

How do i send just a CR, not a CR NUL in Perl with Net::Telnet?

I need to communicate with an appliance via the telnet protocol. It turns out that it can not handle CR-NUL or CR-LF sequences as output record separators, only straight CR. Otherwise it will interpret the character following the CR as belonging to the next command, which then results in errors. So i set Output_record_separator to CR, but then see in the communication dump (in the Dump_Log), that not just a CR (0x0d) was sent, but a CR NUL (0x0d 0x00). I can send single chars, though: if i send just a LF, only a LF (ox0a) is send.
This code shows the problem:
#!/usr/bin/env perl
use strict;
use warnings;
use Net::Telnet;
my $host = "192.168.2.223";
my $port = 5000;
my $telnet = new Net::Telnet (
Timeout => 20,
Errmode => 'die',
Port => $port,
Dump_Log => '/tmp/debug',
Output_record_separator => "\015",
Input_record_separator => "\n\r",
Prompt => '/1_.*0/',
Binmode => 1,
);
$telnet->open($host);
$telnet->put("\015");
$telnet->put("\012");
which results in this:
tail: /tmp/debug: file truncated
> 0x00000: 0d 00 ..
> 0x00000: 0a .
How can i make Net::Telnet send just CR, without adding more stuff?
Set Telnetmode to 0. If Telnetmode is on then CR is translated to CR + 0x00.
From the Net::Telnet source:
## Convert all CR (not followed by LF) to CR NULL.
while (($pos = index($$string, "\015", $pos)) > -1) {
$nextchar = substr $$string, $pos + 1, 1;
substr($$string, $pos, 1) = "\015\000"
unless $nextchar eq "\012";
You can turn the mode on and off using $telnet->telnetmode(...):
telnetmode - turn off/on telnet command interpretation
$mode = $obj->telnetmode;
$prev = $obj->telnetmode($mode);
This method controls whether or not TELNET commands in the data stream are recognized and handled. The TELNET protocol uses certain character sequences sent in the data stream to control the session. If the port you're connecting to isn't using the TELNET protocol, then you should turn this mode off. The default is on.
If no argument is given, the current mode is returned.
If $mode is 0 then telnet mode is off. If $mode is 1 then telnet mode is on.

Parsing TCPDUMP output

Im trying to parse my TCPDUMP command output to print "ok" if a specific server sends data back before a given amount of seconds ( or nanoseconds ) Example:
11:45:41.198150 IP X.X.X.X.662 > Y.Y.Y.Y.161: UDP, length 37
11:45:41.315699 IP Y.Y.Y.Y.161 > X.X.X.X.662: UDP, length 13
11:45:42.198845 IP X.X.X.X.168.662 > Y.Y.Y.Y.161: UDP, length 37
11:45:42.316745 IP Y.Y.Y.Y.161 > X.X.X.X.662: UDP, length 13
as you can see, it first outputs the first row where im sending data, then the server i sent data to repsponds, Now i want it so if the server that i sent data to doesnt respond withen a set amount of seconds, then i do nothing. but if it does, then i print "ok".
Somtimes data will look like this
11:45:41.198150 IP X.X.X.X.662 > Y.Y.Y.Y.161: UDP, length 37
11:45:41.315699 IP Y.Y.Y.Y.161 > X.X.X.X.662: UDP, length 13
11:45:42.198845 IP X.X.X.X.168.662 > Y.Y.Y.Y.161: UDP, length 37
11:45:42.198845 IP X.X.X.X.168.662 > Y.Y.Y.Y.161: UDP, length 37
11:45:42.198845 IP X.X.X.X.168.662 > Y.Y.Y.Y.161: UDP, length 37
11:45:42.316745 IP Y.Y.Y.Y.161 > X.X.X.X.662: UDP, length 13
And The ips will respond at diffrent times, how could i still parse this.
With the information from your other question Parsing TCPDUMP output and since you asked about parsing the file, there are several ways it can be done. I have generate a simple script to read in the data and get it into a hash. I'm going with the data from your other posting as the input you want to parse. It does not do data validation and expects all lines to be the same format in the file.
# Checking for errors (Good practice to always use)
use strict;
# open the file (first on on the command line)1
open my $input,$ARGV[0] or die "Unable to open file: $ARGV[0]";
# scalar/variable into which to save the line read from the file
my $line;
# Hash/mapping by machine for the time
my %machine2time;
# Array/List to store parsed line into individual list/array items
my #parsedLineSpace;
# Read line from the file. This will fail when a line cannot be read
while ( $line = <$input> )
{
# Parse the line based on spaces first element is time (index 0),
# the second is IP (index 1)
#parsedLineSpace = split('\s+',$line);
# If the IP exists in the hash/mapping, then the delta time needs to be
# computed as there is a response
if ( exists $machine2time{$parsedLineSpace[1]} )
{
# Get the times which are needed to compute the difference
# and place in scalar/variables
my $firstTime = $machine2time{$parsedLineSpace[1]};
my $responseTime = $parsedLineSpace[0];
# Compute the time difference (Exercise for the user)
# Use an array and split to break the time into individual components or
# the to do that. Make sure you use a \ to escape the . for the split
# and that you check for boundary conditions
# Remove the item from the hash/mapping as it is not needed and
# any remaining items left in the hash would be items which did
# get a response
delete $machine2time{$parsedLineSpace[1]};
}
# else this the first occurrence (or there was no response) so
# save the time for use later
else
{
$machine2time{$parsedLineSpace[1]} = $parsedLineSpace[0];
}
}
# Print out any machines which did not have a matched response
print "\nIPs which did not get a response\n";
# For each key in the hash/mapping (sorted) print out the key which
# is the IP
foreach my $machine ( sort keys %machine2time )
{
print "$machine\n";
}
Hopefully this will get you started on your effort

Pattern matching an array with specific start and end words/characters

Have some output in an array which I am trying to pull details from where the output begins with a specific word and ends with a specific word/character. This output is then to be printed to the screen.
The output in the array which I am working with is:
router rip
version 2
redistribute bgp 45134 metric 3
passive-interface Serial1/3:1.333
passive-interface Serial3/1:3.333
passive-interface Serial3/1:5.333
passive-interface Serial3/2:1.333
passive-interface Serial3/4:1.333
passive-interface Serial3/4:17.333
passive-interface Serial6/1:1.333
no auto-summary
!
address-family ipv4 vrf TestVRF-0001
redistribute bgp 45134 metric 3
network 10.0.0.0
no auto-summary
version 2
exit-address-family
!
The perl code I have generated so far is below:
elsif ( $action eq "show_vrf1" ) {
my $cmd = "show run | begin router rip";
my #lines = $s->cmd(String => $cmd,
Prompt => "/$enableprompt/",
Timeout => 10);
foreach my $line (#lines) {
if(/address-family ipv4 vrf TestVRF-0001.*?!/){
$result=$1;
print $result;
}
}
}
Which I am wanting to only pull the below out of the array:
address-family ipv4 vrf TestVRF-0001
redistribute bgp 45134 metric 3
network 10.0.0.0
no auto-summary
version 2
exit-address-family
!
For some reason when I run the script, I just get a blank screen with no data pulled from the array.
if($line =~ /address-family ipv4 vrf TestVRF-0001.*?!/){
instead of
if(/address-family ipv4 vrf TestVRF-0001.*?!/){
?
You match $line against the regular expression. The output you expect is not on one line, so no line can match it.
You can store the whole multiline string in a scalar:
my $output = join q(), $s->cmd(...);
And then, you can retrieve the output, if you use parentheses to really capture a part of the string:
if ($output =~ /(address-family ipv4 vrf TestVRF-0001.*?!)/s) {
my $result = $1;
Note that the /s modifier is needed to make dot match a newline, which it normaly does not.

return value of recv() function in perl

I have non blocking UDP socket in perl created this way
my $my_sock = IO::Socket::INET->new(LocalPort => $MY_PORT,
Proto => 'udp',
Blocking => '0') or die "socket: $#";
The recv call is
my $retValue = $sock->recv($my_message, 64);
I need to know
a) when there is no data left to read
b) if there is data, how much data was read
c) any error conditions
Surprisingly, I didn't see any return value for recv in perldoc. When I tried it myself, recv returns undef in (a), for b it is an unprintable character
This seems to be an elementary issue. However, I still cannot find the info on googling or on stack overflow.Thanks for any inputs
According to the perldoc, recv "returns the address of the sender if SOCKET's protocol supports this; returns an empty string otherwise. If there's an error, returns the undefined value."
If you are getting an undef, this means recv is encountering an error.
The error in your code is in the following line:
$retValue = $sock->recv($my_message, 64);
The function prototype for recv is:
recv SOCKET,SCALAR,LENGTH,FLAGS
According to perldoc, recv "Attempts to receive LENGTH characters of data into variable SCALAR from the specified SOCKET filehandle. SCALAR will be grown or shrunk to the length actually read."
Try:
$retvalue = recv($sock, $my_message, 64)
This is where I got all the information:
http://perldoc.perl.org/functions/recv.html
The value returned by recv is the address and port that that data was received from
my $hispaddr = $sock->recv($my_message, 64);
if ($retValue) {
my ($port, $iaddr);
($port, $iaddr) = sockaddr_in($hispaddr);
printf("address %s\n", inet_ntoa($iaddr));
printf("port %s\n", $port);
printf("name %s\n", gethostbyaddr($iaddr, AF_INET));
}
The length of the data returned can be determined with
length($my_message);

CSV File Formatting

I wrote a perl script to output a text file filled with ip addresses and ports that i scanned into microsoft excel. Now that the data is in excel my boss wants me to organize the file in csv format such as
Server, port, protocol, random, random, random
ns1, 25, tcp, stuff, stuff, stuff
Can any one help me with this Please?
Code:
#!/usr/bin/perl
$input = `Cat /cygdrive/c/Windows/System32/test11.txt | grep -v 'SYN Stealth'`;
chomp input;
$output =" /cygdrive/c/Users/bpaul/Desktop/194.csv ";
if (! -e "$output")
{
`touch $output`;
}
open (OUTPUTFILE, ">$output") || die "Can't Open file $output";
print OUTPUTFILE "$input\n";
close (OUTPUTFILE);
Here is a piece of my file
Nmap scan report for 69.25.194.2 Host is up (0.072s latency). Not shown: 9992 filtered ports PORT STATE SERVICE 25/tcp open smtp
80/tcp open http
82/tcp open xfer
443/tcp open
https 4443/tcp closed
pharos 5666/tcp closed
nrpe 8080/tcp closed
http-proxy 9443/tcp closed tungsten-https
So far my code took my txt file and outputted it to excel now I need to format the data like this:
Desired Output:
Server, port, protocol, random, random, random
ns1, 25, tcp, stuff, stuff, stuff
I'm assuming you meant 69.25.194.2 when you said ns1.
use strict;
use warnings;
use Text::CSV_XS qw( );
my $csv = Text::CSV_XS->new({ binary => 1, eol => "\n" });
$csv->print(\*STDOUT, [qw(
Server
port
protocol
random
random
random
)]);
my $host = '[unknown]';
while (<>) {
$host = $1 if /Nmap scan report for (\S+)/;
my ($port, $protocol) = m{(\d+)/(\w+) (?:open|closed)/
or next;
$csv->print(\*STDOUT, [
$host,
$port,
$protocol,
'stuff',
'stuff',
'stuff',
]);
}
Usage:
grep -v 'SYN Stealth' /cygdrive/c/Windows/System32/test11.txt | perl to_csv.pl > /cygdrive/c/Users/bpaul/Desktop/194.csv
Text::CSV_XS
Update: Replaced hardcoded ns1 with address of scanned machine.
Update: Replaced generic usage with what the OP would use.