Repository: secdev/scapy

Issue:
ICMPExtensionMPLS 
Hey found something kind of cool that I was hoping to try but I can't figure out what's going on with it or how it was intended to be used, the only docs I could find was this generated list of method signatures: 

https://scapy.readthedocs.io/en/latest/_modules/scapy/contrib/icmp_extensions.html#ICMPExtensionHeader

```
In [90]: send(IP(dst="142.250.209.14")/ICMP()/ICMPExtensionHeader(version = 2)/ICMPExtensionMPLS(classnum = 1, classtype = 1))                              
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-90-400b6ecaea8e> in <module>
----> 1 send(IP(dst="142.250.209.14")/ICMP()/ICMPExtensionHeader(version = 2)/ICMPExtensionMPLS(classnum = 1, classtype = 1))

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/sendrecv.py in send(x, iface, *args, **kargs)
    355         x,
    356         lambda iface: iface.l3socket(), iface=iface,
--> 357         *args, **kargs
    358     )
    359 

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/sendrecv.py in _send(x, _func, inter, loop, iface, count, verbose, realtime, return_packets, socket, **kargs)                                                                                                                                   
    328     results = __gen_send(socket, x, inter=inter, loop=loop,
    329                          count=count, verbose=verbose,
--> 330                          realtime=realtime, return_packets=return_packets)
    331     if need_closing:
    332         socket.close()

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/sendrecv.py in __gen_send(s, x, inter, loop, count, verbose, realtime, return_packets, *args, **kargs)                                                                                                                                          
    302                     else:
    303                         dt0 = ct - float(p.time)
--> 304                 s.send(p)
    305                 if return_packets:
    306                     sent_packets.append(p)

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/arch/linux.py in send(self, x)
    569                     type_x, self.LL)
    570             self.LL = type_x
--> 571         sx = raw(ll(x))
    572         x.sent_time = time.time()
    573         try:

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/compat.py in raw(x)
    243         This function is and will always be cross-version compatible
    244         """
--> 245         return bytes(x)
    246 
    247     def bytes_encode(x):

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in __bytes__(self)
    569     def __bytes__(self):
    570         # type: () -> bytes
--> 571         return self.build()
    572 
    573     def __div__(self, other):

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in build(self)
    700         :return: string of the packet with the payload
    701         """
--> 702         p = self.do_build()
    703         p += self.build_padding()
    704         p = self.build_done(p)

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in do_build(self)
    683         for t in self.post_transforms:
    684             pkt = t(pkt)
--> 685         pay = self.do_build_payload()
    686         if self.raw_packet_cache is None:
    687             return self.post_build(pkt, pay)

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in do_build_payload(self)
    669         :return: a string of payload layer
    670         """
--> 671         return self.payload.do_build()
    672 
    673     def do_build(self):

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in do_build(self)
    683         for t in self.post_transforms:
    684             pkt = t(pkt)
--> 685         pay = self.do_build_payload()
    686         if self.raw_packet_cache is None:
    687             return self.post_build(pkt, pay)

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in do_build_payload(self)
    669         :return: a string of payload layer
    670         """
--> 671         return self.payload.do_build()
    672 
    673     def do_build(self):

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in do_build(self)
    683         for t in self.post_transforms:
    684             pkt = t(pkt)
--> 685         pay = self.do_build_payload()
    686         if self.raw_packet_cache is None:
    687             return self.post_build(pkt, pay)

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in do_build_payload(self)
    669         :return: a string of payload layer
    670         """
--> 671         return self.payload.do_build()
    672 
    673     def do_build(self):

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/packet.py in do_build(self)
    685         pay = self.do_build_payload()
    686         if self.raw_packet_cache is None:
--> 687             return self.post_build(pkt, pay)
    688         else:
    689             return pkt + pay

~/.local/lib/python3.6/site-packages/scapy-2.4.4.dev221-py3.6.egg/scapy/contrib/icmp_extensions.py in post_build(self, p, pay)
     56         if self.chksum is None:
     57             ck = checksum(p)
---> 58             p = p[:2] + chr(ck >> 8) + chr(ck & 0xff) + p[4:]
     59         return p + pay
     60 

TypeError: can't concat str to bytes
```

Hit a similar snag with this one too if you care to unwind it and see whats wrong with it (unrelated): 

```
sudo python3 -c 'import emoji as e; import random as r; from scapy.all import *; import itertools; RT = RadioTap(); D11B = Dot11Beacon(); beacon_send = (lambda e, mac, intf: sendp(RT/Dot11(type=0, subtype=8, addr1="ff:ff:ff:ff:ff:ff", addr2=mac, addr3=mac)/D11B/Dot11Elt(ID="SSID", info=e, len=len(e) ), inter=0, iface=intf, loop=0) ); [ sys.stdout.write("{}\n".format(x)) and beacon_send("".join(r.sample(e.UNICODE_EMOJI.keys(), 8) ), x, "wlan0" ) for x in itertools.cycle( [str(x) for x in [RandMAC()] * 20 ] ) ]'
```

I haven't had much luck with scapy to tell you the truth, but it looks cool I like that operator overloading / factory pattern sort of thing. scapy looks really complete but I wonder how much of it actually works now unfortunately. I wish that help() on objects would give a common, terse usage example, that way I'd have a little confidence that somebody actually knew that it  worked the way it was supposed to, as opposed to tracking things down all over the internet. 


Discussion:

Resolving patch:
diff --git a/scapy/contrib/icmp_extensions.py b/scapy/contrib/icmp_extensions.py
--- a/scapy/contrib/icmp_extensions.py
+++ b/scapy/contrib/icmp_extensions.py
@@ -19,6 +19,7 @@
 import struct
 
 import scapy
+from scapy.compat import chb
 from scapy.packet import Packet, bind_layers
 from scapy.fields import BitField, ByteField, ConditionalField, \
     FieldLenField, IPField, IntField, PacketListField, ShortField, \
@@ -55,7 +56,7 @@ class ICMPExtensionHeader(Packet):
     def post_build(self, p, pay):
         if self.chksum is None:
             ck = checksum(p)
-            p = p[:2] + chr(ck >> 8) + chr(ck & 0xff) + p[4:]
+            p = p[:2] + chb(ck >> 8) + chb(ck & 0xff) + p[4:]
         return p + pay
 
     def guess_payload_class(self, payload):


Test patch:
diff --git a/test/contrib/icmp_extensions.uts b/test/contrib/icmp_extensions.uts
new file mode 100644
--- /dev/null
+++ b/test/contrib/icmp_extensions.uts
@@ -0,0 +1,8 @@
++ ICMP Extensions tests
+
+= Basic build
+
+p = IP(src="192.0.2.1", dst="192.0.2.2")/ICMP()/ICMPExtensionHeader(version = 2)/ICMPExtensionMPLS(classnum = 1, classtype = 1)
+print(raw(p))
+b = b'E\x00\x00$\x00\x01\x00\x00@\x01\xf6\xd4\xc0\x00\x02\x01\xc0\x00\x02\x02\x08\x00\xf6\xfa\x00\x00\x00\x00 \x00\xdf\xff\x00\x04\x01\x01'
+assert raw(p) == b