Repository: secdev/scapy

Issue:
Inconsistent type handling when calculating ICMP packet hash
### Brief description



The ICMP class sets the `seq` and `id` fields only if `type` is present in a list of acceptable values:
 
https://github.com/secdev/scapy/blob/master/scapy/layers/inet.py#L879

```py
ConditionalField(XShortField("id", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]),  # noqa: E501
ConditionalField(XShortField("seq", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]),  # noqa: E501
```

When calculating the packet hash, a different, broader set of values determines if the `id` and `seq` fields are referenced:

https://github.com/secdev/scapy/blob/master/scapy/layers/inet.py#L908

```py
    def hashret(self):
        if self.type in [0, 8, 13, 14, 15, 16, 17, 18, 33, 34, 35, 36, 37, 38]:
            return struct.pack("HH", self.id, self.seq) + self.payload.hashret()  # noqa: E501
        return self.payload.hashret()
```

This can result in conversion from `None` to integer, that results in an exception. 



### Scapy version

e28aa57033219de5690f11513b694e854bbab52a

### Python version

Came up with 2.7, should be the same on 3

### Operating system

Linux 5.4.0

### Additional environment information

_No response_

### How to reproduce

```py
ans,unans=sr(IP(dst=target_ip)/ICMP(type=range(0,40),code=range(0,40)),timeout=0.5)
```

### Actual result

An exception is thrown when the hash value of ICMP packets with certain types are requested.

### Expected result

No exception is thrown on hash calculation, the presence of packet fields are determined based on the same rules in all parts of the ICMP class.

### Related resources

_No response_


Discussion:
Nice catch!

Resolving patch:
diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py
--- a/scapy/layers/inet.py
+++ b/scapy/layers/inet.py
@@ -950,13 +950,16 @@ def mysummary(self):
                   5: "need-authorization", }, }
 
 
+icmp_id_seq_types = [0, 8, 13, 14, 15, 16, 17, 18, 37, 38]
+
+
 class ICMP(Packet):
     name = "ICMP"
     fields_desc = [ByteEnumField("type", 8, icmptypes),
                    MultiEnumField("code", 0, icmpcodes, depends_on=lambda pkt:pkt.type, fmt="B"),  # noqa: E501
                    XShortField("chksum", None),
-                   ConditionalField(XShortField("id", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]),  # noqa: E501
-                   ConditionalField(XShortField("seq", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]),  # noqa: E501
+                   ConditionalField(XShortField("id", 0), lambda pkt:pkt.type in icmp_id_seq_types),  # noqa: E501
+                   ConditionalField(XShortField("seq", 0), lambda pkt:pkt.type in icmp_id_seq_types),  # noqa: E501
                    ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13, 14]),  # noqa: E501
                    ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13, 14]),  # noqa: E501
                    ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13, 14]),  # noqa: E501
@@ -985,7 +988,7 @@ def post_build(self, p, pay):
         return p
 
     def hashret(self):
-        if self.type in [0, 8, 13, 14, 15, 16, 17, 18, 33, 34, 35, 36, 37, 38]:
+        if self.type in icmp_id_seq_types:
             return struct.pack("HH", self.id, self.seq) + self.payload.hashret()  # noqa: E501
         return self.payload.hashret()
 


Test patch:
diff --git a/test/scapy/layers/inet.uts b/test/scapy/layers/inet.uts
--- a/test/scapy/layers/inet.uts
+++ b/test/scapy/layers/inet.uts
@@ -519,6 +519,10 @@ assert(len(l) == 6)
 assert([len(raw(p[IP].payload)) for p in l] == [8, 8, 8, 8, 8, 8])
 assert([(p.frag, p.flags.MF) for p in [IP(raw(p)) for p in l]] == [(0, True), (1, True), (2, True), (0, True), (1, True), (2, False)])
 
+= IPv4 - ICMP hashret
+for x in ICMP(type=range(0,40),code=range(0,40)):
+    (IP()/x).hashret()
+
 = IPv4 - traceroute utilities
 ip_ttl = [("192.168.0.%d" % i, i) for i in six.moves.range(1, 10)]