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

原始套接字是否受iptables规则影响?实践中遇规则阻止发包的矛盾问题

原始套接字是否受iptables规则影响?实践中遇规则阻止发包的矛盾问题

我之前从不少技术资料里了解到,原始套接字(raw socket)是不会受iptables规则影响的,但最近在Debian GNU/Linux 12上实操时,却碰到了iptables阻止我通过原始套接字发包的情况,这明显和之前的认知矛盾,下面是我复现问题的完整步骤,以及对这个矛盾点的分析:


复现步骤

    1. 创建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;
}
    1. 编译代码:gcc main.c -o raw-socket-test
    1. 添加iptables规则,阻止发往8.8.8.8:80的TCP流量:iptables -A OUTPUT -d 8.8.8.8 -p tcp --dport 80 -j DROP
    1. 以root权限运行程序:sudo ./raw-socket-test,此时会收到Failed to send packet: Permission denied或者类似的发包失败提示,说明iptables规则生效了。

矛盾点的核心原因

其实这个认知偏差的关键在于原始套接字的两种工作模式:

  1. 内核辅助构造IP头模式(默认):
    当你创建SOCK_RAW套接字但没有设置IP_HDRINCL选项时,内核会帮你自动构造IP头。这时候数据包的处理流程和普通套接字类似,会经过iptables的OUTPUT链——内核需要对即将发出的数据包做常规的过滤、连接跟踪等处理,所以你设置的DROP规则会生效,阻止发包。

  2. 用户态构造完整数据包模式:
    只有当你通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 11:14:40