# # validate the IPv4 header checksum. # $bytes[] is an array of 16bit values, with $cnt elements in the array. # sub dosum { local($seed) = $_[0]; local($start) = $_[1]; local($max) = $_[2]; local($idx) = $start; local($lsum) = $seed; for ($idx = $start, $lsum = $seed; $idx < $max; $idx++) { $lsum += $bytes[$idx]; } while ($lsum > 65535) { $lsum = ($lsum & 0xffff) + ($lsum >> 16); } $lsum = ~$lsum & 0xffff; return $lsum; } sub ipv4check { local($base) = $_[0]; $hl = $bytes[$base] / 256; return if (($hl >> 4) != 4); # IPv4 ? $hl &= 0xf; $hl <<= 1; # get the header length in 16bit words $hs = &dosum(0, $base, $base + $hl); $osum = $bytes[$base + 5]; if ($hs != 0) { $bytes[$base + 5] = 0; $hs2 = &dosum($base, 0, $base + $hl); $bytes[$base + 5] = $osum; printf " IP: (%x) %x != %x", $hs, $osum, $hs2; } else { print " IP($base): ok "; } # # Recognise TCP & UDP and calculate checksums for each of these. # if (($bytes[$base + 4] & 0xff) == 6) { &tcpcheck($base); } if (($bytes[$base + 4] & 0xff) == 17) { &udpcheck($base); } if (($bytes[$base + 4] & 0xff) == 1) { &icmpcheck($base); } if ($base == 0) { print "\n"; } } sub tcpcheck { local($base) = $_[0]; local($hl) = $bytes[$base] / 256; return if (($hl >> 4) != 4); return if ($bytes[$base + 3] & 0x1fff); $hl &= 0xf; $hl <<= 1; local($hs2); local($hs) = 6; # TCP local($len) = $bytes[$base + 1] - ($hl << 1); $hs += $len; $hs += $bytes[$base + 6]; # source address $hs += $bytes[$base + 7]; $hs += $bytes[$base + 8]; # destination address $hs += $bytes[$base + 9]; local($tcpsum) = $hs; local($thl) = $bytes[$base + $hl + 6] >> 8; $thl &= 0xf0; $thl >>= 2; $x = $bytes[$base + 1]; $y = ($cnt - $base) * 2; $z = 0; if ($bytes[$base + 1] > ($cnt - $base) * 2) { print "[cnt=$cnt base=$base]"; $x = $bytes[$base + 1]; $y = ($cnt - $base) * 2; $z = 1; } elsif (($cnt - $base) * 2 < $hl + 20) { $x = ($cnt - $base) * 2; $y = $hl + 20; $z = 2; } elsif (($cnt - $base) * 2 < $hl + $thl) { $x = ($cnt - $base) * 2; $y = $hl + $thl; $z = 3; } if ($z) { print " TCP: missing data($x $y $z)"; return; } local($tcpat) = $base + $hl; $hs = &dosum($tcpsum, $tcpat, $cnt); if ($hs != 0) { local($osum) = $bytes[$tcpat + 8]; $bytes[$base + $hl + 8] = 0; $hs2 = &dosum($tcpsum, $tcpat, $cnt); $bytes[$tcpat + 8] = $osum; printf " TCP: (%x) %x != %x", $hs, $osum, $hs2; } else { print " TCP: ok ($x $y)"; } } sub udpcheck { local($base) = $_[0]; local($hl) = $bytes[0] / 256; return if (($hl >> 4) != 4); return if ($bytes[3] & 0x1fff); $hl &= 0xf; $hl <<= 1; local($hs2); local($hs) = 17; # UDP local($len) = $bytes[$base + 1] - ($hl << 1); $hs += $len; $hs += $bytes[$base + 6]; # source address $hs += $bytes[$base + 7]; $hs += $bytes[$base + 8]; # destination address $hs += $bytes[$base + 9]; local($udpsum) = $hs; if ($bytes[$base + 1] > ($cnt - $base) * 2) { print " UDP: missing data(1)"; return; } elsif ($bytes[$base + 1] < ($hl << 1) + 8) { print " UDP: missing data(2)"; return; } elsif (($cnt - $base) * 2 < ($hl << 1) + 8) { print " UDP: missing data(3)"; return; } local($udpat) = $base + $hl; $hs = &dosum($udpsum, $udpat, $cnt); local($osum) = $bytes[$udpat + 3]; # # It is valid for UDP packets to have a 0 checksum field. # If it is 0, then display what it would otherwise be. # if ($osum == 0) { printf " UDP: => %x", $hs; } elsif ($hs != 0) { $bytes[$udpat + 3] = 0; $hs2 = &dosum($udpsum, $udpat, $cnt); $bytes[$udpat + 3] = $osum; printf " UDP: (%x) %x != %x", $hs, $osum, $hs2; } else { print " UDP: ok"; } } sub icmpcheck { local($base) = $_[0]; local($hl) = $bytes[$base + 0] / 256; return if (($hl >> 4) != 4); return if ($bytes[3] & 0x1fff); $hl &= 0xf; $hl <<= 1; local($hs); local($hs2); local($len) = $bytes[$base + 1] - ($hl << 1); if ($bytes[$base + 1] > ($cnt - $base) * 2) { print " ICMP: missing data(1)"; return; } elsif ($bytes[$base + 1] < ($hl << 1) + 8) { print " ICMP: missing data(2)"; return; } elsif (($cnt - $base) * 2 < ($hl << 1) + 8) { print " ICMP: missing data(3)"; return; } local($osum) = $bytes[$base + $hl + 1]; $bytes[$hl + 1] = 0; for ($i = $base + $hl, $hs2 = 0; $i < $cnt; $i++) { $hs2 += $bytes[$i]; } $hs = $hs2 + $osum; while ($hs2 > 65535) { $hs2 = ($hs2 & 0xffff) + ($hs2 >> 16); } while ($hs > 65535) { $hs = ($hs & 0xffff) + ($hs >> 16); } $hs2 = ~$hs2 & 0xffff; $hs = ~$hs & 0xffff; if ($osum != $hs2) { printf " ICMP: (%x) %x != %x", $hs, $osum, $hs2; } else { print " ICMP: ok"; } if ($base == 0) { $type = $bytes[$hl] >> 8; if ($type == 3 || $type == 4 || $type == 5 || $type == 11 || $type == 12) { &ipv4check($hl + 4); } } } while ($#ARGV >= 0) { open(I, "$ARGV[0]") || die $!; print "--- $ARGV[0] ---\n"; $multi = 0; while () { chop; s/#.*//g; # # If the first non-comment, non-empty line of input starts # with a '[', then allow the input to be a multi-line hex # string, otherwise it has to be all on one line. # if (/^\[/) { $multi=1; s/^\[[^]]*\]//g; } s/^ *//g; if (length == 0) { next if ($cnt == 0); &ipv4check(0); $cnt = 0; $multi = 0; next; } # # look for 16 bits, represented with leading 0's as required, # in hex. # s/\t/ /g; while (/^[0-9a-fA-F][0-9a-fA-F] [0-9a-fA-F][0-9a-fA-F] .*/) { s/^([0-9a-fA-F][0-9a-fA-F]) ([0-9a-fA-F][0-9a-fA-F]) (.*)/$1$2 $3/; } while (/.* [0-9a-fA-F][0-9a-fA-F] [0-9a-fA-F][0-9a-fA-F] .*/) { $b=$_; s/(.*?) ([0-9a-fA-F][0-9a-fA-F]) ([0-9a-fA-F][0-9a-fA-F]) (.*)/$1 $2$3 $4/g; } while (/^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F].*/) { $x = $_; $x =~ s/([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]).*/$1/; $x =~ s/ *//g; $y = hex $x; s/[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] *(.*)/$1/; $bytes[$cnt] = $y; #print "bytes[$cnt] = $x\n"; $cnt++; } # # Pick up stragler bytes. # if (/^[0-9a-fA-F][0-9a-fA-F]/) { $y = hex $_; $bytes[$cnt++] = $y * 256; } if ($multi == 0 && $cnt > 0) { &ipv4check(0); $cnt = 0; } } if ($cnt > 0) { &ipv4check(0); } close(I); shift(@ARGV); }