数据包从应用层发出时依次经过socket层→TCP/UDP层→IP层→邻居子系统→网络设备驱动→物理介质,每层仅处理自身头部并传递净荷。

数据包从应用层发出时经过哪些内核协议栈层级
Linux 中一个 send() 系统调用发出的数据,并不会直接变成网线上的电信号。它要依次穿过 socket 层 → TCP/UDP 层 → IP 层 → 邻居子系统(ARP)→ 网络设备驱动 → 物理介质。
关键点在于:每一层只处理自己关心的字段,添加或校验对应头部,然后把净荷交给下一层。比如 TCP 层加 tcp_header,IP 层加 ip_header,而以太网帧头(eth_header)是在邻居子系统确认目的 MAC 后才补上的。
- 若目标在同一子网,邻居子系统查
arp_table或触发 ARP 请求获取 MAC; - 若目标跨网关,IP 层会把包发给默认路由对应的接口,MAC 地址填的是网关的;
-
netstat -rn和ip route get可验证路由决策结果; - 使用
tcpdump -i any host能在任意接口抓到未加密的原始包,但注意 loopback 接口(lo)上看到的是 IP 层以上,不含以太网头。
为什么 ping 通但 curl 不通?常见链路断裂点定位
ICMP(ping)走的是内核的 icmp_rcv() 路径,而 HTTP 请求依赖完整的四层栈(TCP 连接 + 应用层交互),任一环节异常都会失败。
- TCP 连接阶段卡住:检查
ss -tn state syn-sent是否堆积,说明 SYN 发出但没收到 SYN-ACK,可能是防火墙丢包、目标端口未监听、或中间设备限速; - 连接建立后无响应:用
tcpdump -i eth0 port 80看是否有 ACK 流量,若 client 发了请求但 server 没回包,重点查 server 的iptables -L -n -v和sysctl net.ipv4.tcp_tw_reuse设置; - 本地策略干扰:某些发行版默认启用
nf_conntrack,大量短连接可能耗尽连接跟踪表,表现为随机超时,可通过cat /proc/sys/net/netfilter/nf_conntrack_count查看当前用量; - DNS 解析成功不代表网络通:
curl默认先走 DNS,再建 TCP,建议用curl -v --resolve example.com:80:192.168.1.100 http://example.com绕过 DNS 直连测试。
tcpdump 抓不到包?三个最常被忽略的过滤与接口问题
tcpdump 默认只监听第一个非 loopback 接口,且无法捕获被内核丢弃前的包(如被 iptables -j DROP 丢弃的包,除非用 nflog 配合)。
- 抓包接口选错:运行
ip link show up列出所有 UP 状态接口,明确指定-i eth0或-i any(后者可捕获所有接口,但 loopback 上的包不含以太网帧头); - BPF 过滤器写错:例如想抓目标端口 22,写成
port 22会同时匹配源和目的,应写dst port 22; - 混杂模式未启用或被禁用:某些云主机或容器环境默认关闭混杂模式,需确认
ip link show eth0 | grep PROMISC,若无输出且需抓其他主机流量,得联系管理员开启; - 抓包时机不对:TCP 三次握手的 SYN 包很小,容易被
tcpdump的缓冲区丢弃,建议加-B 4096扩大缓冲,或用-w file.pcap先保存再分析。
容器或 namespace 中网络不通,优先检查哪些内核对象
容器本质是独立 network namespace,其网络栈与宿主机隔离。不理解命名空间边界,就容易在错误位置排查。
- 确认进程所在 namespace:
readlink /proc/,对比宿主机的/ns/net /proc/1/ns/net; - 进入容器 netns 调试:
nsenter -t看是否有正确配置的 veth 接口和 IP;-n ip addr - 检查 veth 对是否配对:
ethtool -S(部分内核版本支持),或通过| grep peer ip link输出中 interface 名称规律(如vethabc123↔vethdef456)人工匹配; - 宿主机侧 veth 接口是否 UP:
ip link set dev vethxxx up,否则即使容器内 ifconfig 显示 UP,也无法收发; - iptables FORWARD 链默认 DROP:Docker 等工具会操作
DOCKER-USER链,但自建 namespace 若未显式允许转发,需加规则iptables -I FORWARD -i cni0 -o eth0 -j ACCEPT(假设 cni0 是桥接接口)。
网络路径不是一条直线,而是由多个松耦合模块拼接而成。每层都有自己的缓存、队列、策略和错误码,跳过某一层的日志或状态检查,就可能把问题归因到完全错误的方向。









