Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] nf2go: convert nftables rules to golang code #298

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

aojea
Copy link
Contributor

@aojea aojea commented Feb 2, 2025

One of the biggest barriers to adopt the netlink format for nftables is the complexity of writing bytecode.

This commits adds a tool that allows to take an nftables dump and generate the corresponding golang code and validating that the generated code produces the exact same output.

How to use it

pass as parameter the dump obtained via nft list ruleset and it will provide the generated go code in stdout and also report the differences , since there may be bugs or misinterpratations.

go run main.go rules_simple.txt > generated_nft.go
Generated code:
// Code generated by nft2go. DO NOT EDIT.
package main

import (
        "fmt"
        "log"
        "github.com/google/nftables"
        "github.com/google/nftables/expr"
)

func main() {
        n, err:= nftables.New()
        if err!= nil {
                log.Fatal(err)
        }


        var expressions []expr.Any
        var chain *nftables.Chain
        table:= n.AddTable(&nftables.Table{Family: nftables.TableFamilyIPv4,Name: "filter"})
        chain = n.AddChain(&nftables.Chain{Name: "output", Table: table, Type: nftables.ChainTypeFilter, Hooknum: nftables.ChainHookOutput, Priority: nftables.ChainPriorityRef(100)})
        chain = n.AddChain(&nftables.Chain{Name: "input", Table: table, Type: nftables.ChainTypeFilter, Hooknum: nftables.ChainHookInput, Priority: nftables.ChainPriorityRef(0)})
        expressions = []expr.Any{
                &expr.Meta{Key:0x6, SourceRegister:false, Register:0x1},
                &expr.Cmp{Op:0x0, Register:0x1, Data:[]uint8{0x6c, 0x61, 0x6e, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
                &expr.Verdict{Kind:1, Chain:""},
                }
        n.AddRule(&nftables.Rule{
                Table: table,
                Chain: chain,
                Exprs: expressions,
        })
        expressions = []expr.Any{
                &expr.Meta{Key:0x6, SourceRegister:false, Register:0x1},
                &expr.Cmp{Op:0x0, Register:0x1, Data:[]uint8{0x77, 0x61, 0x6e, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
                &expr.Verdict{Kind:0, Chain:""},
                }
        n.AddRule(&nftables.Rule{
                Table: table,
                Chain: chain,
                Exprs: expressions,
        })
        chain = n.AddChain(&nftables.Chain{Name: "forward", Table: table, Type: nftables.ChainTypeFilter, Hooknum: nftables.ChainHookForward, Priority: nftables.ChainPriorityRef(0)})
        expressions = []expr.Any{
                &expr.Meta{Key:0x6, SourceRegister:false, Register:0x1},
                &expr.Cmp{Op:0x0, Register:0x1, Data:[]uint8{0x6c, 0x61, 0x6e, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
                &expr.Meta{Key:0x7, SourceRegister:false, Register:0x1},
                &expr.Cmp{Op:0x0, Register:0x1, Data:[]uint8{0x77, 0x61, 0x6e, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
                &expr.Verdict{Kind:1, Chain:""},
                }
        n.AddRule(&nftables.Rule{
                Table: table,
                Chain: chain,
                Exprs: expressions,
        })
        expressions = []expr.Any{
                &expr.Meta{Key:0x6, SourceRegister:false, Register:0x1},
                &expr.Cmp{Op:0x0, Register:0x1, Data:[]uint8{0x77, 0x61, 0x6e, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
                &expr.Meta{Key:0x7, SourceRegister:false, Register:0x1},
                &expr.Cmp{Op:0x0, Register:0x1, Data:[]uint8{0x6c, 0x61, 0x6e, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
                &expr.Ct{Register:0x1, SourceRegister:false, Key:0x0},
                &expr.Bitwise{SourceRegister:0x1, DestRegister:0x1, Len:0x4, Mask:[]uint8{0x6, 0x0, 0x0, 0x0}, Xor:[]uint8{0x0, 0x0, 0x0, 0x0}},
                &expr.Cmp{Op:0x1, Register:0x1, Data:[]uint8{0x0, 0x0, 0x0, 0x0}},
                &expr.Verdict{Kind:1, Chain:""},
                }
        n.AddRule(&nftables.Rule{
                Table: table,
                Chain: chain,
                Exprs: expressions,
        })

        if err:= n.Flush(); err!= nil {
                log.Fatalf("fail to flush rules: %!v(MISSING)", err)
        }

        fmt.Println("nft ruleset applied.")
}

Note

There are some mismatches that are caused by different order or name interpretation, per example

2025/02/02 17:08:05 nftables ruleset mismatch:
  (
        """
        ... // 4 identical lines

                chain input {
-                       type filter hook input priority 0; policy accept;
+                       type filter hook input priority filter; policy accept;
                        iifname "lan0" accept
                        iifname "wan0" drop
                }

                chain forward {
-                       type filter hook forward priority 0; policy drop;
+                       type filter hook forward priority filter; policy accept;
                        iifname "lan0" oifname "wan0" accept
-                       iifname "wan0" oifname "lan0" ct state related,established accept
+                       iifname "wan0" oifname "lan0" ct state established,related accept
                }
        }
        """
  )

@aojea
Copy link
Contributor Author

aojea commented Feb 2, 2025

cc: @grosskur

@aojea
Copy link
Contributor Author

aojea commented Feb 2, 2025

@stapelberg feel free to suggest directions, right now is very hacky but is a really useful tools to be able to use this library more efficiently

@aojea aojea force-pushed the nf2go branch 2 times, most recently from 5dae564 to 444f1f1 Compare February 3, 2025 15:04
One of the biggest barriers to adopt the netlink format for nftables is
the complexity of writing bytecode.

This commits adds a tool that allows to take an nftables dump and
generate the corresponding golang code and validating that the generated
code produces the exact same output.

Change-Id: I491b35e0d8062de33c67091dd4126d843b231838
Signed-off-by: Antonio Ojea <[email protected]>
Copy link
Collaborator

@stapelberg stapelberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems fine to me overall. My main concerns are not introducing new dependencies for this helper tool and not having to extend the public API of the package.

I left two small pointers but will only review in detail once you say it’s ready.

)

func main() {
args := os.Args[1:]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use flag.Parse :)


// Format the generated code
log.Printf("formating file: %s", tempGoFile)
cmd := exec.Command("gofmt", "-w", "-s", tempGoFile)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use https://pkg.go.dev/go/format#Source instead of shelling out to gofmt

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That has a tradeoff in respecting the current GOTOOLCHAIN at the time of execution vs at the time of building this binary?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants