网络基础
网络基础是本篇的入口。 这里不追求记住每个协议号, 而是回答三个问题: 为什么要分层、 一个数据包如何找到它的目的地、 Linux 用哪条命令能看到正在发生的事。 把这三件事想透, 后面配置、排障、防火墙各篇 都只是在具体场景里复用同一套模型。
内容参考自 Arch Wiki、 RFC 791/793/1122 与鸟哥的私房菜, 见文末参考资料。
学习目标
- 说清网络为什么要分层,以及分层如何带来可替换、可定位、可分工
- 掌握 IP/子网/路由的基本计算,能读懂
ip route的输出 - 理解 DNS 解析链路,知道从
/etc/hosts到递归查询的顺序 - 分清传输层端口与套接字,能用
ss观察连接状态 - 熟练使用
ip与ss观察本机网络,作为后续排障的统一入口 - 对照三发行版网络工具安装差异,知道从哪查起
1. 为什么要分层
1.1 分层解决的三个现实问题
早期的网络程序 把"物理连接怎么走" 和"数据内容怎么解析" 写在同一段代码里, 结果换一块网卡 就要重写全部逻辑。 分层本质上是契约化: 每一层只对上下层承诺有限的接口, 内部怎么实现可以随意更换。 这带来三个直接好处:
- 可替换。 以太网换成 Wi-Fi, 只影响链路层, IP 和 TCP 完全无感; IPv4 升级到 IPv6, 应用层代码也大多不用动。
- 可定位。 故障发生时, 你先判断它属于哪一层, 再用那一层的工具取证, 而不是对着"上不了网"四个字盲猜。 这正是网络故障排除 按层推理的理论依据。
- 可分工。 内核只负责到 IP/端口的交付, 应用层协议(HTTP、DNS) 由用户态处理—— 内核与应用的边界由此清晰。
如果把所有逻辑混在一层, 扩展、排障、协作成本都会失控: 网络工程师改路由要动应用代码, 应用加一个字段要重启整条协议栈。 分层把变更半径限制在单层之内, 这也是互联网能持续演进五十年 而核心协议基本未变的结构性原因。
1.2 OSI 七层与 TCP/IP 四层
OSI 七层模型是教学标准, TCP/IP 四层是互联网的实际运行模型。 对照记忆即可, 日常排障用四层更顺手:
| OSI 层 | TCP/IP 层 | 传输单元 | 典型协议/设备 | 排障对应工具 |
|---|---|---|---|---|
| 7 应用层 | 应用层 | 数据 | HTTP、DNS、SSH | curl、dig、应用日志 |
| 6 表示层 | 应用层 | 数据 | TLS、编码 | openssl s_client |
| 5 会话层 | 应用层 | 数据 | RPC、会话保持 | 应用日志 |
| 4 传输层 | 传输层 | 段(Segment) | TCP、UDP | ss、nc -zv |
| 3 网络层 | 网际层 | 包(Packet) | IP、ICMP、路由 | ip route、ping、traceroute |
| 2 数据链路层 | 网络接口层 | 帧(Frame) | Ethernet、ARP、VLAN | ip link、ip neigh、交换机 |
| 1 物理层 | 网络接口层 | 比特 | 网线、光纤、射频 | ethtool eth0、链路灯 |
值得注意的是, OSI 的 5/6/7 层 在 TCP/IP 中合并为一层—— 实践中没人给"表示层"单独抓包。 面试背七层,干活用四层, 两者并不矛盾: 七层帮你把概念说清楚, 四层帮你把工具选对。
1.3 封装:数据如何穿过各层
数据从应用发出时自上而下封装, 到达对端后自下而上解封装。 以浏览器访问 https://example.com 为例:
- 应用层产出 HTTP 请求字节流。
- 传输层为它套上 TCP 头 (源端口、目的端口 443、序列号), 形成"段"。
- 网际层套上 IP 头 (源 IP、目的 IP), 形成"包"。
- 链路层套上以太网头与帧尾 (源/目的 MAC、FCS), 形成"帧",交给网卡发送。
- 对端网卡收帧后逐层剥离, 最终把字节流交给 监听 443 端口的进程。
每加一层头, 就像快递多贴一张面单: MAC 面单在本段链路有效, IP 面单决定全程路由, TCP 面单决定交给哪个进程。 理解封装, 才能理解为什么"端口通不通" 和"IP 通不通"是两个独立问题—— 这正是排障推理链分步验证的由来: 先证明包到了网卡, 再证明包到了进程, 最后证明进程给出了响应。
封装还有一个隐含推论: 抓包看到的是哪一层的头, 决定你正在排查哪一类问题。 在链路层看到帧但网络层无包, 查 ARP/VLAN/交换机; 网络层有包但传输层无连接, 查路由/防火墙/端口监听; 连接正常但应用报错, 才轮到读应用日志。 顺序颠倒, 就会在错误的层里浪费大量时间。
2. 网际层:IP、子网与路由
2.1 IP 地址与私有网段
IPv4 地址是 32 位二进制, 通常写成点分十进制, 例如 192.168.1.100。 RFC 1918 划出三段私有地址供内网使用, 它们不会出现在公网上:
10.0.0.0 - 10.255.255.255 (10.0.0.0/8, 大型企业常用)
172.16.0.0 - 172.31.255.255 (172.16.0.0/12, 云上 VPC 常用)
192.168.0.0 - 192.168.255.255 (192.168.0.0/16,家庭与小型办公常用)私有地址要访问互联网, 必须经过出口设备做 NAT (网络地址转换), 把源地址换成公网地址。 因此"内网能 ping 通外网 IP、 却访问不了某个站点"时, 问题可能出在 NAT 之后的 DNS 或应用层, 而不是路由—— 分层思维在这里第一次兑现价值: 连通性、解析、应用, 三层要分开测。
IPv6 地址为 128 位, 冒号分十六进制表示(如 fe80::1), 自带全局可路由地址, 通常不再需要 NAT。 本篇示例以 IPv4 为主, 但命令大多对两者通用 (ip -6 addr)。 双栈环境里排障要分清 "只有链路本地地址" 和"有全局地址但路由缺失", 症状完全不同, 处理方式也不同。
2.2 子网掩码与 CIDR
掩码用来回答 "目标和我在不在同一个二层网络"。 CIDR 用前缀长度代替点分掩码, 二者等价:
192.168.1.100/24 → 掩码 255.255.255.0 → 同网段:192.168.1.0 - 192.168.1.255
10.0.0.0/8 → 掩码 255.0.0.0 → 同网段:10.0.0.0 - 10.255.255.255
172.16.0.0/12 → 掩码 255.240.0.0 → 172.16.0.0 - 172.31.255.255判断逻辑: 把双方地址分别与掩码做与运算, 结果相同则同网段, 可直接走链路层 ARP 找 MAC; 否则交给默认网关。 子网配错的典型症状 是"同网段邻居能通、跨网段全不通", 或者反过来—— 掩码配得比实际大, 把本该走网关的流量误判为直连。 可用 ip -br addr 快速核对前缀长度:
$ ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 192.168.1.100/24 fe80::216:3eff:fe12:3456/64同一前缀并不意味着一定同网段: VLAN、VXLAN、云上安全组 都可能在二层之间再插一刀。 掩码对了但 ARP 打不到网关时, 要往链路层与交换机配置方向查, 而不是反复改 IP—— 地址配置正确 只是"可以开始排障"的起点, 不是连通性的充分条件。
2.3 路由:数据包的导航表
内核转发每个包时都会查路由表, 规则很简单: 最长前缀匹配—— 命中前缀最长(最精确)的那条路由。 查看与解读:
$ ip route show
default via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.100 metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 100第一条是默认路由 (default,等价于 0.0.0.0/0), 匹配所有未被更精确条目命中的目标; 第二条是直连路由, 内核在接口拿到地址后自动生成, 表示"这个网段直接从 eth0 走, 不需要网关"。 via 指定下一跳, dev 指定出接口, src 可选地指定源地址。
验证某个具体目标会怎么走, 比通读整张表更有用:
$ ip route get 8.8.8.8
8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.100 uid 1000
cache输出直接告诉你出接口与下一跳。 若这一步结果就错了, 后面 ping 必然失败—— 排障先看路由,再看通不通, 顺序不能颠倒。 多网卡、多网关、策略路由共存时, ip route get 还会体现 规则匹配的结果, 是判断"流量走错出口" 最快的单一命令。
路由表里最常见的三类条目是: 直连路由(内核自动生成)、 静态路由(管理员手工写)、 动态路由(协议学习而来)。 三发行版默认都不跑动态路由守护进程, 所以云上 VPC、容器网络 往往需要额外组件 (如 bird、calico)来注入路由。 先分清条目是谁写的, 再判断该改哪一处配置, 能避免大量无效操作。
2.4 网关的职责
默认网关是一台 同时站在两个网段上的路由器, 它替内网主机做两件事: 转发去往未知网段的包, 以及在需要时做 NAT。 因此"ping 网关" 是把问题一分为二的最快手段—— 通了, 说明本机到链路这一段没问题, 故障在网关之后; 不通, 问题就在本机配置、链路或网关本身。 这条分界线会在 网络故障排除 中反复出现。
网关自身也可能成为故障点: 它可能只回 ICMP、不转发; 可能 ACL 挡了回程; 也可能 MTU 在它这一跳被卡住。 所以"网关通"之后 还必须测"公网 IP 通"和"域名通", 三步缺一不可。 把网关当作一个黑盒的下一跳来验证, 比假设"网关一定没问题" 要可靠得多—— 多数内网故障的真正断点 其实在网关之后的 NAT 或 DNS。
3. DNS:把名字变成地址
3.1 为什么需要 DNS
IP 数字难以记忆, DNS(域名系统) 提供"名字 → 地址"的分布式映射。 注意它是独立于连通性的另一条链路: 机器可能完全能 ping 通 8.8.8.8, 却打不开 example.com—— 这恰好说明连通性测试 必须用 IP 和域名各做一次, 才能区分"网络断了" 和"名字解析坏了"。
Linux 上一次解析的简化路径是: 应用调用 getaddrinfo() → glibc 按 /etc/nsswitch.conf 的 hosts: 行决定顺序 (通常是 files dns, 即先查 /etc/hosts 再查 DNS)→ DNS 查询发往 /etc/resolv.conf 中的 nameserver。 若系统启用了 systemd-resolved, resolv.conf 往往是指向 127.0.0.53 的符号链接, 真正的上游 DNS 记录 在 resolved 自己的配置里。 这条完整链路在 网络配置基础 有专门章节展开。
解析链路长, 意味着故障点多: hosts 写错、 nsswitch 顺序反、 resolv.conf 被 NetworkManager 覆盖、 上游不可达、 缓存返回旧记录—— 每一环都会表现成 "域名打不开"。 按顺序读配置文件 而不是上来就 systemctl restart, 是 DNS 排障的第一纪律。
3.2 观察一次解析
$ cat /etc/resolv.conf
nameserver 192.168.1.1
search example.com
$ dig example.com +short
93.184.216.34search example.com 的含义是: 访问短名 myserver 时 自动补全为 myserver.example.com 再查。 多域名搜索在多区域环境中很实用, 但也可能造成 "解析到了错误的同名主机"这类隐蔽问题—— 排查时用 dig +short 显式查全名, 不要依赖短名补全的结果。
3.3 /etc/hosts 与解析顺序
/etc/hosts 是本地静态映射, 优先级由 nsswitch.conf 决定。 它常被用来做开发环境的临时域名劫持, 但也容易留下过期条目—— "改了 hosts 但没生效"的排查, 第一步永远是确认 nsswitch.conf 中 files 是否真的排在 dns 前面。
hosts 修改不会缓存, 但浏览器与应用层可能有自己的缓存; nsswitch 配错则会让修改永远不生效。 两者症状相似、根因不同, 区分方法是用 getent hosts <name> 直接问系统层—— 若 getent 对了而应用还错, 问题在应用缓存; 若 getent 本身就错, 问题在 hosts/nsswitch 顺序。
4. 传输层:端口与套接字
传输层回答"交给哪个进程"。 TCP 提供面向连接的可靠传输 (三次握手、序号、重传), UDP 则是无连接的尽力而为。 两者都用 16 位端口号标识进程, 范围 0-65535, 其中 1024 以下为特权端口, 需 root 绑定。
TCP 与 UDP 的选型也体现分层思想: 需要确认到达、顺序、 拥塞控制就用 TCP (HTTP、SSH、DNS over TCP); 宁可丢包也要低延迟就用 UDP (视频、游戏、QUIC)。 理解这个取舍, 才能读懂为什么 "UDP 端口不通"有时 根本不回 RST—— 无连接协议没有"拒绝"这个概念, 只能沉默超时。
Linux 中"服务是否在监听" 与"网络是否可达"同样是两件事。 查看监听套接字, ss 是 netstat 的现代替代品, 速度更快且无需安装额外软件包:
$ ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 511 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*-t 限 TCP、 -l 只看监听、 -n 不解析服务名 (避免 /etc/services 干扰)、 -p 显示进程 (需 root 才能看到所有进程)。 Recv-Q/Send-Q 在监听态 表示当前积压连接与积压队列上限, 持续不为零往往预示应用消费不过来—— 这是"服务活着但用户卡住" 最常见的内核侧信号。
netstat 并未消失, 但它属于 net-tools 包, Debian 系需 apt install net-tools, Arch 需 pacman -S net-tools, RHEL 系在新版本中 已不再默认安装。 新脚本一律用 ss; 维护旧脚本时知道这个包名差异即可。 把旧监控脚本里的 netstat 逐步替换成 ss, 也是避免新装机器上脚本静默失效的常规动作。
5. Linux 网络的观察入口:ip 与 ss
5.1 ip 命令族:一个工具管三层
ip 由 iproute2 提供, 是链路层(link)、 网际层(addr/route/neigh) 的统一入口, 三大发行版均可通过对应包管理器 安装或升级: Debian/Ubuntu 为 apt install iproute2, Arch 为 pacman -S iproute2, RHEL/CentOS/Rocky 为 dnf install iproute (默认已预装)。 相比已过时的 ifconfig/route, ip 的输出更完整, 也更适合脚本解析。
# 链路层:接口是否 UP、是否丢包
$ ip -s link show eth0
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
RX: bytes packets errs drop fifo frame compressed multicast
1048576 8234 0 0 0 0 0 0
TX: bytes packets errs drop fifo colls carrier compressed
524288 6102 0 0 0 0 0 0
# 网际层:地址与前缀
$ ip -br addr show eth0
eth0 UP 192.168.1.100/24 fe80::216:3eff:fe12:3456/64
# 邻居表:同网段 MAC 解析是否正常
$ ip neigh
192.168.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE
192.168.1.50 dev eth0 FAILEDip -s link 的 errs/drop 计数器 是判断物理与驱动问题的第一现场: 计数持续增长, 说明帧在链路层就被丢弃, 此时抓包往往看不到完整流量, 应先查线缆、双工协商或环路。 ip neigh 中的 FAILED 则表示 ARP 解析失败—— 目标可能关机, 也可能子网内存在地址冲突。 计数器"是否在增长" 比"当前是多少"更重要, 排障时要间隔几秒看两次差值。
5.2 ss:传输层的显微镜
ss 的价值在于过滤能力。 常用组合:
ss -tlnp # 所有监听端口及进程
ss -tn state established # 已建立的连接
ss -s # 各状态连接汇总统计
ss -tn dst 93.184.216.34 # 只看去往某地址的连接-i 选项还能显示 拥塞窗口、重传次数等 TCP 内部状态, 是诊断"网络慢但 ping 正常"时的关键线索—— retrans 持续升高说明丢包 发生在传输层可见的路径上。 把 ss 的状态计数 与 netstat -s 的重传统计交叉看, 往往能在一分钟内 把"慢"定位到丢包、 应用处理慢、 还是队列积压。
5.3 从模型到命令的映射
把本节内容压成一张对照表, 排障时按层选择工具即可:
| 层 | 问什么 | 用什么 |
|---|---|---|
| 物理/链路 | 接口起没起、有没有错包 | ip link、ip -s link、ethtool、链路灯 |
| 网际 | 地址对不对、路由怎么走、邻居可达吗 | ip addr、ip route、ip neigh、ping、traceroute |
| 传输 | 端口听没听、连接什么状态 | ss -tlnp、nc -zv |
| 应用 | 名字解析对不对、服务答不答 | dig、curl、应用日志 |
这张表也是后续各篇的索引: 配置篇解决"每一层怎么设", 排障篇解决"每一层怎么查", 防火墙篇解决"每一层为什么被丢"。 工具名字变了, 推理结构不会变—— 先把层定位准, 再谈命令熟练度。
6. 三发行版安装对照
工具缺失时按下表补齐, 命令均可直接复制。 Arch 一行统一用 pacman -S, Debian/Ubuntu 用 apt install, RHEL/CentOS/Rocky 用 dnf install:
| 用途 | Debian/Ubuntu | Arch | RHEL/CentOS/Rocky |
|---|---|---|---|
ip/ss(iproute2) | apt install iproute2 | pacman -S iproute2 | dnf install iproute |
dig/nslookup | apt install dnsutils | pacman -S bind | dnf install bind-utils |
traceroute | apt install traceroute | pacman -S traceroute | dnf install traceroute |
mtr | apt install mtr-tty | pacman -S mtr | dnf install mtr |
tcpdump | apt install tcpdump | pacman -S tcpdump | dnf install tcpdump |
netstat(兼容旧脚本) | apt install net-tools | pacman -S net-tools | dnf install net-tools |
Debian 与 RHEL 的更新动作 分别是 apt update 与 dnf check-update (或 dnf makecache); Arch 拉取最新仓库索引用 pacman -Sy, 若只更新已有包用 pacman -Su, 完整升级是 pacman -Syu。 不要在 Arch 上 只执行 pacman -S <包> 却长期不做系统升级, 部分库依赖升级后 可能出现半新半旧的状态, 这是滚动发行版最常见的自伤方式。 装工具前先 pacman -Syu 把基线拉齐, 比事后修复半升级状态省时得多。
7. 常见坑
- 把"能上 QQ/微信"当成网络正常。 许多即时通讯走 UDP 或自带多 IP 兜底, 会掩盖 DNS 故障。 判断连通性请分别用
ping <网关IP>、ping 8.8.8.8、ping example.com三次测试, 三者分别验证链路、路由、解析。 - 子网掩码写成 255.255.255.0 却用 /16 的地址规划。 跨网段主机被误判为直连, ARP 打不到网关, 症状是"网关 ping 不通但接口有地址"。
- 混淆
ip link的state与实际可用性。 接口显示UP只代表管理状态开启,LOWER_UP才代表检测到载波(线插好了)。 虚拟接口或网线未插时 常见UP但无LOWER_UP。 - 用
ping 127.0.0.1通 来断言"网络栈正常"。 回环不经过网卡和路由查找的绝大部分逻辑, 它通了只说明本机 TCP/IP 栈没崩, 不说明任何对外连通性。 - 忽略 IPv6。
ip addr显示的fe80::是链路本地地址,不是故障; 但若只有链路本地地址而无全局地址, 说明 IPv6 SLAAC/DHCPv6 没工作, 部分纯 IPv6 环境 会因此表现为"半通不通"。
每个坑都对应前文某一节的原理: 掩码坑在 2.2, state 坑在 5.1, IPv6 坑在 2.1。 先记住症状, 再回到原理段 比死记结论更耐用—— 换个发行版或换套工具, 症状会变,分层不会变。
8. 动手验证
读完本节后, 用下面一组命令把模型跑一遍, 确认每步输出都与预期一致:
ip -br addr # 我的地址和前缀是什么
ip route show # 默认路由指向谁
ip route get 1.1.1.1 # 去公网具体走哪条
ip neigh # 网关 MAC 解析到了吗
ping -c 2 192.168.1.1 # 链路层到网关
ping -c 2 1.1.1.1 # 网际层到公网
dig +short example.com # DNS 是否可用
ss -tlnp # 本机在提供哪些服务八条命令覆盖了 从链路到应用的完整纵切面。 任何一条输出异常, 就说明对应层级出了问题—— 这正是后续各篇反复展开的起点。 把这八条收进随手可跑的脚本, 新机器上线、 容器进入、 VPN 连通后各跑一遍, 能在用户报告故障之前 把多数配置错误拦在本机。
参考资料
- Arch Wiki - Networking — wiki.archlinux.org
- Arch Wiki - iproute2 — wiki.archlinux.org
- 鸟哥的私房菜 - 网络基础 — linux.vbird.org
- RFC 791(Internet Protocol)、RFC 793(Transmission Control Protocol)
- RFC 1918(私有地址空间)、RFC 1035(DNS 实现与规范)
man ip、man ss、man nsswitch.conf、man resolv.conf