自定义TCP/IP数据包无法被Wireshark识别为TCP的问题求助
解决自行构造TCP数据包被识别为Logical-Link Control的问题
我正在学习网络协议,尝试通过实现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
相关产品推荐
相关产品推荐

