Repository: secdev/scapy

Issue:
[Enhancement] Add support for DLT_LINUX_SLL2
### Brief description

I have a capture file that uses [Linux "cooked" v2 encapsulation format](https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL2.html) which scapy cannot read currently. It would be nice to have support since the format is becoming more popular.


### Scapy version

2.4.5

### Python version

3.8

### Operating system

Linux 5.13.0

### Additional environment information

_No response_

### How to reproduce

Trying to read it gives following error 

```
$ cap = sniff(offline='/home/tsaarni/new.pcap')
WARNING: PcapReader: unknown LL type [276]/[0x114]. Using Raw packets
```


### Actual result

_No response_

### Expected result

_No response_

### Related resources

Currently tcpdump uses LINUX_SLL2 by default when [capturing "any" device](https://github.com/the-tcpdump-group/tcpdump/pull/850).
There is PR https://github.com/secdev/scapy/pull/3479 which intended to add support for the encapsulation, but it has not been updated for a while.


Discussion:

Resolving patch:
diff --git a/scapy/data.py b/scapy/data.py
--- a/scapy/data.py
+++ b/scapy/data.py
@@ -132,6 +132,7 @@
 DLT_BLUETOOTH_LE_LL_WITH_PHDR = 256
 DLT_VSOCK = 271
 DLT_ETHERNET_MPACKET = 274
+DLT_LINUX_SLL2 = 276
 
 # From net/ipv6.h on Linux (+ Additions)
 IPV6_ADDR_UNICAST = 0x01
diff --git a/scapy/layers/can.py b/scapy/layers/can.py
--- a/scapy/layers/can.py
+++ b/scapy/layers/can.py
@@ -127,7 +127,7 @@ def post_build(self, pkt, pay):
 
         This is based on a copy of the Packet.self_build default method.
         The goal is to affect only the CAN layer data and keep
-        under layers (e.g LinuxCooked) unchanged
+        under layers (e.g CookedLinux) unchanged
         """
         if conf.contribs['CAN']['swap-bytes']:
             data = CAN.inv_endianness(pkt)  # type: bytes
diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
--- a/scapy/layers/l2.py
+++ b/scapy/layers/l2.py
@@ -21,13 +21,14 @@
 from scapy.config import conf
 from scapy import consts
 from scapy.data import ARPHDR_ETHER, ARPHDR_LOOPBACK, ARPHDR_METRICOM, \
-    DLT_ETHERNET_MPACKET, DLT_LINUX_IRDA, DLT_LINUX_SLL, DLT_LOOP, \
-    DLT_NULL, ETHER_ANY, ETHER_BROADCAST, ETHER_TYPES, ETH_P_ARP, \
+    DLT_ETHERNET_MPACKET, DLT_LINUX_IRDA, DLT_LINUX_SLL, DLT_LINUX_SLL2, \
+    DLT_LOOP, DLT_NULL, ETHER_ANY, ETHER_BROADCAST, ETHER_TYPES, ETH_P_ARP, \
     ETH_P_MACSEC
 from scapy.error import warning, ScapyNoDstMacException, log_runtime
 from scapy.fields import (
     BCDFloatField,
     BitField,
+    ByteEnumField,
     ByteField,
     ConditionalField,
     FCSField,
@@ -291,21 +292,38 @@ def l2_register_l3(l2, l3):
 conf.neighbor.register_l3(Dot3, LLC, l2_register_l3)
 
 
+COOKED_LINUX_PACKET_TYPES = {
+    0: "unicast",
+    1: "broadcast",
+    2: "multicast",
+    3: "unicast-to-another-host",
+    4: "sent-by-us"
+}
+
+
 class CookedLinux(Packet):
     # Documentation: http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
     name = "cooked linux"
     # from wireshark's database
-    fields_desc = [ShortEnumField("pkttype", 0, {0: "unicast",
-                                                 1: "broadcast",
-                                                 2: "multicast",
-                                                 3: "unicast-to-another-host",
-                                                 4: "sent-by-us"}),
+    fields_desc = [ShortEnumField("pkttype", 0, COOKED_LINUX_PACKET_TYPES),
                    XShortField("lladdrtype", 512),
                    ShortField("lladdrlen", 0),
                    StrFixedLenField("src", b"", 8),
                    XShortEnumField("proto", 0x800, ETHER_TYPES)]
 
 
+class CookedLinuxV2(CookedLinux):
+    # Documentation: https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL2.html
+    name = "cooked linux v2"
+    fields_desc = [XShortEnumField("proto", 0x800, ETHER_TYPES),
+                   ShortField("reserved", 0),
+                   IntField("ifindex", 0),
+                   XShortField("lladdrtype", 512),
+                   ByteEnumField("pkttype", 0, COOKED_LINUX_PACKET_TYPES),
+                   ByteField("lladdrlen", 0),
+                   StrFixedLenField("src", b"", 8)]
+
+
 class MPacketPreamble(Packet):
     # IEEE 802.3br Figure 99-3
     name = "MPacket Preamble"
@@ -674,6 +692,7 @@ class Dot1AD(Dot1Q):
 conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether)
 conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3)
 conf.l2types.register(DLT_LINUX_SLL, CookedLinux)
+conf.l2types.register(DLT_LINUX_SLL2, CookedLinuxV2)
 conf.l2types.register(DLT_ETHERNET_MPACKET, MPacketPreamble)
 conf.l2types.register_num2layer(DLT_LINUX_IRDA, CookedLinux)
 conf.l2types.register(DLT_LOOP, Loopback)


Test patch:
diff --git a/test/scapy/layers/l2.uts b/test/scapy/layers/l2.uts
--- a/test/scapy/layers/l2.uts
+++ b/test/scapy/layers/l2.uts
@@ -83,3 +83,27 @@ assert isinstance(p.payload, NoPayload)
 
 p = ARP(pdst='192.168.178.0/24')
 assert "Net" in repr(p)
+
+
+############
+############
++ CookedLinux
+
+= CookedLinux - Basic Dissection
+
+cl = CookedLinux(b'\x00\x00\x03\x04\x00\x06\x00\x00\x00\x00\x00\x00\x6f\x50\x08\x00')
+assert cl.pkttype == 0 # unicast
+assert cl.lladdrtype == 772 # loopback
+assert cl.lladdrlen == 6
+assert cl.src == b'\x00\x00\x00\x00\x00\x00\x6f\x50'
+assert cl.proto == 2048
+
+= CookedLinuxV2 - Basic Dissection
+
+clv2 = CookedLinuxV2(b'\x08\x00\x00\x00\x00\x00\x00\x03\x00\x01\x00\x06\xaa\x1f\x9c\xc0\x5a\x7e\x00\x00')
+assert clv2.proto == 2048
+assert clv2.ifindex == 3
+assert clv2.lladdrtype == 1 # ether
+assert clv2.pkttype == 0 # unicast
+assert clv2.lladdrlen == 6
+assert clv2.src == b'\xaa\x1f\x9c\xc0\x5a\x7e\x00\000'