手动构造TCP SYN数据包无响应问题排查求助
Hey there, let's walk through the possible issues in your code that might be causing the lack of SYN-ACK responses. I've gone through your code and spotted several key points to check:
1. 错误的TCP标志位设置
发送SYN包时,只有SYN标志位应该设为1,其余标志位(ACK、FIN、RST等)都应该是0。看你的代码,你通过命令行参数接收这些标志位的值——如果你运行程序时不小心传入了ack=1,那你发送的就不是SYN包,而是SYN-ACK包了,远端主机自然不会把它当作连接请求处理。
验证方式:运行程序时确保只传入syn=1,其他标志位都设为0,比如:
./your_program 1 0 0 0 0 0
2. 无效的确认序列号(ack_seq)
对于SYN包来说,ack_seq字段应该设为htonl(0),而不是htonl(1)。因为SYN是连接的第一个包,此时还没有任何需要确认的序列号,这个字段只有在ACK标志位开启时才有效。你现在设置的1会让远端认为这是一个无效的包,直接丢弃。
修改代码:
tcph->ack_seq = htonl(0); // 替换原来的1
3. 校验和函数的符号位bug
你的校验和函数使用了signed char(因为char在大多数系统中是带符号的),当处理值≥0x80的字节时,会因为符号扩展导致计算出错误的16位字值。比如字节0xFF作为带符号char是-1,强制转为uint16_t会变成0xFFFF,左移8位后再截断会得到错误的结果。
修复方法:先把buf转为unsigned char再处理:
unsigned short checksum(const char *buf, int size) { uint32_t sum = 0; uint16_t word16; int i = 0; const unsigned char *u_buf = (const unsigned char *)buf; // 转为无符号char指针 for (i = 0; i < size - 1; i += 2) { word16 = ((uint16_t)u_buf[i] << 8) | u_buf[i + 1]; sum += word16; } if (size & 1) { word16 = (uint16_t)u_buf[i] << 8; sum += word16; } while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } return htons((uint16_t)~sum); }
4. 缺少IP_HDRINCL套接字选项
如果你手动构造IP头,必须告诉内核不要自动添加IP头,否则内核会覆盖你自定义的IP头,导致源IP、校验和等字段不匹配。在创建原始套接字后,添加以下代码:
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); // ... 错误检查 ... int optval = 1; if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)) < 0) { perror("setsockopt IP_HDRINCL"); close(sock); exit(1); }
5. 其他小细节
- 确保你给
rand()设置了种子:在main开头添加srand(time(NULL));,否则每次运行程序都会生成相同的序列号(虽然这不会导致无响应,但不利于测试)。 - 用Wireshark仔细检查发送的SYN包:确认IP头的源/目的IP、TCP头的SYN标志位、ack_seq字段、校验和是否正确。如果校验和显示为"incorrect",那说明你的校验和函数确实有问题。
按照上面的步骤逐一排查,应该就能解决问题了。如果还是不行,可以把Wireshark捕获到的包详情贴出来,进一步分析。
内容来源于stack exchange

