Repository: secdev/scapy

Issue:
[ipv6] Scapy cannot recognize the reply of an ICMP6 echo request.


#### Brief description

Scapy cannot recognize the reply of an ICMP6 echo request.


#### Environment

- Scapy version: 2.4.3
- Python version: 2.7.3
- Operating System: openSUSE 12.3 (x86_64)

my ppp network interface:
```
# ip a s dev dsl0
13: dsl0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qdisc pfifo_fast state UNKNOWN qlen 3
    link/ppp
    inet 61.49.147.46 peer 114.253.80.1/32 scope global dsl0
    inet6 2408:8206:c03:eda1:200a:204f:edca:b4a6/64 scope global temporary dynamic
    inet6 fe80::187c:4d2e:902a:dfc6/10 scope link
```


#### How to reproduce

cannot recognize the reply (always):
```
>>> sr1(IPv6(dst="2001:41d0:701:1100::29c8")/ICMPv6EchoRequest())
...
Received 17 packets, got 0 answers, remaining 1 packets
```

tcpdump can catch the reply:
```
# tcpdump -nn -i dsl0 icmp6
00:09:30.524087 IP6 2408:8206:c03:eda1:200a:204f:edca:b4a6 > 2001:41d0:701:1100::29c8: ICMP6, echo request, seq 0, length 8
00:09:31.057939 IP6 2001:41d0:701:1100::29c8 > 2408:8206:c03:eda1:200a:204f:edca:b4a6: ICMP6, echo reply, seq 0, length 8
```


#### Actual result

"got 0 answers"

#### Expected result

"got 1 answers"


#### Related resources

ping6 OK:
```
# ping6 2001:41d0:701:1100::29c8
PING 2001:41d0:701:1100::29c8(2001:41d0:701:1100::29c8) 56 data bytes
64 bytes from 2001:41d0:701:1100::29c8: icmp_seq=1 ttl=40 time=559 ms
64 bytes from 2001:41d0:701:1100::29c8: icmp_seq=2 ttl=40 time=558 ms
```

IPv4 ICMP OK:
```
>>> sr1(IP(dst="114.114.114.114")/ICMP(), iface="dsl0", retry=1, timeout=3)
...
Received 1 packets, got 1 answers, remaining 0 packets
```

ICMPv6 loopback OK:
```
>>> sr1(IPv6(dst="2408:8206:c03:eda1:200a:204f:edca:b4a6")/ICMPv6EchoRequest(), iface="lo")
...
Received 2 packets, got 1 answers, remaining 0 packets
```

setting some attributes doesn't work:
```
>>> sr1(IPv6(src="2408:8206:c03:eda1:200a:204f:edca:b4a6", dst="2001:41d0:701:1100::29c8")/ICMPv6EchoRequest(id=99, seq=1, data="xxx"), retry=1, timeout=3)
...
Received 5 packets, got 0 answers, remaining 1 packets
```





Discussion:
It is really unlikely that this an issue with ICMPv6 Echo. In your IPv6 example, you did not specify an interface name, can you try with:
`sr1(IPv6(dst="2001:41d0:701:1100::29c8")/ICMPv6EchoRequest(), iface="dsl0")`
Thank you for the reply. I tried it just now, it doesn't work.

```
>>> sr1(IPv6(dst="2001:41d0:701:1100::29c8")/ICMPv6EchoRequest(), iface="dsl0", retry=1, timeout=5)
Begin emission:
Finished sending 1 packets.
......................................Begin emission:
Finished sending 1 packets.
.
Received 39 packets, got 0 answers, remaining 1 packets
```

with `retry=1` , tcpdump got 2 request packets and 2 reply packets:
```
10:33:55.265890 IP6 2408:8206:c08:7cd7:e4d8:a057:bf35:65db > 2001:41d0:701:1100::29c8: ICMP6, echo request, seq 0, length 8
10:33:55.689502 IP6 2001:41d0:701:1100::29c8 > 2408:8206:c08:7cd7:e4d8:a057:bf35:65db: ICMP6, echo reply, seq 0, length 8
10:34:00.266440 IP6 2408:8206:c08:7cd7:e4d8:a057:bf35:65db > 2001:41d0:701:1100::29c8: ICMP6, echo request, seq 0, length 8
10:34:00.694361 IP6 2001:41d0:701:1100::29c8 > 2408:8206:c08:7cd7:e4d8:a057:bf35:65db: ICMP6, echo reply, seq 0, length 8
```
Could you share the content of `conf.iface` `conf.iface6` and `conf.route6` as wells as the sniffed pcap?

