メインコンテンツへスキップ
  1. Linux 関連記事一覧/
  2. Ubuntu 26.04 LTS Server/

Ubuntu 26.04 LTS Server の TFTP サーバー設定(tftpd-hpa)

目次

TFTP サーバーを使う前に

TFTP には認証も暗号化もありません。 誰でもファイルを読み書きできるので、閉じたネットワークの中だけで使ってください。

ルータの設定ファイルの保存や OS イメージの転送など、機器が TFTP しか話せない場面で使います。この記事ではサーバーに tftpd-hpa、クライアントに tftp-hpa を使います。

編集するファイル用途
/etc/default/tftpd-hpa公開ディレクトリ、待ち受けアドレス、動作オプション
/etc/hosts.deny接続元の制限(TCP Wrappers)

TFTP(RFC 1350)の要点は次のとおりです。

項目内容
トランスポートUDP。最初の要求はサーバーの 69 番に送り、転送はサーバーが新しく選んだポートから行う
パケットRRQ(読み出し要求)/ WRQ(書き込み要求)/ DATA / ACK / ERROR
転送モードnetascii(テキスト。改行は CR LF で送る)/ octet(バイト列をそのまま送る)

導入状況を確認する

サーバーもクライアントも入っていません。 設定ファイルも公開ディレクトリもまだありません。

サーバー側(sv1)の確認 実行例
kazulog@sv1:~$ apt-cache policy tftpd-hpa | head -3
tftpd-hpa:
  Installed: (none)
  Candidate: 5.3+20251209-2
kazulog@sv1:~$ ls -l /etc/default/tftpd-hpa /srv/tftp 2>&1
ls: cannot access '/etc/default/tftpd-hpa': No such file or directory
ls: cannot access '/srv/tftp': No such file or directory
kazulog@sv1:~$ sudo ss -ulnp 'sport = :69'; echo "ss rc=$?"
State                  Recv-Q                  Send-Q                                   Local Address:Port                                   Peer Address:Port                 Process
ss rc=0
クライアント側(sv2)の確認 実行例
kazulog@sv2:~$ which tftp; echo "which rc=$?"
which rc=1
kazulog@sv2:~$ apt-cache policy tftp-hpa | head -3
tftp-hpa:
  Installed: (none)
  Candidate: 5.3+20251209-2

TFTP サーバーを導入する

導入コマンド
sudo apt install -y tftpd-hpa

inetd などのスーパーサーバーは使わず、tftpd-hpa.service が単独で UDP 69 番を待ち受けます。スーパーサーバーの仕組みはinetd(スーパーサーバー)設定で解説しています。

導入すると /etc/default/tftpd-hpa が作られます。設定はこの4行だけです。

導入と設定ファイル 実行例
kazulog@sv1:~$ sudo apt install -y tftpd-hpa | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
Installing:
  tftpd-hpa

Suggested packages:
  pxelinux

Summary:
  Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 96
  Download size: 44.5 kB
  Space needed: 128 kB / 63.1 GB available

Get:1 http://archive.ubuntu.com/ubuntu resolute/main amd64 tftpd-hpa amd64 5.3+20251209-2 [44.5 kB]
Preconfiguring packages ...
Fetched 44.5 kB in 1s (40.5 kB/s)
Selecting previously unselected package tftpd-hpa.
(Reading database ... 91400 files and directories currently installed.)
Preparing to unpack .../tftpd-hpa_5.3+20251209-2_amd64.deb ...
Unpacking tftpd-hpa (5.3+20251209-2) ...
Setting up tftpd-hpa (5.3+20251209-2) ...
Created symlink '/etc/systemd/system/multi-user.target.wants/tftpd-hpa.service''/usr/lib/systemd/system/tftpd-hpa.service'.
Processing triggers for man-db (2.13.1-1build1) ...
Scanning processes...
Scanning linux images...

Running kernel seems to be up-to-date.

No services need to be restarted.

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries on this host.
kazulog@sv1:~$ cat /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"
kazulog@sv1:~$ ls -ld /srv/tftp
drwxr-xr-x 2 root nogroup 4096 Sep 12 10:01 /srv/tftp
kazulog@sv1:~$ systemctl status tftpd-hpa --no-pager | head -7 | cat
● tftpd-hpa.service - tftpd-hpa TFTP Server
     Loaded: loaded (/usr/lib/systemd/system/tftpd-hpa.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-09-12 10:01:24 JST; 6s ago
 Invocation: 4d07df8db779481dbcc25f5fda912011
       Docs: man:in.tftpd
    Process: 2706 ExecStart=/usr/sbin/in.tftpd --listen --user $TFTP_USERNAME --address $TFTP_ADDRESS $TFTP_OPTIONS $TFTP_DIRECTORY (code=exited, status=0/SUCCESS)
   Main PID: 2707 (in.tftpd)
kazulog@sv1:~$ sudo ss -ulnp 'sport = :69'
State               Recv-Q              Send-Q                           Local Address:Port                             Peer Address:Port              Process
UNCONN              0                   0                                      0.0.0.0:69                                    0.0.0.0:*                  users:(("in.tftpd",pid=2707,fd=4))              
UNCONN              0                   0                                         [::]:69                                       [::]:*                  users:(("in.tftpd",pid=2707,fd=5))              
変数既定値意味
TFTP_USERNAMEtftpin.tftpd を動かす利用者
TFTP_DIRECTORY/srv/tftp公開するディレクトリ
TFTP_ADDRESS:69待ち受けるアドレスとポート。アドレスを省くと全アドレス
TFTP_OPTIONS--secure追加のオプション

--secure は起動時にルートディレクトリを TFTP_DIRECTORY へ移すオプション(chroot)で、クライアントはディレクトリを付けずにファイル名だけを指定します。/srv/tftproot の持ち物なので、既定のままではアップロードできません。

設定を変えたら sudo systemctl restart tftpd-hpa で反映します。

ファイルを取得する

サーバーの /srv/tftp にファイルを置きます。

ファイルを置く 実行例
kazulog@sv1:~$ echo "hello from tftp server sv1" | sudo tee /srv/tftp/hello.txt
hello from tftp server sv1
kazulog@sv1:~$ sudo ls -l /srv/tftp/
total 4
-rw-r--r-- 1 root root 27 Sep 12 10:01 hello.txt

クライアントを導入して取得します。-c はコマンドを1つだけ実行して終了し、-v は転送の詳細を表示します。

クライアントの導入と取得 実行例
kazulog@sv2:~$ sudo apt install -y tftp-hpa | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
Installing:
  tftp-hpa

Summary:
  Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 96
  Download size: 19.8 kB
  Space needed: 55.3 kB / 63.1 GB available

Get:1 http://archive.ubuntu.com/ubuntu resolute/main amd64 tftp-hpa amd64 5.3+20251209-2 [19.8 kB]
Fetched 19.8 kB in 1s (24.6 kB/s)
Selecting previously unselected package tftp-hpa.
(Reading database ... 91448 files and directories currently installed.)
Preparing to unpack .../tftp-hpa_5.3+20251209-2_amd64.deb ...
Unpacking tftp-hpa (5.3+20251209-2) ...
Setting up tftp-hpa (5.3+20251209-2) ...
Processing triggers for man-db (2.13.1-1build1) ...
Scanning processes...
Scanning linux images...

Running kernel seems to be up-to-date.

No services need to be restarted.

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries on this host.
kazulog@sv2:~$ cd /tmp && tftp -v 192.168.100.10 -c get hello.txt
Connected to 192.168.100.10 (192.168.100.10), port 69
getting from 192.168.100.10:hello.txt to hello.txt [netascii]
Received 28 bytes in 0.1 seconds [2524 bit/s]
kazulog@sv2:/tmp$ cat /tmp/hello.txt
hello from tftp server sv1

tftp-hpa クライアントの既定の転送モードは netascii です。 27 バイトのファイルが 28 バイトで届いているのは、改行(LF)を CR LF にして送るためです。受け取った側で LF に戻すので、保存されたファイルは元と同じです。

アップロードを許可する(/etc/default/tftpd-hpa)

既定では、サーバーに既にあるファイルへの上書きしか許可されていません。

既定の設定でアップロード 実行例
kazulog@sv2:~$ echo "uploaded from sv2" > /tmp/upload.txt
kazulog@sv2:~$ cd /tmp && tftp -v 192.168.100.10 -c put upload.txt
Connected to 192.168.100.10 (192.168.100.10), port 69
putting upload.txt to 192.168.100.10:upload.txt [netascii]
Error code 1: File not found

/etc/default/tftpd-hpa を編集し、TFTP_OPTIONS--create を足します。

編集するファイル
sudo vi /etc/default/tftpd-hpa
/etc/default/tftpd-hpa(変更後)
# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create"
変更後の確認 実行例
kazulog@sv1:~$ cat /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create"
kazulog@sv1:~$ sudo systemctl restart tftpd-hpa
kazulog@sv1:~$ ls -ld /srv/tftp
drwxr-xr-x 2 root nogroup 4096 Sep 12 10:01 /srv/tftp

これだけではまだ失敗します。 公開ディレクトリが root の持ち物のままだからです。

--create だけ足してアップロード 実行例
kazulog@sv2:~$ echo "uploaded from sv2" > /tmp/upload.txt
kazulog@sv2:~$ cd /tmp && tftp -v 192.168.100.10 -c put upload.txt
Connected to 192.168.100.10 (192.168.100.10), port 69
putting upload.txt to 192.168.100.10:upload.txt [netascii]
Error code 0: Request failed

ディレクトリの所有者を tftpTFTP_USERNAME の利用者)にすると、アップロードできます。

所有者を変える 実行例
kazulog@sv1:~$ sudo chown tftp:tftp /srv/tftp
kazulog@sv1:~$ ls -ld /srv/tftp
drwxr-xr-x 2 tftp tftp 4096 Sep 12 10:01 /srv/tftp
アップロード 実行例
kazulog@sv2:~$ echo "uploaded from sv2" > /tmp/upload.txt
kazulog@sv2:~$ cd /tmp && tftp -v 192.168.100.10 -c put upload.txt
Connected to 192.168.100.10 (192.168.100.10), port 69
putting upload.txt to 192.168.100.10:upload.txt [netascii]
Sent 19 bytes in 0.1 seconds [1441 bit/s]

待ち受けアドレスを限定する(/etc/default/tftpd-hpa)

TFTP_ADDRESS にアドレスを書くと、そのアドレスだけで待ち受けます。IPv6 アドレスは [2001:db8:100::10]:69 のように角括弧で囲みます。

編集するファイル
sudo vi /etc/default/tftpd-hpa
/etc/default/tftpd-hpa(変更後)
# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="192.168.100.10:69"
TFTP_OPTIONS="--secure --create"

アップロードされたファイルは誰でも読み書きできる権限(666)で作られ、待ち受けは1行だけになります。

変更後の確認 実行例
kazulog@sv1:~$ sudo ls -l /srv/tftp/
total 8
-rw-r--r-- 1 root root 27 Sep 12 10:01 hello.txt
-rw-rw-rw- 1 tftp tftp 18 Sep 12 10:02 upload.txt
kazulog@sv1:~$ cat /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="192.168.100.10:69"

接続元を制限する(/etc/hosts.deny)

in.tftpd は TCP Wrappers(libwrap)を組み込んでいるので、/etc/hosts.deny で接続元を拒否できます。デーモン名は in.tftpd です。

編集するファイル
sudo vi /etc/hosts.deny
/etc/hosts.deny の末尾に追記
# TFTP は 192.168.100.20 からの接続を拒否する
in.tftpd: 192.168.100.20
変更後の確認 実行例
kazulog@sv1:~$ ldd /usr/sbin/in.tftpd | grep libwrap
	libwrap.so.0 => /usr/lib/x86_64-linux-gnu/libwrap.so.0 (0x00007cfa15a18000)
kazulog@sv1:~$ cat /etc/hosts.deny
# /etc/hosts.deny: list of hosts that are _not_ allowed to access the system.
#                  See the manual pages hosts_access(5) and hosts_options(5).
#
# Example:    ALL: some.host.name, .some.domain
#             ALL EXCEPT in.fingerd: other.host.name, .other.domain
#
# If you're going to protect the portmapper use the name "rpcbind" for the
# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.
#
# The PARANOID wildcard matches any host whose name does not match its
# address.
#
# You may wish to enable this to ensure any programs that don't
# validate looked up hostnames still leave understandable logs. In past
# versions of Debian this has been the default.
# ALL: PARANOID
# TFTP は 192.168.100.20 からの接続を拒否する
in.tftpd: 192.168.100.20

