Monday, July 13, 2009

FreeBSD Pf and Tftp-proxy

Several IP-enabled devices in the lab use TFTP to retrieve configuration files from various locations on the Internet. This pains me. You can probably imagine what these devices are. Unfortunately I don't control how these devices work.

I run Sguil at my lab gateway to the Internet. I watch traffic right before the gateway, before it is NAT'd. I really don't care what's on the other side. I mostly care what is leaving the network, so I concentrate my NSM activities there.

I noticed one of these TFTP-enabled devices trying to retrieve a file repeatedly. I looked closer at the traffic (thanks to Sguil I keep a record of traffic leaving for the Internet) and noticed I never saw any replies. Simultaneously I received an email from tech support for this device. They told me to unplug all Internet devices from my cable modem and plug the troublesome device into the cable modem overnight (!) My answer to that: "heck no."

I decided to run an experiment with a TFTP client inside the lab and a TFTP server on the Internet. By watching traffic on the internal and external sides of the gateway, I could see TFTP requests making it to the TFTP server on the Internet, and TFTP replies coming from the server back to the gateway. However, the TFTP replies never appeared on the internal side of the gateway.

I did some research and determined that FreeBSD's Pf firewall can't handle TFTP traffic by default. Here is why:

18:13:31.205435 IP my.public.ip.addr.64212 > tftp.server.public.ip.69: 17 RRQ "test.txt" octet
18:13:31.282363 IP tftp.server.public.ip.51186 > my.public.ip.addr.64212: UDP, length 29
18:13:31.284161 IP my.public.ip.addr.57880 > tftp.server.public.ip.51186: UDP, length 4

You see the TFTP request to port 69 UDP. The reply, however, comes from port 51186 UDP to port 64212 UDP. Pf doesn't automatically know that packet 2 is associated with the TFTP request in packet 1.

Fortunately, FreeBSD and other operating systems ship with tftp-proxy(8). I tried following the example in the man page, but I ended up adding the following to the configuration file /etc/pf.conf. $local192 is the LAN from which I expect to see TFTP requests.

no nat on $ext_if to port tftp

rdr-anchor "tftp-proxy/*"

rdr on $int_if proto udp from $local192 to port tftp -> \
$int_if port 6969

anchor "tftp-proxy/*"

I added the following to /etc/inetd.conf.

acmsoda dgram udp wait root /usr/libexec/tftp-proxy tftp-proxy -v

acmsoda is the name in /etc/services for port 6969.

I had to enable /etc/inetd in /etc/rc.conf.

inetd_flags="-wW -C 60 -a"

Without the -a flag, tftp-proxy would be listening on all interfaces, and I don't want that.

Now I was ready to reload Pf and restart /etc/inetd.conf.

r200a:/root# pfctl -Fa -f /etc/pf.conf

r200a:/root# /etc/rc.d/inetd restart

I checked to ensure port 6969 UDP was listening.

r200a:/root# sockstat -4 | grep 6969
root inetd 161 5 udp4 *:*

Now I was able to retrieve my test file via TFTP.

tftp> get test.txt
getting from tftp.server.public.ip:test.txt to test.txt [octet]
sent RRQ
received DATA
Received 25 bytes in 0.1 seconds [2000 bits/sec]

I wanted to note that the man page recommended this addition to inetd.conf:

inetd(8) must be configured to spawn the proxy on the port that packets
are being forwarded to by pf(4). An example inetd.conf(5) entry follows: dgram udp wait root \
/usr/libexec/tftp-proxy tftp-proxy

That didn't work for me; I saw this error in /var/log/messages.

Jul 13 17:11:56 r200a inetd[99738]: unknown service

By specifying the port only and using -a to bind inetd where I needed it, I avoided this error. There's probably another way around this though.

The final step will be seeing this TFTP-enabled device updating itself during the next 24 hours.

Richard Bejtlich is teaching new classes in Las Vegas in 2009. Late Las Vegas registration ends 22 July.


Anonymous said...
This comment has been removed by a blog administrator.
Anonymous said...
This comment has been removed by a blog administrator.