1 2# 3# validate the IPv4 header checksum. 4# $bytes[] is an array of 16bit values, with $cnt elements in the array. 5# 6sub dump { 7 print "\n"; 8 for ($i = 0; $i < $#bytes; $i++) { 9 printf "%04x ", $bytes[$i]; 10 } 11 print "\n"; 12} 13 14sub dosum { 15 local($seed) = $_[0]; 16 local($start) = $_[1]; 17 local($max) = $_[2]; 18 local($idx) = $start; 19 local($lsum) = $seed; 20 21 for ($idx = $start, $lsum = $seed; $idx < $max; $idx++) { 22 $lsum += $bytes[$idx]; 23 } 24 $lsum = ($lsum & 0xffff) + ($lsum >> 16); 25 $lsum = ~$lsum & 0xffff; 26 return $lsum; 27} 28 29sub ipv4check { 30 local($base) = $_[0]; 31 $hl = $bytes[$base] / 256; 32 return if (($hl >> 4) != 4); # IPv4 ? 33 $hl &= 0xf; 34 $hl <<= 1; # get the header length in 16bit words 35 36 $hs = &dosum(0, $base, $base + $hl); 37 $osum = $bytes[$base + 5]; 38 39 if ($hs != 0) { 40 $bytes[$base + 5] = 0; 41 $hs2 = &dosum(0, $base, $base + $hl); 42 $bytes[$base + 5] = $osum; 43 printf " IP: ($hl,%x) %x != %x", $hs, $osum, $hs2; 44 } else { 45 print " IP($base): ok "; 46 } 47 48 # 49 # Recognise TCP & UDP and calculate checksums for each of these. 50 # 51 if (($bytes[$base + 4] & 0xff) == 6) { 52 &tcpcheck($base); 53 } 54 55 if (($bytes[$base + 4] & 0xff) == 17) { 56 &udpcheck($base); 57 } 58 59 if (($bytes[$base + 4] & 0xff) == 1) { 60 &icmpcheck($base); 61 } 62 if ($base == 0) { 63 print "\n"; 64 } 65} 66 67sub tcpcheck { 68 local($base) = $_[0]; 69 local($hl) = $bytes[$base] / 256; 70 return if (($hl >> 4) != 4); 71 return if ($bytes[$base + 3] & 0x1fff); 72 $hl &= 0xf; 73 $hl <<= 1; 74 75 local($hs2); 76 local($hs) = 6; # TCP 77 local($len) = $bytes[$base + 1] - ($hl << 1); 78 $hs += $len; 79 $hs += $bytes[$base + 6]; # source address 80 $hs += $bytes[$base + 7]; 81 $hs += $bytes[$base + 8]; # destination address 82 $hs += $bytes[$base + 9]; 83 local($tcpsum) = $hs; 84 85 local($thl) = $bytes[$base + $hl + 6] >> 8; 86 $thl &= 0xf0; 87 $thl >>= 2; 88 89 $x = $bytes[$base + 1]; 90 $y = ($cnt - $base) * 2; 91 $z = 0; 92 if ($bytes[$base + 1] > ($cnt - $base) * 2) { 93 print "[cnt=$cnt base=$base]"; 94 $x = $bytes[$base + 1]; 95 $y = ($cnt - $base) * 2; 96 $z = 1; 97 } elsif (($cnt - $base) * 2 < $hl + 20) { 98 $x = ($cnt - $base) * 2; 99 $y = $hl + 20; 100 $z = 2; 101 } elsif (($cnt - $base) * 2 < $hl + $thl) { 102 $x = ($cnt - $base) * 2; 103 $y = $hl + $thl; 104 $z = 3; 105 } elsif ($len < $thl) { 106 $x = ($cnt - $base) * 2; 107 $y = $len; 108 $z = 4; 109 } 110 111 if ($z) { 112 print " TCP: missing data($x $y $z) $hl"; 113# &dump(); 114 return; 115 } 116 117 local($tcpat) = $base + $hl; 118 $hs = &dosum($tcpsum, $tcpat, $cnt); 119 if ($hs != 0) { 120 local($osum) = $bytes[$tcpat + 8]; 121 $bytes[$base + $hl + 8] = 0; 122 $hs2 = &dosum($tcpsum, $tcpat, $cnt); 123 $bytes[$tcpat + 8] = $osum; 124 printf " TCP: (%x) %x != %x", $hs, $osum, $hs2; 125 } else { 126 print " TCP: ok ($x $y)"; 127 } 128} 129 130sub udpcheck { 131 local($base) = $_[0]; 132 local($hl) = $bytes[0] / 256; 133 return if (($hl >> 4) != 4); 134 return if ($bytes[3] & 0x1fff); 135 $hl &= 0xf; 136 $hl <<= 1; 137 138 local($hs2); 139 local($hs) = 17; # UDP 140 local($len) = $bytes[$base + 1] - ($hl << 1); 141 $hs += $len; 142 $hs += $bytes[$base + 6]; # source address 143 $hs += $bytes[$base + 7]; 144 $hs += $bytes[$base + 8]; # destination address 145 $hs += $bytes[$base + 9]; 146 local($udpsum) = $hs; 147 148 if ($bytes[$base + 1] > ($cnt - $base) * 2) { 149 print " UDP: missing data(1)"; 150 return; 151 } elsif ($bytes[$base + 1] < ($hl << 1) + 8) { 152 print " UDP: missing data(2)"; 153 return; 154 } elsif (($cnt - $base) * 2 < ($hl << 1) + 8) { 155 print " UDP: missing data(3)"; 156 return; 157 } 158 159 local($udpat) = $base + $hl; 160 $hs = &dosum($udpsum, $udpat, $cnt); 161 local($osum) = $bytes[$udpat + 3]; 162 163 # 164 # It is valid for UDP packets to have a 0 checksum field. 165 # If it is 0, then display what it would otherwise be. 166 # 167 if ($osum == 0) { 168 printf " UDP: => %x", $hs; 169 } elsif ($hs != 0) { 170 $bytes[$udpat + 3] = 0; 171 $hs2 = &dosum($udpsum, $udpat, $cnt); 172 $bytes[$udpat + 3] = $osum; 173 printf " UDP: (%x) %x != %x", $hs, $osum, $hs2; 174 } else { 175 print " UDP: ok"; 176 } 177} 178 179sub icmpcheck { 180 local($base) = $_[0]; 181 local($hl) = $bytes[$base + 0] / 256; 182 return if (($hl >> 4) != 4); 183 return if ($bytes[3] & 0x1fff); 184 $hl &= 0xf; 185 $hl <<= 1; 186 187 local($hs); 188 local($hs2); 189 190 local($len) = $bytes[$base + 1] - ($hl << 1); 191 192 if ($bytes[$base + 1] > ($cnt - $base) * 2) { 193 print " ICMP: missing data(1)"; 194 return; 195 } elsif ($bytes[$base + 1] < ($hl << 1) + 8) { 196 print " ICMP: missing data(2)"; 197 return; 198 } elsif (($cnt - $base) * 2 < ($hl << 1) + 8) { 199 print " ICMP: missing data(3)"; 200 return; 201 } 202 203 local($osum) = $bytes[$base + $hl + 1]; 204 $bytes[$base + $hl + 1] = 0; 205 $hs2 = &dosum(0, $base + $hl, $cnt); 206 $bytes[$base + $hl + 1] = $osum; 207 208 if ($osum != $hs2) { 209 printf " ICMP: (%x) %x != %x", $hs, $osum, $hs2; 210 } else { 211 print " ICMP: ok"; 212 } 213 if ($base == 0) { 214 $type = $bytes[$hl] >> 8; 215 if ($type == 3 || $type == 4 || $type == 5 || 216 $type == 11 || $type == 12) { 217 &ipv4check($hl + 4); 218 } 219 } 220} 221 222while ($#ARGV >= 0) { 223 open(I, "$ARGV[0]") || die $!; 224 print "--- $ARGV[0] ---\n"; 225 $multi = 0; 226 while (<I>) { 227 chop; 228 s/#.*//g; 229 230 # 231 # If the first non-comment, non-empty line of input starts 232 # with a '[', then allow the input to be a multi-line hex 233 # string, otherwise it has to be all on one line. 234 # 235 if (/^\[/) { 236 $multi=1; 237 s/^\[[^]]*\]//g; 238 239 } 240 s/^ *//g; 241 if (length == 0) { 242 next if ($cnt == 0); 243 &ipv4check(0); 244 $cnt = 0; 245 $multi = 0; 246 next; 247 } 248 249 # 250 # look for 16 bits, represented with leading 0's as required, 251 # in hex. 252 # 253 s/\t/ /g; 254 while (/^[0-9a-fA-F][0-9a-fA-F] [0-9a-fA-F][0-9a-fA-F] .*/) { 255 s/^([0-9a-fA-F][0-9a-fA-F]) ([0-9a-fA-F][0-9a-fA-F]) (.*)/$1$2 $3/; 256 } 257 while (/.* [0-9a-fA-F][0-9a-fA-F] [0-9a-fA-F][0-9a-fA-F] .*/) { 258$b=$_; 259 s/(.*?) ([0-9a-fA-F][0-9a-fA-F]) ([0-9a-fA-F][0-9a-fA-F]) (.*)/$1 $2$3 $4/g; 260 } 261 if (/.* [0-9a-fA-F][0-9a-fA-F] [0-9a-fA-F][0-9a-fA-F]/) { 262$b=$_; 263 s/(.*?) ([0-9a-fA-F][0-9a-fA-F]) ([0-9a-fA-F][0-9a-fA-F])/$1 $2$3/g; 264 } 265 while (/^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F].*/) { 266 $x = $_; 267 $x =~ s/([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]).*/$1/; 268 $x =~ s/ *//g; 269 $y = hex $x; 270 s/[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] *(.*)/$1/; 271 $bytes[$cnt] = $y; 272#print "bytes[$cnt] = $x\n"; 273 $cnt++; 274 } 275 276 # 277 # Pick up stragler bytes. 278 # 279 if (/^[0-9a-fA-F][0-9a-fA-F]/) { 280 $y = hex $_; 281 $bytes[$cnt++] = $y * 256; 282 } 283 if ($multi == 0 && $cnt > 0) { 284 &ipv4check(0); 285 $cnt = 0; 286 } 287 } 288 289 if ($cnt > 0) { 290 &ipv4check(0); 291 } 292 close(I); 293 shift(@ARGV); 294} 295