拒否した要求には ERROR を返さず、黙って捨てます。 クライアントは要求を再送し続け、最後に Transfer timed out. で終わります。

拒否された側 実行例
kazulog@sv2:~$ cd /tmp && timeout 40 tftp -v 192.168.100.10 -c get hello.txt hello-denied.txt; echo "rc=$?"
Connected to 192.168.100.10 (192.168.100.10), port 69
getting from 192.168.100.10:hello.txt to hello-denied.txt [netascii]
Transfer timed out.
rc=69

拒否はログに残ります。ただし from の後ろに出るのは接続元ではなく、サーバー自身のアドレス(192.168.100.10)です。 tftp-hpa 5.3 の in.tftpd は、判定には接続元のアドレスを使い、ログにはサーバー側のアドレスを書くためです。

拒否のログ 実行例
kazulog@sv1:~$ sudo journalctl -u tftpd-hpa --no-pager -n 3
Sep 12 10:02:55 sv1 in.tftpd[5396]: connection refused from 192.168.100.10

設定ファイルの最終状態

ここまでの設定が入った状態です。触ったファイルは /etc/default/tftpd-hpa/etc/hosts.deny の2つだけです。

設定ファイルとサービスの状態
kazulog@sv1:~$ cat /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="192.168.100.10:69"
TFTP_OPTIONS="--secure --create"
kazulog@sv1:~$ cat /etc/hosts.deny
# /etc/hosts.deny: list of hosts that are _not_ allowed to access the system.
#                  See the manual pages hosts_access(5) and hosts_options(5).
#
# Example:    ALL: some.host.name, .some.domain
#             ALL EXCEPT in.fingerd: other.host.name, .other.domain
#
# If you're going to protect the portmapper use the name "rpcbind" for the
# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.
#
# The PARANOID wildcard matches any host whose name does not match its
# address.
#
# You may wish to enable this to ensure any programs that don't
# validate looked up hostnames still leave understandable logs. In past
# versions of Debian this has been the default.
# ALL: PARANOID
# TFTP は 192.168.100.20 からの接続を拒否する
in.tftpd: 192.168.100.20
kazulog@sv1:~$ sudo ls -l /srv/tftp/
total 8
-rw-r--r-- 1 root root 27 Sep 12 10:01 hello.txt
-rw-rw-rw- 1 tftp tftp 18 Sep 12 10:02 upload.txt
kazulog@sv1:~$ systemctl is-enabled tftpd-hpa; systemctl is-active tftpd-hpa
enabled
active
kazulog@sv1:~$ sudo ss -ulnp 'sport = :69'
State               Recv-Q              Send-Q                            Local Address:Port                            Peer Address:Port              Process
UNCONN              0                   0                                192.168.100.10:69                                   0.0.0.0:*                  users:(("in.tftpd",pid=4631,fd=4))              
ファイル変更内容
/etc/default/tftpd-hpaTFTP_OPTIONS--create を追加、TFTP_ADDRESS192.168.100.10:69 に変更
/etc/hosts.deny末尾に in.tftpd: 192.168.100.20 を追記
/srv/tftp所有者を tftp:tftp に変更(アップロードを受けるため)

ルータの設定を保存する

IOS XE のルータ R1 から、copy コマンドで running-config をアップロードします。確認の問い合わせを省くため、file prompt quiet を設定した状態で実行しています。

--secure はサーバーを /srv/tftp に chroot するので、URL のパスはファイル名だけにします。 ディレクトリ名を付けると %Error opening ... (No such file or directory) になります。

