Skip to content
This repository was archived by the owner on Apr 22, 2024. It is now read-only.

Commit 623310f

Browse files
committed
Improve Ethernet class to accept a list of VLANs
Change single VLAN to list of VLAN headers, with c-TAG and s-TAG support. Fix #516
1 parent 928ac17 commit 623310f

1 file changed

Lines changed: 48 additions & 14 deletions

File tree

pyof/foundation/network_types.py

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
"""
55

66
# System imports
7+
from copy import deepcopy
78
from enum import IntEnum
89

910
# Local source tree imports
1011
from pyof.foundation.base import GenericStruct
1112
from pyof.foundation.basic_types import (
12-
BinaryData, HWAddress, IPAddress, UBInt8, UBInt16)
13+
BinaryData, FixedTypeList, HWAddress, IPAddress, UBInt8, UBInt16)
1314
from 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+
223240
class 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

303337
class GenericTLV(GenericStruct):

0 commit comments

Comments
 (0)