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