嵌入式设备ping不通?tcpdump 5分钟找到凶手,比加printf快10倍

15 阅读6分钟

嵌入式设备ping不通?tcpdump 5分钟找到凶手,比加printf快10倍

嵌入式开发调试网络问题,最常见的画面:开发板ping不通网关,或者跟服务器连不上。然后呢?打着手电筒看网口灯、拿示波器点PHY芯片管脚、在代码里拼命加printk打网络数据——emmm,你确定要这样?

我见过一个兄弟,调试一个MQTT断连问题,在代码里加了30多行printk打数据包内容,编译烧录一次5分钟,折腾了一整天没找到原因。我过去敲了一行tcpdump,30秒定位是服务器端TLS证书过期了

今天这篇不讲花哨的,就跟你聊tcpdump在嵌入式场景下的5种实战用法。随便哪种,都能让你少加100行printk。


前提:你的板子上有tcpdump吗?

大部分嵌入式Linux系统(Buildroot/Yocto出来的)默认没有 tcpdump。别慌,两个办法:

方案一:交叉编译

# 下载tcpdump源码,交叉编译扔到板子上就行
./configure --host=arm-linux-gnueabihf --prefix=/opt/tcpdump
make && make install
# 把tcpdump和libpcap.so拷到板子上
arm-linux-gnueabihf-strip tcpdump  # 减到500K左右
scp tcpdump root@板子IP:/usr/sbin/

方案二:用Busybox自带的(阉割版)

# 如果Busybox编译时勾选了CONFIG_TCPSUPPORT
# 有个阉割版能用,功能少但够用
tcpdump -i eth0 -n

如果空间极度紧张(Flash只有4MB),还有一个骚招:把tcpdump编译成静态链接扔到/tmp里,反正tmpfs是内存。跑完就没了,不占Flash空间。


场景一:板子ping不通网关——看ARP就知道谁在搞鬼

嵌入式设备最常见场景:ping 192.168.1.1,卡住不动了。

# 在板子上抓ARP包
tcpdump -i eth0 -n arp

输出长这样:

12:34:56.789012 ARP, Request who-has 192.168.1.1 tell 192.168.1.100, length 46
12:34:57.789012 ARP, Request who-has 192.168.1.1 tell 192.168.1.100, length 46
12:34:58.789012 ARP, Request who-has 192.168.1.1 tell 192.168.1.100, length 46

只发不收——说明网关没理你。这时候原因只有三个:

  1. IP不在同一网段(最常见):板子配了192.168.1.100/24,但网关是192.168.2.1
  2. MAC地址过滤:交换机的端口安全功能把陌生MAC拦了
  3. 物理层没通:PHY芯片auto-negotiation失败,Link没起来

如果看到:

ARP, Request who-has 192.168.1.1 tell 192.168.1.100
ARP, Reply 192.168.1.1 is-at 00:11:22:33:44:55

ARP通了但ping还不行——那就不是链路层问题了,查路由表或防火墙。

flowchart TD
    A[ping 网关不通] --> B{tcpdump抓ARP包}
    B -->|只发不收| C[链路层问题]
    B -->|有回复但ping不通| D[网络层问题]
    
    C --> C1[检查IP网段]
    C --> C2[检查MAC过滤]
    C --> C3[检查PHY Link状态]
    
    D --> D1[查路由表 route -n]
    D --> D2[查iptables规则]
    D --> D3[检查网关防火墙]
    
    C1 --> E[修复后验证]
    D1 --> E

场景二:TCP连接被拒——别猜,看RST包

另一个经典场景:板子上的应用连服务器,connect返回-1,errno=ECONNREFUSED。

tcpdump -i eth0 -n 'tcp port 8080'

输出:

12:34:56.789 IP 192.168.1.100.12345 > 10.0.0.1.8080: Flags [S], seq 100
12:34:56.790 IP 10.0.0.1.8080 > 192.168.1.100.12345: Flags [R.], seq 0, ack 101

SYN包发出去,立刻收到RST——服务器端口没开或者防火墙拦了。不需要猜,不需要看代码。

如果看到:

12:34:56.789 IP 192.168.1.100.12345 > 10.0.0.1.8080: Flags [S], seq 100
12:34:56.790 IP 10.0.0.1.8080 > 192.168.1.100.12345: Flags [S.], seq 200, ack 101
12:34:56.790 IP 192.168.1.100.12345 > 10.0.0.1.8080: Flags [.], ack 201

三次握手完成——这时如果应用还报错,那是应用层的问题(协议解析、认证、TLS握手等),跟网络无关了。


