Merge pull request #1081 from gpotter2/fix-1079

Do not use hardcoded value in _get_metrics
diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py
index ded401e..abb1ea7 100755
--- a/scapy/arch/windows/__init__.py
+++ b/scapy/arch/windows/__init__.py
@@ -521,7 +521,8 @@
         Only available with Npcap."""
         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
         self._check_npcap_requirement()
-        return sp.Popen([_WlanHelper, self.guid[1:-1], "channel"], stdout=sp.PIPE).communicate()[0].strip().strip()
+        return sp.Popen([_WlanHelper, self.guid[1:-1], "channel"],
+                        stdout=sp.PIPE).communicate()[0].strip()
 
     def setchannel(self, channel):
         """Set the channel of the interface (1-14):
diff --git a/scapy/base_classes.py b/scapy/base_classes.py
index a269796..85c47f5 100644
--- a/scapy/base_classes.py
+++ b/scapy/base_classes.py
@@ -166,7 +166,7 @@
                         resolved_fld.append(f2)
                 else:
                     resolved_fld.append(f)
-        else: # look for a field_desc in parent classes
+        else: # look for a fields_desc in parent classes
             resolved_fld = None
             for b in bases:
                 if hasattr(b,"fields_desc"):
diff --git a/scapy/data.py b/scapy/data.py
index ff06438..71dda32 100644
--- a/scapy/data.py
+++ b/scapy/data.py
@@ -87,6 +87,7 @@
 DLT_IEEE802_11_RADIO = 127
 DLT_LINUX_IRDA  = 144
 DLT_PPI   = 192
+DLT_CAN_SOCKETCAN = 227
 DLT_IPV4 = 228
 DLT_IPV6 = 229
 
diff --git a/scapy/layers/can.py b/scapy/layers/can.py
new file mode 100644
index 0000000..7dc2d33
--- /dev/null
+++ b/scapy/layers/can.py
@@ -0,0 +1,36 @@
+# This file is part of Scapy
+# See http://www.secdev.org/projects/scapy for more informations
+# Copyright (C) Philippe Biondi <phil@secdev.org>
+# This program is published under a GPLv2 license
+
+
+"""A minimal implementation of the CANopen protocol, based on
+Wireshark dissectors. See https://wiki.wireshark.org/CANopen
+
+"""
+
+
+from scapy.config import conf
+from scapy.data import DLT_CAN_SOCKETCAN
+from scapy.fields import BitField, FieldLenField, FlagsField, StrLenField, \
+    ThreeBytesField, XBitField
+from scapy.packet import Packet
+
+
+class CAN(Packet):
+    """A minimal implementation of the CANopen protocol, based on
+    Wireshark dissectors. See https://wiki.wireshark.org/CANopen
+
+    """
+    fields_desc = [
+        FlagsField("flags", 0, 3, ["extended", "remote_transmission_request",
+                                   "error"]),
+        BitField("unknown", 0, 18),
+        XBitField("identifier", 0, 11),
+        FieldLenField("length", None, length_of="data", fmt="B"),
+        ThreeBytesField("reserved", 0),
+        StrLenField("data", "", length_from=lambda pkt: pkt.length),
+    ]
+
+
+conf.l2types.register(DLT_CAN_SOCKETCAN, CAN)
diff --git a/test/can.uts b/test/can.uts
new file mode 100644
index 0000000..ed55dd9
--- /dev/null
+++ b/test/can.uts
@@ -0,0 +1,55 @@
+% Regression tests for the CAN layer
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+
+############
+############
+
++ Basic operations
+
+= Load module
+
+load_layer("can")
+
+= Build a packet
+
+pkt = CAN(flags="error", identifier=1234, data="test")
+
+= Dissect & parse
+
+pkt = CAN(raw(pkt))
+pkt.flags == "error" and pkt.identifier == 1234 and pkt.length == 4 and pkt.data == b"test"
+
+
+############
+############
+
++ Example PCAP file
+
+= Read PCAP file
+* From https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=CANopen.pca
+
+from io import BytesIO
+pcap_fd = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\xe3\x00\x00\x00\xe2\xf3mT\x93\x8c\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x073\x01\x00\x00\x00\x00\xe2\xf3mT\xae\x8c\x03\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x7f\x00\x00\x81\x00\xe2\xf3mTI\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07B\x01\x00\x00\x00\x00\xe2\xf3mTM\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07c\x01\x00\x00\x00\x00\xe2\xf3mTN\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07!\x01\x00\x00\x00\x00\xf8\xf3mTv\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x08\x10\x00\x00\x00\x00\x00\xf8\xf3mT\x96\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00A\x08\x10\x00\x15\x00\x00\x00\xf8\xf3mT\xd4\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\xf8\xf3mT\x12\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mTC\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00\x00UltraHi\xf8\xf3mTx\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mT\xce\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00p\x00\x00\x00\x00\x00\x00\x00\xf8\xf3mT\xe0\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mT \x9a\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mTo\x9a\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x083\xf4mTw\xbe\t\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x08\x10*\x00\x00\x00\x003\xf4mT4\xc0\t\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x08\x10*\x11\x00\t\x06i\xf4mT\xb0\x88\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe5\x08\x7f\x00\x00L\x00\x00\x00\x00\x00\x00\x00i\xf4mT+\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mT-\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mTS\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mT\x99\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x8e\xf4mT\x86\xc4\x04\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01B\x92\xf4mT\xae\xf0\x07\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\xba\xf4mT%\xaa\x0b\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02c\xe8\xf4mT\xbc\x0f\x06\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00#\x00b\x01asdf\xe8\xf4mT\x07\x10\x06\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00b\x01\x00\x00\x02\x06\x0f\xf5mT\x1c\x81\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x00b\x01\x00\x00\x00\x00\x0f\xf5mT\xfe\x81\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00b\x01\x00\x00\x02\x068\xf5mT\x19\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00\xa0\x08\x10\x00\x10\x00\x00\x008\xf5mTg\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00\xc2\x08\x10\x00\x15\x00\x00\x008\xf5mT\xd8\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x088\xf5mT\x17\xc4\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00\xa3\x00\x00\x00\x00\x00\x00\x008\xf5mT\xca\xc4\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08')
+packets = rdpcap(pcap_fd)
+
+= Check if parsing worked: each packet has a CAN layer
+
+all(CAN in pkt for pkt in packets)
+
+= Check if parsing worked: no packet has a Raw or Padding layer
+
+not any(Raw in pkt or Padding in pkt for pkt in packets)
+
+= Identifiers
+
+set(pkt.identifier for pkt in packets) == {0, 1474, 1602, 1825, 1843, 1858, 1891, 2020, 2021}
+
+= Flags
+
+set(pkt.flags for pkt in packets) == {0}
+
+= Data length
+
+set(pkt.length for pkt in packets) == {1, 2, 8}