1PKTT(3) pktt 2.7 PKTT(3)
2
3
4
6 packet.pktt - Packet trace module
7
9 The Packet trace module is a python module that takes a trace file cre‐
10 ated by tcpdump and unpacks the contents of each packet. You can decode
11 one packet at a time, or do a search for specific packets. The main
12 difference between these modules and other tools used to decode trace
13 files is that you can use this module to completely automate your
14 tests.
15
16 How does it work? It opens the trace file and reads one record at a
17 time keeping track where each record starts. This way, very large trace
18 files can be opened without having to wait for the file to load and
19 avoid loading the whole file into memory.
20
21 Packet layers supported:
22 - ETHERNET II (RFC 894)
23 - IP layer (supports IPv4 and IPv6)
24 - UDP layer
25 - TCP layer
26 - RPC layer
27 - NFS v4.0
28 - NFS v4.1 including pNFS file layouts
29 - NFS v4.2
30 - PORTMAP v2
31 - MOUNT v3
32 - NLM v4
33
35 class Header(baseobj.BaseObj)
36 Base class so objects will inherit the methods providing the string
37 representation of the object and a simple debug printing and logging
38 mechanism.
39
40 Usage:
41 from baseobj import BaseObj
42
43 # Named arguments
44 x = BaseObj(a=1, b=2)
45
46 # Dictionary argument
47 x = BaseObj({'a':1, 'b':2})
48
49 # Tuple arguments: first for keys and second for the values
50 x = BaseObj(['a', 'b'], [1, 2])
51
52 # All of the above will create an object having two attributes:
53 x.a = 1 and x.b = 2
54
55 # Add attribute name, this will be the only attribute to be displayed
56 x.set_attrlist("a")
57
58 # Add list of attribute names to be displayed in that order
59 x.set_attrlist(["a", "b"])
60
61 # Set attribute with ordered display rights
62 x.set_attr("a", 1)
63 # This is the same as
64 setattr(x, "a", 1) or x.a = 1
65 x.set_attrlist("a")
66
67 # Set attribute with switch duplicate
68 # The following creates an extra attribute "switch" with
69 # the same value as attribute "a":
70 # x.a == x.switch
71 # x.a is x.switch
72 x.set_attr("a", 1, switch=True)
73
74 # Make the current object flat by allowing all the attributes
75 # for the new attribute to be accessed directly by the current
76 # object so the following is True:
77 # x.d == x.c.d
78 x.set_attr("c", BaseObj(d=11, e=22), switch=True)
79
80 # Set the comparison attribute so x == x.a is True
81 x.set_eqattr("a")
82
83 # Set verbose level of object's string representation
84 x.debug_repr(level)
85
86 # Set string format for verbose level 1
87 x.set_strfmt(1, "arg1:{0}")
88 # In the above example the first positional argument is "a"
89 # so the str(x) gives "arg1:1"
90
91 # Set attribute shared by all instances
92 # If a global or shared attribute is set on one instance,
93 # all other instances will have access to it:
94 # y = BaseObj(d=2, e=3)
95 # then the following is true
96 # x.g == y.g
97 # x.g is y.g
98 x.set_global("g", 5)
99
100 # Set level mask to display all debug messages matching mask
101 x.debug_level(0xFF)
102
103 # Add a debug mapping for mask 0x100
104 x.debug_map(0x100, 'opts', "OPTS: ")
105
106 # Set global indentation to 4 spaces for dprint
107 x.dindent(4)
108
109 # Set global indentation to 4 spaces for displaying objects
110 x.sindent(4)
111
112 # Set global truncation to 64 for displaying string objects
113 x.strsize(64)
114
115 # Do not display timestamp for dprint messages
116 x.tstamp(enable=False)
117
118 # Change timestamp format to include the date
119 x.tstamp(fmt="{0:date:%Y-%m-%d %H:%M:%S.%q} ")
120
121 # Get timestamp if enabled, else return an empty string
122 out = x.timestamp()
123
124 # Open log file
125 x.open_log(logfile)
126
127 # Close log file
128 x.close_log()
129
130 # Write data to log file
131 x.write_log(data)
132
133 # Format the given arguments
134 out = x.format("{0:x} - {1}", 1, "hello")
135
136 # Format the object attributes set by set_attrlist()
137 out = x.format("{0:x} - {1}")
138
139 # Print debug message only if OPTS bitmap matches the current
140 # debug level mask
141 x.dprint("OPTS", "This is an OPTS debug message")
142
143
144 Methods defined here:
145 ---------------------
146
147 __init__(self, pktt)
148 Constructor
149
150 Initialize object's private data according to the arguments given.
151 Arguments can be given as positional, named arguments or a
152 combination of both.
153
154 class Pktt(baseobj.BaseObj)
155 Packet trace object
156
157 Usage:
158 from packet.pktt import Pktt
159
160 x = Pktt("/traces/tracefile.cap")
161
162 # Iterate over all packets found in the trace file
163 for pkt in x:
164 print pkt
165
166
167 Methods defined here:
168 ---------------------
169
170 __contains__(self, expr)
171 Implement membership test operator.
172 Return true if expr matches a packet in the trace file,
173 false otherwise.
174
175 The packet is also stored in the object attribute pkt.
176
177 Examples:
178 # Find the next READ request
179 if ("NFS.argop == 25" in x):
180 print x.pkt.nfs
181
182 See match() method for more information
183
184 __del__(self)
185 Destructor
186
187 Gracefully close the tcpdump trace file if it is opened.
188
189 __getitem__(self, index)
190 Get the packet from the trace file given by the index
191 or raise IndexError.
192
193 The packet is also stored in the object attribute pkt.
194
195 Examples:
196 pkt = x[index]
197
198 __init__(self, tfile, live=False, rpc_replies=True)
199 Constructor
200
201 Initialize object's private data, note that this will not check the
202 file for existence nor will open the file to verify if it is a valid
203 tcpdump file. The tcpdump trace file will be opened the first time a
204 packet is retrieved.
205
206
207 tracefile:
208 Name of tcpdump trace file or a list of trace file names
209 (little or big endian format)
210
211 live: If set to True, methods will not return if encountered <EOF>,
212 they will keep on trying until more data is available in the
213 file. This is useful when running tcpdump in parallel,
214 especially when tcpdump is run with the '-C' option, in which
215 case when <EOF> is encountered the next trace file created by
216 tcpdump will be opened and the object will be re-initialized,
217 all private data referencing the previous file is lost.
218
219 __iter__(self)
220 Make this object iterable.
221
222 __next__(self)
223 Get the next packet from the trace file or raise StopIteration.
224
225 The packet is also stored in the object attribute pkt.
226
227 Examples:
228 # Iterate over all packets found in the trace file using
229 # the iterable properties of the object
230 for pkt in x:
231 print pkt
232
233 # Iterate over all packets found in the trace file using it
234 # as a method and using the object variable as the packet
235 # Must use the try statement to catch StopIteration exception
236 try:
237 while (x.next()):
238 print x.pkt
239 except StopIteration:
240 pass
241
242 # Iterate over all packets found in the trace file using it
243 # as a method and using the return value as the packet
244 # Must use the try statement to catch StopIteration exception
245 while True:
246 try:
247 print x.next()
248 except StopIteration:
249 break
250
251 NOTE:
252 Supports only single active iteration
253
254 clear_xid_list(self)
255 Clear list of outstanding xids
256
257 close(self)
258 Gracefully close the tcpdump trace file and cleanup attributes.
259
260 get_index(self)
261 Get current packet index
262
263 match(self, expr, maxindex=None, rewind=True, reply=False)
264 Return the packet that matches the given expression, also the packet
265 index points to the next packet after the matched packet.
266 Returns None if packet is not found and the packet index points
267 to the packet at the beginning of the search.
268
269
270 expr: String of expressions to be evaluated
271
272 maxindex:
273 The match fails if packet index hits this limit
274
275 rewind:
276 Rewind to index where matching started if match fails
277
278 reply: Match RPC replies of previously matched calls as well
279
280 Examples:
281 # Find the packet with both the ACK and SYN TCP flags set to 1
282 pkt = x.match("TCP.flags.ACK == 1 and TCP.flags.SYN == 1")
283
284 # Find the next NFS EXCHANGE_ID request
285 pkt = x.match("NFS.argop == 42")
286
287 # Find the next NFS EXCHANGE_ID or CREATE_SESSION request
288 pkt = x.match("NFS.argop in [42,43]")
289
290 # Find the next NFS OPEN request or reply
291 pkt = x.match("NFS.op == 18")
292
293 # Find all packets coming from subnet 192.168.1.0/24 using
294 # a regular expression
295 while x.match(r"re.search('192.168.1.*', IP.src)"):
296 print x.pkt.tcp
297
298 # Find packet having a GETATTR asking for FATTR4_FS_LAYOUT_TYPES(bit 62)
299 pkt_call = x.match("NFS.attr_request & 0x4000000000000000L != 0")
300 if pkt_call:
301 # Find GETATTR reply
302 xid = pkt_call.rpc.xid
303 # Find reply where the number 62 is in the array NFS.attributes
304 pkt_reply = x.match("RPC.xid == %d and 62 in NFS.attributes" % xid)
305
306 # Find the next WRITE request
307 pkt = x.match("NFS.argop == 38")
308 if pkt:
309 print pkt.nfs
310
311 # Same as above, but using membership test operator instead
312 if ("NFS.argop == 38" in x):
313 print x.pkt.nfs
314
315 # Get a list of all OPEN and CLOSE packets then use buffered
316 # matching to process each OPEN and its corresponding CLOSE
317 # at a time including both requests and replies
318 pktlist = []
319 while x.match("NFS.op in [4,18]"):
320 pktlist.append(x.pkt)
321 # Enable buffered matching
322 x.set_pktlist(pktlist)
323 while x.match("NFS.argop == 18"): # Find OPEN request
324 print x.pkt
325 index = x.get_index()
326 # Find OPEN reply
327 x.match("RPC.xid == %d and NFS.resop == 18" % x.pkt.rpc.xid)
328 print x.pkt
329 # Find corresponding CLOSE request
330 stid = x.escape(x.pkt.NFSop.stateid.other)
331 x.match("NFS.argop == 4 and NFS.stateid == '%s'" % stid)
332 print x.pkt
333 # Find CLOSE reply
334 x.match("RPC.xid == %d and NFS.resop == 4" % x.pkt.rpc.xid)
335 print x.pkt
336 # Rewind to right after the OPEN request
337 x.rewind(index)
338 # Disable buffered matching
339 x.set_pktlist()
340
341 See also:
342 match_ethernet(), match_ip(), match_tcp(), match_rpc(), match_nfs()
343
344 match_nfs(self, expr)
345 Match NFS values on current packet.
346
347 In NFSv4, there is a single compound procedure with multiple
348 operations, matching becomes a little bit tricky in order to make
349 the matching expression easy to use. The NFS object's name space
350 gets converted into a flat name space for the sole purpose of
351 matching. In other words, all operation objects in array are
352 treated as being part of the NFS object's top level attributes.
353
354 Consider the following NFS object:
355 nfsobj = COMPOUND4res(
356 status=NFS4_OK,
357 tag='NFSv4_tag',
358 array = [
359 nfs_resop4(
360 resop=OP_SEQUENCE,
361 opsequence=SEQUENCE4res(
362 status=NFS4_OK,
363 resok=SEQUENCE4resok(
364 sessionid='sessionid',
365 sequenceid=29,
366 slotid=0,
367 highest_slotid=179,
368 target_highest_slotid=179,
369 status_flags=0,
370 ),
371 ),
372 ),
373 nfs_resop4(
374 resop=OP_PUTFH,
375 opputfh = PUTFH4res(
376 status=NFS4_OK,
377 ),
378 ),
379 ...
380 ]
381 ),
382
383 The result for operation PUTFH is the second in the list:
384 putfh = nfsobj.array[1]
385
386 From this putfh object the status operation can be accessed as:
387 status = putfh.opputfh.status
388
389 or simply as (this is how the NFS object works):
390 status = putfh.status
391
392 In this example, the following match expression 'NFS.status == 0'
393 could match the top level status of the compound (nfsobj.status)
394 or the putfh status (nfsobj.array[1].status)
395
396 The following match expression 'NFS.sequenceid == 25' will also
397 match this packet as well, even though the actual expression should
398 be 'nfsobj.array[0].opsequence.resok.sequenceid == 25' or
399 simply 'nfsobj.array[0].sequenceid == 25'.
400
401 This approach makes the match expressions simpler at the expense of
402 having some ambiguities on where the actual match occurred. If a
403 match is desired on a specific operation, a more qualified name can
404 be given. In the above example, in order to match the status of the
405 PUTFH operation the match expression 'NFS.opputfh.status == 0' can
406 be used. On the other hand, consider a compound having multiple
407 PUTFH results the above match expression will always match the first
408 occurrence of PUTFH where the status is 0. There is no way to tell
409 the match engine to match the second or Nth occurrence of an
410 operation.
411
412 match_pkt(self, expr)
413 Default wrapper function to evaluate a simple string expression.
414
415 rewind(self, index=0)
416 Rewind the trace file by setting the file pointer to the start of
417 the given packet index. Returns False if unable to rewind the file,
418 e.g., when the given index is greater than the maximum number
419 of packets processed so far.
420
421 seek(self, offset, whence=0, hard=False)
422 Position the read offset correctly
423 If new position is outside the current read buffer then clear the
424 buffer so a new chunk of data will be read from the file instead
425
426 set_pktlist(self, pktlist=None)
427 Set the current packet list for buffered matching in which the
428 match method will only use this list instead of getting the next
429 packet from the packet trace file.
430 This could be used when there is a lot of matching going back
431 and forth but only on a particular set of packets.
432 See the match() method for an example of buffered matching.
433
434 show_progress(self, done=False)
435 Display progress bar if enabled and if running on correct terminal
436
437 Static methods defined here:
438 ----------------------------
439
440 escape(data)
441 Escape special characters.
442
443 Examples:
444 # Call as an instance
445 escaped_data = x.escape(data)
446
447 # Call as a class
448 escaped_data = Pktt.escape(data)
449
450 ip_tcp_dst_expr(ipaddr, port=None)
451 Return a match expression to find a packet going to ipaddr:port.
452
453 Examples:
454 # Call as an instance
455 expr = x.ip_tcp_dst_expr('192.168.1.50', 2049)
456
457 # Call as a class
458 expr = Pktt.ip_tcp_dst_expr('192.168.1.50', 2049)
459
460 # Returns "IP.dst == '192.168.1.50' and TCP.dst_port == 2049"
461 # Expression ready for x.match()
462 pkt = x.match(expr)
463
464 ip_tcp_src_expr(ipaddr, port=None)
465 Return a match expression to find a packet coming from ipaddr:port.
466
467 Examples:
468 # Call as an instance
469 expr = x.ip_tcp_src_expr('192.168.1.50', 2049)
470
471 # Call as a class
472 expr = Pktt.ip_tcp_src_expr('192.168.1.50', 2049)
473
474 # Returns "IP.src == '192.168.1.50' and TCP.src_port == 2049"
475 # Expression ready for x.match()
476 pkt = x.match(expr)
477
479 convert_attrs(tree)
480 Convert all valid layer AST Attributes to fully qualified names.
481 Also, return the name of the correct wrapper function to be used.
482
483 NOTE:
484 The tree argument is modified so when tree is unparsed, all layer
485 attributes are expanded correctly.
486
487 get_binop(op)
488 Return the string representation of the operator AST object
489
490 get_bool(op)
491 Return the string representation of the logical operator AST object
492
493 get_op(op)
494 Return the string representation of the logical operator AST object
495
496 get_precedence(op)
497 Return the precedence of operator AST object
498
499 get_unary(op)
500 Return the string representation of the unary operator AST object
501
502 unparse(tree)
503 Older Python releases do not define ast.unparse(). Create function
504 unparse with limited functionality but enough for the matching
505 language it is needed for match(). This function runs twice as fast
506 as ast.unparse(), so always use it regardless if it is defined or
507 not on the ast module.
508
510 baseobj(3), formatstr(3), packet.internet.ipv4(3), packet.inter‐
511 net.ipv6(3), packet.link.erf(3), packet.link.ethernet(3),
512 packet.link.sllv1(3), packet.link.sllv2(3), packet.pkt(3),
513 packet.record(3), packet.transport.rdmainfo(3), packet.unpack(3)
514
515
517 No known bugs.
518
520 Jorge Mora (mora@netapp.com)
521
522
523
524NFStest 3.2 21 March 2023 PKTT(3)