44"""
55
66# System imports
7+ from copy import deepcopy
78from enum import IntEnum
89
910# Local source tree imports
1011from pyof .foundation .base import GenericStruct
1112from pyof .foundation .basic_types import (
12- BinaryData , HWAddress , IPAddress , UBInt8 , UBInt16 )
13+ BinaryData , FixedTypeList , HWAddress , IPAddress , UBInt8 , UBInt16 )
1314from pyof .foundation .exceptions import PackException , UnpackException
1415
1516__all__ = ('ARP' , 'Ethernet' , 'EtherType' , 'GenericTLV' , 'IPv4' , 'VLAN' ,
@@ -184,8 +185,8 @@ def pack(self, value=None):
184185 return super ().pack ()
185186
186187 def _validate (self ):
187- """Assure this is a 802.1q VLAN header instance."""
188- if self .tpid .value != EtherType .VLAN :
188+ """Assure this is a valid VLAN header instance."""
189+ if self .tpid .value not in ( EtherType .VLAN , EtherType . VLAN_QINQ ) :
189190 raise UnpackException
190191 return
191192
@@ -220,6 +221,22 @@ def unpack(self, buff, offset=0):
220221 self .vid = None
221222
222223
224+ class ListOfVLAN (FixedTypeList ):
225+ """List of VLAN tags.
226+
227+ Represented by instances of VLAN.
228+ """
229+
230+ def __init__ (self , items = None ):
231+ """Create a ListOfVLAN with the optional parameters below.
232+
233+ Args:
234+ items (:class:`~pyof.foundation.network_types.VLAN`):
235+ Instance or a list of instances.
236+ """
237+ super ().__init__ (pyof_class = VLAN , items = items )
238+
239+
223240class Ethernet (GenericStruct ):
224241 """Ethernet "struct".
225242
@@ -239,11 +256,11 @@ class Ethernet(GenericStruct):
239256
240257 destination = HWAddress ()
241258 source = HWAddress ()
242- vlan = VLAN ()
259+ vlans = ListOfVLAN ()
243260 ether_type = UBInt16 ()
244261 data = BinaryData ()
245262
246- def __init__ (self , destination = None , source = None , vlan = None ,
263+ def __init__ (self , destination = None , source = None , vlans = None ,
247264 ether_type = None , data = b'' ):
248265 """Create an instance and set its attributes.
249266
@@ -260,7 +277,7 @@ def __init__(self, destination=None, source=None, vlan=None,
260277 super ().__init__ ()
261278 self .destination = destination
262279 self .source = source
263- self .vlan = VLAN () if vlan is None else vlan
280+ self .vlans = ListOfVLAN () if vlans is None else vlans
264281 self .ether_type = ether_type
265282 self .data = data
266283
@@ -273,6 +290,19 @@ def get_hash(self):
273290 """
274291 return hash (self .pack ())
275292
293+ @staticmethod
294+ def _get_vlan_length (buff ):
295+ """Return the total length of VLAN tags in a given Ethernet buffer."""
296+ length = 0
297+ begin = 12
298+
299+ while (buff [begin :begin + 2 ] in (EtherType .VLAN .to_bytes (2 , 'big' ),
300+ EtherType .VLAN_QINQ .to_bytes (2 , 'big' ))):
301+ length += 4
302+ begin += 4
303+
304+ return length
305+
276306 def unpack (self , buff , offset = 0 ):
277307 """Unpack a binary message into this object's attributes.
278308
@@ -290,14 +320,18 @@ def unpack(self, buff, offset=0):
290320 UnpackException: If there is a struct unpacking error.
291321
292322 """
293- # Check if the EtherType bytes are actually equal to EtherType.VLAN,
294- # indicating that the packet is tagged. If it is not, we insert the
295- # equivalent to 'NULL VLAN data' (\x00\x00\x00\x00) to enable the
296- # correct unpacking process.
297- if buff [12 :14 ] != EtherType .VLAN .to_bytes (2 , 'big' ):
298- buff = buff [0 :12 ] + b'\x00 \x00 \x00 \x00 ' + buff [12 :]
299-
300- super ().unpack (buff , offset )
323+ begin = offset
324+
325+ vlan_length = self ._get_vlan_length (buff )
326+
327+ for attribute_name , class_attribute in self .get_class_attributes ():
328+ attribute = deepcopy (class_attribute )
329+ if attribute_name == 'vlans' :
330+ attribute .unpack (buff [begin :begin + vlan_length ])
331+ else :
332+ attribute .unpack (buff , begin )
333+ setattr (self , attribute_name , attribute )
334+ begin += attribute .get_size ()
301335
302336
303337class GenericTLV (GenericStruct ):
0 commit comments