[nfstest PATCH v2] TCP: Reassemble TCP PDU segments for complete NFS message processing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Previously, nfstest was unable to process NFS header messages split across
multiple TCP segments, resulting in missed NFS operations
and numerous test failures. Such as Rocky Linux 9.6.

For example:

4   0.000816 192.168.122.198 → 192.168.122.199 NFS 110 V4 NULL Call
5   0.001073 192.168.122.199 → 192.168.122.198 TCP 66 2049 → 775 [ACK]
		Seq=1 Ack=45 Win=65152 Len=0 TSval=3032720633 TSecr=443583529
6   0.001155 192.168.122.199 → 192.168.122.198 TCP 70 2049 → 775 [PSH, ACK]
		Seq=1 Ack=45 Win=65152 Len=4 TSval=3032720634 TSecr=443583529 [TCP segment of a reassembled PDU]
7   0.001155 192.168.122.199 → 192.168.122.198 NFS 90 V4 NULL Reply (Call In 4)

This patch introduces functionality to reassemble TCP segments,
allowing nfstest to accurately decode complete NFS messages,
similar to Wireshark's "TCP Segment of a Reassembled PDU.

Signed-off-by: Chen Hanxiao <chenhx.fnst@xxxxxxxxxxx>
---
v2:
	fix nfstest_alloc TCP DATA segment reassemble

 packet/application/rpc.py | 28 ++++++++++++++++++++++++++++
 packet/pktt.py            |  5 +++++
 packet/transport/tcp.py   |  5 +++++
 packet/unpack.py          |  6 ++++++
 4 files changed, 44 insertions(+)

diff --git a/packet/application/rpc.py b/packet/application/rpc.py
index 718a300..7bea9f9 100644
--- a/packet/application/rpc.py
+++ b/packet/application/rpc.py
@@ -193,9 +193,35 @@ class RPC(GSS):
         pktt = self._pktt
         unpack = pktt.unpack
         init_size = unpack.size()
+        ip = pktt.pkt.ip
+        tcp = pktt.pkt.tcp
+        streamid = "%s:%d-%s:%d" % (ip.src, tcp.src_port, ip.dst, tcp.dst_port)
+        new_size = 0
         if self._proto == 6:
             # TCP packet
             save_data = ''
+            #if a split header coming and streamid not in pktt._tcp_pdu_map:
+            if tcp.flags.PSH and init_size < 16:
+                if streamid not in pktt._tcp_pdu_map:
+                    pktt._tcp_pdu_map[streamid] = unpack.getbytes()
+                    pktt._tcp_pdu_pkt[streamid] = pktt.pkt
+                    pktt._tcp_pdu_size[streamid] = unpack.size()
+                    return
+            elif tcp.flags.PSH:
+                # TCP PDU reassemble
+                if streamid in pktt._tcp_pdu_map:
+                    pktt._tcp_pdu_map[streamid] += unpack.getbytes()
+                    pktt._tcp_pdu_pkt[streamid] = pktt.pkt
+                    pktt._tcp_pdu_size[streamid] += unpack.size()
+
+                    pktt.unpack.replace_data(pktt._tcp_pdu_map[streamid])
+                    unpack = pktt.unpack
+                    pktt.pkt = pktt._tcp_pdu_pkt[streamid]
+                    new_size = pktt._tcp_pdu_size[streamid] - 4
+
+                    del pktt._tcp_pdu_map[streamid]
+                    del pktt._tcp_pdu_pkt[streamid]
+
             while True:
                 # Decode fragment header
                 psize = unpack.unpack_uint()
@@ -211,6 +237,8 @@ class RPC(GSS):
                         # Concatenate RPC fragments
                         unpack.insert(save_data)
                     break
+            if size < new_size:
+                size = new_size
             self.fragment_hdr = Header(size, last_fragment)
         elif self._proto == 17:
             # UDP packet
diff --git a/packet/pktt.py b/packet/pktt.py
index b9420f9..5e6a7ba 100644
--- a/packet/pktt.py
+++ b/packet/pktt.py
@@ -368,6 +368,11 @@ class Pktt(BaseObj):
         # IPv4 fragments used in reassembly
         self._ipv4_fragments = {}
 
+        # IPv4 PDU fragments
+        self._tcp_pdu_map = {}
+        self._tcp_pdu_pkt = {}
+        self._tcp_pdu_size = {}
+
         # RDMA reassembly object
         self._rdma_info = RDMAinfo()
 
diff --git a/packet/transport/tcp.py b/packet/transport/tcp.py
index e7fadf4..a5385c1 100644
--- a/packet/transport/tcp.py
+++ b/packet/transport/tcp.py
@@ -389,6 +389,11 @@ class TCP(BaseObj):
 
             # Get RPC header
             rpc = RPC(pktt, proto=6)
+            ip = pktt.pkt.ip
+            tcp = pktt.pkt.tcp
+            streamid = "%s:%d-%s:%d" % (ip.src, tcp.src_port, ip.dst, tcp.dst_port)
+            if streamid in pktt._tcp_pdu_size:
+                ldata = pktt._tcp_pdu_size[streamid] - 4;
         else:
             ldata = size - 4
 
diff --git a/packet/unpack.py b/packet/unpack.py
index 223cf60..603102e 100644
--- a/packet/unpack.py
+++ b/packet/unpack.py
@@ -194,6 +194,12 @@ class Unpack(object):
         self._state.append([sid, self._offset])
         return sid
 
+    def replace_data(self, data):
+        """replace current working buffer"""
+        self._data = data
+        self._offset = 0
+        self._state = []
+
     def restore_state(self, sid):
         """Restore state given by the state id"""
         max = len(self._state)
-- 
2.47.1





[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux