1

I'm working from the answer of this question and man nft in order to create some dnat rules in my nftables config.

The relevant config extract is:

define src_ip       = 192.168.1.128/26
define dst_ip       = 192.168.1.1
define docker_dns   = 172.20.10.5

table inet nat {
    map dns_nat {
        type ipv4_addr . ipv4_addr . ip_proto . inet_service : ipv4_addr . inet_service
        flags interval
        elements = {
            $src_ip . $dst_ip . udp . 53 : $docker_dns . 5353,
        }
    }
    chain prerouting {
        type nat hook prerouting priority -100; policy accept;
        
        dnat to ip saddr . ip daddr . ip protocol . th dport map @dns_nat;
    }
}

When I apply this rule with nft -f, I see no command output so I presume it's succeeded. However when I inspect the ruleset using nft list ruleset the rules aren't present. When the dnat to ... line is commented out the rules appear to be applied, however when the line is present the rules are not applied.

The collection of rules in the prerouting chain I'm attempting to replace is:

ip saddr $src_ip ip daddr $dst_ip udp dport 53 dnat to $docker_dns:5353;
...

Version information:

# nft -v
nftables v1.0.6 (Lester Gooch #5)
# uname -r
6.1.0-11-amd64

Why might this not be working? Thanks

0

1 Answer 1

1

There are 3 problems.

  • no error is displayed

    This looks to be a bug in nftables 1.0.6, see following bullets.

    Here with the same version and OP's ruleset in /tmp/ruleset.nft:

    # nft -V
    nftables v1.0.6 (Lester Gooch #5)
    [...]
    # nft -f /tmp/ruleset.nft 
    /tmp/ruleset.nft:7:38-45: Error: unknown datatype ip_proto
            type ipv4_addr . ipv4_addr . ip_proto . inet_service : ipv4_addr . inet_service
                                         ^^^^^^^^
    /tmp/ruleset.nft:6:9-15: Error: set definition does not specify key
        map dns_nat {
            ^^^^^^^
    
  • Error: unknown datatype ip_proto

    The original linked Q/A used the correct type inet_proto. This should not have been replaced with ip_proto which is an unknown type. So replace back:

    type ipv4_addr . ipv4_addr . ip_proto . inet_service : ipv4_addr . inet_service
    

    with the correct original spelling:

    type ipv4_addr . ipv4_addr . inet_proto . inet_service : ipv4_addr . inet_service
    

    A list of available types can be found in nft(8) at PAYLOAD EXPRESSION and more precisely for this case at IPV4 HEADER EXPRESSION:

    Keyword Description Type
    [...]
    protocol Upper layer protocol inet_proto
    [...]

    typeof ip protocol <=> type inet_proto (not type ip_proto).

    Normally typeof should be preferred to type to avoid having to guess the correct type, but as I wrote in the linked Q/A, some versions of nftables might not cope correctly with this precise case. The replacement would have been:

    typeof ip saddr . ip daddr . ip protocol . th dport : ip daddr . th dport
    

    which is almost a cut/paste from the rule using it, but its behavior should be thoroughly tested.

  • no error is displayed - take 2

    Once this previous error is fixed (and the result put in /tmp/ruleset2.nft), then, as OP wrote, trying again the ruleset fails silently:

    # nft -V
    nftables v1.0.6 (Lester Gooch #5)
      cli:        editline
      json:       yes
      minigmp:    no
      libxtables: yes
    # nft -f /tmp/ruleset2.nft
    # echo $?
    1
    #
    

    The only clue it failed is non-0 return code.

    While with a newer nftables version:

    # nft -V
    nftables v1.0.8 (Old Doc Yak #2)
      cli:        editline
      json:       yes
      minigmp:    no
      libxtables: yes
    # nft -f /tmp/ruleset2.nft 
    /tmp/ruleset2.nft:16:9-12: Error: specify `dnat ip' or 'dnat ip6' in inet table to disambiguate
            dnat to ip saddr . ip daddr . ip protocol . th dport map @dns_nat;
            ^^^^
    #
    

    Now the error is displayed. Whatever was the issue in 1.0.6 it has been fixed at least with version 1.0.8.

  • Error: specify `dnat ip' or 'dnat ip6' in inet table to disambiguate

    Because NAT is done in the inet family (combined IPv4+IPv6) rather than either ip (IPv4) or ip6 (IPv6) family, one parameter which is usually optional becomes mandatory: state the IP version NAT should be applied to (even if one could infer it from the map table layout (IPv4)). Documentation tells:

    NAT STATEMENTS

    snat [[ip | ip6] to] ADDR_SPEC [:PORT_SPEC] [FLAGS]
    dnat [[ip | ip6] to] ADDR_SPEC [:PORT_SPEC] [FLAGS]
    masquerade [to :PORT_SPEC] [FLAGS]
    redirect [to :PORT_SPEC] [FLAGS]
    

    [...]

    When used in the inet family (available with kernel 5.2), the dnat and snat statements require the use of the ip and ip6 keyword in case an address is provided, see the examples below.

    So:

    dnat to ip saddr . ip daddr . ip protocol . th dport map @dns_nat;
    

    should be replaced with:

    dnat ip to ip saddr . ip daddr . ip protocol . th dport map @dns_nat
    

    The original Q/A didn't state the family, so it would be assumed it was the default ip family which wouldn't require this.

    Of course, this will work with nftables 1.0.6, only the error reporting had a problem. The return code will now be 0.

1
  • Ah sorry the ip_proto was a stupid typo from me when writing the question >.<! Thanks, the dnat ip change has fixed it. Hopefully there aren't many other bugs like this Commented Sep 30, 2023 at 15:01

You must log in to answer this question.

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