R1 から設定を保存 実行例
R1#copy running-config tftp://192.168.100.10/r1-confg
!!
4964 bytes copied in 0.597 secs (8315 bytes/sec)
サーバー側 実行例
kazulog@sv1:~$ sudo ls -l /srv/tftp/
total 16
-rw-r--r-- 1 root root   27 Sep 12 10:01 hello.txt
-rw-rw-rw- 1 tftp tftp 4964 Sep 12 10:05 r1-confg
-rw-rw-rw- 1 tftp tftp   18 Sep 12 10:02 upload.txt
kazulog@sv1:~$ sudo head -3 /srv/tftp/r1-confg
!
! Last configuration change at 01:04:58 UTC Sat Sep 12 2026

削除する

削除すると /etc/default/tftpd-hpa は消えますが、/srv/tftp と中のファイル、tftp 利用者は残ります。

削除 実行例
kazulog@sv1:~$ sudo apt purge -y tftpd-hpa | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
REMOVING:
  tftpd-hpa*

Summary:
  Upgrading: 0, Installing: 0, Removing: 1, Not Upgrading: 96
  Freed space: 128 kB

(Reading database ... 91411 files and directories currently installed.)
Removing tftpd-hpa (5.3+20251209-2) ...
Processing triggers for man-db (2.13.1-1build1) ...
(Reading database ... 91399 files and directories currently installed.)
Purging configuration files for tftpd-hpa (5.3+20251209-2) ...
kazulog@sv1:~$ dpkg -l tftpd-hpa 2>&1 | tail -1
dpkg-query: no packages found matching tftpd-hpa
kazulog@sv1:~$ ls -l /etc/default/tftpd-hpa 2>&1
ls: cannot access '/etc/default/tftpd-hpa': No such file or directory
kazulog@sv1:~$ sudo ls -ld /srv/tftp 2>&1
drwxr-xr-x 2 tftp tftp 4096 Sep 12 10:05 /srv/tftp
kazulog@sv1:~$ getent passwd tftp
tftp:x:103:109:tftp daemon:/srv/tftp:/usr/sbin/nologin
kazulog@sv1:~$ sudo ss -ulnp 'sport = :69'; echo "ss rc=$?"
State                  Recv-Q                  Send-Q                                   Local Address:Port                                   Peer Address:Port                 Process
ss rc=0

キャプチャーで中身を見る

sv1 とスイッチの間のリンクで、sv2 との UDP をキャプチャーしました。取得・失敗した2回のアップロード・成功したアップロード・拒否されたときの再送が1本に入っています。

キャプチャーの一覧 実行例
$ tshark -r ubuntu-tftp.pcap -n -t ad
    1 2026-09-12 10:01:45.203039 192.168.100.20 → 192.168.100.10 TFTP 63 Read Request, File: hello.txt, Transfer type: netascii
    2 2026-09-12 10:01:45.209098 192.168.100.10 → 192.168.100.20 TFTP 74 Data Packet, Block: 1 (last)
    3 2026-09-12 10:01:45.209729 192.168.100.20 → 192.168.100.10 UDP 46 54144 → 55822 Len=4
    4 2026-09-12 10:02:07.874218 192.168.100.20 → 192.168.100.10 TFTP 64 Write Request, File: upload.txt, Transfer type: netascii
    5 2026-09-12 10:02:07.880190 192.168.100.10 → 192.168.100.20 TFTP 61 Error Code, Code: File not found, Message: File not found
    6 2026-09-12 10:02:13.525002 192.168.100.20 → 192.168.100.10 TFTP 64 Write Request, File: upload.txt, Transfer type: netascii
    7 2026-09-12 10:02:13.532589 192.168.100.10 → 192.168.100.20 TFTP 61 Error Code, Code: Not defined, Message: Request failed
    8 2026-09-12 10:02:19.359314 192.168.100.20 → 192.168.100.10 TFTP 64 Write Request, File: upload.txt, Transfer type: netascii
    9 2026-09-12 10:02:19.367159 192.168.100.10 → 192.168.100.20 TFTP 46 Acknowledgement, Block: 0
   10 2026-09-12 10:02:19.367998 192.168.100.20 → 192.168.100.10 UDP 65 52988 → 54084 Len=23
   11 2026-09-12 10:02:19.369136 192.168.100.10 → 192.168.100.20 TFTP 46 Acknowledgement, Block: 1
   12 2026-09-12 10:02:50.855073 192.168.100.20 → 192.168.100.10 TFTP 63 Read Request, File: hello.txt, Transfer type: netascii
   13 2026-09-12 10:02:55.856019 192.168.100.20 → 192.168.100.10 TFTP 63 Read Request, File: hello.txt, Transfer type: netascii
   14 2026-09-12 10:03:00.856469 192.168.100.20 → 192.168.100.10 TFTP 63 Read Request, File: hello.txt, Transfer type: netascii
   15 2026-09-12 10:03:05.856777 192.168.100.20 → 192.168.100.10 TFTP 63 Read Request, File: hello.txt, Transfer type: netascii
   16 2026-09-12 10:03:10.858100 192.168.100.20 → 192.168.100.10 TFTP 63 Read Request, File: hello.txt, Transfer type: netascii
