You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C语言基于libpcap复现IP/TCP层功能并提取应用层PDU?

Great question—reconstructing application-layer PDUs exactly as the server sees them from raw packet captures is a common challenge when working with protocol analyzers or troubleshooting industrial protocols like IEC 60870-5-104. Let's break down how to implement this with libpcap in C, plus some tools that can save you from reinventing the wheel:

1. Core Workflow to Reconstruct Application-Layer Traffic

To match what the server's application layer receives, you need to replicate the OS's TCP/IP stack behavior step by step:

1.1 IP Fragment Reassembly

First, you'll need to handle fragmented IP packets:

  • Check the IP header's MF (More Fragments) flag and fragment offset field to identify partial packets.
  • Use a hash map (keyed by source IP, destination IP, and IP ID) to track in-progress fragment sets.
  • When all fragments for an IP datagram are collected, concatenate them into a full IP packet before passing it to the TCP processing stage.
  • Implement a timeout mechanism to clean up stale fragment sets (in case some fragments are lost in capture).

Here's a quick code snippet to detect fragments:

#include <netinet/ip.h>

// Assume packet_data points to the start of the Ethernet frame
struct iphdr *ip_header = (struct iphdr *)(packet_data + ETH_HLEN);
bool is_fragment = (ip_header->frag_off & IP_MF) || (ip_header->frag_off & IP_OFFSET);

if (is_fragment) {
    // Add fragment to reassembly cache
} else {
    // Pass full IP packet directly to TCP processing
}

1.2 TCP Stream Processing (Out-of-Order, Retransmissions, Duplicates)

This is the most complex part—you need to mimic the server's TCP receive buffer behavior:

  • Identify unique TCP streams using the 4-tuple: source IP, destination IP, source port, destination port.
  • For each stream, track:
    • The next expected sequence number
    • A sorted cache for out-of-order segments (stored by sequence number)
    • A buffer for contiguous, ready-to-process payload data
  • Duplicate segments: Check if the segment's sequence range already falls within received data—if so, discard it.
  • Out-of-order segments: Store them in the sorted cache. When a subsequent segment fills the gap, concatenate all contiguous segments into the ready buffer.
  • Retransmissions: Treat them like duplicates unless they carry updated TCP options (e.g., SACK) that require state adjustments.

Example stream structure to track state:

typedef struct TcpStream {
    uint32_t src_ip;
    uint32_t dst_ip;
    uint16_t src_port;
    uint16_t dst_port;
    uint32_t next_expected_seq;
    struct OutOfOrderSegment *ooo_cache; // Sorted linked list of out-of-order segments
    uint8_t *ready_payload;
    size_t ready_payload_len;
} TcpStream;

1.3 IEC 60870-5-104 PDU Splitting

Since IEC 60870-5-104 uses a byte-stream over TCP, you need to handle sticky packets and partial PDUs:

  • The protocol's APCI header includes a length field—use this to determine the full size of each PDU.
  • Maintain an application-layer buffer: append new TCP payload data to it, then repeatedly check if you have enough bytes to extract a full PDU.
  • If the buffer has insufficient bytes for a full PDU, wait for more data from subsequent TCP segments.

Simplified code for splitting PDUs:

#define IEC104_MIN_APCI_LEN 4 // Start char (1) + Length (2) + Type ID (1)

void process_iec104_data(uint8_t *tcp_payload, size_t payload_len, uint8_t **app_buffer, size_t *app_buf_len) {
    // Append new data to application buffer
    *app_buffer = realloc(*app_buffer, *app_buf_len + payload_len);
    memcpy(*app_buffer + *app_buf_len, tcp_payload, payload_len);
    *app_buf_len += payload_len;

    // Extract complete PDUs
    while (*app_buf_len >= IEC104_MIN_APCI_LEN) {
        uint16_t pdu_total_len = ntohs(*(uint16_t *)(*app_buffer + 1)) + 2; // Add start char + length field size
        if (*app_buf_len < pdu_total_len) {
            break; // Not enough data for a full PDU
        }
        // Process the complete PDU (matches what the server's app layer gets)
        handle_complete_iec104_pdu(*app_buffer, pdu_total_len);
        // Shift remaining data to the start of the buffer
        memmove(*app_buffer, *app_buffer + pdu_total_len, *app_buf_len - pdu_total_len);
        *app_buf_len -= pdu_total_len;
    }
}

Implementing full TCP/IP stack logic from scratch is error-prone. These mature libraries handle the heavy lifting:

  • libnids: A dedicated network intrusion detection library with built-in IP reassembly and TCP stream reconstruction. It delivers contiguous TCP payloads to your callback function, so you only need to handle the IEC 60870-5-104 PDU splitting. It natively handles out-of-order segments, retransmissions, and duplicates.
  • libtcpflow: A lightweight library focused on TCP stream reconstruction. It can write streams to files or pass them directly to your application, eliminating the need to code TCP state management.
  • PF_RING + ntop Libraries: For high-performance capture scenarios, PF_RING provides ultra-fast packet capture, and ntop's accompanying libraries include robust stream reassembly logic.
3. Critical Tips for Accuracy
  • Follow TCP specifications strictly, including 32-bit sequence number wrap-around.
  • Handle TCP options like SACK (Selective Acknowledgment), which can change how out-of-order segments are processed.
  • Mimic the server's receive window behavior—if capture data includes window updates, adjust your buffer logic to avoid incorrectly discarding segments.
  • Validate your implementation using known good IEC 60870-5-104 traffic: compare your reconstructed PDUs with a packet capture taken directly at the server's application layer.

内容的提问来源于stack exchange,提问作者reddi hari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:58:31