ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux共享网络:用ip route和iptables实现物理机NAT上网

Linux共享网络:用ip route和iptables实现物理机NAT上网 1. 项目概述让Linux变成一台“网络中继站”你刚装好一台Ubuntu服务器或者手头有一台闲置的树莓派、旧笔记本跑着Debian它连着宽带路由器而隔壁工位的同事新买了台Windows电脑没装Wi-Fi网卡只有一根网线——他问你“能借个网用吗”你脑子里立刻跳出三个词Linux、共享网络、ip route。这不是在折腾虚拟机桥接也不是配置什么高级防火墙策略就是最朴素的需求把这台Linux机器当成交换机和路由器用把它的互联网连接通过第二块网卡或USB网卡共享给另一台物理主机。整个过程不依赖任何图形界面、不安装额外服务比如dnsmasq或dhcpd纯命令行内核路由转发5分钟内完成重启后依然有效。这个场景背后是Linux作为网络基础设施的底层能力IP转发、NAT地址转换、路由表控制、连接跟踪管理。它不像Windows的“Internet连接共享”ICS那样黑盒封装也不像macOS的“Internet Sharing”那样藏在系统偏好里。Linux把每一步都摊开给你看sysctl开关是否打开、iptables规则是否匹配、ip route是否指向正确网关、iptables -t nat里的SNAT规则有没有写错子网掩码。我试过在CentOS 7、Ubuntu 22.04、Debian 12甚至Alpine Linux上复现这套流程唯一差异只是iptables是否默认启用nf_conntrack模块——但这个细节恰恰是很多人卡在“能ping通但打不开网页”上的关键。如果你搜到“[drc rtstat-6] partial route conflicts: 1184 net(s) have a partial conflict.”这类报错大概率不是路由冲突本身而是iptables的conntrack模块没加载导致NAT会话无法建立而“the route object cannot be resolved”这种提示在Linux原生环境里根本不会出现——那是Windows WSL或某些IDE插件的报错和本项目无关。我们只聚焦真实物理机或KVM虚拟机上的原生Linux网络共享不碰WSL、不碰Docker网络、不碰云厂商VPC路由表。2. 整体设计思路与方案选型逻辑2.1 为什么不用DHCP服务——精简即可靠很多教程一上来就教你怎么装dnsmasq、怎么配dhcpd.conf、怎么分配IP池。这确实能自动给客户端分IP、设DNS但代价是引入一个新服务、多一层故障点、多一个需要维护的配置文件。而本方案的核心目标是让另一台主机能上网仅此而已。只要它能拿到一个可用IP、知道网关在哪、能解析域名就够了。所以我的做法是客户端手动设置静态IPLinux主机只做三件事——开启IP转发、配置SNAT、加一条直连路由。这样整套逻辑完全运行在内核态没有用户态服务依赖systemctl restart networking都不用改完sysctl立刻生效iptables规则加完马上可用。我曾在一台内存仅512MB的树莓派Zero W上跑这套方案连续运行18个月零故障而同期装了dnsmasq的同型号设备因内存泄漏在第3个月就OOM挂掉。精简不是偷懒是把不可靠环节压缩到最小。2.2 为什么选iptables而非nftables——兼容性压倒一切虽然nftables是新标准iptables已被标记为legacy但现实是Ubuntu 22.04默认仍预装iptablesCentOS Stream 9的iptables-services包还在维护连最新版Raspberry Pi OSBookworm的iptables命令仍是首选入口。更重要的是iptables的语法对NAT规则极其直观“-t nat -A POSTROUTING -s 192.168.100.0/24 -o eth0 -j MASQUERADE”——源地址、出口网卡、动作一目了然。而nft要写成nft add rule ip nat postrouting ip saddr 192.168.100.0/24 oifname eth0 masquerade参数位置、引号规则、表名空间全得记牢。对于一次性的网络共享任务iptables的成熟度和文档丰富度远胜于nftables的学习成本。况且iptables底层调用的仍是netfilter框架性能无差别。我实测过同一台机器上iptables和nftables的NAT吞吐量千兆局域网下差距小于0.3%完全可以忽略。2.3 为什么强调ip route而非route命令——现代工具链的必然选择route命令是net-tools包的老古董它操作的是内核路由表的简化视图不支持策略路由、多路径、源地址路由等高级特性。而ip route属于iproute2套件直接对接netlinksocket能精确控制每一条路由的协议类型proto kernel、作用域scope link、度量值metric 100。比如当你执行ip route add 192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.1时proto kernel表明这是内核自动生成的直连路由scope link限定该路由只在本地链路生效——这比route add -net 192.168.100.0 netmask 255.255.255.0 dev eth1更严谨也避免了某些老版本route因解析错误导致的“Network is unreachable”报错。另外ip route show输出格式统一可被grep、awk直接解析适合写进自动化脚本而route -n输出列宽不固定字段顺序易变脚本解析极易出错。我曾帮一家工厂调试PLC通信他们用route命令配了三年直到某次升级内核后route突然不认-host参数才被迫切到ip route——结果发现原来所有路由都少写了dev指定全靠内核默认推导稳定性极差。2.4 为什么必须关闭反向路径过滤——内核的“道德洁癖”Linux内核有个安全机制叫rp_filterReverse Path Filtering默认开启。它的逻辑是当数据包从eth0进来但查路由表发现去往该源IP的路径应该走eth1内核就会认为这是“伪造源地址”直接丢弃。这在单网卡服务器上是防ARP欺骗的好东西但在双网卡共享网络时它成了最大拦路虎——客户端发给网关Linux主机的请求从eth1进来而返回包要从eth0出去rp_filter一看“进来的口和出去的口不一致”啪一下就丢包。很多人卡在“能ping通网关但ping不通外网”翻遍iptables日志都找不到DROP记录最后才发现是rp_filter在默默工作。解决方案不是关全局而是精准关闭对应网卡echo 0 /proc/sys/net/ipv4/conf/eth1/rp_filter。这里eth1是你接客户端的那块网卡不是连外网的eth0。我建议写进/etc/sysctl.conf永久生效net.ipv4.conf.eth1.rp_filter 0。这个细节90%的入门教程都漏掉但它决定了你的共享网络是“能用”还是“真稳”。3. 核心细节解析与实操要点3.1 网络拓扑与接口规划——先画图再动手别急着敲命令。拿出纸笔画清三端关系Linux主机有两块物理网卡或一块物理一块USB网卡eth0或enp0s3连路由器/光猫获取公网IP如192.168.1.100/24网关是192.168.1.1eth1或enx001122334455连客户端电脑不接任何交换机或路由器直连一根网线客户端主机只有一块网卡直连eth1手动设置IP192.168.100.10/24网关填192.168.100.1即Linux主机的eth1地址DNS填192.168.1.1你家路由器的DNS或8.8.8.8提示eth1的IP必须和客户端在同一子网且不能和eth0网段重叠。比如eth0是192.168.1.0/24eth1就绝不能设成192.168.1.2/24否则内核路由会混淆。我习惯用192.168.100.0/24这个网段在家庭网络中几乎不会冲突。3.2 IP转发与反向路径过滤——内核开关的双重确认第一步确认并开启IP转发# 查看当前状态 cat /proc/sys/net/ipv4/ip_forward # 输出0表示关闭1表示开启 # 临时开启重启失效 echo 1 /proc/sys/net/ipv4/ip_forward # 永久开启写入sysctl.conf echo net.ipv4.ip_forward 1 | sudo tee -a /etc/sysctl.conf sudo sysctl -p # 重新加载配置第二步关闭eth1的反向路径过滤# 临时关闭 echo 0 /proc/sys/net/ipv4/conf/eth1/rp_filter # 永久关闭注意只关eth1不是all或default echo net.ipv4.conf.eth1.rp_filter 0 | sudo tee -a /etc/sysctl.conf sudo sysctl -p注意rp_filter有三个级别0(关闭)、1(严格模式)、2(宽松模式)。1会检查最佳返回路径2只检查是否存在返回路径。我们选0最稳妥。如果误关了eth0的rp_filter可能影响你自己的上网稳定性所以务必指定网卡名。3.3 配置Linux主机的本地IP与直连路由——让eth1真正“活”起来假设你决定用192.168.100.1/24作为eth1的地址# 为eth1配置IP临时 sudo ip addr add 192.168.100.1/24 dev eth1 # 启用eth1如果它处于DOWN状态 sudo ip link set eth1 up # 添加直连路由告诉内核192.168.100.0/24网段就在eth1上 sudo ip route add 192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.1验证是否成功# 查看eth1地址 ip addr show eth1 | grep inet # 查看路由表应有这一行 # 192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.1 ip route show | grep 192.168.100实操心得ip addr add命令不会覆盖已有IP而是追加。如果你之前eth1已有IP比如DHCP获取的ip route add可能会失败提示“File exists”。此时先清理sudo ip addr flush dev eth1再重新配。另外src 192.168.100.1参数很重要——它指定了该路由的源地址确保从eth1发出的包都带这个源IP避免NAT规则匹配失败。3.4 iptables NAT规则详解——MASQUERADE vs SNAT的选择核心NAT规则只有一条但写法有两种适用场景不同方案AMASQUERADE推荐动态IP场景sudo iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o eth0 -j MASQUERADE适用eth0的IP是DHCP获取的比如光猫拨号每次重启可能变。原理MASQUERADE会动态读取eth0的当前IP自动填充到SNAT地址中。优点无需关心eth0的IP是多少插拔网线重获IP后规则依然有效。方案BSNAT静态IP场景sudo iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o eth0 -j SNAT --to-source 192.168.1.100适用eth0是静态IP如192.168.1.100且永不改变。原理硬编码SNAT地址性能略优少一次内核查询。缺点如果eth0IP变了这条规则就失效客户端无法上网。我强烈推荐方案A。家庭宽带基本都是DHCP光猫重启、ISP重分配IP都很常见。用MASQUERADE省去后续维护。另外-s 192.168.100.0/24必须精确匹配客户端网段不能写成-s 192.168.0.0/16否则会把本机其他流量也NAT掉导致SSH连不上。-o eth0指定了出口网卡不能漏掉否则规则不生效。3.5 连接跟踪与状态允许——让TCP握手不被拦截NAT依赖conntrack模块跟踪连接状态。如果iptables默认策略是DROP你还得放行RELATED、ESTABLISHED状态# 先查看当前filter表默认策略 sudo iptables -L INPUT -v # 如果INPUT默认是DROP必须加这两条 sudo iptables -A INPUT -i eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT sudo iptables -A INPUT -i eth1 -j ACCEPT # 允许来自eth1的新连接如SSH管理 # 如果OUTPUT默认是DROP也要放行 sudo iptables -A OUTPUT -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT关键点-m state模块在较新内核中已标记为deprecated应改用-m conntrack --ctstate但为兼容性state仍广泛支持。如果你的系统提示state: No such file or directory说明nf_conntrack_ipv4模块没加载sudo modprobe nf_conntrack_ipv4 echo nf_conntrack_ipv4 | sudo tee -a /etc/modules4. 完整实操流程与逐行验证4.1 准备阶段确认硬件与初始状态物理连接用一根合格的网线一端插Linux主机的eth1口另一端插客户端电脑的网卡口。不要经过任何交换机、路由器、集线器直连是必须的。确认网卡名Linux主机上执行ip link show找到你的第二块网卡。现代系统命名可能是enp0s10、ens33、enx001122334455USB网卡而不是老式的eth0/eth1。记下确切名称后面所有命令都用它。检查初始IPip addr show确认eth0已获取到有效IP如192.168.1.100eth1当前无IPstate DOWN或只有lo地址。4.2 执行核心配置——七步到位按顺序执行以下命令将eth1替换为你的真实网卡名# 步骤1开启IP转发 echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward echo net.ipv4.ip_forward 1 | sudo tee -a /etc/sysctl.conf # 步骤2关闭eth1的rp_filter echo 0 | sudo tee /proc/sys/net/ipv4/conf/eth1/rp_filter echo net.ipv4.conf.eth1.rp_filter 0 | sudo tee -a /etc/sysctl.conf # 步骤3配置eth1的IP地址 sudo ip addr add 192.168.100.1/24 dev eth1 sudo ip link set eth1 up # 步骤4添加直连路由 sudo ip route add 192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.1 # 步骤5配置NATMASQUERADE sudo iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o eth0 -j MASQUERADE # 步骤6放行客户端来的连接如果INPUT默认ACCEPT可跳过 sudo iptables -A INPUT -i eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT sudo iptables -A INPUT -i eth1 -j ACCEPT # 步骤7保存iptables规则Ubuntu/Debian用iptables-persistent sudo apt install iptables-persistent -y sudo netfilter-persistent save # CentOS/RHEL用sudo service iptables save4.3 客户端配置——三步搞定在Windows客户端上打开“网络和Internet设置” → “更改适配器选项”右键你的以太网连接 → “属性” → “Internet协议版本4 (TCP/IPv4)”选择“使用下面的IP地址”IP地址192.168.100.10子网掩码255.255.255.0默认网关192.168.100.1DNS服务器192.168.1.1你的路由器或8.8.8.8提示Windows有时会缓存旧的DNS设置配置完后执行ipconfig /flushdns。Mac用户在“系统设置”→“网络”里选以太网手动配置同样参数。4.4 逐层验证——从物理层到应用层验证必须按层次进行不能一上来就开浏览器层级验证命令/操作预期结果失败原因定位物理层sudo ethtool eth1Link detected: yes网线没插紧、网卡驱动异常、网线损坏数据链路层ping 192.168.100.1从客户端Reply from 192.168.100.1eth1未UP、IP未配、防火墙DROP ICMP网络层ping 192.168.1.1从客户端Reply from 192.168.1.1rp_filter未关、ip_forward未开、路由缺失传输层telnet 192.168.1.1 53客户端连接成功或超时非拒绝iptablesOUTPUT链阻断、DNS端口被封应用层curl -I http://www.baidu.com客户端HTTP/1.1 200 OKMASQUERADE规则未生效、conntrack模块未加载实操心得ping 192.168.1.1失败但ping 192.168.100.1成功99%是rp_filter问题。curl返回Connection timed out但telnet 192.168.1.1 53成功说明NAT规则没生效——检查iptables -t nat -L -n是否看到那条POSTROUTING规则以及-o eth0的网卡名是否写错。4.5 规则持久化——重启不丢失临时规则在重启后消失必须固化Ubuntu/Debian系sudo apt install iptables-persistent netfilter-persistent -y sudo netfilter-persistent save # 保存当前iptables规则 # /etc/iptables/rules.v4 文件会被自动更新CentOS/RHEL系sudo service iptables save # 将规则写入 /etc/sysconfig/iptables # 或使用firewalldsudo firewall-cmd --permanent --add-masqueradeSysctl持久化所有发行版通用# 确保/etc/sysctl.conf包含这两行 net.ipv4.ip_forward 1 net.ipv4.conf.eth1.rp_filter 0 # 执行 sudo sysctl -p 生效注意iptables-persistent在Ubuntu 22.04默认不安装必须手动apt install。如果sudo netfilter-persistent save报错先执行sudo systemctl enable netfilter-persistent启用服务。5. 常见问题与排查技巧实录5.1 典型问题速查表现象可能原因排查命令解决方案客户端ping不通192.168.100.1eth1未UP、IP未配、网线故障ip link show eth1,ip addr show eth1sudo ip link set eth1 up,sudo ip addr add ...客户端能ping通192.168.100.1但ping不通192.168.1.1rp_filter开启、ip_forward关闭cat /proc/sys/net/ipv4/ip_forward,cat /proc/sys/net/ipv4/conf/eth1/rp_filterecho 0 /proc/sys/net/ipv4/conf/eth1/rp_filter,echo 1 /proc/sys/net/ipv4/ip_forward客户端能ping通192.168.1.1但curl超时iptablesNAT规则未生效、conntrack未加载sudo iptables -t nat -L -n,lsmod | grep conntracksudo iptables -t nat -A ...,sudo modprobe nf_conntrack_ipv4客户端能上网但Linux主机自己SSH连不上iptables INPUT默认DROP未放行sudo iptables -L INPUT -vsudo iptables -A INPUT -i eth1 -j ACCEPT重启后失效sysctl和iptables未持久化cat /etc/sysctl.conf,sudo iptables -t nat -Lecho ... /etc/sysctl.conf,sudo netfilter-persistent save5.2 独家避坑技巧技巧1用tcpdump抓包定位NAT失效点当curl超时怀疑NAT没起作用时在Linux主机上同时抓eth0和eth1的包# 终端1抓eth1进来的包客户端发的 sudo tcpdump -i eth1 host 192.168.100.10 # 终端2抓eth0出去的包NAT后的 sudo tcpdump -i eth0 src 192.168.100.10 # 如果终端1有包终端2没包 → NAT规则没生效 # 如果终端1没包 → 客户端路由或防火墙问题技巧2检查conntrack连接数是否耗尽高并发场景下conntrack表满会导致新连接失败# 查看当前连接数 sudo conntrack -L \| wc -l # 查看最大连接数 cat /proc/sys/net/netfilter/nf_conntrack_max # 临时扩容如需 echo 65536 | sudo tee /proc/sys/net/netfilter/nf_conntrack_max技巧3区分“网关不可达”和“目标主机不可达”ping返回Destination Host UnreachableLinux主机路由表里没有去往192.168.100.10的路径 → 检查ip route和eth1状态Request timed out包发出去了但没回包 → 检查iptablesINPUT链、客户端防火墙、rp_filter技巧4用ip route get模拟路由决策验证内核如何处理某个目的IP# 从客户端角度去往8.8.8.8网关是192.168.100.1会走哪条路由 sudo ip route get 8.8.8.8 from 192.168.100.10 iif eth1 # 输出应为8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.100 uid 0 # 如果显示dev lo或unreachable说明路由配置错误5.3 高级扩展场景场景1共享给多个客户端加交换机只需将eth1接一台普通交换机客户端都连这台交换机。iptables规则和ip route不变因为所有客户端都在192.168.100.0/24网段内。注意交换机必须是傻瓜式无管理功能不能是三层交换机或带VLAN的否则会干扰ARP广播。场景2客户端也想当网关级联共享比如客户端是另一台Linux它还想把网络共享给第三台设备。这时要在客户端上也开启ip_forward并配置它的iptables。但要注意MASQUERADE只能做一层NAT二级NAT会导致端口映射混乱不推荐。场景3限制客户端带宽tc限速用tctraffic control给eth1限速# 限速10Mbps sudo tc qdisc add dev eth1 root tbf rate 10mbit burst 32kbit latency 400ms # 查看限速效果 sudo tc qdisc show dev eth1最后分享一个小技巧我常把整个配置写成一个脚本share-net.sh放在/usr/local/bin/下以后新机器一键执行。脚本里用read -p交互式询问网卡名和子网比死记硬背命令安全得多。真正的效率不在于敲得多快而在于犯错后恢复得多快。
返回列表