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

自定义TCP/IP数据包无法被Wireshark识别为TCP的问题求助

我正在学习网络协议,尝试通过实现TCP协议深入理解其工作原理,但目前无法成功发送TCP数据包。用Wireshark抓包发现,自行构造的数据包未被识别为TCP协议,而是标记为“Logical-Link Control”。但将发送的数据替换为Wireshark抓取到的正常TCP数据包时,就能被正确识别。问题应该出在自行构造的数据包中,但无法定位具体错误,代码如下:

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <stdlib.h>

int getMacAddress(const char *interface, unsigned char *macAddress)
{
    struct ifreq ifr;
    int sockfd;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        return -1;
    }

    strncpy(ifr.ifr_name, interface, IFNAMSIZ);

    if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == -1) {
        perror("ioctl");
        close(sockfd);
        return -1;
    }

    memcpy(macAddress, ifr.ifr_hwaddr.sa_data, 6);

    close(sockfd);

    return 0;
}

void printBinary(char array[], size_t size)
{
    for(int i = 0; i < size; i++)
    {
        for(int j = 0; j < 8; j++)
        {
            if(array[i] & 1)
                printf("1");
            else
                printf("0");
            array[i] >>= 1;
        }
    }
}

int main()
{
    int packet_socket = socket(AF_PACKET, SOCK_RAW, ETH_P_IP);

    const char* interface = "wlp3s0";
    unsigned char macAddress[6];
    getMacAddress(interface, macAddress);

    struct sockaddr_ll socket_address;
    socket_address.sll_family = AF_PACKET;
    socket_address.sll_protocol = htons(ETH_P_IP);
    socket_address.sll_ifindex = if_nametoindex(interface);
    socket_address.sll_hatype = 0;
    socket_address.sll_pkttype = PACKET_OTHERHOST;
    socket_address.sll_halen = 6;
    memcpy(socket_address.sll_addr, macAddress, 6);

    int res = bind(packet_socket, (struct sockaddr*)&socket_address, sizeof(socket_address));
    
    unsigned char rawPacket[] =
    {
    // IPv4 Header
    0x45, 0x00, 0x00, 0x28,  // Version, IHL, Type of Service, Total Length
    0x04, 0x01, 0x00, 0x00,  // Identification, Flags, Fragment Offset
    0x40, 0x06, 0xA6, 0xEC,  // Time to Live, Protocol (TCP), Header Checksum
    0x17, 0x79, 0x4F, 0xFD,  // Source IP Address 
    0xC0, 0xA8, 0x2B, 0x41,  // Destination IP Address (Google's IP)

    // TCP Header
    0xDC, 0xB6, 0x01, 0xBB,  // Source Port, Destination Port (443)
    0xC7, 0x9B, 0x13, 0xD6,  // Sequence Number
    0x6A, 0xF5, 0xB5, 0xA7,  // Acknowledgment Number
    0x80, 0x10, 0x01, 0xF5,  // Data Offset, Flags, Window Size
    0x16, 0x20, 0x00, 0x00,  // Checksum, Urgent Pointer
    };

    
    int bytes = send(packet_socket, (const void*)&rawPacket[0], sizeof(rawPacket), 0);
    printf("%d\n", bytes);

    char buffer[300];
    int size = recv(packet_socket, &buffer, 300, 0);
    printf("%d\n", size);


    printBinary(buffer, size);


    close(packet_socket);
}

关键错误点及修正方案

1. TCP头的Data Offset(数据偏移)字段错误

TCP头的第一个字节(0x80)的高4位是Data Offset,代表TCP头的长度(以4字节为单位)。你当前设置的0x80意味着TCP头长度为32字节,但实际你的TCP头只有20字节(5个4字节块),这会导致Wireshark解析失败。正确值应为0x50(高4位为5)。

错误代码:

0x80, 0x10, 0x01, 0xF5,  // Data Offset, Flags, Window Size

修正后:

0x50, 0x10, 0x01, 0xF5,  // Data Offset=5(20字节), Flags, Window Size

