1. 从一个真实场景说起:为什么你需要搞懂路由
很多人第一次接触“路由”这个词,是在家里换了新路由器、发现上不了网的时候。但如果你是一个开发者、运维人员,或者只是喜欢折腾自己那台 Linux 机器的人,路由这个概念迟早会以更硬核的方式出现在你面前——比如你发现容器里的服务访问不了外网,比如你搭了一台虚拟机却怎么都连不上宿主机,比如你手里有两块网卡却不知道流量到底走了哪一条。
路由的本质其实特别朴素:当一个数据包要离开本机时,操作系统需要决定把它从哪块网卡发出去、发给谁。这个“决定”的过程就是路由,而做决定的依据就是一张表——路由表。你可以把路由表想象成快递分拣中心墙上贴的那张派送区域对照表:看到某个地址段,就丢进对应的筐里,交给对应的车。没有这张表,数据包就只能在本地打转,出不了门。
这篇文章我想聊的是 Linux 下的路由概念和路由器设置。这里说的“路由器”有两层含义:一是你家里那台硬件设备,二是 Linux 主机本身被配置成一台路由器(也就是开启转发功能,帮别的机器转发数据包)。后者才是真正有意思、也真正容易踩坑的部分。我会从路由表怎么读、路由怎么加、转发怎么开、NAT 怎么做这几个角度,把整套东西讲透,并且给出可以直接抄的命令。适合有一定 Linux 基础、想搞清楚网络数据到底怎么走的人,也适合刚接触网络配置、被ip route输出搞得一头雾水的新手。
2. 路由的核心概念拆解:路由表到底在说什么
2.1 路由表的三要素:目标、下一跳、出口
先别急着敲命令,我们先把路由表里每一行的含义掰开。你用ip route show看到的典型输出大概长这样:
default via 192.168.1.1 dev eth0 proto static metric 100 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1每一行就是一条路由条目,它回答三个问题:
- 目标(Destination):这条路由是给谁用的。
default表示“其他所有没匹配上的都走我”,192.168.1.0/24表示这个网段。 - 下一跳(Gateway / via):包要交给谁。
via 192.168.1.1就是说交给网关,让它继续转发。注意直连网段是没有via的,因为它就在本地链路上,直接 ARP 找到对方 MAC 发过去就行。 - 出口(Device / dev):从哪块网卡出去。
dev eth0就是走 eth0。
还有一个容易被忽略但很重要的字段是src,它指定了从这条路由出去时,源 IP 用哪个。多网卡机器上这个字段经常决定你的服务能不能被正确访问到。
理解这三要素之后,很多“玄学”问题就有了解释。比如为什么我 ping 不通外网?先看有没有default路由。为什么两台机器互相访问不了?看目标网段的路由指向的出口对不对。
2.2 路由匹配规则:最长前缀优先
路由表里可能同时存在多条能“匹配”某个目标地址的条目,那系统选哪条?答案是最长前缀匹配(Longest Prefix Match)。
举个例子,假设表里有这三条:
default via 192.168.1.1 dev eth0 192.168.0.0/16 via 192.168.1.2 dev eth0 192.168.1.0/24 dev eth0现在你要访问192.168.1.50。三条都能匹配,但/24比/16比/0(default)更具体,所以选第三条,直接走本地链路。如果你访问192.168.5.10,/24不匹配了,/16匹配,走192.168.1.2。访问8.8.8.8,只有 default 匹配。
这个规则是路由的灵魂。很多人配了静态路由却发现不生效,往往就是因为有一条更具体的路由抢先匹配了。排查时用ip route get 目标IP最直接:
ip route get 8.8.8.8 # 输出:8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.100它会明确告诉你这个目标会走哪条路、用哪个源地址,比你自己在表里找快得多。
2.3 直连路由、静态路由、动态路由的区别
路由的来源大致分三类,理解来源有助于你知道“这条路由是谁加的、能不能删”。
直连路由是内核自动生成的。你给网卡配了 IP,内核就自动加一条该网段的路由,proto kernel就是标志。这种路由你一般不用手动管,网卡 down 了它也会消失。
静态路由是人工用ip route add加的,proto static。它不会自己变,适合网络结构固定的小环境。缺点是网络一变就得手动改。
动态路由是靠路由协议(比如 OSPF、BGP、RIP)自动学习交换的。Linux 上可以用 FRR、BIRD 这类软件跑协议。它的好处是网络拓扑变化时能自动收敛,适合多台路由器组网的场景。家用和小型办公基本用不上,但如果你要搭多子网互通的实验环境,了解一下没坏处。
提示:家用场景 99% 的情况只需要直连路由加一条 default 就够了。别一上来就想着搞动态路由,那是给自己找麻烦。
3. Linux 路由器设置实操:把一台机器变成路由器
3.1 准备工作:确认网卡与 IP 规划
要让一台 Linux 机器当路由器,它至少得有两块网卡(或者一块网卡配多个子接口),分别连接两个不同的网段。假设我们有一台机器,规划如下:
| 网卡 | 角色 | IP 地址 | 连接网段 |
|---|---|---|---|
| eth0 | 外网口 | 192.168.1.100/24 | 192.168.1.0/24(通往上联) |
| eth1 | 内网口 | 10.0.0.1/24 | 10.0.0.0/24(内网客户端) |
内网的客户端机器把网关指向10.0.0.1,也就是这台 Linux 路由器的内网口。上联那边假设有个网关192.168.1.1能出外网。
先用ip addr确认两块网卡都起来了、IP 配对了:
ip addr show eth0 ip addr show eth1如果 IP 还没配,用ip addr add临时加上(重启失效,持久化后面说):
ip addr add 192.168.1.100/24 dev eth0 ip addr add 10.0.0.1/24 dev eth1 ip link set eth0 up ip link set eth1 up这一步的关键是两个网段不能重叠。我见过有人把内外网都配成 192.168.1.x,结果路由表直接乱套,数据包不知道该往哪走。规划阶段就把网段分清楚,能省掉后面一堆麻烦。
3.2 开启内核转发:路由器的开关
Linux 默认是不转发数据包的——收到一个目标不是自己的包,直接丢掉。要当路由器,必须打开转发开关。这是整个设置里最关键的一步,也是最容易被忘的一步。
临时开启(立即生效,重启失效):
sysctl -w net.ipv4.ip_forward=1或者直接写 proc 文件:
echo 1 > /proc/sys/net/ipv4/ip_forward永久生效要改配置文件。在/etc/sysctl.conf或/etc/sysctl.d/下新建一个文件,加上:
net.ipv4.ip_forward = 1然后sysctl -p让它生效。我个人的习惯是单独建一个/etc/sysctl.d/99-router.conf,把路由相关的内核参数都放一起,方便管理和迁移。
注意:
sysctl -w改的是运行时值,重启就没了。生产环境一定要写进配置文件,否则某天重启后网络莫名其妙不通,排查半天才发现是转发没开。
验证是否开启:
cat /proc/sys/net/ipv4/ip_forward # 输出 1 就是开了3.3 配置路由与转发规则
转发开了之后,路由器本身能转发包了,但还有两件事要做:一是确保路由器自己知道怎么去各个网段,二是处理内网客户端出外网的问题。
先看路由表。正常情况下,配好 IP 后内核会自动生成两条直连路由:
10.0.0.0/24 dev eth1 proto kernel scope link src 10.0.0.1 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100但还缺一条 default 路由,让路由器自己知道出外网走哪:
ip route add default via 192.168.1.1 dev eth0到这里,路由器自己能上网了,内网客户端也能把包发到路由器了。但客户端访问外网时,包到了路由器,路由器转发出去,回包却回不来——因为外网只认识192.168.1.100,不认识内网的10.0.0.x。这就轮到 NAT 出场了。
3.4 NAT 配置:让内网机器共享外网出口
NAT(网络地址转换)的作用是把内网客户端的源地址改写成路由器的外网口地址,这样回包就能回到路由器,再由路由器转回内网。家用路由器天天在干这事,Linux 上靠 iptables 或 nftables 实现。
用 iptables 的 MASQUERADE(动态源地址转换,适合外网口 IP 不固定的情况):
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE这条规则的意思是:来自10.0.0.0/24的包,从 eth0 出去时,把源地址伪装成 eth0 的地址。
如果外网口 IP 是固定的,用 SNAT 更高效(省去每次查出口地址的开销):
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j SNAT --to-source 192.168.1.100还要放行转发链的流量,否则 FORWARD 链默认策略是 DROP 的话包过不去:
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT第二条尤其重要,它允许已建立连接的回包通过。少了它,出去的包能走,回来的包被拦,表现就是“能发不能收”。
提示:iptables 规则默认重启失效。持久化可以用
iptables-save > /etc/iptables/rules.v4,再配合开机加载。不同发行版路径不一样,Debian/Ubuntu 系常用iptables-persistent,RHEL 系用iptables-services。
3.5 用 nftables 的现代写法
新一点的发行版逐渐用 nftables 替代 iptables。同样的 NAT 需求,nftables 写法如下:
nft add table ip nat nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; } nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname "eth0" masquerade转发放行:
nft add table ip filter nft add chain ip filter forward { type filter hook forward priority 0 \; policy drop \; } nft add rule ip filter forward iifname "eth1" oifname "eth0" accept nft add rule ip filter forward iifname "eth0" oifname "eth1" ct state established,related acceptnftables 的语法更统一,性能也更好,新项目我一般直接上 nftables。但如果你维护的是老系统,iptables 依然是主流,两者原理完全一致,只是语法不同。
4. 路由配置的持久化与多网卡场景
4.1 让配置在重启后依然生效
前面很多命令都是临时的,重启就没了。真正要落地,得写进发行版的网络配置里。不同发行版差异很大,这里说几种常见的。
Netplan(Ubuntu 18.04+),编辑/etc/netplan/xx.yaml:
network: version: 2 ethernets: eth0: addresses: [192.168.1.100/24] routes: - to: default via: 192.168.1.1 eth1: addresses: [10.0.0.1/24]然后netplan apply。
NetworkManager(桌面版和很多服务器)用nmcli:
nmcli con mod eth0 ipv4.addresses 192.168.1.100/24 nmcli con mod eth0 ipv4.gateway 192.168.1.1 nmcli con mod eth0 ipv4.method manual nmcli con up eth0systemd-networkd则在/etc/systemd/network/下写.network文件。
我踩过的坑是:临时用ip route add加的路由,和配置文件里的路由可能冲突,重启后行为不一致。所以调试阶段可以用临时命令快速验证,但验证通过后一定要落到配置文件,并且重启一次确认无误。
4.2 多网卡多路由的优先级问题
一台机器有多块网卡、多条 default 路由时,到底走哪条?这涉及metric(度量值)。metric 越小优先级越高。你可以给不同路由设不同 metric 来控制主备:
ip route add default via 192.168.1.1 dev eth0 metric 100 ip route add default via 192.168.2.1 dev eth1 metric 200正常走 eth0,eth0 断了自动切 eth1。这就是最简单的双线备份。
还有一种情况是策略路由:不同来源的流量走不同出口。比如内网流量走 eth1,管理流量走 eth0。这需要用到路由表(routing table)和规则(rule):
ip rule add from 10.0.0.0/24 table 100 ip route add default via 192.168.1.1 dev eth0 table 100ip rule决定“什么样的包查哪张表”,ip route往指定表里加路由。默认表是 main(编号 254),你可以建自己的表。这套机制在容器网络、多租户环境里非常常见,值得花时间理解。
4.3 容器网络中的路由影子
如果你用过 Docker,其实已经在不知不觉中用到了 Linux 路由。Docker 默认会创建一个docker0网桥,给容器分配172.17.0.0/16的地址,并在宿主机路由表里加一条:
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1容器出外网靠的就是宿主机的转发加 NAT,和前面手动配的路由器原理一模一样。区别只是 Docker 帮你把这些命令自动化了。理解了手动配置,再看 Docker 的网络就通透多了——docker network inspect看到的网关、子网,本质就是路由表和网桥的配置。
5. 常见问题与排查技巧实录
5.1 排查思路:从下往上,逐层确认
网络不通时,最忌讳东一榔头西一棒子。我习惯按这个顺序查:
- 链路层:网卡起来了吗?
ip link show,看状态是不是 UP。 - IP 层:IP 配了吗?
ip addr show,看地址和掩码对不对。 - 路由层:路由表对吗?
ip route show,看有没有 default,目标网段指向对不对。 - 转发层:转发开了吗?
cat /proc/sys/net/ipv4/ip_forward。 - 过滤层:防火墙拦了吗?
iptables -L -n -v或nft list ruleset,看计数器有没有命中。 - NAT 层:
iptables -t nat -L -n -v,看 NAT 规则有没有匹配。
这个顺序能覆盖 90% 的问题。下面整理几个高频故障。
5.2 常见问题速查表
| 现象 | 可能原因 | 排查命令 | 解决方向 |
|---|---|---|---|
| 内网能通路由器,出不了外网 | 没开转发 / 没配 NAT | cat /proc/sys/net/ipv4/ip_forward;iptables -t nat -L | 开转发,加 MASQUERADE |
| 能 ping 通 IP,域名解析不了 | DNS 没配 | cat /etc/resolv.conf | 给客户端配 DNS |
| 出去的包有,回包没有 | FORWARD 链拦了回包 | iptables -L FORWARD -v | 放行 ESTABLISHED,RELATED |
| 两条 default 路由,走错出口 | metric 相同或顺序问题 | ip route get 目标IP | 调整 metric |
| 重启后配置全没了 | 没做持久化 | 检查网络配置文件 | 写入 netplan/nmcli 配置 |
| 容器访问不了外网 | 宿主机转发或 NAT 缺失 | sysctl net.ipv4.ip_forward | 开转发,检查 docker 的 NAT 链 |
5.3 几个我踩过的坑
坑一:改了 iptables 规则把自己锁在外面。远程操作防火墙时,如果默认策略设成 DROP 又没放行 SSH,连接立刻断。我的做法是:改规则前先加一条放行自己 IP 的规则,或者用at定时任务在几分钟后自动恢复规则,给自己留后路。
坑二:MASQUERADE 和 SNAT 混用导致回包异常。同一个出口同时配了两条 NAT 规则,包被转两次,源地址变得莫名其妙。规则要精简,一条出口一条 NAT 规则就够。
坑三:网段规划重叠。内网用 192.168.1.0/24,上联也是 192.168.1.0/24,路由表里两条直连路由打架。规划阶段就把网段错开,比如内网用 10.x 或 172.16.x。
坑四:忘了 conntrack 表满。高并发场景下,NAT 依赖的连接跟踪表可能被打满,新连接建不起来。可以调net.netfilter.nf_conntrack_max,并监控nf_conntrack_count。
提示:排查时
tcpdump是终极武器。在路由器两个口分别抓包,看包进没进、出没出、源目地址对不对,比盯着配置猜快得多。tcpdump -i eth1 -nn icmp抓内网口的 ping 包,一眼就能看出问题在哪一层。
6. 写在最后的一点个人体会
路由这东西,看文档觉得简单,真上手配才发现细节全是坑。我的经验是:先把一台机器的路由配通,再考虑多台组网;先用临时命令验证,再落到配置文件;先保证能通,再考虑优化和策略。别一上来就追求完美架构,网络问题最怕的就是一次改太多,出了问题不知道是哪一步导致的。
另外,ip route get和tcpdump这两个工具,我建议你练到条件反射的程度。前者告诉你“系统打算怎么走”,后者告诉你“实际怎么走的”,两者一对比,绝大多数路由问题都无处遁形。至于 iptables 和 nftables,选一个学透就行,原理是通的,语法查手册即可。真正难的不是命令,而是脑子里那张数据包流动的地图——这张地图建起来了,配什么都是顺水推舟。