Repository: secdev/scapy

Issue:
Using wrpcap() to write packets from two pcaps to merge in one
Environment: 
 - Scapy version 2.4.3rc4.dev3
 - Linux Debian bullseye/sid
 - Python 3.7.4
 
What I am doing:
I want to read two pcaps and write the output of all the packets into a single one, such as with this code:
```
#!/usr/bin/python3
from scapy.all import Ether, IP, TCP, UDP, rdpcap, wrpcap

pcap1="rdpscan.pcap"
pcap2="rdp.pcap"

allpackets = []

packets = rdpcap(pcap1)
for p in packets:
    allpackets.append(p)
    
packets = rdpcap(pcap2)
for p in packets:
    allpackets.append(p)

wrpcap("out.pcap", allpackets)
```

The two pcap used for input are attached here: 
[rdp.pcap.gz](https://github.com/secdev/scapy/files/3459930/rdp.pcap.gz)
[rdpscan.pcap.gz](https://github.com/secdev/scapy/files/3459931/rdpscan.pcap.gz)

The output should be the two pcaps concatenated, unfortunately, as soon as it reads rdp.pcap, it exports packets that can be seen in tshark like this:
```
    6   0.005251  172.16.0.23 → 172.16.0.28  TCP 56 41524 → 3389 [ACK] Seq=47 Ack=20 Win=29312 Len=0
    7   0.005443  172.16.0.23 → 172.16.0.28  TCP 56 41524 → 3389 [FIN, ACK] Seq=47 Ack=20 Win=29312 Len=0
    8   0.005511  172.16.0.28 → 172.16.0.23  TCP 56 3389 → 41524 [ACK] Seq=20 Ack=48 Win=63954 Len=0
    9   0.005575  172.16.0.28 → 172.16.0.23  TCP 56 3389 → 41524 [RST, ACK] Seq=20 Ack=48 Win=0 Len=0
=== ^ Reading rdpscan.pcap, this was the LAST packet
 |=== Now reading rdp.pcap
v 
   10 -6175217.454171              →              0x4500 66 Unknown (12)
   11 -6175217.454005              →              0x4500 66 Unknown (12)
   12 -6175217.453913              →              0x4500 54 Unknown (12)
   13 -6175217.453704              →              0x4500 73 Unknown (12)
   14 -6175217.451059              →              0x4500 54 Unknown (12)
   15 -6175217.450936              →              0x4500 73 Unknown (12)
```

Notice that if I switch the order, then the rdp.pcap file is written correctly, but not the rdpscan.pcap.







Discussion:
if I loop on allpackets before wrpcap, such as:
```
for pkt in allpackets:
    if pkt.haslayer(IP):
        print(pkt.getlayer(IP).src)
```

I can read the IP address correctly

It seems that you need to add an `Ether()` layer to the packets contained in `rdpscan.pcap`:
```
$ file rdp*
rdp.pcap:     tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 262144)
rdpscan.pcap: tcpdump capture file (little-endian) - version 2.4 (Linux "cooked", capture length 26214
```

I tried and it works fine: the resulting `out.pcap` file contains valid frames. 
You are correct, adding:
```
if CookedLinux in packet:
    packet = Ether() / packet.payload
```

Fixed it.

I still think there is a bug for those two reasons:
1) If I inverse the order of reading the pcaps, it writes the first one correctly, but not the second
2) There is no warning / I cannot just re-write what I read fine

I am not closing because of those reasons, what do you think?

Most likely the two pcaps don't have the same link type, therefore cannot be merged into one without conversion.
so in this case, I believe writing a warning would be great
thinking out loud here, since I talked to Guillaume this morning, maybe a function that is slow but offers various checks (including this one) could be a nice to have.

Resolving patch:
diff --git a/scapy/utils.py b/scapy/utils.py
--- a/scapy/utils.py
+++ b/scapy/utils.py
@@ -1305,8 +1305,16 @@ def write(self, pkt):
             else:
                 pkt = pkt.__iter__()
             for p in pkt:
+
                 if not self.header_present:
                     self._write_header(p)
+
+                if self.linktype != conf.l2types.get(type(p), None):
+                    warning("Inconsistent linktypes detected!"
+                            " The resulting PCAP file might contain"
+                            " invalid packets."
+                            )
+
                 self._write_packet(p)
 
     def _write_packet(self, packet, sec=None, usec=None, caplen=None,


Test patch:
diff --git a/test/regression.uts b/test/regression.uts
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -7144,6 +7144,18 @@ wrpcap(filename, SndRcvList(res=[(Ether()/IP(), Ether()/IP())]))
 assert len(rdpcap(filename)) == 2
 os.remove(filename)
 
+= Check wrpcap() with different packets types
+
+import mock
+import os
+import tempfile
+
+with mock.patch("scapy.utils.warning") as warning:
+    filename = tempfile.mktemp()
+    wrpcap(filename, [IP(), Ether(), IP(), IP()])
+    os.remove(filename)
+    assert any("Inconsistent" in arg for arg in warning.call_args[0])
+
 ############
 ############
 + Sessions