原始套接字是否受iptables规则影响?实践中遇规则阻止发包的矛盾问题
原始套接字是否受iptables规则影响?实践中遇规则阻止发包的矛盾问题
我之前从不少技术资料里了解到,原始套接字(raw socket)是不会受iptables规则影响的,但最近在Debian GNU/Linux 12上实操时,却碰到了iptables阻止我通过原始套接字发包的情况,这明显和之前的认知矛盾,下面是我复现问题的完整步骤,以及对这个矛盾点的分析:
复现步骤
- 创建
main.c文件,代码如下:
- 创建
#include <errno.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> int main(void) { // 创建TCP类型的原始套接字 int ip_sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); if (ip_sock_fd == -1) { fprintf(stderr, "Failed to create socket: %s\n", strerror(errno)); exit(1); } // 设置目标地址为8.8.8.8的80端口 struct sockaddr_in dest_addr = { .sin_family = AF_INET, .sin_addr = (struct in_addr){.s_addr = htonl(0x08080808)}, // 8.8.8.8 .sin_port = htons(80), }; // 构造HTTP请求 payload char payload[] = "GET / HTTP/1.1\r\nHost: 8.8.8.8\r\n\r\n"; // 发送数据包 if (sendto(ip_sock_fd, payload, sizeof(payload) - 1, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) == -1) { fprintf(stderr, "Failed to send packet: %s\n", strerror(errno)); close(ip_sock_fd); exit(1); } close(ip_sock_fd); return 0; }
- 编译代码:
gcc main.c -o raw-socket-test
- 编译代码:
- 添加iptables规则,阻止发往8.8.8.8:80的TCP流量:
iptables -A OUTPUT -d 8.8.8.8 -p tcp --dport 80 -j DROP
- 添加iptables规则,阻止发往8.8.8.8:80的TCP流量:
- 以root权限运行程序:
sudo ./raw-socket-test,此时会收到Failed to send packet: Permission denied或者类似的发包失败提示,说明iptables规则生效了。
- 以root权限运行程序:
矛盾点的核心原因
其实这个认知偏差的关键在于原始套接字的两种工作模式:
内核辅助构造IP头模式(默认):
当你创建SOCK_RAW套接字但没有设置IP_HDRINCL选项时,内核会帮你自动构造IP头。这时候数据包的处理流程和普通套接字类似,会经过iptables的OUTPUT链——内核需要对即将发出的数据包做常规的过滤、连接跟踪等处理,所以你设置的DROP规则会生效,阻止发包。用户态构造完整数据包模式:
只有当你通过setsockopt设置IP_HDRINCL选项,完全由用户态代码构造整个IP数据包(包括IP头、传输层头和payload)时,内核才会跳过iptables的部分链(比如OUTPUT链)的处理,直接把数据包发送到网络设备,这时候iptables规则就不会影响它了。
验证方法
修改代码,添加IP_HDRINCL选项并构造完整的IP+TCP数据包,再测试:
在创建套接字成功后,添加以下代码:
int optval = 1; // 设置IP_HDRINCL选项,告诉内核我们自己构造IP头 if (setsockopt(ip_sock_fd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)) == -1) { fprintf(stderr, "Failed to set IP_HDRINCL: %s\n", strerror(errno)); close(ip_sock_fd); exit(1); }
然后自行构造完整的IP头和TCP头(这部分需要手动计算校验和等字段),再重新编译运行,你会发现即使iptables的DROP规则存在,数据包依然能正常发送。
备注:内容来源于stack exchange,提问作者dipea
相关产品推荐
相关产品推荐

