Repository: secdev/scapy

Issue:
conf.checkIPsrc Is not working as intended.
Greetings,

We believe we've found a bug, which still occurs in the latest version and can be seen on the master branch, where conf.checkIPsrc is not working properly. We've been running into issues where running the send receive function was not returning responses on packets sent to the destination. To us it appeared as if Scapy was not ignoring the inner packet source and that's when we found the conf.checkIPsrc option and proceeded to set it to 0 from its default of 1. We continued to get the same issue where we were not getting any responses except for the destination hop. We searched around in the source and we believe we've located a bug where you have src and dst mixed up in the following:

```
--- a/scapy/layers/inet.py
+++ b/scapy/layers/inet.py
@@ -726,8 +726,8 @@
     def answers(self, other):
         if not isinstance(other, IP):
             return 0
-        if not ( ((conf.checkIPsrc == 0) or (self.dst == other.dst)) and
-                 (self.src == other.src) and
+        if not ( ((conf.checkIPsrc == 0) or (self.src == other.src)) and
+                 (self.dst == other.dst) and
                  ( ((conf.checkIPID == 0)
                     or (self.id == other.id)
                     or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and
```

After modifying the above locally within our container we no longer had any issues when passing conf.checkIPsrc=0.

Here's an example of running the sr function before we update the code:
```
>>> res,un=sr(IP(dst="4.2.2.2",ttl=2,flags='DF',id=RandShort())/ICMP(id=os.getpid()), timeout=2)
Begin emission:
.Finished sending 1 packets.
..............................
Received 31 packets, got 0 answers, remaining 1 packets
>>> res.summary()
>>> un.summary()
IP / ICMP 10.128.0.7 > 4.2.2.2 echo-request 0
>>> conf.checkIPsrc=0
>>> res,un=sr(IP(dst="4.2.2.2",ttl=2,flags='DF',id=RandShort())/ICMP(id=os.getpid()), timeout=2)
Begin emission:
Finished sending 1 packets.
..............................
Received 30 packets, got 0 answers, remaining 1 packets
>>> res.summary()
>>> un.summary()
IP / ICMP 10.128.0.7 > 4.2.2.2 echo-request 0
```

And after the fact:
```
>>> conf.checkIPsrc=0
>>> res,un=sr(IP(dst="4.2.2.2",ttl=2,flags='DF',id=RandShort())/ICMP(id=os.getpid()), timeout=2)
Begin emission:
.Finished sending 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
>>> res.summary()
IP / ICMP 10.128.0.7 > 4.2.2.2 echo-request 0 ==> IP / ICMP W.X.Y.Z > 10.128.0.7 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
>>> un.summary()
```

Would it be possible to have someone look into this and possibly update? Here's some additional info about the system I'm on:
```
# cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.5.2
PRETTY_NAME="Alpine Linux v3.5"
HOME_URL="http://alpinelinux.org"
BUG_REPORT_URL="http://bugs.alpinelinux.org"

# python --version
Python 3.5.6
```
My Scapy version is Version 2.4.3rc1.dev7

Please let me know if any additional information is required.


Discussion:
Thanks for reporting an issue with details.

Could you share the full query/answer that were matched after your patch (for example, pcap format, or .command()) ? We will need them to debug.

