google / nftables

This repository contains a Go module to interact with Linux nftables (the iptables successor).
Apache License 2.0
1.12k stars 140 forks source link

Use a CIDR prefix as target in a NAT rule #248

Closed cheina97 closed 11 months ago

cheina97 commented 1 year ago

Hi, I work for an open source project called liqo. We are migrating from iptables to nftables and we are trying to use your package to interact with nftables.

In particular, we have trouble creating some NAT rules (DNAT and SNAT) that translate IPs matching a subnet to another one.

What we are trying to create is a rule similar to:

nft add rule ip nat PREROUTING ip saddr 10.0.0.0/24 dnat ip prefix to 10.0.1.0/24

Using this setup :

var rule nftables.Rule
rule.Exprs = []expr.Any{
    &expr.Payload{
        DestRegister: 1,
        Base:         expr.PayloadBaseNetworkHeader,
        Offset:       12,
        Len:          4,
    }, &expr.Bitwise{
        SourceRegister: 1,
        DestRegister:   1,
        Len:            4,
        Xor:            []byte{0x0, 0x0, 0x0, 0x0},
        Mask:           []byte{0xff, 0xff, 0xff, 0x0},
    }, &expr.Cmp{
        Op:       expr.CmpOpEq,
        Register: 1,
        Data:     net.ParseIP("10.0.0.0").To4(),
    }, &expr.Bitwise{
        SourceRegister: 1,
        DestRegister:   1,
        Len:            4,
        Xor:            []byte{0x0, 0x0, 0x0, 0x0},
        Mask:           []byte{0xff, 0xff, 0xff, 0x0},
    }, &expr.Immediate{
        Register: 1,
        Data:     net.ParseIP("10.0.1.0").To4(),
    }, &expr.NAT{
        Type:       expr.NATTypeDestNAT,
        RegAddrMin: 1,
        Family:     uint32(nftables.TableFamilyINet),
    },
}

I get the rule:

 ip saddr 10.0.0.0/24 dnat to 10.0.1.0

I think that using 'immediate' is not the correct solution.

How can I get:

 ip saddr 10.0.0.0/24 dnat ip prefix to 10.0.1.0/24

Thanks for your answer and for your work

cheina97 commented 1 year ago

I found a partial solution using the nft --debug=netlink command. I was able to replicate this command with a double 'immediate' in reg1 and reg2

nft --debug=netlink add rule nat OUTPUT ip saddr 10.0.0.0/24 dnat to 20.0.0.0/24
ip nat OUTPUT
  [ payload load 3b @ network header + 12 => reg 1 ]
  [ cmp eq reg 1 0x0000000a ]
  [ immediate reg 1 0x00000014 ]
  [ immediate reg 2 0xff000014 ]
  [ nat dnat ip addr_min reg 1 addr_max reg 2 ]

But the command I'm trying to recreate is:

nft --debug=netlink add rule ip nat OUTPUT ip saddr 10.0.0.0/24 dnat ip prefix to 20.0.0.0/24
ip nat OUTPUT
  [ payload load 3b @ network header + 12 => reg 1 ]
  [ cmp eq reg 1 0x0000000a ]
  [ immediate reg 1 0x00000014 ]
  [ immediate reg 2 0xff000014 ]
  [ nat dnat ip addr_min reg 1 addr_max reg 2 flags 0x40 ]

The difference is the flag 0x40 and I think it is a feature which is not implemented at the moment.

cheina97 commented 1 year ago

It's my first time with nftables so I could be wrong, but it doesn't seem a hard feature to implement. If you think it can be useful for the community I can work on it.

Update: I was curious and I tried to add the flag. It seems to work properly https://github.com/cheina97/nftables/commit/f658cd146651d438de564ce309c9db20a3eb7f6c

cheina97 commented 11 months ago

The feature has been implemented here #251