root/umpa/branches/protocols/umit/umpa/protocols/Ethernet.py @ 5791

Revision 5791, 8.4 kB (checked in by kosma, 3 years ago)

Ethernet.py: comment on EtherType? value when no payload is present

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (C) 2008-2009 Adriano Monteiro Marques.
5#
6# Author: Bartosz SKOWRON <getxsick at gmail dot com>
7#
8# This library is free software; you can redistribute it and/or modify
9# it under the terms of the GNU Lesser General Public License as published
10# by the Free Software Foundation; either version 2.1 of the License, or
11# (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful, but
14# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16# License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public License
19# along with this library; if not, write to the Free Software Foundation,
20# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
22"""
23Ethernet protocol implementation.
24"""
25
26import struct
27
28from umit.umpa.protocols import _protocols
29from umit.umpa.protocols import _fields
30from umit.umpa.protocols import _consts
31
32__all__ = [ "Ethernet", ]
33
34class _HIEEE8021Q(_fields.Field):
35    """
36    Set this field to True to tag the Ethernet frame with an 802.1Q header
37    containing VLAN ID and priority information.
38    """
39
40    # Note: this is a virtual field: it doesn't appear in the final frame
41    #       but controls the inclusion of other fields; see _HDynamicIntField
42    #       class and _pre_raw function below for details.
43
44    bits = 0
45    auto = True
46
47    def _is_valid(self, value):
48        """
49        Always valid, since it's just a boolean value.
50        """
51
52        return True
53
54    def _generate_value(self):
55        """
56        Doesn't matter, since it's a virtual field.
57        """
58
59        return 0
60
61    def _raw_value(self):
62        """
63        Return zero to avoid clobbering the existing data.
64        """
65
66        return 0
67
68class _HDynamicIntField(_fields.Field):
69    """
70    Abstract superclass for int fields that support enabling and disabling
71    the field.
72    """
73   
74    auto = True
75
76    def _is_valid(self, value):
77        """
78        Validate if the value is not bigger than expected. Differs from the
79        IntField implementation in that it uses self._real_bits instead of
80        _self.bits (since the latter is changed on the fly via the _set_active
81        function).
82
83        @param value: the new value.
84
85        @rtype: C{bool}
86        @return: result of the validation.
87        """
88
89        if 2**self._real_bits > value >= 0:
90            return True
91        else:
92            return False
93
94    def _set_active(self, active):
95        """
96        Enable/disable the field by changing the `bits' member variable.
97        """
98
99        if active:
100            self.bits = self._real_bits
101        else:
102            self.bits = 0
103
104    def _raw_value(self):
105        """
106        Return the raw value; similar to IntField.
107       
108        Important: Returns zero when the field is disabled to avoid clobbering
109        the existing data.
110
111        @rtype: C{number}
112        @return: raw value of the field.
113        """
114
115        if self.bits > 0:
116            return self._value
117        else:
118            return 0
119
120class _HTPID(_HDynamicIntField):
121    """
122    The TPID field contains the 802.1Q EtherType (normally 0x8100) indicating
123    that the frame is tagged.
124    """
125
126    _real_bits = 16
127
128    def _generate_value(self):
129        """
130        Returns the default 802.1Q EtherType.
131        """
132
133        return 0x8100
134
135class _HPCP(_HDynamicIntField):
136    """
137    The PCP field contains frame priority. Its exact meaning is not defined
138    and varies with implementation.
139    """
140
141    _real_bits = 3
142
143    def _generate_value(self):
144        """
145        Returns the default PCP (zero).
146        """
147
148        return 0
149
150class _HCFI(_HDynamicIntField):
151    """
152    The CFI field is defined for compatibility with Token Ring networks and is
153    set to zero for Ethernet switches.
154    """
155
156    _real_bits = 1
157
158    def _generate_value(self):
159        """
160        Returns the default CFI for Ethernet networks.
161        """
162
163        return 0
164
165class _HVID(_HDynamicIntField):
166    """
167    The VID contains the VLAN identificator. VID=0 means the frame is not VLAN
168    tagged and contains only priority information. VID=1 is a default value,
169    often used for management. VID=0xFFF is a reserved value.
170    """
171
172    _real_bits = 12
173
174    def _generate_value(self):
175        """
176        Return the default VID.
177        """
178
179        return 1
180
181class _HType(_fields.SpecialIntField, _fields.EnumField):
182    """
183    The Type field indicates which protocol is encapsulated in upper layer.
184    """
185    bits = 16
186    auto = True
187    enumerable = {
188        "ARP"   : _consts.ETHERTYPE_ARP,
189        "RARP"  : _consts.ETHERTYPE_RARP,
190        "IP"    : _consts.ETHERTYPE_IP,
191        "IPv6"  : _consts.ETHERTYPE_IPV6,
192        "PPPoE" : _consts.ETHERTYPE_PPPOE,
193        }
194
195    def _generate_value(self):
196        """
197        Generate value for undefined field yet.
198
199        @return: auto-generated value of the field.
200        """
201        return self._tmp_value
202
203class Ethernet(_protocols.Protocol):
204    """
205    Ethernet protocol implementation.
206
207    The main protocol in second layer of OSI model.
208    This is Ethernet Version 2 (so-called DIX frame).
209    @note: This implemenation handles with MAC header in fact.
210    CRC checksum field is untouched.
211    """
212
213    layer = 2       # layer of OSI
214    protocol_id = _consts.DLT_EN10MB
215    payload_fieldname = '_type'
216    name = "Ethernet"
217
218    _ordered_fields = ('dst', 'src',
219                       'vlan', 'tpid', 'pcp', 'cfi', 'vid',
220                       '_type')
221
222    def __init__(self, **kwargs):
223        fields_list = [ _fields.MACAddrField('Destination',
224                                                        '00:00:00:00:00:00'),
225                        _fields.MACAddrField('Source', '00:00:00:00:00:00'),
226                        _HIEEE8021Q('vlan', False),
227                        _HTPID('tpid', 0x8100),
228                        _HPCP('pcp', 0),
229                        _HCFI('cfi', 0),
230                        _HVID('vid', 1),
231                        _HType('Type') ]
232
233        super(Ethernet, self).__init__(fields_list, **kwargs)
234
235    def _pre_raw(self, raw_value, bit, protocol_container, protocol_bits):
236        """
237        Handle with fields before calling fillout() for them.
238
239        Set Padding field, calculate header and total length,
240        set protocol of the upper layer.
241
242        @type raw_value: C{int}
243        @param raw_value: currently raw value for the packet.
244
245        @type bit: C{int}
246        @param bit: currently length of the protocol.
247
248        @type protocol_container: C{tuple}
249        @param protocol_container: tuple of protocols included in the packet.
250
251        @type protocol_bits: C{int}
252        @param protocol_bits: currently length of the packet.
253
254        @return: C{raw_value, bit}
255        """
256
257        # Enable/disable the VLAN header, depending on the `vlan' flag.
258        state = self.get_field('vlan').get()
259        for name in ('tpid', 'pcp', 'cfi', 'vid'):
260            self.get_field(name)._set_active(state)
261
262        # Type
263        # field indicates the next level type
264        if self.payload:
265            type_id = self.payload.protocol_id
266        else:
267            type_id = 0 # zero payload length
268        self.get_field('_type')._tmp_value = type_id
269
270        return raw_value, bit
271
272    def _post_raw(self, raw_value, bit, protocol_container, protocol_bits):
273        """
274        Handle with fields after calling fillout() for them.
275
276        Calculate header checksum.
277
278        @type raw_value: C{int}
279        @param raw_value: currently raw value for the packet.
280
281        @type bit: C{int}
282        @param bit: currently length of the protocol.
283
284        @type protocol_container: C{tuple}
285        @param protocol_container: tuple of protocols included in the packet.
286
287        @type protocol_bits: C{int}
288        @param protocol_bits: currently length of the packet.
289
290        @return: C{raw_value, bit}
291        """
292
293        return raw_value, bit
294
295    def load_raw(self, buffer):
296        """
297        Load raw and update a protocol's fields.
298
299        @return: raw payload
300        """
301
302        header_size = 14
303        header_format = '!6B6BH'
304        fields = struct.unpack(header_format, buffer[:header_size])
305
306        self.dst = ':'.join(['%.2x'] * 6) % (fields[0:6])
307        self.src = ':'.join(['%.2x'] * 6) % (fields[6:12])
308        self._type = fields[12]
309
310        return buffer[header_size:]
311
312protocols = [ Ethernet, ]
Note: See TracBrowser for help on using the browser.