Flyinsky
Flyinsky3月1日

nftables 内核端口转发TCP流量

记录如何用 nftables 在内核层完成 TCP 端口转发,包含规则编写、转发思路和常见排错方法。

nftables 内核端口转发TCP流量 1

1)你需要准备什么

  • 一台 入口机:有公网 IP,负责接收外部连接

  • 一台 落地机:运行真实服务,入口机把流量转发到它

  • 明确三样参数:

    • 落地机 IP(DST_IP)

    • 入口机公网网卡名(一般 eth0

    • 要转发的端口(可 1 个或多个)


2)安装 nftables 并启用服务

apt update
apt install -y nftables
systemctl enable --now nftables

3)写入 nftables 规则(DNAT + 仅对 DNAT 连接做 masquerade)

下面这份规则具备几个关键点:

  • prerouting 做 DNAT:把入口机的端口转到落地机

  • postrouting 做 SNAT/masquerade:只对“被 DNAT 的连接”做伪装,避免误伤本机其它直连

  • forward 链过滤:默认 drop,仅放行 DNAT 新建连接与已建立连接

  • MSS clamp(可选但强烈建议):降低 PMTU/MSS 引发的重传/吞吐抖动,对“落地爆 CPU/小包放大”常见问题很有帮助

你只要替换:落地IP、端口、网卡名(如有需要)

cat <<'EOF' > /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset

define DST_IP  = 落地IP
define WAN_IF  = "eth0"

# ====== MSS clamp:降低 PMTU/MSS 引发的重传/小包放大 ======
# 保守 MSS=1360,优先求稳;确认整条路径 MTU=1500 且无隧道时可调到 1400/1420
table inet mangle {
  chain forward {
    type filter hook forward priority -150; policy accept;
    ip daddr $DST_IP tcp dport { 端口1, 端口2 } tcp flags syn tcp option maxseg size set 1360
  }
}

# ====== NAT ======
table ip nat {
  set dst_ports {
    type inet_service;
    elements = { 端口1, 端口2 }
  }

  chain prerouting {
    type nat hook prerouting priority -100; policy accept;

    # 只处理公网口进来的新连接(多端口就多写几行)
    iifname $WAN_IF tcp dport 端口1 ct state new dnat to $DST_IP:目标端口1
    iifname $WAN_IF tcp dport 端口2 ct state new dnat to $DST_IP:目标端口2
  }

  chain postrouting {
    type nat hook postrouting priority 100; policy accept;

    # 仅对“被 DNAT 的连接”做 masquerade,避免误伤本机直连 DST_IP
    ct status dnat ip daddr $DST_IP tcp dport @dst_ports masquerade
  }
}

# ====== 转发过滤 ======
table inet filter {
  chain forward {
    type filter hook forward priority 0; policy drop;

    ct state invalid drop
    ct state established,related accept

    # 仅放行被 DNAT 的新建连接
    ct status dnat ip daddr $DST_IP tcp dport { 端口1, 端口2 } ct state new accept
  }
}
EOF

端口写法说明(很重要)

  • { 端口1, 端口2 } 是“数组”

  • 只有一个端口就写 { 57559 }(不需要逗号)

  • prerouting每个端口要写一行 dnat(最直观、最不容易错)

  • 建议入站端口 = 目标端口(省心),当然也可以不同

网卡名怎么确认

ip addr

看带公网 IP 的网卡,一般就是 eth0


4)开启 IPv4 转发 + sysctl 参数

cat <<'EOF' > /etc/sysctl.d/99-nft-forward.conf
# IPv4 转发
net.ipv4.ip_forward = 1

# 更积极的 MTU 探测:转发链路常见“吞吐抖动/重传放大”时很有帮助
net.ipv4.tcp_mtu_probing = 1

# conntrack:按需调整(保守值)
net.netfilter.nf_conntrack_max = 32768
net.netfilter.nf_conntrack_buckets = 8192
EOF

5)应用配置(立即生效)

sysctl --system
nft -f /etc/nftables.conf

6)验证是否生效(建议做)

查看规则是否加载:

nft list ruleset

检查入口机是否真的在转发(有连接时看计数会涨):

nft list chain ip nat prerouting
nft list chain inet filter forward

看 conntrack(有新连接时会出现记录):

conntrack -L 2>/dev/null | head

如果没有 conntrack 命令:apt install -y conntrack


7)单端口示例(可直接抄)

假设只转发 57559 到落地机 198.1.1.1:57559

#!/usr/sbin/nft -f
flush ruleset

define DST_IP  = 198.1.1.1
define WAN_IF  = "eth0"

table inet mangle {
  chain forward {
    type filter hook forward priority -150; policy accept;
    ip daddr $DST_IP tcp dport { 57559 } tcp flags syn tcp option maxseg size set 1360
  }
}

table ip nat {
  set dst_ports {
    type inet_service;
    elements = { 57559 }
  }

  chain prerouting {
    type nat hook prerouting priority -100; policy accept;
    iifname $WAN_IF tcp dport 57559 ct state new dnat to $DST_IP:57559
  }

  chain postrouting {
    type nat hook postrouting priority 100; policy accept;
    ct status dnat ip daddr $DST_IP tcp dport @dst_ports masquerade
  }
}

table inet filter {
  chain forward {
    type filter hook forward priority 0; policy drop;
    ct state invalid drop
    ct state established,related accept
    ct status dnat ip daddr $DST_IP tcp dport { 57559 } ct state new accept
  }
}

8)完成后的使用方式

到此 入口机转发已完成。你只需要在 落地机 上:

  • 在对应端口启动你的服务

  • 外部用户连接入口机的端口,即会被内核转发到落地机

Linux#Linux#nftables#端口转发
浏览量368
暂无评论,来说第一句吧
← 下一条Debian/Ubuntu 服务器一键提升:Zsh + Oh My Zsh + Spaceship 主题 + 自动补全/高亮 + Fail2Ban 防爆破上一条 →FRP 内网穿透实战:FRPS + FRPC 配置教程(Linux)
登录后发表评论