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

Commit b27ccf6

Browse files
authored
Add IPv6 packet structure (#608)
1 parent e6fd3f7 commit b27ccf6

File tree

2 files changed

+131
-2
lines changed

2 files changed

+131
-2
lines changed

pyof/foundation/network_types.py

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
# Local source tree imports
1111
from pyof.foundation.base import GenericStruct
1212
from pyof.foundation.basic_types import (
13-
BinaryData, FixedTypeList, HWAddress, IPAddress, UBInt8, UBInt16)
13+
BinaryData, FixedTypeList, HWAddress, IPAddress, IPv6Address, UBInt8,
14+
UBInt16, UBInt32)
1415
from pyof.foundation.exceptions import PackException, UnpackException
1516

1617
__all__ = ('ARP', 'Ethernet', 'EtherType', 'GenericTLV', 'IPv4', 'VLAN',
@@ -611,6 +612,105 @@ def unpack(self, buff, offset=0):
611612
self.options = b''
612613

613614

615+
class IPv6(GenericStruct):
616+
"""IPv6 packet "struct".
617+
618+
Contains all fields of an IP version 6 packet header, plus the upper layer
619+
content as binary data.
620+
Some of the fields were merged together because of their size being
621+
inferior to 8 bits. They are represented as a single class attribute, but
622+
pack/unpack methods will take into account the values in individual
623+
instance attributes.
624+
"""
625+
626+
#: _version_tclass_flabel (:class:`UBInt32`): IP protocol version +
627+
#: Traffic Class + Flow Label
628+
_version_tclass_flabel = UBInt32()
629+
#: length (:class:`UBInt16`): Payload length (bytes)
630+
length = UBInt16()
631+
#: next_header (:class:`UBInt8`): Next header
632+
next_header = UBInt8()
633+
#: hop_limit (:class:`UBInt8`): Hop limit
634+
hop_limit = UBInt8()
635+
#: source (:class:`IPv6Address`): Source IPv6 address
636+
source = IPv6Address()
637+
#: destination (:class:`IPv6Address`): Destination IPv6 address
638+
destination = IPv6Address()
639+
#: data (:class:`BinaryData`): Packet data
640+
data = BinaryData()
641+
642+
def __init__(self, version=6, tclass=0, flabel=0, length=0,
643+
next_header=0, hop_limit=255, source="0:0:0:0:0:0:0:0",
644+
destination="0:0:0:0:0:0:0:0", data=b''):
645+
"""Create an IPv6 with the parameters below.
646+
Args:
647+
version (int): IP protocol version. Defaults to 6.
648+
tclass (int): DS (6 bits) + ECN (2 bits). Default is 0.
649+
flabel (int): Flow label. Defaults to 0.
650+
length (int): Payload length in bytes. Defaults to 0.
651+
next_header (int): Type of next header or protocol field.
652+
Defaults to 0.
653+
hop_limit (int): Packet hop limit. Defaults to 255.
654+
source (str): Source IPv6 address.
655+
Defaults to "0:0:0:0:0:0:0:0".
656+
destination (str): Destination IPv6 address.
657+
Defaults to "0:0:0:0:0:0:0:0".
658+
data (bytes): Packet data. Defaults to empty bytes.
659+
"""
660+
super().__init__()
661+
self.version = version
662+
self.tclass = tclass
663+
self.flabel = flabel
664+
self.length = length
665+
self.next_header = next_header
666+
self.hop_limit = hop_limit
667+
self.source = IPv6Address(source)
668+
self.destination = IPv6Address(destination)
669+
self.data = data
670+
671+
def pack(self, value=None):
672+
"""Pack the struct in a binary representation.
673+
674+
Merge some fields to ensure correct packing.
675+
676+
Returns:
677+
bytes: Binary representation of this instance.
678+
679+
"""
680+
# Set the correct packet length based on header length and data
681+
self.length = len(self.data)
682+
683+
_version_tclass = (self.version << 28) | (self.tclass << 20)
684+
self._version_tclass_flabel = _version_tclass | self.flabel
685+
686+
return super().pack()
687+
688+
def unpack(self, buff, offset=0):
689+
"""Unpack a binary struct into this object's attributes.
690+
691+
Return the values instead of the lib's basic types.
692+
693+
Args:
694+
buff (bytes): Binary buffer.
695+
offset (int): Where to begin unpacking.
696+
697+
Raises:
698+
:exc:`~.exceptions.UnpackException`: If unpack fails.
699+
700+
"""
701+
super().unpack(buff, offset)
702+
703+
self.version = self._version_tclass_flabel.value >> 28
704+
self.tclass = (self._version_tclass_flabel.value >> 20) & 255
705+
self.flabel = self._version_tclass_flabel.value & 1048575
706+
self.length = self.length.value
707+
self.next_header = self.next_header.value
708+
self.hop_limit = self.hop_limit.value
709+
self.source = self.source.value
710+
self.destination = self.destination.value
711+
self.data = self.data.value
712+
713+
614714
class TLVWithSubType(GenericTLV):
615715
"""Modify the :class:`GenericTLV` to a Organization Specific TLV structure.
616716

tests/unit/test_foundation/test_network_types.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
from pyof.foundation.basic_types import BinaryData
55
from pyof.foundation.exceptions import UnpackException
6-
from pyof.foundation.network_types import ARP, VLAN, Ethernet, GenericTLV, IPv4
6+
from pyof.foundation.network_types import (
7+
ARP, VLAN, Ethernet, GenericTLV, IPv4, IPv6)
78

89

910
class TestARP(unittest.TestCase):
@@ -170,3 +171,31 @@ def test_IPv4_checksum(self):
170171
data=b'testdata')
171172
packet.pack()
172173
self.assertEqual(packet.checksum, 709)
174+
175+
176+
class TestIPv6(unittest.TestCase):
177+
"""Test IPv6 packets."""
178+
179+
def test_IPv6_pack(self):
180+
"""Test pack/unpack of IPv6 class."""
181+
packet = IPv6(next_header=6, hop_limit=64, source="::1",
182+
destination="::2", data=b'testdata')
183+
packed = packet.pack()
184+
expected = b'`\x00\x00\x00\x00\x08\x06@\x00\x00'
185+
expected += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
186+
expected += b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
187+
expected += b'\x00\x00\x00\x02testdata'
188+
self.assertEqual(packed, expected)
189+
190+
def test_IPv6_unpack(self):
191+
"""Test unpack of IPv6 binary packet."""
192+
raw = b'`\x00\x00\x00\x00\x0c\x06@\x00\x00'
193+
raw += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
194+
raw += b'\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
195+
raw += b'\x00\x00\x00\x01somemoredata'
196+
expected = IPv6(next_header=6, hop_limit=64, source="::2",
197+
destination="::1", data=b'somemoredata')
198+
expected.pack()
199+
unpacked = IPv6()
200+
unpacked.unpack(raw)
201+
self.assertEqual(unpacked, expected)

0 commit comments

Comments
 (0)