场景三:嵌入式设备做服务端——看看谁在扫你

你的板子当服务器(比如HTTP API),有客户端连不上来,先抓包看看有没有人来找你:

tcpdump -i eth0 -n 'tcp port 80'

如果根本没包——是客户端找不到你,跟板子没关系。查DNS解析、查路由可达性。

如果有SYN但没回复:

12:34:56.789 IP 192.168.1.200.54321 > 192.168.1.100.80: Flags [S], seq 789
# ... 没下文了

说明你的服务端程序压根没在监听,或者防火墙把入站连接丢了。netstat -tlnp看一眼80端口有没有LISTEN就清楚了。


场景四:嵌入式设备上网慢——看TCP重传

设备联网了,但奇慢无比。一条AT指令要等好几秒?这就是TCP重传的典型症状。

tcpdump -i eth0 -n 'tcp[13] & 0x0c != 0'
# 只抓RST和SYN包,精确定位连接问题

或者更直接的:

# 抓重传包
tcpdump -i eth0 -n 'tcp[13] & 4 != 0'

看到大量重传(RST标志位),基本可以确定:

  • WiFi/4G信号弱(嵌入式无线设备最常见)
  • 服务器太远延迟太高
  • 缓冲区太小,TCP window满了

进一步看看延迟:

# 带时间戳抓包,看延迟
tcpdump -i eth0 -n -tttt 'host 服务器IP'

对比每个包的时间戳,如果RTT超过500ms,那不管你怎么优化代码都没用——瓶颈在网络本身。


场景五:嵌入式板子没DHCP地址?抓DHCP包

tcpdump -i eth0 -n -e port 67 or port 68

DHCP有四步握手:

# DISCOVER:板子找DHCP服务器
IP 0.0.0.0.68 > 255.255.255.255.67: DHCP-DISCOVER
# OFFER:服务器回复
IP DHCP服务器.67 > 255.255.255.255.68: DHCP-OFFER
# REQUEST:板子确认
IP 0.0.0.0.68 > 255.255.255.255.67: DHCP-REQUEST
# ACK:服务器最终确认
IP DHCP服务器.67 > 255.255.255.255.68: DHCP-ACK

缺了哪一步就查哪一步。没有OFFER=网络里没有DHCP服务器(或者说DHCP中继没配)。有OFFER没ACK=IP冲突或服务器拒绝了。

flowchart LR
    subgraph 板子
        A[DHCP Client]
    end
    subgraph 网络
        B[DHCP Server]
    end
    
    A -->|1. DISCOVER 广播| B
    B -->|2. OFFER 单播/广播| A
    A -->|3. REQUEST 广播| B
    B -->|4. ACK 确认| A
    
    C{哪个阶段断了} --> C1[无DISCOVER: 网口没UP]
    C --> C2[无OFFER: 没有DHCP服务器]
    C --> C3[无ACK: IP冲突或服务器拒绝]

几个嵌入式场景下的tcpdump骚操作

场景:Flash空间只剩2MB

# 不要-i eth0全量抓,先指定过滤条件缩小
# 抓完用-W限制文件大小,-C做轮转

tcpdump -i eth0 -n -C 10 -W 5 -w /tmp/capture.pcap 'host 目标IP'
# 最多5个文件,每个10MB,循环覆盖
# 抓完了scp拿下来慢慢分析

场景:分析之前抓的包

# 不在板子上看,把pcap拉回PC用Wireshark
# 或者在板子上离线读取
tcpdump -r /tmp/capture.pcap -n

场景:板子没屏幕没键盘

# 开机自动抓包,出问题后分析
# 加到/etc/init.d/rcS里
tcpdump -i eth0 -C 5 -W 10 -w /tmp/netlog.pcap 'not port 22' &
# 排除SSH端口避免把自己抓进去

总结

嵌入式网络调试,顺序应该是:

看灯 → 抓包 → 看代码

先物理层,再链路层,再网络层,最后才是应用层代码。大多数时候,tcpdump在第二三步就把问题解决了,根本不需要改代码。

我见过太多人一上来就翻代码、加log、反复编译烧录——折腾半天发现是IP配错了或者网线松了。工具先上,脑子后用。

课后练习

  1. 你的板子ping不通外网,arp回复有,默认网关能ping通——怎么排查?
  2. 板子上跑HTTP服务器,客户端connect一直超时,tcpdump看不到任何SYN包到板子——问题可能在哪?
  3. 嵌入式4G模块上网,偶尔断连。怎么用tcpdump抓完整网络日志又不占太多Flash?

答案我就不写了,自己抓两把包就全明白了。