Repository: secdev/scapy

Issue:
Linux: (ethernet) padding no longer automatically added
Commit [9c7077622dd917457ced680a23b7f175769471d9](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9c7077622dd917457ced680a23b7f175769471d9) in the Linux kernel introduces a check to reject too short packets.

Scapy currently hopes that the underlying driver will add a payload if needed, but that is no longer the case, at least under Linux, so we might have to add the payload if needed.

This bug prevents Scapy from working correctly at least under ArchLinux with an Ethernet connexion.

On most Ethernet drivers, the value for `net->hard_header_len` seem to be 14, which only prevent Scapy from sending frames smaller than the Ethernet header (which is an uncommon use case), so the bug is hard to trigger.

On some drivers however, the value for `net->hard_header_len` can be (much) higher.

For example, on RNDIS-based links (when sharing a smartphone's Internet connection via USB), this value is [set to 58](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/net/usb/rndis_host.c#n344) (14 + 11 \* 4) bytes. In such cases, the bug will be triggered when trying to send an ARP request (`len(Ether()/ARP()) == 42 < 58`) (this prevents Scapy from updating its ARP cache and hence from sending layer 3 packets) or when trying to send a simple ICMP packet (`len(Ether()/IP()/ICMP()) == 42 < 58`), making Scapy effectively unusable.



Discussion:

Resolving patch:
diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py
--- a/scapy/arch/linux.py
+++ b/scapy/arch/linux.py
@@ -13,6 +13,7 @@
 from fcntl import ioctl
 import scapy.utils
 import scapy.utils6
+from scapy.packet import Packet, Padding
 from scapy.config import conf
 from scapy.data import *
 from scapy.supersocket import SuperSocket
@@ -394,18 +395,18 @@ def send(self, x):
             sdto = (iff, conf.l3types[type(x)])
         if sn[3] in conf.l2types:
             ll = lambda x:conf.l2types[sn[3]]()/x
+        sx = str(ll(x))
+        x.sent_time = time.time()
         try:
-            sx = str(ll(x))
-            x.sent_time = time.time()
             self.outs.sendto(sx, sdto)
-        except socket.error,msg:
-            x.sent_time = time.time()  # bad approximation
-            if conf.auto_fragment and msg[0] == 90:
+        except socket.error, msg:
+            if msg[0] == 22 and len(sx) < conf.min_pkt_size:
+                self.outs.send(sx + "\x00" * (conf.min_pkt_size - len(sx)))
+            elif conf.auto_fragment and msg[0] == 90:
                 for p in x.fragment():
                     self.outs.sendto(str(ll(p)), sdto)
             else:
                 raise
-                    
 
 
 
@@ -460,6 +461,17 @@ def recv(self, x=MTU):
             q = conf.raw_layer(pkt)
         q.time = get_last_packet_timestamp(self.ins)
         return q
+    def send(self, x):
+        try:
+            return SuperSocket.send(self, x)
+        except socket.error, msg:
+            if msg[0] == 22 and len(x) < conf.min_pkt_size:
+                padding = "\x00" * (conf.min_pkt_size - len(x))
+                if isinstance(x, Packet):
+                    return SuperSocket.send(self, x / Padding(load=padding))
+                else:
+                    return SuperSocket.send(self, str(x) + padding)
+            raise
 
 
 class L2ListenSocket(SuperSocket):
diff --git a/scapy/config.py b/scapy/config.py
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -345,6 +345,7 @@ class Conf(ConfClass):
     L3socket = None
     L2socket = None
     L2listen = None
+    min_pkt_size = 60
     histfile = os.path.join(os.path.expanduser("~"), ".scapy_history")
     padding = 1
     except_filter = ""


Test patch: