4

We currently have some rules setup in IPtables that look like the below:

-A PREROUTING -d dstip/32 -p udp -m udp --dport 27035 -m u32 --u32 "0x0>>0x16&0x3c@0x8=0xffffffff&&0x0>>0x16&0x3c@0xc=0x54536f75&&0x0>>0x16&0x3c@0x10=0x72636520&&0x0>>0x16&0x3c@0x14=0x456e6769&&0x0>>0x16&0x3c@0x18=0x6e652051&&0x0>>0x16&0x3c@0x1c=0x75657279" -j REDIRECT --to-ports 21010

This redirects packets containing that payload to our caching programs, this works great. However, only "new" packets are hitting this NAT rule. After some looking around, I managed to solve this issue by setting the following:

sudo sysctl -w net.netfilter.nf_conntrack_udp_timeout=0
sudo sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=0

This introduced another problem where ALL UDP packets were being set to this. During a small UDP flood, this would cause all udp traffic on the server to halt.

I'm needing to redirect ALL UDP packets containing a certain payload to another port on the same machine. We have another program listening on this port that replies to this payload. This is due to a query flood that usually freezes up one of our applications, so we offload this query to another program.

Any solutions to this? Been looking around for months without any sort of answer.

12
  • I don't see how you can translate a network address without using NAT. What are your objections to using network address translation in some form? Nov 18, 2016 at 18:18
  • NAT is fine, except it needs to redirect all matching packets and not just NEW ones. Established connections etc do not go through NAT Nov 18, 2016 at 18:32
  • I basically just need a way to redirect UDP packets containing x payload to another port Nov 18, 2016 at 18:36
  • I would suggest then that you edit your question to remove the "without NAT" stipulation. Nov 18, 2016 at 22:49
  • 1
    Can you edit the question and add you all rules in the NAT table ? Nov 19, 2016 at 4:58

1 Answer 1

1

I have two completely different methods to solve this problem:

iptables with conntrack zones

Contrack zones allow multiple conntrack instances (per network namespace).

By adding an orig-zone tag to the packet and thus flow, it's possible to have two conntrack instances splitting the (half created) flow in two parts: the normal packets and the matching packets, each in its own flow. Thus usual NAT rules will continue to work and can happen twice instead of once: once for the normal packets and once for the matching packets.

Instead of duplicating the test, a mark will be put in place after the one test done on the packet.

iptables -t raw -A PREROUTING -d dstip/32 -p udp -m udp --dport 27035 -m u32 --u32 "0x0>>0x16&0x3c@0x8=0xffffffff&&0x0>>0x16&0x3c@0xc=0x54536f75&&0x0>>0x16&0x3c@0x10=0x72636520&&0x0>>0x16&0x3c@0x14=0x456e6769&&0x0>>0x16&0x3c@0x18=0x6e652051&&0x0>>0x16&0x3c@0x1c=0x75657279" -j MARK --set-mark 1
iptables -t raw -A PREROUTING -m mark --mark 1 -j CT --zone-orig 1
iptables -t nat -A PREROUTING -p udp -m mark --mark 1 -j REDIRECT --to-ports 21010

Example of conntrack results:

# conntrack -E -p udp --orig-port-dst 27035
    [NEW] udp      17 30 src=10.0.3.1 dst=10.0.3.66 sport=52670 dport=27035 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=27035 dport=52670
    [NEW] udp      17 30 src=10.0.3.1 dst=10.0.3.66 sport=52670 dport=27035 zone-orig=1 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=21010 dport=52670
 [UPDATE] udp      17 30 src=10.0.3.1 dst=10.0.3.66 sport=52670 dport=27035 src=10.0.3.66 dst=10.0.3.1 sport=27035 dport=52670
 [UPDATE] udp      17 30 src=10.0.3.1 dst=10.0.3.66 sport=52670 dport=27035 zone-orig=1 src=10.0.3.66 dst=10.0.3.1 sport=21010 dport=52670

nftables with notrack and packet header field mangling

requires kernel >= 4.10

(Of course the zones method could be implemented with nftables).

Here NAT is done statelessly, with conntrack disabled, for matching incoming packets and all outgoing reply packets from the redirected port.

nft add table ip raw
nft add chain ip raw prerouting '{type filter hook prerouting priority -300;}'
nft add chain ip raw output '{type filter hook output priority -300;}'
nft add rule ip raw prerouting ip daddr dstip/32 udp dport 27035 @th,64,32 == 0xffffffff @th,96,32 == 0x54536f75 @th,128,32 == 0x72636520 @th,160,32 == 0x456e6769 @th,192,32 == 0x6e652051 @th,224,32 == 0x75657279 notrack udp dport set 21010
nft add rule ip raw output udp sport 21010 notrack udp sport set 27035

(note that the u32 equivalent raw payload filters can be simplified and are actually simplified automatically by nftables: it appears nftables handles u128 internally as can be seen with nft --debug=netlink list ruleset -a).

4
  • Could you please explain the commands in more details so that we can understand and adapt as necessary? For example, what does 0x72636520 mean?
    – Ned64
    Feb 24, 2020 at 13:28
  • @Ned64 that's the payload given by OP. those values were in OP, I didn't invent them. 54536f7572636520456e67696e65205175657279 decodes as ASCII TSource Engine Query
    – A.B
    Feb 24, 2020 at 15:05
  • OK, thanks, I was thinking perhaps hex flags for TCP like tcp flags:!0x17/0x02. Sorry!
    – Ned64
    Feb 24, 2020 at 15:13
  • The question is about splitting a flow normally handled as one single flow by netfilter into two flows: the normal and the "rejected". The answer is about how to do this by using some rare features of netfilter (zones) or bypassing netfilter
    – A.B
    Feb 24, 2020 at 15:15

You must log in to answer this question.

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