4. TFTP 传输故障:文件路径错误、权限不足、防火墙阻断、超时与重传机制调优
TFTP 这玩意儿,看着简单,用起来坑不少。很多刚入行的兄弟觉得它不就是个传文件的小工具嘛,能出啥问题?嘿,我告诉你,恰恰因为它简单,所以一旦出故障,排查起来反而容易让人抓瞎。没有认证、没有复杂的握手,出了问题,日志就那么几行,全靠经验猜。
今天咱们就把 TFTP 传输故障的四个大头——文件路径、权限、防火墙、超时重传——掰开揉碎了讲。我这些年踩过的坑,都在这儿了。
4.1 文件路径错误:最常见的“找不到文件”
TFTP 服务端启动时,会指定一个根目录。客户端请求文件时,路径是相对于这个根目录的。很多人以为路径写对了,其实不然。
核心原则:TFTP 没有“..”回退,路径必须严格在根目录下。你给客户端传 /boot/vmlinuz,服务端实际找的是 /tftpboot/boot/vmlinuz(假设根目录是 /tftpboot)。
我遇到过最典型的案例:有人把 PXE 配置文件放在 /tftpboot/pxelinux.cfg/ 下,但客户端请求的是 pxelinux.cfg/default。服务端根目录是 /tftpboot,结果死活找不到。为什么?因为客户端请求路径里没有前导斜杠,而服务端拼接路径时,有的实现会多一个斜杠,有的不会。
排查路径问题,我建议你按这个顺序来:
- 确认服务端根目录:用
ps aux | grep tftp看启动参数,比如-s /tftpboot就是根目录。 - 模拟客户端请求:在服务端本地用
tftp localhost连进去,用get命令下载文件,看报错。 - 检查文件是否存在:在根目录下用
ls -la确认文件路径完全匹配。 - 注意大小写:TFTP 协议本身不区分大小写,但底层文件系统区分。Linux 下
vmlinuz和VMLINUZ是两个文件。
小技巧:开启 TFTP 服务的详细日志。比如在 dnsmasq 里加 --log-dhcp --log-tftp,或者在 tftp-hpa 里用 -v -v 参数。日志会告诉你客户端到底请求了什么路径,一对比就知道问题在哪。
4.2 权限不足:服务跑起来了,但读不了文件
路径对了,文件也在,但客户端还是报“Access violation”或“File not found”。这时候八成是权限问题。
TFTP 服务进程通常以 nobody 或 tftp 用户运行。这个用户对根目录和文件必须有读权限。注意,是目录的执行权限(x)和文件的读权限(r),缺一不可。
我记得有一次帮客户排查,他们把所有文件权限都设成了 777,但还是报错。我过去一看,好家伙,根目录 /tftpboot 的属主是 root,权限是 755,没问题。但里面有个子目录 /tftpboot/images,权限是 700,属主是另一个用户。TFTP 进程根本进不去这个子目录。
权限检查清单:
| 检查项 | 命令/方法 | 正确状态 |
|---|---|---|
| 服务运行用户 | ps -eo user,comm | grep tftp |
通常是 nobody 或 tftp |
| 根目录权限 | ls -ld /tftpboot |
至少 755,属主不重要,但 other 要有 r-x |
| 子目录权限 | ls -ld /tftpboot/*/ |
每一级目录都要有 r-x 权限 |
| 文件权限 | ls -la /tftpboot/boot/vmlinuz |
至少 644,other 要有 r-- |
| SELinux/AppArmor | getenforce / aa-status |
检查是否拦截了 tftp 的读操作 |
注意:如果你用了 SELinux,光改文件权限没用。需要检查 tftp_t 上下文,或者直接 setsebool -P tftp_home_dir on。我曾经在这个坑里浪费了半天,后来发现是 SELinux 把 TFTP 的读操作给 block 了。
4.3 防火墙阻断:数据包有去无回
TFTP 使用 UDP 69 端口做控制连接。但数据传输时,会使用动态端口(通常是 1024 以上)。这就麻烦了——防火墙如果只开了 69 端口,数据包根本回不来。
为什么会这样?TFTP 有两种传输模式:
- 标准模式:服务端从随机端口(比如 34567)向客户端的随机端口发数据。防火墙必须允许来自服务端高端口的 UDP 包进入客户端。
- TSIZE 选项:部分实现支持,但本质不变。
我建议你在防火墙上做这几件事:
- 开启 UDP 69 端口:这是基础,允许客户端连接。
- 开启临时端口范围:允许来自服务端 1024-65535 的 UDP 包进入客户端。或者更严格一点,只允许来自 TFTP 服务端 IP 的包。
- 考虑使用 TFTP 中继或代理:如果跨网段传输,防火墙规则会变得很复杂。用
atftp或tftp-hpa的-m选项指定端口范围,可以缩小防火墙开放范围。
实战命令:在 Linux 上用 iptables 开放 TFTP:
# 开放 69 端口
iptables -A INPUT -p udp --dport 69 -j ACCEPT
# 开放临时端口范围(假设服务端 IP 是 192.168.1.100)
iptables -A INPUT -p udp -s 192.168.1.100 --sport 1024:65535 -j ACCEPT
嗯,这里要注意,如果你用 systemd 的 tftp.socket,它会自动监听 69 端口并启动服务。但防火墙规则还是要手动配。
4.4 超时与重传机制调优:传输慢得像蜗牛
TFTP 的可靠性全靠超时重传。它发一个数据块,等 ACK,等不到就重传。默认超时时间通常是 5 秒,重传次数 5 次。你想想看,如果网络延迟稍微大一点,或者丢包率 1%,整个传输过程会慢到什么程度?
我调优过的最极端案例:一个客户在跨国链路上用 TFTP 传 100MB 的固件,默认参数下传了 40 分钟还没完。我一看抓包,好家伙,每次超时都等 5 秒,重传 5 次,然后放弃。实际上链路延迟只有 300ms,丢包率 2%。
调优的核心参数:
| 参数 | 默认值 | 调优建议 | 说明 |
|---|---|---|---|
| 超时时间 (timeout) | 5 秒 | 1-3 秒 | 根据 RTT 设置,RTT 的 3-5 倍即可 |
| 重传次数 (retries) | 5 次 | 3-8 次 | 丢包率高就加大,但别超过 10 次 |
| 块大小 (blksize) | 512 字节 | 1024-1468 字节 | 加大块大小减少交互次数,但别超过 MTU |
| 窗口大小 (windowsize) | 1 | 4-16 | 允许连续发多个块再等 ACK,大幅提升吞吐 |
怎么调?用 atftp 客户端,它支持这些选项:
# 设置超时 2 秒,重传 3 次,块大小 1024,窗口大小 8
atftp --option "timeout 2" --option "retries 3" \
--option "blksize 1024" --option "windowsize 8" \
-g -r firmware.bin 192.168.1.100
服务端也要支持这些选项。比如 tftp-hpa 的 tftpd,启动时加 -B 1024 -w 8 来设置块大小和窗口大小。
我的经验:调优前先抓包看 RTT 和丢包率。用 tcpdump -i eth0 port 69 or portrange 1024-65535 抓个 30 秒,然后用 Wireshark 分析。如果 RTT 稳定在 10ms 以内,超时设 1 秒就够了。如果丢包率超过 5%,先修网络,别指望重传能解决一切。
4.5 知识体系:一张图看懂 TFTP 故障排查
说了这么多,我把整个排查逻辑画成了一张流程图。你照着这个走,90% 的 TFTP 问题都能定位到。
这张图从左到右,从上到下,就是标准的排查顺序。别跳步,一步步来。我见过太多人一上来就调超时参数,结果发现是路径写错了——白忙活。
4.6 避坑指南:我曾经踩过的雷
最后分享几个我亲身经历过的坑,你遇到了能少走弯路:
- 坑一:符号链接。TFTP 服务默认不跟随符号链接。你在
/tftpboot下建个软链接指向/boot,客户端访问这个链接会报错。解决办法:用mount --bind替代软链接。 - 坑二:文件大小限制。老版本的 TFTP 最大只支持 32MB 文件(因为块号用 16 位表示)。现在大部分实现支持大文件,但如果你用的嵌入式设备上的 TFTP 客户端,可能还有这个限制。传大文件前先确认。
- 坑三:多客户端并发。默认的
tftp-hpa是单线程的,一个客户端传输时,其他客户端会排队。高并发场景下,用dnsmasq或atftpd的多线程模式。 - 坑四:Windows 防火墙。Windows 自带的 TFTP 客户端和服务端,防火墙规则经常漏配。我建议直接用
tftp命令行工具测试,别依赖图形界面。
好了,TFTP 传输故障这块,核心就是路径、权限、防火墙、超时重传这四个维度。你把这四个点吃透了,以后遇到 TFTP 问题,心里就有底了。