doctor_moron has asked for the wisdom of the Perl Monks concerning the following question:

hi, im trying to send a single icmp packet with this code, but the icmp checksum always error (i got that from ethereal log), any advices ? here's the code :
use Socket; use strict; socket(SOCK,PF_INET,SOCK_RAW,getprotobyname('icmp')) || die $!; my $checksum = 0; use constant ICMP_ECHOREPLY => 0; use constant ICMP_ECHO => 8; use constant ICMP_STRUCT => "C2 n3"; use constant SUBCODE => 0; use constant ICMP_FLAGS => 0; use constant ICMP_PORT => 0; my $seq=1; my $msg = pack(ICMP_STRUCT,ICMP_ECHO,SUBCODE,$checksum,$$,$seq); my $chks = &ceksum($msg); $msg = pack(ICMP_STRUCT,ICMP_ECHO,SUBCODE,$chks,$$,$seq); my $host = sockaddr_in(ICMP_PORT,inet_aton('192.168.0.1')); send(SOCK,$msg,ICMP_FLAGS,$host) || warn "$! \n"; sub ceksum { my ($msg) = @_; my ($len_msg,$num_short,$short,$chk); $len_msg = length($msg); $num_short =int($len_msg/2); $chk = 0; foreach $short(unpack("n$num_short", $msg)) { $chk += $short; } $chk +=(unpack("C",substr($msg,$len_msg-1,1)) << 8) if $len_msg % 2; $chk = ($chk >> 16) + ($chk & 0xffff); return(~(($chk >> 16) + $chk) & 0xffff); }

##Update-2##
Since i have no idea why this script wont work in my LAN, i asked someone in yahoo chat (thanks Chad_Lewis_89 for your time) to test it, and he said the script's working fine.
I re-checked all lines in this code, and seems its fine, so i remade a script (shorter than before), here's the code:

use Socket; socket(SOCK,PF_INET,SOCK_RAW,getprotobyname('icmp')) || die "Error: $! +\n"; $msg = pack("C2 n3",8,0,32127,0,1); print "the value of checksum is:", unpack('H4', pack('n',32127)), "\n" +; print "[And you should see this in your tcpdump -i lo icmp -xxx]\n"; send(SOCK,$msg,0,sockaddr_in(0,inet_aton('localhost'))) || die "Error: + $! \n";

As you can see, there's no subroutine to calculate checksum, yea, because im going to try it to LOCALHOST and as far as i know, no 'valid checksum' needed for localhost (so i add 32127 for checksum).
I ran this script, i got the response (echo reply) and i can see '32127' or '0x7d7f' (in hex) in tcpdump, here's tcpdump log :
01:55:23.930771 zacko > zacko: icmp: echo request (DF)
4500 001c 0000 4000 4001 3cdf 7f00 0001
7f00 0001 0800 7d7f 0000 0001
01:55:23.930852 zacko > zacko: icmp: echo reply
4500 001c cbff 0000 4001 b0df 7f00 0001
7f00 0001 0000 fffe 0000 0001

everything's work normal.
let's try to change the checksum value, change all 32127 (in code above) to 32128, well i still got the response (echo reply), but when i look closer into tcpdump log, i feel something wrong there, there's no 32128 or '0x7d80' (in hex) , here's tcpdump log :
02:09:11.912392 zacko > zacko: icmp: echo request (DF)
4500 001d 0000 4000 4001 3cde 7f00 0001
7f00 0001 0800 7dc2 8000 0000 01
02:09:11.912502 zacko > zacko: icmp: echo reply
4500 001d cc01 0000 4001 b0dc 7f00 0001
7f00 0001 0000 7eff 8000 0000 01

There's no 0x7d80 but 7dc2 (and i think 7dc2 is 32194).
If ICMP_ECHO=8, SUBCODE=0, $pid=0, and $seq=1, and then we pass these variables to subroutine checksum, we'll get 63486, and of course if i send icmp packet to another host, with checksum value=63486, i'll get no response back (as long as checksum value > 32127).

Well, i have no idea, what's going on here, by the way, i am using celeron 300 233 Mhz, 64 MB SDRAM, 1.2 GB Hdd, and my pc's screwed up, everytime this pc turned on, i need to go to BIOS setup
My OS's Redhat Fedora 2.0.20-8, libcap version 0.7.2
##EndOfUpdate-2##

##update-3##
The problem is SOLVED when i switched to SuSe, with kernel 2-4-21.99
##EndOfUpdate-3##

I wish, i can explain this problem in better english, i'll ask my friend to translate this question :[

thanks, zac

Replies are listed 'Best First'.
Re: icmp code, w/o Net::Ping and problem
by InfiniteSilence (Curate) on Aug 31, 2005 at 21:23 UTC
    I agree with merlyn...I tried your code and it died without any explanation at the send() command, but then I wrote the following (almost an exact copy of the POD code for Net::Ping) and got a response:
    C:\Temp>perl -MNet::Ping -e "$p = Net::Ping->new(); print qq|host is a +live.\n| if $p->ping('127.0.0.1');" perl -MNet::Ping -e "$p = Net::Ping->new(); print qq|host is alive.\n| + if $p->ping('127.0.0.1');" host is alive.

    Celebrate Intellectual Diversity

Re: icmp code, w/o Net::Ping and problem
by merlyn (Sage) on Aug 31, 2005 at 21:19 UTC
      I just want to know..., i surfed ping.pm (Net::Ping), copy & paste sub icmp_ping to another *.pl file, and try to run the code manually (separately), and i got different result.. it's only because of my curiosity, and i hope u undertand.. thanks
        Per RFC792 (http://www.faqs.org/rfcs/rfc792.html):

        "The checksum is the 16-bit ones's complement of the one's complement sum of the ICMP message starting with the ICMP Type. For computing the checksum , the checksum field should be zero. This checksum may be replaced in the future."

        Therefore, simply replace your '$chk += $short;' with '$chk ^= $short;' and it should work.

        BTW: Thanks for the code! :-)

        Happy Hacking!