nftables: duplicate UDP packets for specific destination IP:port to a (second) destination IP:port

I would like to mirror only a very limited packet traffic destined to a single IP and UDP port to the same IP but a second port. I’ve locked at https://superuser.com/questions/1593995/iptables-nftables-forward-udp-data-to-multiple-targets but it seems as if nftable’s dup statement only allows duplicating to another IP but not the same IP and different port.

For instance, traffic to 127.0.0.1 port 123 should be duplicated to 127.0.0.1 port 456. As this is the only single duplication I don’t have any problems with the original port number getting lost. Now I wonder if this kind of duplication is possible at all because 127.0.0.1 is not a "drain"/outgoing interface but the duplicate’s final destination. Might there be some way to combine this with DNAT?

Is there some other mechanism available short of attaching an eBPF probe to the netdev with the incoming UDP traffic?

Asked By: TheDiveO

||

This can be done with nftables and netdev family with an ingress chain and a dup statement. It requires using a mark to avoid an infinite loop. Depending on the exact use case, the duplication can also probably be done on egress (since it’s on the loopback interface, the duplicated egress packet will appear back as ingress) but this would require kernel >= 5.17 for support, while ingress has been available for a long time.

Requires kernel >= 4.10 (for the stateless UDP alteration correct checksum support).

# nft -f - <<'EOF'
table netdev t_dup        # for idempotency
delete table netdev t_dup # for idempotency

table netdev t_dup {
    chain c_ingress {
        type filter hook ingress device "lo" priority filter; policy accept;
        iif lo udp dport 123 meta mark != 1 meta mark set 1 dup to lo udp dport set 456
    }
}
EOF
  • The candidate packet is checked for a mark, and processed only if there’s no mark, starting by setting a mark: this will prevent a loop later.

  • It’s duplicated. The mark, being part of the duplicated sk_buff, is also duplicated

    The duplicate will actually be the unchanged packet: sent to the same place (lo) and also to port 123

  • the dup statement, contrary to any iptables‘ target, including its TEE target, isn’t a rule terminating statement. Rule continues with a stateless alteration of the packet: UDP port is changed to 456

  • The duplicated packet also arrives in ingress but as it’s marked the rule ignores it: loop is prevented

The duplicated port can be tested with socat:

socat -u udp4-recv:456,bind=127.0.0.1 -

Notes:

  • If nothing is listening on the duplicated port, an ICMP port unreachable will be emitted, but since this port (456) doesn’t match the port the sending application is sending to (123), it will be ignored by the network stack.

  • Netfilter’s ingress happening after AF_PACKET, tcpdump will not capture the changed port nor the duplicated packet.

Answered By: A.B

I needed to do this for netflow/IPFix packets. I tried your solution but it didn’t work for me as it is.

However, inspired by yours and another answer, I eventually got a working one. Tested to work on Debian 11 with kernel 5.10:

table ip mangle
delete table ip mangle

table ip mangle {
    chain prerouting {
        type filter hook prerouting priority mangle; policy accept;
        iifname "eth0" udp dport 6660 
      dup to 127.0.0.1 device lo udp dport set 6666 notrack 
      dup to 127.0.0.1 device lo udp dport set 6667 notrack 
      dup to 127.0.0.1 device lo udp dport set 6668 notrack
    }

    chain input {
        type filter hook input priority mangle; policy accept;
        iifname lo udp dport 6666 ip daddr set 127.0.0.1 notrack
        iifname lo udp dport 6667 ip daddr set 127.0.0.1 notrack
        iifname lo udp dport 6668 ip daddr set 127.0.0.1 notrack
    }
}

This will duplicate anything arriving on eth0 for UDP/6660 to localhost UDP ports 6666, 6667 and 6668 and set destination to 127.0.0.1. The latter is needed because some software doesn’t like it otherwise.

I have tested this with Fastnetmon, nfdump/nfsen and AS-Stats.

Answered By: Alfredo
Categories: Answers Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.