File: pkt.py

package info (click to toggle)
nfstest 3.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,580 kB
  • sloc: python: 24,102; makefile: 3
file content (175 lines) | stat: -rw-r--r-- 7,224 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#===============================================================================
# Copyright 2012 NetApp, Inc. All Rights Reserved,
# contribution by Jorge Mora <mora@netapp.com>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#===============================================================================
"""
Pkt module

Provides the object for a packet and the string representation of the packet.
This object has an attribute for each of the layers in the packet so each layer
can be accessed directly instead of going through each layer. To access the nfs
layer object you can use 'x.nfs' instead of using 'x.ethernet.ip.tcp.rpc.nfs'
which would be very cumbersome to use. Also, since NFS can be used with either
TCP or UDP it would be harder to access the nfs object independently of
the protocol.

Packet object attributes:
    Pkt(
        record   = Record information (frame number, etc.)
        ethernet = ETHERNET II (RFC 894) object
        ip       = IPv4 object
        tcp      = TCP object
        rpc      = RPC object
        nfs      = NFS object
    )
"""
import nfstest_config as c
from baseobj import BaseObj

# Module constants
__author__    = "Jorge Mora (%s)" % c.NFSTEST_AUTHOR_EMAIL
__copyright__ = "Copyright (C) 2012 NetApp, Inc."
__license__   = "GPL v2"
__version__   = "1.4"

# The order in which to display all layers in the packet
PKT_layers = [
    'record',
    'ethernet', 'erf', 'vlan', 'sll',
    'ip', 'arp', 'rarp',
    'tcp', 'udp', 'ib', 'mpa', 'ddp', 'rdmap',
    'rpcordma', 'rpc', 'ntp', 'dns', 'krb',
    'gssd', 'nfs', 'mount', 'portmap', 'nlm', 'gssc',
]
# Required layers for debug_repr(1)
_PKT_rlayers = {'record', 'ip', 'ib'}
# Do not display these layers for debug_repr(1)
_PKT_nlayers = {'gssd', 'gssc'}
_maxlen = len(max(PKT_layers, key=len))

class Pkt(BaseObj):
    """Packet object

       Usage:
           from packet.pkt import Pkt

           x = Pkt()

           # Check if this is an NFS packet
           if x == 'nfs':
               print x.nfs
    """
    # Class attributes
    _attrlist = tuple(PKT_layers)

    # Do not use BaseObj constructor to have a little bit of
    # performance improvement
    def __init__(self):
        self._layers = ["record"]

    @property
    def is_truncated(self):
        return (not self.record or self.record.length_orig != self.record.length_inc)

    def __eq__(self, other):
        """Comparison method used to determine if object has a given layer"""
        if isinstance(other, str):
            return getattr(self, other.lower(), None) is not None
        return False

    def __ne__(self, other):
        """Comparison method used to determine if object does not have a given layer"""
        return not self.__eq__(other)

    def __str__(self):
        """String representation of object

           The representation depends on the verbose level set by debug_repr().
           If set to 0 the generic object representation is returned.
           If set to 1 the representation of is condensed into a single line.
           It contains, the frame number, IP source and destination and/or the
           last layer:
               '1 0.386615 192.168.0.62 -> 192.168.0.17 TCP 2049 -> 708, seq: 3395733180, ack: 3294169773, ACK,SYN'
               '5 0.530957 00:0c:29:54:09:ef -> ff:ff:ff:ff:ff:ff, type: 0x806'
               '19 0.434370 192.168.0.17 -> 192.168.0.62 NFS v4 COMPOUND4 call  SEQUENCE;PUTFH;GETATTR'

           If set to 2 the representation of the object is a line for each layer:
               'Pkt(
                    RECORD:   frame 19 @ 0.434370 secs, 238 bytes on wire, 238 bytes captured
                    ETHERNET: 00:0c:29:54:09:ef -> e4:ce:8f:58:9f:f4, type: 0x800(IPv4)
                    IP:       192.168.0.17 -> 192.168.0.62, protocol: 6(TCP), len: 224
                    TCP:      src port 708 -> dst port 2049, seq: 3294170673, ack: 3395734137, len: 172, flags: ACK,PSH
                    RPC:      CALL(0), program: 100003, version: 4, procedure: 1, xid: 0x1437d3d5
                    NFS:      COMPOUND4args(tag='', minorversion=1, argarray=[nfs_argop4(argop=OP_SEQUENCE, ...), ...])
                )'
        """
        rdebug = self.debug_repr()
        if rdebug > 0:
            out = "Pkt(\n" if rdebug == 2 else ''
            index = 0
            if rdebug == 1:
                layer_list = [x for x in self._layers if x not in _PKT_nlayers]
            else:
                layer_list = self._layers
            lastkey = len(layer_list) - 1
            for key in layer_list:
                value = getattr(self, key, None)
                if value is not None:
                    if rdebug == 1 and (index == lastkey or key in _PKT_rlayers or \
                      (not self.ip and not self.ib and key == "ethernet")):
                        out += str(value)
                    elif rdebug == 2:
                        if getattr(value, "_strname", None) is not None:
                            # Use object's name as layer name
                            name = value._strname
                        else:
                            name = key.upper()
                        sps = " " * (_maxlen - len(name))
                        out += "    %s:%s %s\n" % (name, sps, str(value))
                        if index == lastkey and getattr(value, "data", "") and key != "nfs":
                            sps = " " * (_maxlen - 4)
                            out += "    DATA:%s 0x%s\n" % (sps, value.data.hex())
                index += 1
            out += ")\n" if rdebug == 2 else ""
        else:
            out = BaseObj.__str__(self)
        return out

    def __repr__(self):
        """Formal string representation of packet object"""
        rdebug = self.debug_repr()
        if rdebug > 0:
            sindent = self.sindent()
            out = "Pkt(\n"
            # Display layers in the order in which they were added
            for key in self._layers:
                layer = getattr(self, key, None)
                if layer is not None:
                    # Add indentation to every line in the
                    # layer's representation
                    value = repr(layer).replace("\n", "\n"+sindent)
                    out += "%s%s = %s,\n" % (sindent, key, value)
            out += ")\n"
        else:
            out = object.__repr__(self)
        return out

    def add_layer(self, name, layer):
        """Add layer to name and object to the packet"""
        layer._pkt = self
        setattr(self, name, layer)
        self._layers.append(name)

    def get_layers(self):
        """Return the list of layers currently in the packet"""
        # Return a tuple instead of the list so it cannot be modified
        return tuple(self._layers)