Skip to content

Commit

Permalink
feat: add delete --all flag
Browse files Browse the repository at this point in the history
Signed-off-by: Alessio Greggi <[email protected]>
  • Loading branch information
alegrey91 committed Mar 18, 2024
1 parent b5ef80e commit 931463d
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 41 deletions.
12 changes: 12 additions & 0 deletions cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var deleteCmd = &cobra.Command{
fmt.Println(err)
os.Exit(1)
}
return
}

// Loop over file content and delete rule one-by-one.
Expand All @@ -65,6 +66,16 @@ var deleteCmd = &cobra.Command{
os.Exit(1)
}
}
return
}

if cmd.Flags().Lookup("all").Changed {
err := ipt.DeleteAllForwards()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return
}
},
}
Expand All @@ -74,5 +85,6 @@ func init() {

deleteCmd.Flags().IntVarP(&ruleId, "id", "n", 0, "delete rule through number")
deleteCmd.Flags().StringVarP(&file, "file", "f", "", "delete rule through rules file")
deleteCmd.Flags().BoolP("all", "a", false, "delete rule through rules file")
deleteCmd.MarkFlagsMutuallyExclusive("id", "file")
}
37 changes: 37 additions & 0 deletions internal/extractor/extract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package extractor

import (
"fmt"
"strings"
)

// ExtractRuleInfo extract forward information from rule
// if it matches the requirements
func ExtractRuleInfo(rule string) ([]string, error) {
// extract rules info:
// -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 3000 -j DNAT --to-destination 192.168.199.105:80
// result:
// slice ruleInfo: [ , eth0, tcp, 3000, 192.168.199.105, 80]
ruleSplit := strings.Split(rule, " ")
ruleInfo := make([]string, 6)
for id, arg := range ruleSplit {
switch arg {
case "-i":
ruleInfo[1] = ruleSplit[id+1]
case "-p":
ruleInfo[2] = ruleSplit[id+1]
case "--dport":
ruleInfo[3] = ruleSplit[id+1]
case "--to-destination":
ruleInfo[4] = strings.Split(ruleSplit[id+1], ":")[0]
ruleInfo[5] = strings.Split(ruleSplit[id+1], ":")[1]
}
}

for i := 1; i < len(ruleInfo); i++ {
if ruleInfo[i] == "" {
return []string{}, fmt.Errorf("unable to retrieve elements from rule")
}
}
return ruleInfo, nil
}
45 changes: 45 additions & 0 deletions internal/extractor/extract_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package extractor

import (
"reflect"
"testing"
)

func TestExtractRuleInfo(t *testing.T) {
tests := []struct {
name string
rule string
want []string
wantErr bool
}{
// TODO: Add test cases.
{
name: "example_1",
rule: "-A PREROUTING -i lo -p tcp -m tcp --dport 3001 -m comment --comment fwdctl -j DNAT --to-destination 127.0.0.1:80",
want: []string{"", "lo", "tcp", "3001", "127.0.0.1", "80"},
},
{
name: "example_2",
rule: "-A PREROUTING -i eth0 -p tcp -m tcp --dport 3001 -m comment --comment fwdctl -j DNAT --to-destination 127.0.0.1:80",
want: []string{"", "eth0", "tcp", "3001", "127.0.0.1", "80"},
},
{
name: "example_3",
rule: "-A PREROUTING -i eth0 -p tcp -m tcp --dport 3001 -m comment --comment fwdctl -j DNAT",
want: []string{""},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ExtractRuleInfo(tt.rule)
if (err != nil) tt.wantErr {

Check failure on line 36 in internal/extractor/extract_test.go

View workflow job for this annotation

GitHub Actions / integration-test

expected ';', found tt

Check failure on line 36 in internal/extractor/extract_test.go

View workflow job for this annotation

GitHub Actions / lint

expected ';', found tt (typecheck)

Check failure on line 36 in internal/extractor/extract_test.go

View workflow job for this annotation

GitHub Actions / unit-test

expected ';', found tt
t.Errorf("ExtractRuleInfo() error = %v", err)
return

Check failure on line 38 in internal/extractor/extract_test.go

View workflow job for this annotation

GitHub Actions / lint

expected '{', found 'return' (typecheck)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ExtractRuleInfo() = %v, want %v", got, tt.want)
}
})
}
}
46 changes: 44 additions & 2 deletions pkg/iptables/forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"net"
"strconv"
"strings"

"github.com/alegrey91/fwdctl/internal/extractor"
)

var (
Expand Down Expand Up @@ -143,10 +145,9 @@ func ListForward(outputFormat string) (map[int]string, error) {
return nil, fmt.Errorf("failed: %v", err)
}

//ruleList, err := ipt.ListWithCounters(fwdTable, fwdChain)
ruleList, err := ipt.List(FwdTable, FwdChain)
if err != nil {
return nil, fmt.Errorf("failed: %v", err)
return nil, fmt.Errorf("failed listing rules: %v", err)
}

// check listed rules are tagged with custom tag
Expand Down Expand Up @@ -197,3 +198,44 @@ func DeleteForwardByRule(iface string, proto string, dport int, saddr string, sp
}
return nil
}