2. 目的MAC地址设置错误

你把本地网卡MAC设为sockaddr_ll的目的地址,导致数据包只会发送给自己,无法到达目标IP。需要将sll_addr设置为网关的MAC地址,可通过arp -a命令查询网关MAC。

3. IP和TCP校验和硬编码无效

硬编码的校验和仅适用于特定的IP地址、端口组合,一旦这些字段修改,校验和就会失效,导致数据包被丢弃或无法解析。必须动态计算校验和:

  • IP头校验和:仅计算IP头部分,使用16位补码求和再取反的互联网校验和算法。
  • TCP校验和:需要包含伪头部(源IP、目的IP、协议类型、TCP长度)+TCP头+数据,同样使用互联网校验和算法。

4. sll_pkttype设置错误

sll_pkttype设为PACKET_OTHERHOST属于冗余设置,对于发送到外部主机的数据包,直接设为0即可,由内核自动处理。


修正后的核心代码示例

以下是修正了关键字段的核心代码片段(假设网关MAC为0x11,0x22,0x33,0x44,0x55,0x66):

// 修正后的数据包结构
unsigned char rawPacket[] =
{
// IPv4 Header
0x45, 0x00, 0x00, 0x28,  // Version, IHL, Type of Service, Total Length
0x04, 0x01, 0x00, 0x00,  // Identification, Flags, Fragment Offset
0x40, 0x06, 0x00, 0x00,  // 校验和先设为0,后续动态计算
0x17, 0x79, 0x4F, 0xFD,  // Source IP Address 
0xC0, 0xA8, 0x2B, 0x41,  // Destination IP Address

// TCP Header
0xDC, 0xB6, 0x01, 0xBB,  // Source Port, Destination Port (443)
0xC7, 0x9B, 0x13, 0xD6,  // Sequence Number
0x6A, 0xF5, 0xB5, 0xA7,  // Acknowledgment Number
0x50, 0x10, 0x01, 0xF5,  // Data Offset=5(20字节), Flags, Window Size
0x00, 0x00, 0x00, 0x00,  // 校验和先设为0,后续动态计算
};

// 设置网关MAC为目的地址
unsigned char gateway_mac[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
memcpy(socket_address.sll_addr, gateway_mac, 6);
socket_address.sll_pkttype = 0; // 移除错误的PACKET_OTHERHOST设置

// 计算IP头校验和的函数
uint16_t calculate_checksum(uint8_t *data, int length) {
    uint32_t sum = 0;
    for (int i = 0; i < length; i += 2) {
        sum += ((data[i] << 8) | data[i+1]);
        if (sum > 0xFFFF) sum -= 0xFFFF;
    }
    return ~sum;
}

// 计算并设置IP校验和
*(uint16_t*)(&rawPacket[10]) = calculate_checksum(rawPacket, 20);

// 构造TCP伪头部并计算TCP校验和
uint8_t tcp_pseudo_header[12];
memcpy(tcp_pseudo_header, &rawPacket[12], 4); // 源IP
memcpy(tcp_pseudo_header+4, &rawPacket[16],4); // 目的IP
tcp_pseudo_header[8] = 0;
tcp_pseudo_header[9] = IPPROTO_TCP;
uint16_t tcp_length = sizeof(rawPacket) - 20;
*(uint16_t*)(tcp_pseudo_header+10) = htons(tcp_length);

uint8_t tcp_checksum_buf[12+40];
memcpy(tcp_checksum_buf, tcp_pseudo_header, 12);
memcpy(tcp_checksum_buf+12, &rawPacket[20], tcp_length);
*(uint16_t*)(&rawPacket[36]) = calculate_checksum(tcp_checksum_buf, 12+tcp_length);

额外建议

  • 用Wireshark对比正常TCP数据包和你构造的数据包的每一个字段,重点检查以太网帧头、IP头版本/长度、TCP头数据偏移/标志位等关键字段。
  • 运行程序时必须拥有root或CAP_NET_RAW权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:37:03