Iptables – Getting IPTables to properly forward NTP traffic

iptablesntpdopenbsd

I have the following setup:

    NTP
  10.21.3.169
    |
    |
  10.21.3.160 (eth1)
   Linux
  10.0.0.67 (eth0)
    |
    |
  10.0.0.65 (pcn1)
   OpenBSD

The idea is to allow the NTPD client (not OpenNTP) on the OpenBSD box to get the time from the NTP server by using port forwarding on the OpenBSD box and iptables on the Linux box.

I am at the stage where the following is true:

  • The OpenBSD box's pf.conf has been set up correctly such that running tcpdump udp on the Linux box shows the correct traffic from the OpenBSD box when I run ntpd -d -s
  • The Linux box's iptables configuration has been set up correctly such that running ntpd -d -s on the OpenBSD box, the traffic shows up using tcpdump udp on the NTP server

But nothing comes back – there is no traffic on either machine in the other direction.

The iptables rules I am using on the Linux machine are:

iptables -A PREROUTING -t nat -i eth0 -p udp --dport 123 -j DNAT --to-destination 10.21.3.169:123
iptables -A FORWARD -t filter -p udp -d 10.21.3.169 --dport 123 -j ACCEPT

Which seems correct to me. There is also an "accept state RELATED/ESTABLISHED" rule in the IPTables configuration that was there before I started this work.

What am I doing wrong? Is there something missing? Perhaps some extra rules for the reply?

Edit

I have followed the advice in @gromit's answer and @MadHatter's comments, and have the following information to add:

On the Linux box, running cat /proc/sys/net/ipv4/ip_forward gives 1. Running ntpd -d -s on the OpenBSD box with tcpdump monitoring eth0 and "eth1 on the Linux box (in different terminals, at the same time) gives the following output.

OpenBSD

[root@OpenBSDBox ~]# ntpd -d -s
ntp engine ready
no reply received in time, skipping initial time setting
no reply from 10.0.0.67 received in time, next query 3227s

Linux Box

tcpdump -n -n -i eth0 port 123

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
10:05:26.448943 IP 10.0.0.65.63822 > 10.0.0.67.123: NTPv4, Client, length 48

tcpdump -n -n -i eth1 port 123

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 96 bytes
10:05:26.449220 IP 10.21.3.160.63822 > 10.21.3.169.123: NTPv4, Client, length 48
10:05:26.449148 IP 10.21.3.169.123 > 10.21.3.160.63822: NTPv4, Server, length 48

So it looks like the routing is still not right on the Linux box – the reply is coming back from the NTP box but is not being sent to OpenBSD.

Just for clarity, the iptables routing that I have added now looks like this:

iptables -t nat -A PREROUTING -i eth0 -p udp --dport 123 -j DNAT --to-destination 10.21.3.169:123
iptables -t filter -A FORWARD -p udp -d 10.21.3.169 --dport 123 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth1 -p udp --dport 123 -j MASQUERADE
//for the final line, I changed @gromit's suggestion slightly, as the --from option wasn't recognised
iptables -t nat -A POSTROUTING -p udp --sport 123 -j SNAT --to-source 10.21.3.169

Leaving that final iptables line out seems to make no difference to the tcpdump output.

Edit 2

I have now got the following IPtables entries, and I am able to update the clock on the OpenBSD box:

iptables -t nat -A PREROUTING -i eth0 -p udp --dport 123 -j DNAT --to-destination 10.21.3.169:123
iptables -t nat -A PREROUTING -i eth1 -p udp --sport 123 -j DNAT --to-destination 10.0.0.65:123
iptables -t filter -A FORWARD -p udp -d 10.21.3.169 --dport 123 -j ACCEPT
iptables -t filter -A FORWARD -p udp -d 10.0.0.65 --sport 123 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth1 -p udp --dport 123 -j MASQUERADE
iptables -t nat -A POSTROUTING -p udp --sport 123 -j SNAT --to-source 10.21.3.169

However the second prerouting and second filter commands seem like overkill to me, as, as I understand it, they forward all UDP packets from port 123 on the NTP server. I have a feeling that this means that all NTP replies that go to the Linux box (ie including when the Linux box itself asks for the time) will get forwarded to the OpenBSD box.

Best Answer

this looks like you have a routing problem.

Does the system serving the time (3.169) know how to connect to the requesting system?

If not add the following to the linux box's iptables:

// this rule, redirects the packets for NTP to the NTP server on the host
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 123 -j DNAT --to 10.21.3.169

// this rule make it look for the ntp server as if the linux box is requesting the packets, which
// makes routing not necessary and lets the packet flow back to the linux box
iptables -t nat -A POSTROUTING -o eth1 -p udp --dport 123 -j MASQUERADE

// now the linux box has to check, that the packets coming back are looking like they are from the
// main source
iptables -t nat -A POSTROUTING -i eth1 -p udp --sport 123 -j SNAT --from $ip_the_client_requested_the_time_from

Now it looks like the ntp packages are coming back and the time is synced.

It would be easier if the ntp daemon and the redirect would run on the same system. Then you could use the "-j REDIRECT" feature of iptables that does the whole magic for you, but it works only at the localhost.

KR,

Gromit