func DeleteAllForwards() error {
ipt, err := getIPTablesInstance()
if err != nil {
return fmt.Errorf("failed: %v", err)
}

ruleList, err := ipt.List(FwdTable, FwdChain)
if err != nil {
return fmt.Errorf("failed listing rules: %v", err)
}

// check listed rules are tagged with custom tag
fwdRules := make(map[int]string)
for ruleId, rule := range ruleList {
if strings.Contains(rule, label) {
fwdRules[ruleId] = rule
}
}

for _, rule := range fwdRules {
r, err := extractor.ExtractRuleInfo(rule)
if err != nil {
return fmt.Errorf("error extracting rule info: %v", err)
}
ruleSpec := []string{
"-i", r[1],
"-p", r[2],
"-m", r[2],
"--dport", r[3],
"-m", "comment", "--comment", "fwdctl",
"-j", FwdTarget,
"--to-destination", r[4] + ":" + r[5],
}
err = ipt.Delete(FwdTable, FwdChain, ruleSpec...)
if err != nil {
return fmt.Errorf("error deleting rule: %v", err)
}
}
return nil
}
3 changes: 2 additions & 1 deletion pkg/printer/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"strconv"

"github.com/alegrey91/fwdctl/internal/extractor"
"github.com/alegrey91/fwdctl/internal/rules"
"github.com/alegrey91/fwdctl/pkg/iptables"
)
Expand All @@ -19,7 +20,7 @@ func NewJson() *Json {
func (j *Json) PrintResult(ruleList map[int]string) error {
rules := rules.NewRuleSet()
for _, rule := range ruleList {
jsonRule, err := extractRuleInfo(rule)
jsonRule, err := extractor.ExtractRuleInfo(rule)
if err != nil {
continue
}
Expand Down
36 changes: 0 additions & 36 deletions pkg/printer/printer_interface.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package printer

import (
"fmt"
"strings"
)

type Printer interface {
PrintResult(ruleList map[int]string) error
}
Expand All @@ -21,34 +16,3 @@ func NewPrinter(printFormat string) Printer {
return NewTable()
}
}

// extractRuleInfo extract forward information from rule
// if it matches the requirements
func extractRuleInfo(rule string) ([]string, error) {
// extract rules info:
// -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 3000 -j DNAT --to-destination 192.168.199.105:80
// result:
// slice ruleInfo: [ , eth0, tcp, 3000, 192.168.199.105, 80]
ruleSplit := strings.Split(rule, " ")
ruleInfo := make([]string, 6)
for id, arg := range ruleSplit {
switch arg {
case "-i":
ruleInfo[1] = ruleSplit[id+1]
case "-p":
ruleInfo[2] = ruleSplit[id+1]
case "--dport":
ruleInfo[3] = ruleSplit[id+1]
case "--to-destination":
ruleInfo[4] = strings.Split(ruleSplit[id+1], ":")[0]
ruleInfo[5] = strings.Split(ruleSplit[id+1], ":")[1]
}
}

for i := 1; i < len(ruleInfo); i++ {
if ruleInfo[i] == "" {
return []string{}, fmt.Errorf("unable to retrieve elements from rule")
}
}
return ruleInfo, nil
}
3 changes: 2 additions & 1 deletion pkg/printer/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"os"
"strconv"

"github.com/alegrey91/fwdctl/internal/extractor"
"github.com/olekukonko/tablewriter"
)

Expand All @@ -18,7 +19,7 @@ func (t *Table) PrintResult(ruleList map[int]string) error {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"number", "interface", "protocol", "external port", "internal ip", "internal port"})
for ruleId, rule := range ruleList {
tabRule, err := extractRuleInfo(rule)
tabRule, err := extractor.ExtractRuleInfo(rule)
if err != nil {
continue
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/printer/yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

yaml "gopkg.in/yaml.v3"

"github.com/alegrey91/fwdctl/internal/extractor"
"github.com/alegrey91/fwdctl/internal/rules"
"github.com/alegrey91/fwdctl/pkg/iptables"
)
Expand All @@ -20,7 +21,7 @@ func NewYaml() *Yaml {
func (y *Yaml) PrintResult(ruleList map[int]string) error {
rules := rules.NewRuleSet()
for _, rule := range ruleList {
jsonRule, err := extractRuleInfo(rule)
jsonRule, err := extractor.ExtractRuleInfo(rule)
if err != nil {
continue
}
Expand Down
12 changes: 12 additions & 0 deletions tests/delete.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ exec fwdctl delete -n 3
exec fwdctl delete -n 2
exec fwdctl delete -n 1

# create forwards and then delete them with '--all#
exec fwdctl apply -f rules.yaml
fwd_exists lo tcp 3000 127.0.0.1 80
fwd_exists lo tcp 3001 127.0.0.1 80
fwd_exists lo tcp 3002 127.0.0.1 80
exec fwdctl delete --all
! fwd_exists lo tcp 3000 127.0.0.1 80
! fwd_exists lo tcp 3001 127.0.0.1 80
! fwd_exists lo tcp 3002 127.0.0.1 80

! exec fwdctl delete -n 1 -a -f rules.yaml

-- rules.yaml --
rules:
- dport: 3000
Expand Down

0 comments on commit 931463d

Please sign in to comment.