iptables and TFTP HOWTO

Reminder to self on iptables and TFTP HOWTO.


iptables on a TFTP server:

iptables -I INPUT -j ACCEPT -p udp -m udp --dport 69

iptables on a TFTP client:

## nf_conntrack_helper = 0 is the default nowadays
## you need CT target for helpers to work
echo 0 > /proc/sys/net/netfilter/nf_conntrack_helper
modprobe nf_conntrack_tftp
iptables -t raw -I OUTPUT -j CT -p udp -m udp --dport 69 --helper tftp

iptables on NAT/router between TFTP client and server; optionally, iptables does NAT for client:

modprobe nf_nat_tftp
iptables -t raw -I PREROUTING -j CT -p udp -m udp --dport 69 --helper tftp

More verbose…

iptables on TFTP server

Exemplary INPUT chain on RHEL 7:

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
166M 192G ACCEPT all -- * * ctstate RELATED,ESTABLISHED
765K 46M ACCEPT all -- lo *
33M 1962M INPUT_direct all -- * *
33M 1962M INPUT_ZONES_SOURCE all -- * *
33M 1962M INPUT_ZONES all -- * *
288 22872 ACCEPT icmp -- * *
47541 6359K REJECT all -- * * reject-with icmp-host-prohibited

Enable tftp server:
firewall-cmd --zone=$INZONE --enable-service tftp
## OR
## iptables -I INPUT 2 -j ACCEPT -p udp -m udp --dport 69


iptables --list -n -v
## in firewalld this ends up in IN_$INZONE_allow chain
Chain IN_$INZONE_allow (1 references)
pkts bytes target prot opt in out source destination
240 14341 ACCEPT udp -- * * udp dpt:69 ctstate NEW

iptables on TFTP client

Exemplary -t raw OUTPUT chain on RHEL 7:

iptables -t raw --list OUTPUT -n -v
Chain OUTPUT (policy ACCEPT 14196 packets, 1824K bytes)
pkts bytes target prot opt in out source destination
112K 5213K OUTPUT_direct all -- * *

Enable TFTP:

firewall-cmd --direct --add-rule ipv4 raw OUTPUT 0 -j CT -p udp -m udp --dport 69 --helper tftp
## OR
## iptables -t raw -I OUTPUT -j CT -p udp -m udp --dport 69 --helper tftp


## firewalld
# iptables -t raw --list OUTPUT_direct -n -v
Chain OUTPUT_direct (1 references)
pkts bytes target prot opt in out source destination
6 357 CT udp -- * * udp dpt:69 CT helper tftp

# perform a TFTP transaction to view connection/expectation
# is the client; is the server
# conntrack -L | grep ^udp.*
conntrack v1.4.3 (conntrack-tools): 24 flow entries have been shown.

## this is the expectation, setup by tftp helper
udp 17 27 src= dst= sport=43709 dport=47512 src= dst= sport=47512 dport=43709 mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=2

## this is the connection
udp 17 27 src= dst= sport=47512 dport=69 [UNREPLIED] src= dst= sport=69 dport=47512 mark=0 secctx=system_u:object_r:unlabeled_t:s0 helper=tftp use=2

iptables on NAT/router between TFTP client and server

  • Server:
  • NAT/router: ##configure iptables here
  • Client:

Exemplary -t raw PREROUTING chain in RHEL 7:

# iptables -t raw --list PREROUTING -n -v
Chain PREROUTING (policy ACCEPT 17541 packets, 19M bytes)
pkts bytes target prot opt in out source destination
477K 176M PREROUTING_direct all -- * *
477K 176M PREROUTING_ZONES all -- * *

Enable TFTP:

## we assume there are already rules for FORWARD/MASQUERADE
firewall-cmd --zone=internal --enable-service tftp-client
## OR
## iptables -t raw -I PREROUTING -j CT -p udp -m udp --dport 69 --helper tftp


# firewalld: the client is on the internal zone
# iptables -t raw --list PRE_internal_allow -n -v
Chain PRE_internal_allow (1 references)
pkts bytes target prot opt in out source destination
0 0 CT udp -- * * udp dpt:69 CT helper tftp

## perform a TFTP transaction...then
# conntrack -L | grep ^udp.*
conntrack v1.4.3 (conntrack-tools): 21 flow entries have been shown.
## this is the connection through the NAT+router
udp 17 27 src= dst= sport=52718 dport=69 [UNREPLIED] src= dst= sport=69 dport=52718 mark=0 secctx=system_u:object_r:unlabeled_t:s0 helper=tftp use=2
## this is the expectation setup by tftp helper
udp 17 27 src= dst= sport=59356 dport=52718 src= dst= sport=52718 dport=59356 mark=0 secctx=system_u:object_r:unlabeled_t:s0 use=1

  • Expectations are installed in the reverse direction from the connection (that caused them)
  • TFTP Expectation dport = TFTP Connection sport
  • Recall that in TFTP the server makes a new connection from a random source port to the client port that was used to start the session to the server (69/udp)

GRUB2 UEFI & PXE — Cobbler ready!

Note: my experience with GRUB2 is the heavily hacked version from Fedora 19. I don’t think upstream behaves this way (TODO testing on upstream).

GRUB2 on UEFI/PXE seems to have matured enough that it might be usable in Cobbler to replace grub-legacy. It has network support now and it searches for its grub.cfg in creative ways from the TFTP server by using name-mangling just like PXELINUX and grub-legacy (UEFI/PXE)

Recap: PXELINUX boot will try to find its config by constructing  a filename from the mac address, IP address-digit-striping. E.g.,01-88-99-aa-bb-cc-dd  C000025B C000025

Cobbler makes use of this for per-system installation configs. For UEFI/PXE systems: grub-legacy 0.97 that comes with Red Hat-like systems emulates PXELINUX behaviour. When grub-legacy is booted using UEFI/PXE it will reach back to the TFTP server and pull its config with filenames constructed from  UUID, mac address, IP address-digit-striping.

Unfortunately, grub-legacy UEFI/PXE + RHEL6.x installer gives me problems on Dell PowerEdge servers. On some boots I cannot get console graphics, as if the UEFI-GOP is not initialised correctly.  The installer works because I am able to use the serial console and anaconda over vnc. I have hit this on M910, R715 and a whole assortment of PowerEdge servers. The funny thing is that when the installation completes, I have no problems booting the system with grub-legacy and seeing the Linux penguin icons at the top of the boot messages. Perhaps the installer initialises the video in a more conservative fashion.

E.g., CentOS 6.4 installer ISO on UEFI — no console during installation but serial console and vnc both work.

I was hoping to move to GRUB2 as I have had it boot the OS installer (RHEL6.x type)  without fail on Dell PowerEdge in UEFI mode. Dell’s own OpenManage Deployment Toolkit v4.3 (Linux) has also switched to GRUB2; it touts UEFI-support and I’m guessing the move to GRUB2 is due to the rather fragile grub-legacy/UEFI behaviour.

However GRUB2/UEFI/PXE as a Cobbler grub-legacy replacement component depends on a working PXE stack and config file search algorithm. That day has arrived!

Prepared a PXE/UEFI-aware GRUB2 image (from Fedora 19) using:

grub2-mkstandalone –net-directory /var/lib/tftpboot  –subdir grub2-efi

The resulting core.efi bootloader has efinet, tftp and other essential network modules loaded; the variables root and prefix are set correctly so that grub2 can find its modules and config file. In fact, before loading the config file, the bootloader immediately reaches back to the TFTP server and pulls some modules.

Here is the full list of files that GRUB2/UEFI/PXE pulled down. You can see the search algorithm.

BOOTX64.efi <– copied /grub2-efi/x86_64-efi/core.efi here


Now to see when Cobbler incorporates this as an alternate bootloader.

One weirdness of Fedora 19-GRUB2 is that some variables don’t seem to be populated. Bug, much?


Hmmmm…… why all the blank variables? Shouldn’t they be copied from net_efinet1_XX? The missing *_server variables look fishy. P.S. Further testing show that upstream GRUB2 fills in all the aforementioned vars.

Linux & Broadband Modems 1/2: D-Link DWM-156

The D-Link DWM-156 is a USB broadband modem that supports HSUPA.

It’s a composite device that appears first as a USB storage device with Windows and Mac drivers, USB ID 0x07d1:0xa800. After driver installation there is a “personality” change and it presents a new USB ID 0x07d1:0x3e02 (alas, not PnP CDC_ACM). Why don’t manufacturers stick to good ol’ CDC_ACM??

The device is too recent to have usb_modeswitch support.

However, bizarro, I found a trick on a web page (in Mandarin):

#in Ubuntu the USB storage device is automatically mounted as /media/CONNMGR
touch /media/CONNMGR/wcdma.cfg

Just creating this file causes USB mode switch. The new device can be driven by usbserial.ko or option.ko:

modprobe usbserial vendor=0x07d1 product=0x3e02
modprobe option
echo -n "07d1 3e02" > /sys/bus/usb-serial/drivers/option1/new_id 

Four device files: /dev/ttyUSB0-3 are created. The data device is /dev/ttyUSB0. When all is well you should see:

modem-manager: (Generic): GSM modem /sys/devices/pci0000:00/0000:00:1d.7/usb2/2-3 claimed port ttyUSB0
modem-manager: Added modem /sys/devices/pci0000:00/0000:00:1d.7/usb2/2-3
modem-manager: Exported modem /sys/devices/pci0000:00/0000:00:1d.7/usb2/2-3 as /org/freedesktop/ModemManager/Modems/1
NetworkManager:   (ttyUSB0): new GSM device (driver: 'generic')
NetworkManager:   (ttyUSB0): exported as /org/freedesktop/NetworkManager/Devices/3
NetworkManager:   (ttyUSB0): now managed

Unfortunately, ttyUSB2 and ttyUSB3 also respond to the MM probes and may be mistakenly identified as the data device. There is a race condition – whichever port answers first gets exported as the modem!

FIXME: How can I fix this? I want ModemManager’s generic modem prober to restrict itself to ttyUSB0??

TODO: figure out udev or usb_modeswitch to do mode switching on plugin, then get MM to identify only /dev/ttyUSB0 as the data port …

TODO: Find out the chipset and AT command set so that I can get signal strength and other cool meta-information on the connection …

Dracut and lvm

Built a 2.6.33-rc? kernel from git to try on Fedora 12. I wanted two new modules that are not in the current distro: updated fintek sensor module and k10temp for powernow stuff.

Curiously, dracut started creating a ginormous generic initramfs until it filled up the /boot partition. Next tried it manually with dracut -H. This was a slim initramfs but left out LVM (FIXME: why?). In the end:

dracut -H -a 90lvm …

is your friend for manual kernel builds. Behind all the black magic, the new kernel was bootable with plymouth, modesetting on an ATI HD4650 setup.

wvdial, /etc/resolv.conf on Fedora 10

I think I’ve solved a curious problem on Fedora 10 with wvdial and pppd not updating /etc/resolv.conf. The result is even though there is a network connection, no name resolution is possible unless I manually add the nameservers to /etc/resolv.conf.

I use wvdial to connect to the Internet on mobile broadband (3G/HSDPA) using my Nokia E51 and E66. Of course, I could use NetworkManager, but why do everything the easy way? Anyway, when Fedora 10 came out, the E51 and E66 were not recognised by NM as mobile broadband devices which is why I had to learn wvdial/pppd.

One thing I noticed with wvdial (or pppd from the command line) is that they didn’t update /etc/resolv.conf even though configured to do so. Other people on the Internet seem to have encountered the same problem.

The root cause of the problem is this: when the ppp interface comes up, pppd calls /etc/ppp/ip-up which calls /etc/sysconfig/network-scripts/ifup-post. ifup-post is the magic script which populates /etc/resolv.conf. However /etc/ppp/ip-up is meant to be called with 6 arguments (say 6th arg = XXXX), then it looks for the existence of /etc/sysconfig/network-scripts/ifcfg-XXXX. Only if this file exists is ifup-post called. So…drum roll… wvdial invokes pppd which invokes /etc/ppp/ip-up with 5 arguments! The result is ip-up looking for /etc/sysconfig/network-scripts/ifcfg-.

The solution is to create a dummy file /etc/sysconfig/network-scripts/ifcfg- with the following lines:
##contents of /etc/sysconfig/network-scripts/ifcfg-

These two lines are enough for ifup-post to do its magic with DNS /etc/resolv.conf.

FIXME: Frankly I don’t know how NM does it; under NM ip-up gets called with e.g. the following args
ppp0 /dev/ttyACM0 115200 /org/freedesktop/NetworkManager/PPP/0

No way does /etc/sysconfig/network-scripts/ifcfg-/org/freedesktop/NetworkManager/PPP/0 exist! There must other stuff going on. Under wvdial /etc/ppp/ip-up is called e.g. with

ppp0 /dev/ttyACM0 460800

Notice only 5 args! BTW these scripts are highly Fedora 10 specific so this “solution” may not apply to random Linux distribution.