2

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?

2
  • As I quoted from man page, dup has two different syntaxes depending if used from from netdev family or from an ip/ip6/inet family. None will change the duplicated packet (the "to address" is a gateway not a destination). So the duplicate must be further mangled. Can't do an answer because I'm not sure of the layout in the question.
    – A.B
    Commented Jun 3, 2022 at 16:15
  • @A.B I've updated my question as to hopefully give enough details regarding the layout you're unclear about; please ask for more details you need.
    – TheDiveO
    Commented Jun 7, 2022 at 7:47

2 Answers 2

1

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.

0

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.

1
  • Welcome to the site. Please keep in mind that this is not a discussion forum, and hence the order of posts is subject to user preferences. When referring to another answer, please add a direct link to it so that others can understand what you refer to. For the same reason, please don't write answers as addressing a different user, except the original question poster.
    – AdminBee
    Commented Mar 27, 2023 at 12:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .