kbandla / dpkt

fast, simple packet creation / parsing, with definitions for the basic TCP/IP protocols
Other
1.08k stars 270 forks source link

Incorrect Unpacking of Ethernet Frames with VLAN ETH_TYPE_8021Q Tag #658

Open perjah20 opened 7 months ago

perjah20 commented 7 months ago

It is my first time creating an issue on Github so if I've done anything incorrectly I apologize in advance.

Describe the bug I have encountered an issue with the dpkt package related to handling VLAN tagged frames in Ethernet packets. Specifically, the issue arises when packing and then unpacking an Ethernet frame with VLAN ETH_TYPE_8021Q tags. The unpacked frame does not maintain the same structure and data as it had before being packed, indicating a potential bug in the handling of VLAN tags during these processes.

To Reproduce I've written 3 test cases in python that can be used to look and see where the discrepancies are. The first two sets the "type" of the ethernet frame to ETH_TYPE_8021Q . The third test sets the "type" to ETH_TYPE_IP. This result in the unpacking I would like to see in the first two experiments.

Expected behavior The byte string should be the same, and it should not create two VLAN tags.

Screenshots Here is a screenshot of the output in the console. I've highlighted the inconsistencies in yellow.
First and second experiment: image Third experiment: image

Details(please complete the following information):

Code to reproduce `

import unittest
import dpkt

class EthernetExperiment(unittest.TestCase):
    def test_experiment_1(self):
        # Create an Ethernet object
        eth = dpkt.ethernet.Ethernet(
        src = b'\x01\x02\x03\x04\x05\x06',
        dst = b'\x01\x02\x03\x04\x05\x06',
        type = dpkt.ethernet.ETH_TYPE_8021Q,
    )

    # Create a VLAN tagged frame (802.1Q)
    vlan = dpkt.ethernet.VLANtag8021Q()
    vlan.pri = 7  # Priority
    vlan.id = 10  # VLAN ID
    vlan.type = dpkt.ethernet.ETH_TYPE_8021Q  # Encapsulated protocol type

    data = b'\x01\x02\x03\x04\x05\x06'
    eth.vlan_tags = [vlan]
    eth.data = data

    print(f"Ethernet frame (before packing):\n"
          f"Bytes: {str(eth)}\n"
          f"Object: {repr(eth)}\n")

    # Get the packed Ethernet frame bytes
    eth_bytes = eth.pack()
    print(f"Ethernet frame (after packing):\n {eth_bytes}\n")

    # Unpack the Ethernet frame
    eth_unpacked = dpkt.ethernet.Ethernet(eth_bytes)
    print(f"Ethernet frame (after unpacking):\n"
          f"Bytes: {str(eth_unpacked)}\n"
          f"Object: {repr(eth_unpacked)}\n")

    self.assertEqual(str(eth_unpacked), str(eth))

def test_experiment_2(self):
    # Create an Ethernet object
    eth = dpkt.ethernet.Ethernet(
        src = b'\x01\x02\x03\x04\x05\x06',
        dst = b'\x01\x02\x03\x04\x05\x06',
        type = dpkt.ethernet.ETH_TYPE_8021Q,
    )

    # Create a VLAN tagged frame (802.1Q)
    vlan = dpkt.ethernet.VLANtag8021Q()
    vlan.pri = 7
    vlan.id = 10
    vlan.type = dpkt.ethernet.ETH_TYPE_8021Q

    data = b'\x01\x02\x03\x04\x05\x06'
    eth.data = vlan.pack() + data

    print(f"Ethernet frame (before packing):\n"
          f"Bytes: {str(eth)}\n"
          f"Object: {repr(eth)}\n")

    # Get the packed Ethernet frame bytes
    eth_bytes = eth.pack()
    print(f"Ethernet frame (after packing):\n {eth_bytes}\n")

    # Unpack the Ethernet frame
    eth_unpacked = dpkt.ethernet.Ethernet(eth_bytes)
    print(f"Ethernet frame (after unpacking):\n"
          f"Bytes: {str(eth_unpacked)}\n"
          f"Object: {repr(eth_unpacked)}\n")

    self.assertEqual(str(eth_unpacked), str(eth))

def test_experiment_3(self):
    # Create an Ethernet object
    eth = dpkt.ethernet.Ethernet(
        src=b'\x01\x02\x03\x04\x05\x06',
        dst=b'\x01\x02\x03\x04\x05\x06',
        type=dpkt.ethernet.ETH_TYPE_IP,
    )

    # Create a VLAN tagged frame (802.1Q)
    vlan = dpkt.ethernet.VLANtag8021Q()
    vlan.pri = 7
    vlan.id = 10
    vlan.type = dpkt.ethernet.ETH_TYPE_8021Q

    data = b'\x01\x02\x03\x04\x05\x06'
    eth.vlan_tags = [vlan]
    eth.data = data

    print(f"Ethernet frame (before packing):\n"
          f"Bytes: {str(eth)}\n"
          f"Object: {repr(eth)}\n")

    # Get the packed Ethernet frame bytes
    eth_bytes = eth.pack()
    print(f"Ethernet frame (after packing):\n {eth_bytes}\n")

    # Unpack the Ethernet frame
    eth_unpacked = dpkt.ethernet.Ethernet(eth_bytes)
    print(f"Ethernet frame (after unpacking):\n"
          f"Bytes: {str(eth_unpacked)}\n"
          f"Object: {repr(eth_unpacked)}\n")

    self.assertEqual(str(eth_unpacked), str(eth))

`