$ tshark -r ubuntu-tftp.pcap -n -V -O tftp -Y 'frame.number == 2'
Frame 2: Packet, 74 bytes on wire (592 bits), 74 bytes captured (592 bits)
Ethernet II, Src: 52:54:00:b7:4d:fd, Dst: 52:54:00:ba:01:74
Internet Protocol Version 4, Src: 192.168.100.10, Dst: 192.168.100.20
User Datagram Protocol, Src Port: 55822, Dst Port: 54144
Trivial File Transfer Protocol
    Opcode: Data Packet (3)
    [Destination File: hello.txt]
    [Read Request in frame 1]
    Block: 1
    [Full Block Number: 1]
Data (28 bytes)

$ tshark -r ubuntu-tftp-no2.pcap -n -x
0000  52 54 00 ba 01 74 52 54 00 b7 4d fd 08 00 45 00   RT...tRT..M...E.
0010  00 3c 53 dc 00 00 40 11 dd 65 c0 a8 64 0a c0 a8   .<S...@..e..d...
0020  64 14 da 0e d3 80 00 28 08 c3 00 03 00 01 68 65   d......(......he
0030  6c 6c 6f 20 66 72 6f 6d 20 74 66 74 70 20 73 65   llo from tftp se
0040  72 76 65 72 20 73 76 31 0d 0a                     rver sv1..

$ tshark -r ubuntu-tftp.pcap -n -Y 'tftp.opcode == 5' -T fields -e frame.number -e tftp.error.code -e tftp.error.message
5	1	File not found
7	0	Request failed
$ tshark -r ubuntu-tftp.pcap -n -t ad -Y 'frame.number >= 8 && frame.number <= 11'
    8 2026-09-12 10:02:19.359314 192.168.100.20 → 192.168.100.10 TFTP 64 Write Request, File: upload.txt, Transfer type: netascii
    9 2026-09-12 10:02:19.367159 192.168.100.10 → 192.168.100.20 TFTP 46 Acknowledgement, Block: 0
   10 2026-09-12 10:02:19.367998 192.168.100.20 → 192.168.100.10 UDP 65 52988 → 54084 Len=23
   11 2026-09-12 10:02:19.369136 192.168.100.10 → 192.168.100.20 TFTP 46 Acknowledgement, Block: 1
$ tshark -r ubuntu-tftp.pcap -n -Y 'frame.number >= 12' -T fields -e frame.number -e frame.time_delta_displayed -e udp.srcport -e udp.dstport -e tftp.source_file
12		45348	69	hello.txt

要求はサーバーの 69 番宛てですが、応答はサーバーが選んだ別のポートから返ります(No.1 は 69 番宛て、No.2 は 55822 番から)。

DATA の中身 実行例
$ tshark -r ubuntu-tftp.pcap -n -V -O tftp -Y 'frame.number == 2'
Frame 2: Packet, 74 bytes on wire (592 bits), 74 bytes captured (592 bits)
Ethernet II, Src: 52:54:00:b7:4d:fd, Dst: 52:54:00:ba:01:74
Internet Protocol Version 4, Src: 192.168.100.10, Dst: 192.168.100.20
User Datagram Protocol, Src Port: 55822, Dst Port: 54144
Trivial File Transfer Protocol
    Opcode: Data Packet (3)
    [Destination File: hello.txt]
    [Read Request in frame 1]
    Block: 1
    [Full Block Number: 1]
Data (28 bytes)

netascii なので、データの末尾が 0d 0a(CR LF)になっています。

No.2 を16進で見る 実行例
$ tshark -r ubuntu-tftp-no2.pcap -n -x
0000  52 54 00 ba 01 74 52 54 00 b7 4d fd 08 00 45 00   RT...tRT..M...E.
0010  00 3c 53 dc 00 00 40 11 dd 65 c0 a8 64 0a c0 a8   .<S...@..e..d...
0020  64 14 da 0e d3 80 00 28 08 c3 00 03 00 01 68 65   d......(......he
0030  6c 6c 6f 20 66 72 6f 6d 20 74 66 74 70 20 73 65   llo from tftp se
0040  72 76 65 72 20 73 76 31 0d 0a                     rver sv1..
上のtshark出力のパケット(No.2 DATA)のpcapをダウンロード

失敗した2回は、エラー番号が違います。

ERROR パケット 実行例
$ tshark -r ubuntu-tftp.pcap -n -Y 'tftp.opcode == 5' -T fields -e frame.number -e tftp.error.code -e tftp.error.message
5	1	File not found
7	0	Request failed

成功したアップロードは WRQ → ACK(Block 0)→ DATA → ACK(Block 1)と進みます。

アップロードの流れ 実行例
$ tshark -r ubuntu-tftp.pcap -n -t ad -Y 'frame.number >= 8 && frame.number <= 11'
    8 2026-09-12 10:02:19.359314 192.168.100.20 → 192.168.100.10 TFTP 64 Write Request, File: upload.txt, Transfer type: netascii
    9 2026-09-12 10:02:19.367159 192.168.100.10 → 192.168.100.20 TFTP 46 Acknowledgement, Block: 0
   10 2026-09-12 10:02:19.367998 192.168.100.20 → 192.168.100.10 UDP 65 52988 → 54084 Len=23
   11 2026-09-12 10:02:19.369136 192.168.100.10 → 192.168.100.20 TFTP 46 Acknowledgement, Block: 1

拒否されたときは、同じ送信元ポートからの要求が5秒おきに5回並び、サーバーからは何も返りません。

拒否されたときの再送 実行例
$ tshark -r ubuntu-tftp.pcap -n -Y 'frame.number >= 12' -T fields -e frame.number -e frame.time_delta_displayed -e udp.srcport -e udp.dstport -e tftp.source_file
12		45348	69	hello.txt
13	5.000946000	45348	69	hello.txt
14	5.000450000	45348	69	hello.txt
15	5.000308000	45348	69	hello.txt
16	5.001323000	45348	69	hello.txt

検証環境と実行ログ

CML 上に Ubuntu 26.04 LTS Server 2台と IOS XE のルータ1台を置いて採取しました。Ubuntu のディスクは wipe した直後の状態から始めています(前の検証で入れたパッケージが残っていると手順が再現しないため)。

検証構成
  sv1                         sv2                         R1
  ens3 192.168.100.10/24      ens3 192.168.100.20/24      Gi2 192.168.100.1/24
  tftpd-hpa (UDP 69)          tftp-hpa                    IOS XE 17.03.08a
   |                           |                           |
   +--------- LAB-SW ----------+---------------------------+
   ^ このリンクでキャプチャー(sv2 との UDP のみ)

キャプチャー全体のファイルです。

TFTP 通信のキャプチャー(ubuntu-tftp.pcap)
手順sv1sv2R1
初期状態(wipe 直後)show / conf / logshow / conf / log
導入と設定ファイルの確認show / conf / log
ファイルを置いて取得show / conf / logshow / conf / log
既定の設定でアップロードshow / conf / log
–create を足すshow / conf / logshow / conf / log
所有者を変えるshow / conf / logshow / conf / log
待ち受けアドレスの限定show / conf / log
接続元の制限show / conf / logshow / conf / log
拒否のログshow / conf / log
設定ファイルの最終状態show / conf / log
ルータからの保存show / conf / logshow
削除(最終状態)show / conf / log
キャプチャーの解析tshark

参考リンク

RFC 1350 - The TFTP Protocol (Revision 2)

関連記事

Ubuntu 公式ページ