[pcap.tar.gz](https://github.com/secdev/scapy/files/3715438/pcap.tar.gz)


```
>>> conf.iface
'dsl0'
>>> conf.iface6
'dsl0'
```

```
>>> conf.route6
Destination                                Next Hop                  Iface   Src candidates                         Metric
::1/128                                    ::                        lo      ::1                                    256
2408:8206:c0a:5a3::/64                     ::                        dsl0    2408:8206:c0a:5a3:6860:31d0:2a09:b6d9  256
fe80::/64                                  ::                        vmnet1  fe80::250:56ff:fec0:1                  256
fe80::/64                                  ::                        vmnet8  fe80::250:56ff:fec0:8                  256
fe80::/64                                  ::                        eth0    fe80::226:22ff:fe06:fe41               256
fe80::/10                                  ::                        dsl0    fe80::a89f:2e26:ba7b:48fe              1
fe80::/10                                  ::                        dsl0    fe80::a89f:2e26:ba7b:48fe              256
::1/128                                    ::                        lo      ::1                                    0
2408:8206:c0a:5a3:6860:31d0:2a09:b6d9/128  ::                        lo      ::1                                    0
fe80::226:22ff:fe06:fe41/128               ::                        lo      ::1                                    0
fe80::250:56ff:fec0:1/128                  ::                        lo      ::1                                    0
fe80::250:56ff:fec0:8/128                  ::                        lo      ::1                                    0
fe80::a89f:2e26:ba7b:48fe/128              ::                        lo      ::1                                    0
2001:41d0:701:1100::29c8/128               fe80::2d0:f6ff:fe9a:c580  dsl0    2408:8206:c0a:5a3:6860:31d0:2a09:b6d9  0
2404:6800:4008:801::200e/128               fe80::2d0:f6ff:fe9a:c580  dsl0    2408:8206:c0a:5a3:6860:31d0:2a09:b6d9  0
::/0                                       fe80::2d0:f6ff:fe9a:c580  dsl0    2408:8206:c0a:5a3:6860:31d0:2a09:b6d9  1024
ff02::fb/128                               ff02::fb                  vmnet1  fe80::250:56ff:fec0:1                  0
ff02::fb/128                               ff02::fb                  vmnet8  fe80::250:56ff:fec0:8                  0
ff02::fb/128                               ff02::fb                  eth0    fe80::226:22ff:fe06:fe41               0
```

dsl0:
```
# ip -6 a s dev dsl0
6: dsl0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qlen 3
    inet6 2408:8206:c0a:5a3:6860:31d0:2a09:b6d9/64 scope global temporary dynamic
       valid_lft 85542sec preferred_lft 2742sec
    inet6 fe80::a89f:2e26:ba7b:48fe/10 scope link
       valid_lft forever preferred_lft forever
```

Thanks. Could you now do the following and share the resulting pcap? I would like to have a look at the packets received by Scapy.
```
>>> conf.debug_match = True
>>> sr1(IPv6(dst="2001:41d0:701:1100::29c8")/ICMPv6EchoRequest(), timeout=5)
>>> wrpcap("debug.recv.v6.pcap", [p for p in debug.recv if IPv6 in p])
```
Thank you for your patience.

I did it, but the resulting pcap is empty. So I checked `debug.recv` and found that the received ICMP6 reply doesn't match the `IPv6 in p` condition. So I removed `if IPv6 in p` and got a pcap with 2 pkts in it. One pkt is the ICMP6 reply, another is an IPv4 pkt which you may want to ignore.

What I did:
```
>>> from scapy.all import *
>>> conf.debug_match = True
>>> sr1(IPv6(dst="2001:41d0:701:1100::29c8")/ICMPv6EchoRequest(), timeout=5)
Begin emission:
Finished sending 1 packets.
..
Received 2 packets, got 0 answers, remaining 1 packets
>>> wrpcap("debug.recv.v6.pcap", [p for p in debug.recv if IPv6 in p])

>>> debug.recv
<Received: TCP:1 UDP:0 ICMP:0 Other:1>
>>> wrpcap("debug.recv.all.pcap", [p for p in debug.recv])
```


[debug_recv.tar.gz](https://github.com/secdev/scapy/files/3720338/debug_recv.tar.gz)

Thanks, does the following work?
`sr1(IPv6(dst="2001:41d0:701:1100::29c8")/ICMPv6EchoRequest(), timeout=5, nofilter=True)`
Sorry for the late reply. 

It doesn't work, just as above. I tried more than 3 times =)

```
>>> sr1(IPv6(dst="2001:41d0:701:1100::29c8")/ICMPv6EchoRequest(), timeout=5, nofilter=True)
Begin emission:
Finished sending 1 packets.
....
Received 4 packets, got 0 answers, remaining 1 packets
```
I had another look at the issue and noticed that there is a route to `fe80::/10` on `dsl0`. Can you change it to `fe80::/64` and try sending the ICMPv6 Echo Request again?
Oh, didn't work :(

```
# ip -6 a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
8: dsl0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qlen 3
    inet6 2408:8206:c04:b36e:6d12:e72a:ff97:1d18/64 scope global temporary dynamic
       valid_lft 86011sec preferred_lft 3211sec
    inet6 fe80::f077:1ee7:8875:3eb5/64 scope link
       valid_lft forever preferred_lft forever
```

```
# ip -6 r s
2408:8206:c04:b36e::/64 dev dsl0  proto kernel  metric 256  expires 86309sec
fe80::/64 dev dsl0  proto kernel  metric 256
default via fe80::2d0:f6ff:fe9a:c580 dev dsl0  proto ra  metric 1024  expires 4409sec
```

Even I deleted the "fe80" address and route, I got same result (ping6 OK, scapy not OK).

***

I remember one thing, that I modified one line of scapy code in my environment.

I'm using:
- openSUSE 12.3
- tcpdump version 4.3.0
- libpcap version 1.3.0

The exit code of `tcpdump --version` is 1, so I modified one line in scapy/arch/common.py:
```
TCPDUMP = WINDOWS or _check_tcpdump()
```
was changed to:
```
TCPDUMP = True
```

I don't know whether this affects this issue or not. But at least I'm using an outdated OS and outdated softwares. If you cannot reproduce this issue in your environment, maybe it will be OK if I update my system. I will try that.

Thank you for your time =)
@wangsitan does this work https://github.com/secdev/scapy/issues/2513#issuecomment-597587063 ?
I finally managed to reproduce the issue using information from https://nakkaya.com/2012/08/20/poor-man-s-vpn-using-ppp-over-ssh/ This shell
[connect_ppp.sh.txt](https://github.com/secdev/scapy/files/4510249/connect_ppp.sh.txt)
script provides a link local IPv6 connectivity.

Sniffing `ppp0` on botch ends shows that the  ICMPv6EchoRequest() is received and that an answer is received. Activating `conf.debug_match=True` gives the following:
```
>>> sr1(IPv6(dst="fe80::48df:f10d:da52:147f")/ICMPv6EchoRequest())
Begin emission:
Finished sending 1 packets.
.^C
Received 1 packets, got 0 answers, remaining 1 packets
>>> debug.sent[0]
<IPv6  nh=ICMPv6 dst=fe80::48df:f10d:da52:147f |<ICMPv6EchoRequest  |>>
>>> debug.recv[0]
<IP  version=6 ihl=0 tos=0x4 len=24722 id=8 flags=MF frag=6720 ttl=254 proto=128 chksum=0x0 src=0.0.0.0 dst=72.223.241.13 options=[<IPOption  copy_flag=1 optclass=debug option=26 length=82 value='\x14\x7f\xfe\x80\x00\x00' |>] |<Raw  load='\x00\x00\x00\x00\xdd\x96\xb8kK[\xfch\x81\x00{5\x00\x00\x00\x00' |>>
```

This is indeed the answer:
```
>>>
>>> IPv6(raw(debug.recv[0]))
<IPv6  version=6 tc=0 fl=286866 plen=8 nh=ICMPv6 hlim=64 src=fe80::48df:f10d:da52:147f dst=fe80::dd96:b86b:4b5b:fc68 |<ICMPv6EchoReply  type=Echo Reply code=0 cksum=0x7b35 id=0x0 seq=0x0 |>>
>>> IPv6(raw(debug.recv[0])).answers(debug.sent[0])
True
```

Replacing the default raw layer with IPv6 works:
```
> >>> conf.raw_layer = IPv6
> >>> sr1(IPv6(dst="fe80::48df:f10d:da52:147f")/ICMPv6EchoRequest())
> Begin emission:
> Finished sending 1 packets.
> <class 'scapy.base_classes.Packet_metaclass'> <class 'scapy.layers.inet.IP'>
> *
> Received 1 packets, got 1 answers, remaining 0 packets
> <IPv6  version=6 tc=0 fl=286866 plen=8 nh=ICMPv6 hlim=64 src=fe80::48df:f10d:da52:147f dst=fe80::dd96:b86b:4b5b:fc68 |<ICMPv6EchoReply  type=Echo Reply code=0 cksum=0x7b35 id=0x0 seq=0x0 |>>
```



Resolving patch:
diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py
--- a/scapy/arch/linux.py
+++ b/scapy/arch/linux.py
@@ -559,10 +559,15 @@ def send(self, x):
         self.outs.bind(sdto)
         sn = self.outs.getsockname()
         ll = lambda x: x
-        if type(x) in conf.l3types:
-            sdto = (iff, conf.l3types[type(x)])
+        type_x = type(x)
+        if type_x in conf.l3types:
+            sdto = (iff, conf.l3types[type_x])
         if sn[3] in conf.l2types:
             ll = lambda x: conf.l2types[sn[3]]() / x
+        if self.lvl == 3 and type_x != self.LL:
+            warning("Incompatible L3 types detected using %s instead of %s !",
+                    type_x, self.LL)
+            self.LL = type_x
         sx = raw(ll(x))
         x.sent_time = time.time()
         try:


Test patch: