line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::Pcap::Reassemble; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
require 5.002; |
4
|
1
|
|
|
1
|
|
3331
|
use strict; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
89
|
|
5
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
52
|
|
6
|
1
|
|
|
1
|
|
6
|
use vars qw($VERSION %pending $callback $linktype $debug $stripl2); |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
131
|
|
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
5860
|
use Net::Pcap; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Carp; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# |
12
|
|
|
|
|
|
|
# Copyright (c) 2006-2010 James Raftery . All rights reserved. |
13
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or |
14
|
|
|
|
|
|
|
# modify it under the same terms as Perl itself. |
15
|
|
|
|
|
|
|
# Please submit bug reports, patches and comments to the author. |
16
|
|
|
|
|
|
|
# |
17
|
|
|
|
|
|
|
# $Id: Reassemble.pm,v 1.22 2010/05/13 18:29:13 james Exp $ |
18
|
|
|
|
|
|
|
# |
19
|
|
|
|
|
|
|
# This module is a wrapper for the loop() function of the Net::Pcap |
20
|
|
|
|
|
|
|
# module. It performs IP fragment reassembly for fragmented datagrams |
21
|
|
|
|
|
|
|
# in the libpcap dump data. You require the Net::Pcap module to use |
22
|
|
|
|
|
|
|
# Net::Pcap::Reassemble. See the Net::Pcap::Reassemble(3) man page for |
23
|
|
|
|
|
|
|
# more information. |
24
|
|
|
|
|
|
|
# |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
$VERSION = '0.05'; |
27
|
|
|
|
|
|
|
$debug = 0; |
28
|
|
|
|
|
|
|
$stripl2 = 0; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
#### |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# |
33
|
|
|
|
|
|
|
# Wrapper around Net::Pcap's loop() function. This takes the same |
34
|
|
|
|
|
|
|
# arguments as Net::Pcap's loop(). |
35
|
|
|
|
|
|
|
# |
36
|
|
|
|
|
|
|
sub loop ($$&$) { |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my ($pcap_t, $num, $user_data); |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
($pcap_t, $num, $callback, $user_data) = @_ or |
41
|
|
|
|
|
|
|
croak('Missing arguments to loop()'); |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
defined($linktype = Net::Pcap::datalink($pcap_t)) or die; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# |
46
|
|
|
|
|
|
|
# A reference to the user's callback is in $callback, which is |
47
|
|
|
|
|
|
|
# declared as a package global. We call Net::Pcap::loop, |
48
|
|
|
|
|
|
|
# specifying instead our own _reassemble() sub as its callback. |
49
|
|
|
|
|
|
|
# _reassemble() will give a packet to the sub referenced in |
50
|
|
|
|
|
|
|
# $callback when it has a complete datagram. |
51
|
|
|
|
|
|
|
# |
52
|
|
|
|
|
|
|
return Net::Pcap::loop($pcap_t, $num, \&_reassemble, $user_data); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub flush () { |
56
|
|
|
|
|
|
|
undef %pending; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# |
60
|
|
|
|
|
|
|
# Callback function. Read the IP version from the packet header and call |
61
|
|
|
|
|
|
|
# the appropriate function to read it. If that function returns data |
62
|
|
|
|
|
|
|
# (i.e. a complete datagram) then summon up the user's callback, |
63
|
|
|
|
|
|
|
# supplying the packet. |
64
|
|
|
|
|
|
|
# |
65
|
|
|
|
|
|
|
sub _reassemble ($$$) { |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
my ($user_data, $header, $packet, $ver, $l2); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
($user_data, $header, $packet) = @_ or |
70
|
|
|
|
|
|
|
croak('Missing arguments to _reassemble()'); |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
($l2, $packet) = _splitpkt($packet); |
73
|
|
|
|
|
|
|
$ver = unpack('C', $packet) >> 4; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
if ($ver == 4) { |
76
|
|
|
|
|
|
|
$packet = _readIPv4pkt($packet); |
77
|
|
|
|
|
|
|
} elsif ($ver == 6) { |
78
|
|
|
|
|
|
|
$packet = _readIPv6pkt($packet); |
79
|
|
|
|
|
|
|
} else { |
80
|
|
|
|
|
|
|
$packet = undef; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
if ($packet) { |
84
|
|
|
|
|
|
|
# Put back the layer 2 header data from the most recent packet |
85
|
|
|
|
|
|
|
$packet = $l2.$packet unless $stripl2; |
86
|
|
|
|
|
|
|
&$callback($user_data, $header, $packet); |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# |
91
|
|
|
|
|
|
|
# Split the packet into layer 2 header and IP datagram (+ optional padding). |
92
|
|
|
|
|
|
|
# |
93
|
|
|
|
|
|
|
sub _splitpkt ($) { |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
my ($packet, $bytes); |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
$packet = shift or croak('Missing argument to _splitpkt()'); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
if ($linktype == DLT_EN10MB) { |
100
|
|
|
|
|
|
|
# ethernet header |
101
|
|
|
|
|
|
|
$bytes = 14; |
102
|
|
|
|
|
|
|
} elsif ($linktype == DLT_LOOP or $linktype == DLT_NULL) { |
103
|
|
|
|
|
|
|
# loopback header |
104
|
|
|
|
|
|
|
$bytes = 4; |
105
|
|
|
|
|
|
|
} elsif ($linktype == DLT_RAW) { |
106
|
|
|
|
|
|
|
# no header |
107
|
|
|
|
|
|
|
$bytes = 0; |
108
|
|
|
|
|
|
|
} elsif ($linktype == DLT_LINUX_SLL) { |
109
|
|
|
|
|
|
|
# linux 'cooked' |
110
|
|
|
|
|
|
|
$bytes = 16; |
111
|
|
|
|
|
|
|
} else { |
112
|
|
|
|
|
|
|
# barf |
113
|
|
|
|
|
|
|
croak("unsupported linktype: $linktype"); |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
return unpack("a$bytes a*", $packet); |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# |
120
|
|
|
|
|
|
|
# Read an IPv4 packet. |
121
|
|
|
|
|
|
|
# |
122
|
|
|
|
|
|
|
sub _readIPv4pkt ($) { |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
my ($packet, $i, $ver, $ihl, $pktlen, $id, $mf, $offset, $proto, |
125
|
|
|
|
|
|
|
$src, $dst, $payload, $datalen); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
$packet = shift or croak('Missing argument to _readIPv4pkt()'); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# The x's are: tos, ttl, chksum, options+data |
130
|
|
|
|
|
|
|
($i, $pktlen, $id, $offset, $proto, $src, $dst) = |
131
|
|
|
|
|
|
|
unpack('C x n3 x C x2 a4 a4', $packet); |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
$ver = $i >> 4; |
134
|
|
|
|
|
|
|
$ihl = ($i & 0x0f) * 4; |
135
|
|
|
|
|
|
|
$mf = ($offset >> 13) & 0x01; # More fragments flag |
136
|
|
|
|
|
|
|
$offset = ($offset & 0x1fff) << 3; |
137
|
|
|
|
|
|
|
$src = join('.', unpack('C*', $src)); |
138
|
|
|
|
|
|
|
$dst = join('.', unpack('C*', $dst)); |
139
|
|
|
|
|
|
|
$datalen = $pktlen - $ihl; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
print "ver:$ver ihl:$ihl packetlen:$pktlen id:$id mf:$mf " . |
142
|
|
|
|
|
|
|
"offset:$offset datalen:$datalen proto:$proto\n". |
143
|
|
|
|
|
|
|
"src:$src dst:$dst\n" if $debug; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
print "Dropping padding\n" if ($debug and length($packet) > $pktlen); |
146
|
|
|
|
|
|
|
print "Incomplete packet\n" if (length($packet) < $pktlen); |
147
|
|
|
|
|
|
|
$packet = substr($packet, 0, $pktlen); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# |
150
|
|
|
|
|
|
|
# Fragment 1: MF == 1, offset == 0 |
151
|
|
|
|
|
|
|
# Fragment 2..(n-1): MF == 1, offset > 0 |
152
|
|
|
|
|
|
|
# Fragment n: MF == 0, offset > 0 |
153
|
|
|
|
|
|
|
# |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# |
156
|
|
|
|
|
|
|
# Can you encounter a negative offset? Maybe if we unpack the |
157
|
|
|
|
|
|
|
# data incorrectly. |
158
|
|
|
|
|
|
|
# |
159
|
|
|
|
|
|
|
# If this isn't a fragment we drop down to the return statement |
160
|
|
|
|
|
|
|
# which passes back the unmodified $packet data. |
161
|
|
|
|
|
|
|
# |
162
|
|
|
|
|
|
|
if (($mf and $offset >= 0) or ($offset > 0)) { |
163
|
|
|
|
|
|
|
print "Fragment! ver:$ver ihl:$ihl packetlen:$pktlen id:$id ". |
164
|
|
|
|
|
|
|
"mf:$mf offset:$offset datalen:$datalen proto:$proto\n". |
165
|
|
|
|
|
|
|
"src:$src dst:$dst\n" if $debug; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
$i = "$src $dst $id $proto"; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# |
170
|
|
|
|
|
|
|
# If initial fragment - use the whole packet as the data. |
171
|
|
|
|
|
|
|
# XXX The user callback gets a packet with the header |
172
|
|
|
|
|
|
|
# from the first fragment. 'total length' and MF |
173
|
|
|
|
|
|
|
# are going to be wrong w.r.t. the reassembled |
174
|
|
|
|
|
|
|
# packet. |
175
|
|
|
|
|
|
|
# |
176
|
|
|
|
|
|
|
if ($offset == 0) { |
177
|
|
|
|
|
|
|
$payload = $packet; |
178
|
|
|
|
|
|
|
} else { |
179
|
|
|
|
|
|
|
$payload = substr($packet, $ihl, $datalen); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# |
183
|
|
|
|
|
|
|
# XXX We don't expunge old entries |
184
|
|
|
|
|
|
|
# |
185
|
|
|
|
|
|
|
if (exists $pending{$i}) { |
186
|
|
|
|
|
|
|
$pending{$i}->addfragment($offset, $datalen, $mf, |
187
|
|
|
|
|
|
|
$payload) or |
188
|
|
|
|
|
|
|
print STDERR "addfragment: $offset $datalen $mf failed\n"; |
189
|
|
|
|
|
|
|
} else { |
190
|
|
|
|
|
|
|
$pending{$i} = Net::Pcap::Reassemble::Packet->new($i, |
191
|
|
|
|
|
|
|
$offset, $datalen, $mf, $payload) or |
192
|
|
|
|
|
|
|
print STDERR "new Packet: $i $offset $datalen, $mf failed\n"; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
print $pending{$i}->listfragments if $debug; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# We get a packet if all the fragments have arrived, or |
198
|
|
|
|
|
|
|
# an empty string if not. |
199
|
|
|
|
|
|
|
$packet = $pending{$i}->iscomplete; |
200
|
|
|
|
|
|
|
if ($packet) { |
201
|
|
|
|
|
|
|
delete $pending{$i}; |
202
|
|
|
|
|
|
|
print "Fragment '$i' is complete.\n" if $debug; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
return $packet; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# |
210
|
|
|
|
|
|
|
# Read an IPv6 header/packet. |
211
|
|
|
|
|
|
|
# |
212
|
|
|
|
|
|
|
sub _readIPv6pkt ($) { |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
my ($packet, $ver, $payloadlen, $nexthdr, $src, $dst, $payload, $i, |
215
|
|
|
|
|
|
|
$offset, $id, $m, $hdrlen, $exthdrlentotal, $unfrag, |
216
|
|
|
|
|
|
|
$unfragoffset, $prevhdr, $prevhdrlen); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
$packet = shift or croak('Missing argument to _readIPv6pkt()'); |
219
|
|
|
|
|
|
|
$prevhdr = 0; # Hackity, hack, hack |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# The x's are: class, label, hlim |
222
|
|
|
|
|
|
|
($ver, $payloadlen, $nexthdr, $src, $dst) = |
223
|
|
|
|
|
|
|
unpack('C x3 n C x a16 a16', $packet); |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
$ver >>= 4; |
226
|
|
|
|
|
|
|
$src = join(':', unpack('H4'x8, $src)); |
227
|
|
|
|
|
|
|
$dst = join(':', unpack('H4'x8, $dst)); |
228
|
|
|
|
|
|
|
$exthdrlentotal = 0; # extension header bytes read so far |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
print "ver:$ver payloadlen:$payloadlen nexthdr:$nexthdr\n" . |
231
|
|
|
|
|
|
|
"src:$src\ndst:$dst\n" if $debug; |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# XXX not tested |
234
|
|
|
|
|
|
|
print "Dropping padding\n" if ($debug and length($packet) > 40+$payloadlen); |
235
|
|
|
|
|
|
|
print "Incomplete packet\n" if (length($packet) < 40+$payloadlen); |
236
|
|
|
|
|
|
|
$packet = substr($packet, 0, 40+$payloadlen); |
237
|
|
|
|
|
|
|
$payload = substr($packet, 40); |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# |
240
|
|
|
|
|
|
|
# Since this module isn't a v6 capable end-host it doesn't |
241
|
|
|
|
|
|
|
# implement TCP or UDP or any other `upper-layer' protocol. How |
242
|
|
|
|
|
|
|
# do we decide when to stop looking ahead to the next header |
243
|
|
|
|
|
|
|
# (and return some data to the caller)? We stop when we find |
244
|
|
|
|
|
|
|
# a `next header' which isn't a known Extension Header: |
245
|
|
|
|
|
|
|
# |
246
|
|
|
|
|
|
|
# 0 Hop-by-Hop Options |
247
|
|
|
|
|
|
|
# 43 Routing |
248
|
|
|
|
|
|
|
# 44 Fragment |
249
|
|
|
|
|
|
|
# 50 Encapsulating Security Payload |
250
|
|
|
|
|
|
|
# 51 Authentication |
251
|
|
|
|
|
|
|
# 60 Destination Options |
252
|
|
|
|
|
|
|
# |
253
|
|
|
|
|
|
|
# This means this will fail to deal with any subsequently added |
254
|
|
|
|
|
|
|
# Extension Headers, which is sucky, but the alternative is to |
255
|
|
|
|
|
|
|
# list all the other `next header' values and then break when a |
256
|
|
|
|
|
|
|
# new one of them is defined :) |
257
|
|
|
|
|
|
|
# |
258
|
|
|
|
|
|
|
EXTHEADER: for (;;) { |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
if ($nexthdr == 0 or $nexthdr == 43 or $nexthdr == 50 or |
261
|
|
|
|
|
|
|
$nexthdr == 51 or $nexthdr == 60) { |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
$prevhdr = $nexthdr; |
264
|
|
|
|
|
|
|
$prevhdrlen = $hdrlen; |
265
|
|
|
|
|
|
|
$exthdrlentotal += $hdrlen; |
266
|
|
|
|
|
|
|
($nexthdr, $hdrlen, $payload) = _readIPv6Extheader($payload); |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
next EXTHEADER; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
last EXTHEADER if ($nexthdr != 44); |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# |
274
|
|
|
|
|
|
|
# Fragment Header |
275
|
|
|
|
|
|
|
# |
276
|
|
|
|
|
|
|
($nexthdr, $offset, $id, $m, $payload) = _readIPv6Fragheader($payload); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
$i = "$src $dst $id"; |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# |
281
|
|
|
|
|
|
|
# Initial fragment - use the whole packet minus the Fragment |
282
|
|
|
|
|
|
|
# header as the data. |
283
|
|
|
|
|
|
|
# Munge the Next Header value from 44 (Fragment Header) to that |
284
|
|
|
|
|
|
|
# of the subsequent header. |
285
|
|
|
|
|
|
|
# |
286
|
|
|
|
|
|
|
# XXX The user callback gets a packet with the header from the |
287
|
|
|
|
|
|
|
# first fragment. `length' is going to be wrong w.r.t. the |
288
|
|
|
|
|
|
|
# reassembled packet. |
289
|
|
|
|
|
|
|
# |
290
|
|
|
|
|
|
|
if ($offset == 0) { |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# Offset to the start of the unfragmentable part |
293
|
|
|
|
|
|
|
$unfragoffset = 40+$exthdrlentotal; |
294
|
|
|
|
|
|
|
$unfrag = substr($packet, 0, $unfragoffset); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
if ($prevhdr == 0) { |
297
|
|
|
|
|
|
|
# 6 bytes into IPv6 header |
298
|
|
|
|
|
|
|
substr($unfrag, 6, 1) = $nexthdr; |
299
|
|
|
|
|
|
|
} else { |
300
|
|
|
|
|
|
|
# XXX not tested |
301
|
|
|
|
|
|
|
# We've read N extension headers |
302
|
|
|
|
|
|
|
# Wind back one header length ($prevhdrlen) |
303
|
|
|
|
|
|
|
# from the start of the unfragmentable part |
304
|
|
|
|
|
|
|
# ($unfragoffset). |
305
|
|
|
|
|
|
|
substr($unfrag, $unfragoffset-$prevhdrlen, 1) = $nexthdr; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
$payload = $unfrag . $payload; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# |
312
|
|
|
|
|
|
|
# Fragmentable part length = |
313
|
|
|
|
|
|
|
# packet payload length - length of extension headers read |
314
|
|
|
|
|
|
|
# (add 8 bytes for the Fragment header) |
315
|
|
|
|
|
|
|
# |
316
|
|
|
|
|
|
|
$payloadlen -= ($exthdrlentotal+8); |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# |
319
|
|
|
|
|
|
|
# XXX We don't expunge old entries |
320
|
|
|
|
|
|
|
# |
321
|
|
|
|
|
|
|
if (exists $pending{$i}) { |
322
|
|
|
|
|
|
|
$pending{$i}->addfragment($offset, $payloadlen, $m, |
323
|
|
|
|
|
|
|
$payload) or |
324
|
|
|
|
|
|
|
print STDERR "addfrag: $i $offset $payloadlen $m failed\n"; |
325
|
|
|
|
|
|
|
} else { |
326
|
|
|
|
|
|
|
$pending{$i} = Net::Pcap::Reassemble::Packet->new($i, |
327
|
|
|
|
|
|
|
$offset, $payloadlen, $m, $payload) or |
328
|
|
|
|
|
|
|
print STDERR "Packet: $i $offset $payloadlen $m failed\n"; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
print $pending{$i}->listfragments if $debug; |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# We get a packet if all the fragments have arrived or an |
334
|
|
|
|
|
|
|
# empty string if not. |
335
|
|
|
|
|
|
|
$packet = $pending{$i}->iscomplete; |
336
|
|
|
|
|
|
|
if ($packet) { |
337
|
|
|
|
|
|
|
delete $pending{$i}; |
338
|
|
|
|
|
|
|
print "Fragment '$i' is complete.\n" if $debug; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
last EXTHEADER; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
} # End: EXTHEADER |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
return $packet; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# |
349
|
|
|
|
|
|
|
# Read a standard IPv6 Extension Header. Extract the Next Header and |
350
|
|
|
|
|
|
|
# Header Length values, and the payload. |
351
|
|
|
|
|
|
|
# |
352
|
|
|
|
|
|
|
sub _readIPv6Extheader ($) { |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
my ($packet, $nexthdr, $hdrlen, $payload); |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
$packet = shift or croak('Missing argument to _readIPv6Extheader()'); |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
($nexthdr, $hdrlen) = unpack('CC', $packet); |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
$hdrlen = $hdrlen*8 + 8; |
361
|
|
|
|
|
|
|
print "Extension header is $hdrlen octets, nexthdr: $nexthdr\n" if $debug; |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
# XXX not tested |
364
|
|
|
|
|
|
|
# use substr? |
365
|
|
|
|
|
|
|
$payload = unpack("x$hdrlen a*", $packet); |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
return($nexthdr, $hdrlen, $payload); |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# |
371
|
|
|
|
|
|
|
# Read an IPv6 Fragment Header. Extract the fragment's offset, ID, M |
372
|
|
|
|
|
|
|
# flag and payload. |
373
|
|
|
|
|
|
|
# |
374
|
|
|
|
|
|
|
sub _readIPv6Fragheader ($) { |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
my ($packet, $nexthdr, $offset, $m, $id, $payload); |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
$packet = shift or croak('Missing argument to _readIPv6Fragheader()'); |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
($nexthdr, $offset, $id, $payload) = unpack('C x n N a*', $packet); |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
$m = $offset & 0x0001; |
383
|
|
|
|
|
|
|
$offset >>= 3; |
384
|
|
|
|
|
|
|
$offset *= 8; |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
print "Fragment! header: nexthdr:$nexthdr offset:$offset ". |
387
|
|
|
|
|
|
|
"id:$id,0x". unpack('H*', pack('N', $id)) ." m:$m ". |
388
|
|
|
|
|
|
|
length($packet) . ' ' . length($payload) ."\n" if $debug; |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
$nexthdr = pack('C', $nexthdr); |
391
|
|
|
|
|
|
|
return ($nexthdr, $offset, $id, $m, $payload); |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
#### |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
package Net::Pcap::Reassemble::Packet; |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
use strict; |
399
|
|
|
|
|
|
|
use warnings; |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
use Carp; |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
# |
404
|
|
|
|
|
|
|
# Constructor for a `Packet' object. |
405
|
|
|
|
|
|
|
# |
406
|
|
|
|
|
|
|
sub new ($$$$$$) { |
407
|
|
|
|
|
|
|
my $proto = shift or croak; |
408
|
|
|
|
|
|
|
my $class = ref($proto) || $proto; |
409
|
|
|
|
|
|
|
defined(my $id = shift) or croak "No ID in $class constructor"; |
410
|
|
|
|
|
|
|
defined(my $offset = shift) or croak "No offset in $class constructor"; |
411
|
|
|
|
|
|
|
defined(my $length = shift) or croak "No length in $class constructor"; |
412
|
|
|
|
|
|
|
defined(my $mf = shift) or croak "No MF in $class constructor"; |
413
|
|
|
|
|
|
|
defined(my $data = shift) or croak "No data in $class constructor"; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# |
416
|
|
|
|
|
|
|
# Each `Packet' object contains: |
417
|
|
|
|
|
|
|
# 1. ID: IPv4: 'srcip dstip IPid protocol' |
418
|
|
|
|
|
|
|
# IPv6: 'srcip dstip IPid' |
419
|
|
|
|
|
|
|
# 2. A list of Net::Pcap::Reassemble::Fragment object references |
420
|
|
|
|
|
|
|
# 3. The final octet, learned from the packet with MF==0. |
421
|
|
|
|
|
|
|
# 4. A `sorted' flag to indicate if the fragment list is sorted |
422
|
|
|
|
|
|
|
# |
423
|
|
|
|
|
|
|
my $self = { |
424
|
|
|
|
|
|
|
ID => $id, |
425
|
|
|
|
|
|
|
FRAGS => [], |
426
|
|
|
|
|
|
|
LASTOCTET => undef, |
427
|
|
|
|
|
|
|
SORTED => 1, |
428
|
|
|
|
|
|
|
}; |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
bless($self, $class); |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
return undef if !$self->addfragment($offset, $length, $mf, $data); |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
return $self; |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# |
438
|
|
|
|
|
|
|
# Add a fragment to a Packet object. |
439
|
|
|
|
|
|
|
# |
440
|
|
|
|
|
|
|
sub addfragment ($$$$$) { |
441
|
|
|
|
|
|
|
my $self = shift; |
442
|
|
|
|
|
|
|
ref($self) or croak; |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
my ($offset, $length, $mf, $data) = @_ or croak; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
my $frag = |
447
|
|
|
|
|
|
|
Net::Pcap::Reassemble::Fragment->new($offset, $length, $mf, $data); |
448
|
|
|
|
|
|
|
return undef if !$frag; |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# If this is the last fragment, save the last octet value in the |
451
|
|
|
|
|
|
|
# object. |
452
|
|
|
|
|
|
|
# XXX Check for more than one fragment with MF==0? |
453
|
|
|
|
|
|
|
$self->{LASTOCTET} = $offset+$length if !$mf; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
# The list can't be considered sorted any more. |
456
|
|
|
|
|
|
|
$self->{SORTED} = 0; |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# XXX Test for overlap? |
459
|
|
|
|
|
|
|
return push(@{$self->{FRAGS}}, $frag); |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
# |
463
|
|
|
|
|
|
|
# Return a string showing the fragments that have been recieved by the object. |
464
|
|
|
|
|
|
|
# |
465
|
|
|
|
|
|
|
sub listfragments ($) { |
466
|
|
|
|
|
|
|
my $self = shift; |
467
|
|
|
|
|
|
|
ref($self) or croak; |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
my ($s, $frag); |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
$s .= "Packet ID:$self->{ID}\n"; |
472
|
|
|
|
|
|
|
$s .= "Last octet:$self->{LASTOCTET}\n" if (defined $self->{LASTOCTET}); |
473
|
|
|
|
|
|
|
foreach $frag (@{$self->{FRAGS}}) { |
474
|
|
|
|
|
|
|
$s .= 'Fragment ' . $frag->vitals . "\n"; |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
return $s; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# |
481
|
|
|
|
|
|
|
# Check if all the fragments for a Packet have been received. If they have, |
482
|
|
|
|
|
|
|
# splice the fragment data back together and return to the caller. If they |
483
|
|
|
|
|
|
|
# have not, return no data. |
484
|
|
|
|
|
|
|
# |
485
|
|
|
|
|
|
|
sub iscomplete ($) { |
486
|
|
|
|
|
|
|
my $self = shift; |
487
|
|
|
|
|
|
|
ref($self) or croak; |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
my $nextfrag = 0; # The first fragment starts at octet zero |
490
|
|
|
|
|
|
|
my $data = ''; |
491
|
|
|
|
|
|
|
my $frag; |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# |
494
|
|
|
|
|
|
|
# If we don't know LASTOCTET yet then we're missing at least the |
495
|
|
|
|
|
|
|
# final (MF==0) fragment so we don't need to proceed any further. |
496
|
|
|
|
|
|
|
# |
497
|
|
|
|
|
|
|
return if (!defined $self->{LASTOCTET}); |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
# |
500
|
|
|
|
|
|
|
# Sort the fragment list so we only need to scan it once. |
501
|
|
|
|
|
|
|
# If it was unordered we would need to scan through it repeatedly. |
502
|
|
|
|
|
|
|
# That said, sort() is pretty slow :) |
503
|
|
|
|
|
|
|
# |
504
|
|
|
|
|
|
|
FRAGMENT: foreach $frag (@{$self->_sortfragments}) { |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
# |
507
|
|
|
|
|
|
|
# If the first octet in this fragment is the octet we're |
508
|
|
|
|
|
|
|
# searching for and the last octet is the last octet of the |
509
|
|
|
|
|
|
|
# complete datagram then we have all the packet data. If not, |
510
|
|
|
|
|
|
|
# the next fragment we search for is the one that starts where |
511
|
|
|
|
|
|
|
# this one ends. |
512
|
|
|
|
|
|
|
# |
513
|
|
|
|
|
|
|
if ($frag->start == $nextfrag) { |
514
|
|
|
|
|
|
|
last FRAGMENT if ($frag->end == $self->{LASTOCTET}); |
515
|
|
|
|
|
|
|
$nextfrag = $frag->end; |
516
|
|
|
|
|
|
|
next FRAGMENT; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# |
520
|
|
|
|
|
|
|
# If we reach here, we're missing at least one fragment so |
521
|
|
|
|
|
|
|
# just give up. |
522
|
|
|
|
|
|
|
# |
523
|
|
|
|
|
|
|
return; |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
# |
527
|
|
|
|
|
|
|
# The datagram is complete. Splice the fragments' data together |
528
|
|
|
|
|
|
|
# to return the complete packet. |
529
|
|
|
|
|
|
|
# |
530
|
|
|
|
|
|
|
return $self->_data; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
# |
534
|
|
|
|
|
|
|
# Return concatenated fragment data. |
535
|
|
|
|
|
|
|
# Warning: missing fragments are blithely ignored. Use iscomplete() for |
536
|
|
|
|
|
|
|
# a sanity-checked interface! |
537
|
|
|
|
|
|
|
# |
538
|
|
|
|
|
|
|
sub _data ($) { |
539
|
|
|
|
|
|
|
my $self = shift; |
540
|
|
|
|
|
|
|
ref($self) or croak; |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
my ($frag, $data); |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
foreach $frag (@{$self->_sortfragments}) { |
545
|
|
|
|
|
|
|
$data .= $frag->data; |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
return $data; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# |
552
|
|
|
|
|
|
|
# Sort the fragment list by starting octet value and return a reference |
553
|
|
|
|
|
|
|
# the list. |
554
|
|
|
|
|
|
|
# |
555
|
|
|
|
|
|
|
sub _sortfragments ($) { |
556
|
|
|
|
|
|
|
my $self = shift; |
557
|
|
|
|
|
|
|
ref($self) or croak; |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
if (!$self->{SORTED}) { |
560
|
|
|
|
|
|
|
@{$self->{FRAGS}} = sort {$a->start<=>$b->start} @{$self->{FRAGS}}; |
561
|
|
|
|
|
|
|
$self->{SORTED} = 1; |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
return $self->{FRAGS}; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
#### |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
package Net::Pcap::Reassemble::Fragment; |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
use strict; |
571
|
|
|
|
|
|
|
use warnings; |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
use Carp; |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
# |
576
|
|
|
|
|
|
|
# Constructor for a `Fragment' object. |
577
|
|
|
|
|
|
|
# |
578
|
|
|
|
|
|
|
sub new ($$$$$) { |
579
|
|
|
|
|
|
|
my $proto = shift or croak; |
580
|
|
|
|
|
|
|
my $class = ref($proto) || $proto; |
581
|
|
|
|
|
|
|
defined(my $offset = shift) or croak "No offset in $class constructor"; |
582
|
|
|
|
|
|
|
defined(my $length = shift) or croak "No length in $class constructor"; |
583
|
|
|
|
|
|
|
defined(my $mf = shift) or croak "No MF in $class constructor"; |
584
|
|
|
|
|
|
|
defined(my $data = shift) or croak "No data in $class constructor"; |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
# |
587
|
|
|
|
|
|
|
# Each `Fragment' object contains: |
588
|
|
|
|
|
|
|
# 1. Start octet |
589
|
|
|
|
|
|
|
# 2. End octet |
590
|
|
|
|
|
|
|
# 3. (M)ore (F)ragments flag (`MF' in IPv4; `M' in IPv6) |
591
|
|
|
|
|
|
|
# 4. Payload data |
592
|
|
|
|
|
|
|
# |
593
|
|
|
|
|
|
|
my $self = { |
594
|
|
|
|
|
|
|
START => $offset, |
595
|
|
|
|
|
|
|
END => $offset+$length, |
596
|
|
|
|
|
|
|
MF => $mf, |
597
|
|
|
|
|
|
|
DATA => $data, |
598
|
|
|
|
|
|
|
}; |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
bless($self, $class); |
601
|
|
|
|
|
|
|
return $self; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
# |
605
|
|
|
|
|
|
|
# Accessor function for start octet value. |
606
|
|
|
|
|
|
|
# |
607
|
|
|
|
|
|
|
sub start ($) { |
608
|
|
|
|
|
|
|
my $self = shift; |
609
|
|
|
|
|
|
|
ref($self) or croak; |
610
|
|
|
|
|
|
|
return $self->{START} |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
# |
614
|
|
|
|
|
|
|
# Accessor function for end octet value. |
615
|
|
|
|
|
|
|
# |
616
|
|
|
|
|
|
|
sub end ($) { |
617
|
|
|
|
|
|
|
my $self = shift; |
618
|
|
|
|
|
|
|
ref($self) or croak; |
619
|
|
|
|
|
|
|
return $self->{END} |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
# |
623
|
|
|
|
|
|
|
# Accessor function for MF/M flag. |
624
|
|
|
|
|
|
|
# |
625
|
|
|
|
|
|
|
sub mf ($) { |
626
|
|
|
|
|
|
|
my $self = shift; |
627
|
|
|
|
|
|
|
ref($self) or croak; |
628
|
|
|
|
|
|
|
return $self->{MF} |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
# |
632
|
|
|
|
|
|
|
# Accessor function for fragment data. |
633
|
|
|
|
|
|
|
# |
634
|
|
|
|
|
|
|
sub data ($) { |
635
|
|
|
|
|
|
|
my $self = shift; |
636
|
|
|
|
|
|
|
ref($self) or croak; |
637
|
|
|
|
|
|
|
return $self->{DATA} |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
# |
641
|
|
|
|
|
|
|
# Return a string listing a fragment's vital statistics. |
642
|
|
|
|
|
|
|
# |
643
|
|
|
|
|
|
|
sub vitals ($) { |
644
|
|
|
|
|
|
|
my $self = shift; |
645
|
|
|
|
|
|
|
ref($self) or croak; |
646
|
|
|
|
|
|
|
return 'start:'. $self->start .' end:'. $self->end .' mf:'. $self->mf; |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
#### |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
1; |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
__END__ |