From 3c47e7566b48c9c51aeca5d50e78579f7b819553 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Wed, 22 Apr 2026 21:54:59 +0500 Subject: [PATCH] Refactor to consolidate APIs into `contract` package and introduce `NFT` interface for better modularity and maintainability. --- contract/command.go | 12 +++++++++ contract/nft.go | 31 ++++++++++++++++++++++ contract/nft/chain.go | 46 ++++++++++++++++++++++++++++++++ contract/nft/rule.go | 31 ++++++++++++++++++++++ contract/nft/table.go | 24 +++++++++++++++++ contract/nft/version.go | 8 ++++++ internal/chain/chain.go | 47 +++------------------------------ internal/command/command.go | 12 +++------ internal/rule/rule.go | 34 +++--------------------- internal/table/table.go | 28 +++----------------- nft.go | 52 ++++++++++--------------------------- version.go | 11 ++------ 12 files changed, 182 insertions(+), 154 deletions(-) create mode 100644 contract/command.go create mode 100644 contract/nft.go create mode 100644 contract/nft/chain.go create mode 100644 contract/nft/rule.go create mode 100644 contract/nft/table.go create mode 100644 contract/nft/version.go diff --git a/contract/command.go b/contract/command.go new file mode 100644 index 0000000..ec8764e --- /dev/null +++ b/contract/command.go @@ -0,0 +1,12 @@ +package contract + +// Run is a function that executes nft command. +type Run func(arg ...string) error + +type Command interface { + // Run nft command. + Run(arg ...string) error + + // RunWithOutput Run nft command with output. + RunWithOutput(arg ...string) (string, error) +} diff --git a/contract/nft.go b/contract/nft.go new file mode 100644 index 0000000..28303e6 --- /dev/null +++ b/contract/nft.go @@ -0,0 +1,31 @@ +package contract + +import "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract/nft" + +// NFT A client for working with nftables +type NFT interface { + // Command returns the command used to execute nft. + // You can execute your raw request. + Command() Command + + // Clear clears all rules. + // + // This command is equivalent to: + // nft flush ruleset + Clear() error + + // Version returns the version of nftables. + // + // This command is equivalent to: + // nft -V + Version() (nft.Version, error) + + // Table returns an API for working with tables. + Table() nft.Table + + // Chain returns an API for working with chains. + Chain() nft.Chain + + // Rule returns an API for working with rules. + Rule() nft.Rule +} diff --git a/contract/nft/chain.go b/contract/nft/chain.go new file mode 100644 index 0000000..2f6376d --- /dev/null +++ b/contract/nft/chain.go @@ -0,0 +1,46 @@ +package nft + +import ( + chain2 "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain" + "git.kor-elf.net/kor-elf-shield/go-nftables-client/family" +) + +// Chain for working with chains. +type Chain interface { + // Add adds a new chain. + // + // This command is equivalent to: + // nft add chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} + // nft add chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type (filter|route|nat) hook (ingress|prerouting|forward|input|output|postrouting|egress) priority (priority_value = int32) ;}' + // nft add chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type filter hook (forward|input|output) priority (priority_value = int32) ; policy (accept|drop) ;}' + // nft add chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type (filter|route|nat) hook (ingress|egress) device {device} priority (priority_value = int32) ;}' + Add(family family.Type, tableName string, chainName string, baseChain chain2.ChainOptions) error + + // Create creates a new chain. + // Similar to the Add, but returns an error if the chain already exists. + // + // This command is equivalent to: + // nft create chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} + // nft create chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type (filter|route|nat) hook (ingress|prerouting|forward|input|output|postrouting|egress) priority (priority_value = int32) ;}' + // nft create chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type filter hook (forward|input|output) priority (priority_value = int32) ; policy (accept|drop) ;}' + // nft create chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type (filter|route|nat) hook (ingress|egress) device {device} priority (priority_value = int32) ;}' + Create(family family.Type, tableName string, chainName string, baseChain chain2.ChainOptions) error + + // Delete deletes a chain. + // + // This command is equivalent to: + // nft delete chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} + Delete(family family.Type, tableName string, chainName string) error + + // Clear clears all rules in a chain. + // + // This command is equivalent to: + // nft flush chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} + Clear(family family.Type, tableName string, chainName string) error + + // Rename renames a chain. + // + // This command is equivalent to: + // nft rename chain (ip|ip6|inet|arp|bridge) {table_name} {old_chain_name} {new_chain_name} + Rename(family family.Type, tableName string, oldChainName string, newChainName string) error +} diff --git a/contract/nft/rule.go b/contract/nft/rule.go new file mode 100644 index 0000000..7ac7918 --- /dev/null +++ b/contract/nft/rule.go @@ -0,0 +1,31 @@ +package nft + +import "git.kor-elf.net/kor-elf-shield/go-nftables-client/family" + +// Rule is the interface for rule manipulation. +type Rule interface { + // Add adds a new rule. + // + // This command is equivalent to: + // nft add rule (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ expr }' + Add(family family.Type, tableName string, chainName string, expr ...string) error + + // Insert inserts a new rule. + // Inserted rules are placed at the beginning of the chain, by default. + // + // This command is equivalent to: + // nft insert rule (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ expr }' + Insert(family family.Type, tableName string, chainName string, expr ...string) error + + // Replace replaces a rule. + // + // This command is equivalent to: + // nft replace rule (ip|ip6|inet|arp|bridge) {table_name} {chain_name} {handle} '{ expr }' + Replace(family family.Type, tableName string, chainName string, handle uint64, expr ...string) error + + // Delete deletes a rule. + // + // This command is equivalent to: + // nft delete rule (ip|ip6|inet|arp|bridge) {table_name} {chain_name} {handle} + Delete(family family.Type, tableName string, chainName string, handle uint64) error +} diff --git a/contract/nft/table.go b/contract/nft/table.go new file mode 100644 index 0000000..d77f550 --- /dev/null +++ b/contract/nft/table.go @@ -0,0 +1,24 @@ +package nft + +import "git.kor-elf.net/kor-elf-shield/go-nftables-client/family" + +// Table for working with tables. +type Table interface { + // AddTable adds a new table. + // + // This command is equivalent to: + // nft add table (ip|ip6|inet|arp|bridge) {table_name} + Add(family family.Type, tableName string) error + + // DeleteTable deletes a table. + // + // This command is equivalent to: + // nft delete table (ip|ip6|inet|arp|bridge) {table_name} + Delete(family family.Type, tableName string) error + + // ClearTable clears all rules in a table. + // + // This command is equivalent to: + // nft flush table (ip|ip6|inet|arp|bridge) {table_name} + Clear(family family.Type, tableName string) error +} diff --git a/contract/nft/version.go b/contract/nft/version.go new file mode 100644 index 0000000..aa99ba7 --- /dev/null +++ b/contract/nft/version.go @@ -0,0 +1,8 @@ +package nft + +type Version interface { + // Version returns the version of the nftables client. + Version() string + // Opts returns the options of the nftables client. + Opts() map[string]string +} diff --git a/internal/chain/chain.go b/internal/chain/chain.go index 1a99b44..007ab58 100644 --- a/internal/chain/chain.go +++ b/internal/chain/chain.go @@ -2,55 +2,16 @@ package chain import ( chain2 "git.kor-elf.net/kor-elf-shield/go-nftables-client/chain" + "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract" + "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract/nft" "git.kor-elf.net/kor-elf-shield/go-nftables-client/family" - "git.kor-elf.net/kor-elf-shield/go-nftables-client/internal/command" ) -// API for working with chains. -type API interface { - // Add adds a new chain. - // - // This command is equivalent to: - // nft add chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} - // nft add chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type (filter|route|nat) hook (ingress|prerouting|forward|input|output|postrouting|egress) priority (priority_value = int32) ;}' - // nft add chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type filter hook (forward|input|output) priority (priority_value = int32) ; policy (accept|drop) ;}' - // nft add chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type (filter|route|nat) hook (ingress|egress) device {device} priority (priority_value = int32) ;}' - Add(family family.Type, tableName string, chainName string, baseChain chain2.ChainOptions) error - - // Create creates a new chain. - // Similar to the Add, but returns an error if the chain already exists. - // - // This command is equivalent to: - // nft create chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} - // nft create chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type (filter|route|nat) hook (ingress|prerouting|forward|input|output|postrouting|egress) priority (priority_value = int32) ;}' - // nft create chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type filter hook (forward|input|output) priority (priority_value = int32) ; policy (accept|drop) ;}' - // nft create chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ type (filter|route|nat) hook (ingress|egress) device {device} priority (priority_value = int32) ;}' - Create(family family.Type, tableName string, chainName string, baseChain chain2.ChainOptions) error - - // Delete deletes a chain. - // - // This command is equivalent to: - // nft delete chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} - Delete(family family.Type, tableName string, chainName string) error - - // Clear clears all rules in a chain. - // - // This command is equivalent to: - // nft flush chain (ip|ip6|inet|arp|bridge) {table_name} {chain_name} - Clear(family family.Type, tableName string, chainName string) error - - // Rename renames a chain. - // - // This command is equivalent to: - // nft rename chain (ip|ip6|inet|arp|bridge) {table_name} {old_chain_name} {new_chain_name} - Rename(family family.Type, tableName string, oldChainName string, newChainName string) error -} - type chain struct { - command command.NFT + command contract.Command } -func New(command command.NFT) API { +func New(command contract.Command) nft.Chain { return &chain{ command: command, } diff --git a/internal/command/command.go b/internal/command/command.go index e197b91..44adb28 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -3,21 +3,15 @@ package command import ( "errors" "os/exec" + + "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract" ) -type NFT interface { - // Run nft command. - Run(arg ...string) error - - // RunWithOutput Run nft command with output. - RunWithOutput(arg ...string) (string, error) -} - type execNFT struct { nftPath string } -func New(path string) (NFT, error) { +func New(path string) (contract.Command, error) { if err := checkingNFT(path); err != nil { return nil, err } diff --git a/internal/rule/rule.go b/internal/rule/rule.go index 3a90968..8ab2b51 100644 --- a/internal/rule/rule.go +++ b/internal/rule/rule.go @@ -3,42 +3,16 @@ package rule import ( "strconv" + "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract" + "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract/nft" "git.kor-elf.net/kor-elf-shield/go-nftables-client/family" - "git.kor-elf.net/kor-elf-shield/go-nftables-client/internal/command" ) -type API interface { - // Add adds a new rule. - // - // This command is equivalent to: - // nft add rule (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ expr }' - Add(family family.Type, tableName string, chainName string, expr ...string) error - - // Insert inserts a new rule. - // Inserted rules are placed at the beginning of the chain, by default. - // - // This command is equivalent to: - // nft insert rule (ip|ip6|inet|arp|bridge) {table_name} {chain_name} '{ expr }' - Insert(family family.Type, tableName string, chainName string, expr ...string) error - - // Replace replaces a rule. - // - // This command is equivalent to: - // nft replace rule (ip|ip6|inet|arp|bridge) {table_name} {chain_name} {handle} '{ expr }' - Replace(family family.Type, tableName string, chainName string, handle uint64, expr ...string) error - - // Delete deletes a rule. - // - // This command is equivalent to: - // nft delete rule (ip|ip6|inet|arp|bridge) {table_name} {chain_name} {handle} - Delete(family family.Type, tableName string, chainName string, handle uint64) error -} - type rule struct { - command command.NFT + command contract.Command } -func New(command command.NFT) API { +func New(command contract.Command) nft.Rule { return &rule{ command: command, } diff --git a/internal/table/table.go b/internal/table/table.go index 3ee1522..2309358 100644 --- a/internal/table/table.go +++ b/internal/table/table.go @@ -1,36 +1,16 @@ package table import ( + "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract" + "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract/nft" "git.kor-elf.net/kor-elf-shield/go-nftables-client/family" - "git.kor-elf.net/kor-elf-shield/go-nftables-client/internal/command" ) -// API for working with tables. -type API interface { - // AddTable adds a new table. - // - // This command is equivalent to: - // nft add table (ip|ip6|inet|arp|bridge) {table_name} - Add(family family.Type, tableName string) error - - // DeleteTable deletes a table. - // - // This command is equivalent to: - // nft delete table (ip|ip6|inet|arp|bridge) {table_name} - Delete(family family.Type, tableName string) error - - // ClearTable clears all rules in a table. - // - // This command is equivalent to: - // nft flush table (ip|ip6|inet|arp|bridge) {table_name} - Clear(family family.Type, tableName string) error -} - type table struct { - command command.NFT + command contract.Command } -func New(command command.NFT) API { +func New(command contract.Command) nft.Table { return &table{ command: command, } diff --git a/nft.go b/nft.go index d423a0a..505cbce 100644 --- a/nft.go +++ b/nft.go @@ -5,50 +5,24 @@ import ( "regexp" "strings" + "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract" + nftContract "git.kor-elf.net/kor-elf-shield/go-nftables-client/contract/nft" "git.kor-elf.net/kor-elf-shield/go-nftables-client/internal/chain" "git.kor-elf.net/kor-elf-shield/go-nftables-client/internal/command" "git.kor-elf.net/kor-elf-shield/go-nftables-client/internal/rule" "git.kor-elf.net/kor-elf-shield/go-nftables-client/internal/table" ) -// NFT A client for working with nftables -type NFT interface { - // Command returns the command used to execute nft. - // You can execute your raw request. - Command() command.NFT - - // Clear clears all rules. - // - // This command is equivalent to: - // nft flush ruleset - Clear() error - - // Version returns the version of nftables. - // - // This command is equivalent to: - // nft -V - Version() (Version, error) - - // Table returns an API for working with tables. - Table() table.API - - // Chain returns an API for working with chains. - Chain() chain.API - - // Rule returns an API for working with rules. - Rule() rule.API -} - type nft struct { - command command.NFT - table table.API - chain chain.API - rule rule.API + command contract.Command + table nftContract.Table + chain nftContract.Chain + rule nftContract.Rule } // New Returns a client for working with nftables. // Searches for nft in paths: nft, /usr/sbin/nft, /sbin/nft -func New() (NFT, error) { +func New() (contract.NFT, error) { paths := []string{"nft", "/usr/sbin/nft", "/sbin/nft"} for _, path := range paths { nftClient, err := NewWithPath(path) @@ -61,7 +35,7 @@ func New() (NFT, error) { } // NewWithPath Returns the client for working with nftables with its path specified. -func NewWithPath(path string) (NFT, error) { +func NewWithPath(path string) (contract.NFT, error) { nftCommand, err := command.New(path) if err != nil { return nil, err @@ -80,7 +54,7 @@ func (n *nft) Clear() error { return n.command.Run(args...) } -func (n *nft) Version() (Version, error) { +func (n *nft) Version() (nftContract.Version, error) { args := []string{"-V"} out, err := n.command.RunWithOutput(args...) if err != nil { @@ -114,18 +88,18 @@ func (n *nft) Version() (Version, error) { }, nil } -func (n *nft) Table() table.API { +func (n *nft) Table() nftContract.Table { return n.table } -func (n *nft) Chain() chain.API { +func (n *nft) Chain() nftContract.Chain { return n.chain } -func (n *nft) Rule() rule.API { +func (n *nft) Rule() nftContract.Rule { return n.rule } -func (n *nft) Command() command.NFT { +func (n *nft) Command() contract.Command { return n.command } diff --git a/version.go b/version.go index 78c53fd..ef9c43a 100644 --- a/version.go +++ b/version.go @@ -1,21 +1,14 @@ package nft -type Version interface { - // Version returns the version of the nftables client. - Version() string - // Opts returns the options of the nftables client. - Opts() map[string]string -} - type version struct { version string opts map[string]string } -func (v version) Version() string { +func (v *version) Version() string { return v.version } -func (v version) Opts() map[string]string { +func (v *version) Opts() map[string]string { return v.opts }