Docker for Mac: Unable to get TFTP server to communicate

Hi, I’ve been trying to create a containerised TFTP server for receiving Cisco Switch & Router configurations from devices elsewhere on my LAN, so I need to use the Mac’s IP address rather than localhost. The container runs just fine, and the client can open a session to the TFTP server, but no data (other than the filename) is ever transferred.

OS Version/build

Host: macOS Sierra 10.12

$ docker version
Client:
 Version:      1.12.1
 API version:  1.24
 Go version:   go1.7.1
 Git commit:   6f9534c
 Built:        Thu Sep 15 11:20:26 2016
 OS/Arch:      darwin/amd64
 Experimental: true

Server:
 Version:      1.12.1
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   23cf638
 Built:        Thu Aug 18 17:32:24 2016
 OS/Arch:      linux/amd64
 Experimental: true

and:

Docker for Mac: version: 1.12.1-beta26.1 (12c3e63)
OS X: version 10.12 (build: 16A323)
logs: /tmp/9816F48B-248B-44D7-89C9-AB3CD155430E/20160922-123158.tar.gz
[OK]     docker-cli
[OK]     virtualization kern.hv_support
[OK]     menubar
[OK]     moby-syslog
[OK]     dns
[OK]     disk
[OK]     system
[OK]     app
[OK]     osxfs
[OK]     virtualization VT-X
[OK]     db
[OK]     slirp
[OK]     logs
[OK]     env
[OK]     vmnetd
[OK]     moby-console
[OK]     moby
[OK]     driver.amd64-linux

Steps to reproduce

Build an image from this Dockerfile:

# tftp in a container
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y tftpd-hpa
RUN mkdir /tmp/tftp
RUN chmod 777 /tmp/tftp
RUN chown nobody /tmp/tftp
EXPOSE 69/udp
EXPOSE 49152-49160/udp
VOLUME /var/lib/tftpboot
CMD /usr/sbin/in.tftpd --foreground --create --permissive --user nobody --port-range 49152:49160  --secure /tmp/tftp

I believe the port range is needed for communication as TFTPD uses ephemeral ports after the initial packet exchange. Here, the TFTPD is restricted to just 9 ports. Run with the following command replacing with the value relevant for your Mac:

docker run -it -d -p <host ip>:69:69/udp -p <host ip>:49152-49160:49152-49160/udp  --name tftp jjo93sa/tftp

Once the container is running, issue commands on the host:

echo "some text" > test.file
tftp <host ip>
put test.file

The transfer will stall. Accessing /tmp/tftp in the container shows the file “test.file” has been created, but is empty at 0 bytes. It is correctly owned, as specified in the container’s TFTP command.

Expected behaviour
File should be transferred in its entirety.

Debug

Running tcpdump inside the container, gives the following output before the client times-out.

root@dea9744a97f0:/tmp/tftp# tcpdump -i eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
10:31:36.241109 IP 172.17.0.1.53805 > dea9744a97f0.tftp:  22 WRQ "test.file" netascii 
10:31:36.243890 IP dea9744a97f0.49155 > 172.17.0.1.53805: UDP, length 4
10:31:36.243998 IP 172.17.0.1 > dea9744a97f0: ICMP 172.17.0.1 udp port 53805 unreachable, length 40
10:31:36.373294 IP dea9744a97f0.47557 > 192.168.65.1.domain: 43983+ PTR? 1.0.17.172.in-addr.arpa. (41)
10:31:36.379272 IP 192.168.65.1.domain > dea9744a97f0.47557: 43983 NXDomain* 0/1/0 (95)
10:31:37.372050 IP dea9744a97f0.50898 > 192.168.65.1.domain: 2963+ PTR? 1.65.168.192.in-addr.arpa. (43)
10:31:37.377597 IP 192.168.65.1.domain > dea9744a97f0.50898: 2963 NXDomain* 0/1/0 (98)
10:31:41.243966 IP 172.17.0.1.53805 > dea9744a97f0.tftp:  22 WRQ "test.file" netascii 
10:31:41.244686 IP dea9744a97f0.49155 > 172.17.0.1.53805: UDP, length 4
10:31:41.244719 IP 172.17.0.1 > dea9744a97f0: ICMP 172.17.0.1 udp port 53805 unreachable, length 40
10:31:41.247355 ARP, Request who-has 172.17.0.1 tell dea9744a97f0, length 28
10:31:41.247378 ARP, Request who-has dea9744a97f0 tell 172.17.0.1, length 28
10:31:41.247389 ARP, Reply dea9744a97f0 is-at 02:42:ac:11:00:02 (oui Unknown), length 28
10:31:41.247398 ARP, Reply 172.17.0.1 is-at 02:42:ae:87:ce:b9 (oui Unknown), length 28
10:31:46.247751 IP 172.17.0.1.53805 > dea9744a97f0.tftp:  22 WRQ "test.file" netascii 
10:31:46.248900 IP dea9744a97f0.49155 > 172.17.0.1.53805: UDP, length 4
10:31:46.248950 IP 172.17.0.1 > dea9744a97f0: ICMP 172.17.0.1 udp port 53805 unreachable, length 40
10:31:51.252939 IP 172.17.0.1.53805 > dea9744a97f0.tftp:  22 WRQ "test.file" netascii 
10:31:51.254093 IP dea9744a97f0.49155 > 172.17.0.1.53805: UDP, length 4
10:31:51.254145 IP 172.17.0.1 > dea9744a97f0: ICMP 172.17.0.1 udp port 53805 unreachable, length 40
10:31:56.252974 IP 172.17.0.1.53805 > dea9744a97f0.tftp:  22 WRQ "test.file" netascii 
10:31:56.253727 IP dea9744a97f0.49155 > 172.17.0.1.53805: UDP, length 4
10:31:56.253759 IP 172.17.0.1 > dea9744a97f0: ICMP 172.17.0.1 udp port 53805 unreachable, length 40

I can see the file Write ReQuest (WRQ) is received on port 69, as expected. I assume it is upon receipt of this packet that the tftp daemon creates the empty file.

10:31:36.241109 IP 172.17.0.1.53805 > dea9744a97f0.tftp:  22 WRQ "test.file" netascii 

The tftp server should respond with an Ack, and the port number of the ACK should be used by the client for future packets. The TFTP server has chosen port 49155 for the ACK, which is within the configured range. However, the ACK is blocked by the address 172.17.0.1 (in my case) because the port 53085 is unreachable. However, I’m not a tcpdump expert so I might be mis-reading this trace.

10:31:36.243890 IP dea9744a97f0.49155 > 172.17.0.1.53805: UDP, length 4
10:31:36.243998 IP 172.17.0.1 > dea9744a97f0: ICMP 172.17.0.1 udp port 53805 unreachable, length 40

So, it seems that the server - client communication is blocked, despite the port mapping statements in the run command & EXPOSE commands. Please can someone advise what am I doing wrong?

I meet the same bug on mac, but there is no problem on linux, I think this is a bug on mac.