Sure thing. Here it is:
```
>>> res[0][1].command()
"IP(src='151.139.48.19', frag=0, tos=192, dst='10.128.0.7', flags=0, proto=1, version=4, ihl=5, chksum=11495, len=56, ttl=63, id=31737)/ICMP(seq=None, length=0, type=11, addr_mask=None, nexthopmtu=None, ts_ori=None, chksum=62719, reserved=0, id=None, code=0, ptr=None, unused=None, gw=None, ts_tx=None, ts_rx=None)/IPerror(src='151.139.51.15', frag=0, tos=0, dst='4.2.2.2', flags=2, proto=1, version=4, ihl=5, chksum=17415, len=28, ttl=1, id=25916)/ICMPerror(code=0, type=8, chksum=49272, seq=0, id=14215)"
```
Please let me know if any additional information is required.
Thanks.
Could you share `res[0][0]` (the query) and `res[0][1]` (the answer)?
Like so? : 
```
>>> conf.checkIPsrc=0
>>> res,un=sr(IP(dst="4.2.2.2",ttl=2,flags='DF',id=RandShort())/ICMP(id=os.getpid()), timeout=2)
Begin emission:
.Finished sending 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
>>> res[0][0]
<IP  id=42676 flags=DF frag=0 ttl=2 proto=icmp dst=4.2.2.2 |<ICMP  id=0x1a |>>
>>> res[0][0].command()
"IP(id=42676, flags=2, ttl=2, dst='4.2.2.2')/ICMP(id=26)"
>>> res[0][1]
<IP  version=4 ihl=5 tos=0xc0 len=56 id=60282 flags= frag=0 ttl=63 proto=icmp chksum=0xbd65 src=151.139.48.19 dst=10.128.0.7 options=[] |<ICMP  type=time-exceeded code=ttl-zero-during-transit chksum=0xf4ff reserved=0 length=0 unused=None |<IPerror  version=4 ihl=5 tos=0x0 len=28 id=42676 flags=DF frag=0 ttl=1 proto=icmp chksum=0x287 src=151.139.51.23 dst=4.2.2.2 options=[] |<ICMPerror  type=echo-request code=0 chksum=0xf7e5 id=0x1a seq=0x0 |>>>>
>>> res[0][1].command()
"IP(version=4, ihl=5, tos=192, len=56, id=60282, flags=0, frag=0, ttl=63, proto=1, chksum=48485, src='151.139.48.19', dst='10.128.0.7', options=[])/ICMP(type=11, code=0, chksum=62719, id=None, seq=None, ts_ori=None, ts_rx=None, ts_tx=None, gw=None, ptr=None, reserved=0, length=0, addr_mask=None, nexthopmtu=None, unused=None)/IPerror(version=4, ihl=5, tos=0, len=28, id=42676, flags=2, frag=0, ttl=1, proto=1, chksum=647, src='151.139.51.23', dst='4.2.2.2', options=[])/ICMPerror(type=8, code=0, chksum=63461, id=26, seq=0)"
```
1. If I am correct, you are sending a `query` packet with Scapy from 10.128.0.7 to 4.2.2.2 with a small TTL, the "network" is replying with an `answer` packet from 151.139.48.19 to 10.128.0.7 with an encapsulated IP error packet from 151.139.51.23 to 4.2.2.2 such as:

```
query = IP(id=42676, src='10.128.0.7', dst='4.2.2.2')/ICMP(id=26)
answer = IP(src='151.139.48.19', dst='10.128.0.7')/ICMP(type=11)/IPerror(id=42676, src='151.139.51.23', dst='4.2.2.2')/ICMPerror(id=26)
```

`answer` cannot match `query` because the IP addresses (10.128.0.7 and 151.139.51.23) don't match.

2. you tried to use `conf.checkIPsrc=0` to make these addresses and found a bug in Scapy. Your fix ensure that the source IP addresses are not checked when setting  `conf.checkIPsrc=0`.

It looks like that you found a legit bug that we need to fix =)

Resolving patch:
diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py
--- a/scapy/layers/inet.py
+++ b/scapy/layers/inet.py
@@ -853,13 +853,21 @@ class IPerror(IP):
     def answers(self, other):
         if not isinstance(other, IP):
             return 0
-        if not (((conf.checkIPsrc == 0) or (self.dst == other.dst)) and
-                (self.src == other.src) and
-                (((conf.checkIPID == 0) or
-                  (self.id == other.id) or
-                  (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and  # noqa: E501
-                (self.proto == other.proto)):
+
+        # Check if IP addresses match
+        test_IPsrc = not conf.checkIPsrc or self.src == other.src
+        test_IPdst = self.dst == other.dst
+
+        # Check if IP ids match
+        test_IPid = not conf.checkIPID or self.id == other.id
+        test_IPid |= conf.checkIPID and self.id == socket.htons(other.id)
+
+        # Check if IP protocols match
+        test_IPproto = self.proto == other.proto
+
+        if not (test_IPsrc and test_IPdst and test_IPid and test_IPproto):
             return 0
+
         return self.payload.answers(other.payload)
 
     def mysummary(self):


Test patch:
diff --git a/test/regression.uts b/test/regression.uts
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -869,6 +869,15 @@ p1 = IP(src=a1, dst=a2)/TCP(flags='A', seq=s1+9, ack=s2+10, sport=prt1, dport=pr
 assert not p2.answers(p1)
 assert not p1.answers(p2)
 
+= conf.checkIPsrc
+
+conf_checkIPsrc = conf.checkIPsrc
+conf.checkIPsrc = 0
+query = IP(id=42676, src='10.128.0.7', dst='192.168.0.1')/ICMP(id=26)
+answer = IP(src='192.168.48.19', dst='10.128.0.7')/ICMP(type=11)/IPerror(id=42676, src='192.168.51.23', dst='192.168.0.1')/ICMPerror(id=26)
+assert answer.answers(query)
+conf.checkIPsrc = conf_checkIPsrc
+
 
 ############
 ############