diff --git a/family_type.go b/family_type.go new file mode 100644 index 0000000..82b6dd1 --- /dev/null +++ b/family_type.go @@ -0,0 +1,30 @@ +package nft + +import "fmt" + +type FamilyType int8 + +const ( + IP FamilyType = iota + 1 + IP6 + INET + ARP + BRIDGE +) + +func (f FamilyType) String() string { + switch f { + case IP: + return "ip" + case IP6: + return "ip6" + case INET: + return "inet" + case ARP: + return "arp" + case BRIDGE: + return "bridge" + default: + return fmt.Sprintf("Encoding(%d)", f) + } +} diff --git a/nft.go b/nft.go new file mode 100644 index 0000000..2668d85 --- /dev/null +++ b/nft.go @@ -0,0 +1,53 @@ +package nft + +import ( + "errors" +) + +// NFT A client for working with nftables +type NFT interface { + // Clear clears all rules. + Clear() error + + // AddTable adds a new table. + AddTable(family FamilyType, name string) error +} + +type nft struct { + path string +} + +// New Returns a client for working with nftables. +// Searches for nft in paths: nft, /usr/sbin/nft, /sbin/nft +func New() (NFT, error) { + paths := []string{"nft", "/usr/sbin/nft", "/sbin/nft"} + for _, path := range paths { + nftClient, err := NewWithPath(path) + if err == nil { + return nftClient, nil + } + } + + return nil, errors.New("nft not found") +} + +// NewWithPath Returns the client for working with nftables with its path specified. +func NewWithPath(path string) (NFT, error) { + if err := checkingNFT(path); err != nil { + return nil, err + } + + return &nft{ + path: path, + }, nil +} + +func (n *nft) Clear() error { + args := []string{"flush", "ruleset"} + return executeCommand(n.path, args...) +} + +func (n *nft) AddTable(family FamilyType, name string) error { + args := []string{"add", "table", family.String(), name} + return executeCommand(n.path, args...) +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..73ca09e --- /dev/null +++ b/utils.go @@ -0,0 +1,64 @@ +package nft + +import ( + "errors" + "fmt" + "os/exec" + "regexp" + "strings" +) + +func executeCommand(name string, arg ...string) error { + cmd := exec.Command(name, arg...) + out, err := cmd.CombinedOutput() + if err != nil { + if len(out) > 0 { + return errors.New(string(out)) + } + return err + } + + return nil +} + +func checkingNFT(path string) error { + if path == "" { + return errors.New("path is empty") + } + + cmd := exec.Command(path, "-V") + out, err := cmd.CombinedOutput() + if err != nil { + return errors.New("nftables not found") + } + + lines := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1) + json := false + for index, line := range lines { + line = strings.TrimSpace(line) + + if index == 0 { + if !strings.HasPrefix(line, "nftables") { + return errors.New("nftables not found") + } + continue + } + + if strings.HasPrefix(line, "json:") && strings.HasSuffix(line, "yes") { + json = true + } + } + + if !json { + return errors.New("nftables disabled json") + } + + cmd = exec.Command(path, "list", "ruleset") + out, err = cmd.CombinedOutput() + + if err != nil { + return fmt.Errorf("nftables is not available or not supported by the kernel: %s", string(out)) + } + + return nil +}