Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Compensate for bad inet checksum routine

by VinsWorldcom (Priest)
on Mar 29, 2013 at 14:24 UTC ( #1026156=perlquestion: print w/ replies, xml ) Need Help??
VinsWorldcom has asked for the wisdom of the Perl Monks concerning the following question:

Greetings,

I found the solution for my problem here http://fossies.org/dox/wireshark-1.9.1/packet-cdp_8c_source.html starting at line 230. Of course the solution is in C and I need it in Perl (obviously). I already did it and it's working (code below), but there HAS to be a better way than my hack job so if you have some time and want to show me a more elegant way, please proceed.

Background:

Cisco Discovery Protocol (CDP) uses the internet checksum routine to calculate it's packet checksum. Of course there's a bug (or perhaps proprietary glitch by design). In any case, the standard internet checksum sub (which I already have) will work, as long as you "massage" the input to compensate. A nice explanation and C code is provided in the link above.

Essentially, if the length of the CDP packet data is odd, internet checksum routine would add \x00 padding before calculating. The bug prevents this from working. Instead, because of an endian-ness assumption, I need to determine if the packet is odd length *BEFORE* sending to inet checksum. If so, instead of adding \x00 padding, we strip the last byte, add the \x00 padding and the re-add the stripped last byte. With an even packet length now, there's one more compensation.

If after the byte-swap, the last byte is now greater than \x80, we need to decrement the last two byes (the added \x00 padding and the re-added original last byte) each by 1.

I created a quick script to pass some data into the prepchksum() sub I wrote. The BEFORE represents the hex (unpack) versions of real CDP packet data payload (after the header) and the output I expect for each follows in the AFTER output:

# Even length payload = 24 bytes, no change BEFORE = 00010006523200060013436973636f203132303030475352 AFTER = 00010006523200060013436973636f203132303030475352 # Odd length payload = 23 bytes, add \x00 padding and then # byte-swap the last word (16-bit value) BEFORE = 00010006523200060012436973636f3132303030475352 MID1 = 00010006523200060012436973636f313230303047530052 AFTER = 00010006523200060012436973636f313230303047530052 # Ugly. Same as above, odd length payload = 25 bytes # but now, the last byte is >= \x80 # Compensate for glitch by decrementing each byte in last word by 1 # (\x00-1 = \xff, (-1/255 decimal) \x84-1 = \x83 (131 decimal) BEFORE = 00010006523200060013436973636f20313230303047535284 MID1 = 00010006523200060013436973636f2031323030304753520084 CMPSAT = -1 131 AFTER = 00010006523200060013436973636f203132303030475352ff83 # Even length payload = 24 bytes, # no change even though last byte is greater than \x80 BEFORE = 00010006523200060012436973636f313230303047535284 AFTER = 00010006523200060012436973636f313230303047535284

And now, the code - 'sub prepchksum' - could use some attention if you'd like to educate me on a better way to do it:

use strict; use warnings; my @payloads = qw( 00010006523200060013436973636f203132303030475352 00010006523200060012436973636f3132303030475352 00010006523200060013436973636f20313230303047535284 00010006523200060012436973636f313230303047535284 ); for (@payloads) { prepchksum(pack "H*", $_); print "\n" } sub prepchksum { my ($payload) = @_; ###DEBUG: printf "BEFORE = %s\n", (CORE::unpack "H*", $payload); if (length($payload)%2) { my $padded = substr $payload, 0, -1; $padded .= "\0"; $padded .= substr $payload, -1; ###DEBUG: printf "MID1 = %s\n", (CORE::unpack "H*", $padded); # Compensate off-by-one error if (ord(substr $padded, -1) >= 128) { my $end = ord(substr $padded, -2, 1); my $endend = ord(substr $padded, -1); $end--; $endend--; ###DEBUG: printf "CMPSAT = %i %i\n", $end, $endend; # if 0 before --, then -1 results in warning to pack below # -1 is in essense \xff or 255 if ($end == -1) { $end = 255 } if ($endend == -1) { $endend = 255 } $padded = substr $padded, 0, -2; $padded .= CORE::pack('CC', $end, $endend); } $payload = $padded; } ###DEBUG: printf "AFTER = %s\n", (CORE::unpack "H*", $payload); }

Comment on Compensate for bad inet checksum routine
Select or Download Code
Re: Compensate for bad inet checksum routine
by kschwab (Priest) on Mar 29, 2013 at 16:16 UTC
    Probably slower, but leveraging RE's and getting rid of the copy to $padded makes it more compact.
    sub prepchksum { my ($payload) = @_; printf "BEFORE = %s\n", (CORE::unpack "H*", $payload); if (length($payload)%2) { $payload =~ s#(.)$#\0$1#; printf "MID1 = %s\n", (CORE::unpack "H*", $payload); # Compensate off-by-one error if (ord(substr $payload, -1) >= 128) { $payload=~s#(.)(.)$#pack('CC',(ord($1)-1)&255, (ord($2)-1)&255)#e; } } printf "AFTER = %s\n", (CORE::unpack "H*", $payload); }
      Just noticed this...
      In both the original, and my version, there seems to be some wasted logic. See comments that start with ## below.
      if (length($payload)%2) { my $padded = substr $payload, 0, -1; $padded .= "\0"; $padded .= substr $payload, -1; ## above: ok, so the next to the last byte ## is guaranteed to be null since we just ## explicitly set it if (ord(substr $padded, -1) >= 128) { my $end = ord(substr $padded, -2, 1); ## ## above: now we're grabbing the next to the last ## byte, even though we already know it's a null? ## my $endend = ord(substr $padded, -1); $end--; ## now we decrement it, knowing full well it's ## going to be -1? $endend--; ###DEBUG: printf "CMPSAT = %i %i\n", $end, $endend; # if 0 before --, then -1 results in warning to pack below # -1 is in essense \xff or 255 if ($end == -1) { $end = 255 } ## ## above,of course, it's going to == -1, and thus ## at this point, always reset to 255 ## if ($endend == -1) { $endend = 255 } $padded = substr $padded, 0, -2; $padded .= CORE::pack('CC', $end, $endend); } $payload = $padded; }
      It would seem then, that you could simplify all of that out, and just decrement the last byte by one?
      sub prepchksum { my ($payload) = @_; printf "BEFORE = %s\n", (CORE::unpack "H*", $payload); if (length($payload)%2) { $payload =~ s#(.)$#\0$1#; printf "MID1 = %s\n", (CORE::unpack "H*", $payload); # Compensate off-by-one error if (ord(substr $payload, -1) >= 128) { $payload=~s#(.)$#pack('C',(ord($2)-1)&255)#e; } } printf "AFTER = %s\n", (CORE::unpack "H*", $payload); }
      Or perhaps there's a bug somewhere, either in the 'C' implementation you based this on, or your original port?
        Argh. Meant to simplify by decrementing the last byte and explicity setting the the next to last byte to 255
        sub prepchksum { my ($payload) = @_; printf "BEFORE = %s\n", (CORE::unpack "H*", $payload); if (length($payload)%2) { $payload =~ s#(.)$#\0$1#; printf "MID1 = %s\n", (CORE::unpack "H*", $payload); # Compensate off-by-one error if (ord(substr $payload, -1) >= 128) { $payload=~s#(.)(.)$#pack('CC',255, (ord($2)-1)&255)#e; } } printf "AFTER = %s\n", (CORE::unpack "H*", $payload); }

        You know, I thought about that and wasn't sure I had all the cases. I do appreciate you seeing it also and confirming that the logic was extra and able to be eliminated.

Re: Compensate for bad inet checksum routine
by jwkrahn (Monsignor) on Mar 29, 2013 at 17:35 UTC
    sub prepchksum { my ( $payload ) = @_; ###DEBUG: printf "BEFORE = %s\n", unpack 'H*', $payload; if ( length( $payload ) % 2 == 1 ) { if ( substr( $payload, -1 ) ge "\x80" ) { substr $payload, -1, 1, chr ord( substr $payload, -1 ) - 1 +; substr $payload, -1, 0, "\xff"; } else { substr $payload, -1, 0, "\0"; } } ###DEBUG: printf "AFTER = %s\n", unpack 'H*', $payload; }

      Beautiful! ++

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1026156]
Approved by Perlbotics
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (21)
As of 2014-08-22 17:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (161 votes), past polls