From 42899f4954697dd88099c61768b802b5d7b8e96d Mon Sep 17 00:00:00 2001 From: brianchennn <46628572+brianchennn@users.noreply.github.com> Date: Wed, 1 Mar 2023 00:42:32 +0800 Subject: [PATCH] refactor ip_filter_rule.go & modify Encode/Decode function (#6) Co-authored-by: brianchennn --- flowdesc/ip_filter_rule.go | 486 +++++++++++++------------- flowdesc/ip_filter_rule_field.go | 77 ---- flowdesc/ip_filter_rule_field_test.go | 99 ------ flowdesc/ip_filter_rule_test.go | 59 ++-- 4 files changed, 269 insertions(+), 452 deletions(-) delete mode 100644 flowdesc/ip_filter_rule_field.go delete mode 100644 flowdesc/ip_filter_rule_field_test.go diff --git a/flowdesc/ip_filter_rule.go b/flowdesc/ip_filter_rule.go index 2ddbcac..1005aae 100644 --- a/flowdesc/ip_filter_rule.go +++ b/flowdesc/ip_filter_rule.go @@ -1,7 +1,6 @@ package flowdesc import ( - "errors" "fmt" "net" "regexp" @@ -34,218 +33,71 @@ func flowDescErrorf(format string, a ...interface{}) error { return fmt.Errorf("flowdesc: %s", msg) } +type PortRanges []PortRange + // IPFilterRule define RFC 3588 that referd by TS 29.212 type IPFilterRule struct { - action Action - dir Direction - proto uint8 // protocol number - srcIP string //
- srcPorts string // [ports] - dstIP string //
- dstPorts string // [ports] + Action Action + Dir Direction + Proto uint8 // protocol number + Src string //
+ SrcPorts PortRanges // [ports] + Dst string //
+ DstPorts PortRanges // [ports] +} + +type PortRange struct { + Start uint16 + End uint16 } // NewIPFilterRule returns a new IPFilterRule instance func NewIPFilterRule() *IPFilterRule { r := &IPFilterRule{ - action: Permit, - dir: Out, - proto: ProtocolNumberAny, - srcIP: "", - srcPorts: "", - dstIP: "", - dstPorts: "", + Action: Permit, + Dir: Out, + Proto: ProtocolNumberAny, + Src: "", + SrcPorts: []PortRange{}, + Dst: "", + DstPorts: []PortRange{}, } return r } -// SetAction sets action of the IPFilterRule -func (r *IPFilterRule) SetAction(action Action) error { - switch action { - case Permit: - r.action = action - case Deny: - r.action = action - default: - return flowDescErrorf("'%s' is not allow, action only accept 'permit' or 'deny'", action) - } - return nil -} - -// GetAction returns action of the IPFilterRule -func (r *IPFilterRule) GetAction() Action { - return r.action -} - -// SetDirection sets direction of the IPFilterRule -func (r *IPFilterRule) SetDirection(dir Direction) error { - switch dir { - case Out: - r.dir = dir - case In: - return flowDescErrorf("dir cannot be 'in' in core-network") - default: - return flowDescErrorf("'%s' is not allow, dir only accept 'out'", dir) - } - return nil -} - -// GetDirection returns direction of the IPFilterRule -func (r *IPFilterRule) GetDirection() Direction { - return r.dir -} - -// SetProtocol sets IP protocol number of the IPFilterRule -// 0xfc stand for ip (any) -func (r *IPFilterRule) SetProtocol(proto uint8) error { - r.proto = proto - return nil -} - -// GetProtocol returns the ip protocol number of the IPFilterRule -func (r *IPFilterRule) GetProtocol() uint8 { - return r.proto -} - -// SetSourceIP sets source IP of the IPFilterRule -func (r *IPFilterRule) SetSourceIP(networkStr string) error { - if networkStr == "" { - return flowDescErrorf("Empty string") - } - if networkStr == "any" || networkStr == "assigned" { - r.srcIP = networkStr - return nil +func (p *PortRange) String() string { + portRange := "" + if p == nil { + return portRange } - if networkStr[0] == '!' { - return flowDescErrorf("Base on TS 29.212, ! expression shall not be used") - } - - var ipStr string - - ip := net.ParseIP(networkStr) - if ip == nil { - _, ipNet, err := net.ParseCIDR(networkStr) - if err != nil { - return flowDescErrorf("Source IP format error") - } - ipStr = ipNet.String() + if p.Start != p.End { + // for range port e.g. 2000-5000 + portRange = portRange + fmt.Sprint(p.Start) + "-" + fmt.Sprint(p.End) } else { - ipStr = ip.String() + portRange = portRange + fmt.Sprint(p.Start) } - - r.srcIP = ipStr - return nil -} - -// GetSourceIP returns src of the IPFilterRule -func (r *IPFilterRule) GetSourceIP() string { - return r.srcIP + return portRange } -// SetSourcePorts sets source ports of the IPFilterRule -func (r *IPFilterRule) SetSourcePorts(ports string) error { - if ports == "" { - r.srcPorts = "" - return nil +func (ps PortRanges) String() string { + ports := "" + if ps == nil { + return ports } - - if match, err := regexp.MatchString("^[0-9]+(-[0-9]+)?(,[0-9]+)*$", ports); err != nil || !match { - return flowDescErrorf("not valid format of port number") - } - - // Check port range - portSlice := regexp.MustCompile(`[\\,\\-]+`).Split(ports, -1) - for _, portStr := range portSlice { - port, err := strconv.Atoi(portStr) - if err != nil { - return err - } - if port < 0 || port > 65535 { - return errors.New("Invalid port number") + lastPortIdx := len(ps) - 1 + for i, port := range ps { + ports += port.String() + if i != lastPortIdx { + ports += "," } } - - r.srcPorts = ports - return nil -} - -// GetSourcePorts returns src ports of the IPFilterRule -func (r *IPFilterRule) GetSourcePorts() string { - return r.srcPorts -} - -// SetDestinationIP sets destination IP of the IPFilterRule -func (r *IPFilterRule) SetDestinationIP(networkStr string) error { - if networkStr == "any" || networkStr == "assigned" { - r.dstIP = networkStr - return nil - } - if networkStr[0] == '!' { - return flowDescErrorf("Base on TS 29.212, ! expression shall not be used") - } - - var ipDst string - - ip := net.ParseIP(networkStr) - if ip == nil { - _, ipNet, err := net.ParseCIDR(networkStr) - if err != nil { - return flowDescErrorf("Source IP format error") - } - ipDst = ipNet.String() - } else { - ipDst = ip.String() - } - - r.dstIP = ipDst - return nil -} - -// GetDestinationIP returns dst of the IPFilterRule -func (r *IPFilterRule) GetDestinationIP() string { - return r.dstIP -} - -// SetDestinationPorts sets destination ports of the IPFilterRule -func (r *IPFilterRule) SetDestinationPorts(ports string) error { - if ports == "" { - r.dstPorts = ports - return nil - } - - match, err := regexp.MatchString("^[0-9]+(-[0-9]+)?(,[0-9]+)*$", ports) - if err != nil { - return flowDescErrorf("Regex match error") - } - if !match { - return flowDescErrorf("Ports format error") - } - - // Check port range - portSlice := regexp.MustCompile(`[\\,\\-]+`).Split(ports, -1) - for _, portStr := range portSlice { - port, err := strconv.Atoi(portStr) - if err != nil { - return err - } - if port < 0 || port > 65535 { - return flowDescErrorf("Invalid port number") - } - } - - r.dstPorts = ports - return nil -} - -// GetDestinationPorts returns src ports of the IPFilterRule -func (r *IPFilterRule) GetDestinationPorts() string { - return r.dstPorts + return ports } // SwapSourceAndDestination swap the src and dst of the IPFilterRule -func (r *IPFilterRule) SwapSourceAndDestination() { - r.srcIP, r.dstIP = r.dstIP, r.srcIP - r.srcPorts, r.dstPorts = r.dstPorts, r.srcPorts +func (r *IPFilterRule) SwapSrcAndDst() { + r.Src, r.Dst = r.Dst, r.Src + r.SrcPorts, r.DstPorts = r.DstPorts, r.SrcPorts } // Encode function out put the IPFilterRule from the struct @@ -256,50 +108,76 @@ func Encode(r *IPFilterRule) (string, error) { ipFilterRuleStr = make([]string, 0, 9) // action - switch r.action { + switch r.Action { case Permit: ipFilterRuleStr = append(ipFilterRuleStr, "permit") case Deny: ipFilterRuleStr = append(ipFilterRuleStr, "deny") + default: + return "", flowDescErrorf("invalid action") } // dir - switch r.dir { + switch r.Dir { case Out: ipFilterRuleStr = append(ipFilterRuleStr, "out") + default: + return "", flowDescErrorf("for now, only support \"out\" ") } // proto - if r.proto == ProtocolNumberAny { + if r.Proto == ProtocolNumberAny { ipFilterRuleStr = append(ipFilterRuleStr, "ip") } else { - ipFilterRuleStr = append(ipFilterRuleStr, strconv.Itoa(int(r.proto))) + ipFilterRuleStr = append(ipFilterRuleStr, strconv.Itoa(int(r.Proto))) } // from ipFilterRuleStr = append(ipFilterRuleStr, "from") // src - if r.srcIP != "" { - ipFilterRuleStr = append(ipFilterRuleStr, r.srcIP) + src := r.Src + if src != "" { + if validAddrsFormat(src) { + ipFilterRuleStr = append(ipFilterRuleStr, src) + } else { + return "", flowDescErrorf("source addresses format error %s", src) + } } else { ipFilterRuleStr = append(ipFilterRuleStr, "any") } - if r.srcPorts != "" { - ipFilterRuleStr = append(ipFilterRuleStr, r.srcPorts) + + srcPort := r.SrcPorts.String() + if srcPort != "" { + if validPortsFormat(srcPort) { + ipFilterRuleStr = append(ipFilterRuleStr, srcPort) + } else { + return "", flowDescErrorf("source ports format error %s", srcPort) + } } // to ipFilterRuleStr = append(ipFilterRuleStr, "to") // dst - if r.dstIP != "" { - ipFilterRuleStr = append(ipFilterRuleStr, r.dstIP) + dst := r.Dst + if dst != "" { + if validAddrsFormat(dst) { + ipFilterRuleStr = append(ipFilterRuleStr, dst) + } else { + return "", flowDescErrorf("destination addresses format error %s", dst) + } } else { ipFilterRuleStr = append(ipFilterRuleStr, "any") } - if r.dstPorts != "" { - ipFilterRuleStr = append(ipFilterRuleStr, r.dstPorts) + + dstPort := r.DstPorts.String() + if dstPort != "" { + if validPortsFormat(dstPort) { + ipFilterRuleStr = append(ipFilterRuleStr, dstPort) + } else { + return "", flowDescErrorf("destination ports format error %s", dstPort) + } } // according TS 29.212 IPFilterRule cannot use [options] @@ -307,77 +185,89 @@ func Encode(r *IPFilterRule) (string, error) { return strings.Join(ipFilterRuleStr, " "), nil } -func removeIntermediateSpace(s []string) []string { - parts := make([]string, 0) - for _, val := range s { - if val != "" { - parts = append(parts, val) - } - } - return parts -} - // Decode parsing the string to IPFilterRule func Decode(s string) (*IPFilterRule, error) { - s = strings.TrimSpace(s) - parts := strings.Split(s, " ") - parts = removeIntermediateSpace(parts) + s = strings.ToLower(s) + parts := strings.Fields(s) + + var err error - var ptr int r := NewIPFilterRule() + ptr := 0 // action - if err := r.SetAction(Action(parts[ptr])); err != nil { + if ptr >= len(parts) { + return nil, fmt.Errorf("too few fields %v", len(parts)) + } + r.Action, err = parseAction(parts[ptr]) + if err != nil { return nil, err } ptr++ // dir - if err := r.SetDirection(Direction(parts[ptr])); err != nil { + if ptr >= len(parts) { + return nil, fmt.Errorf("too few fields %v", len(parts)) + } + r.Dir, err = parseDirection(parts[ptr]) + if err != nil { return nil, err } ptr++ // proto - var protoNumber uint8 - if parts[ptr] == "ip" { - r.proto = ProtocolNumberAny - } else { - if proto, err := strconv.Atoi(parts[ptr]); err != nil { - return nil, flowDescErrorf("parse proto failed: %s", err) - } else { - protoNumber = uint8(proto) - } - if err := r.SetProtocol(protoNumber); err != nil { - return nil, flowDescErrorf("parse proto failed: %s", err) - } + if ptr >= len(parts) { + return nil, fmt.Errorf("too few fields %v", len(parts)) + } + r.Proto, err = parseProto(parts[ptr]) + if err != nil { + return nil, err } ptr++ // from + if ptr >= len(parts) { + return nil, fmt.Errorf("too few fields %v", len(parts)) + } if from := parts[ptr]; from != "from" { return nil, flowDescErrorf("parse faild: must have 'from'") } ptr++ // src - if err := r.SetSourceIP(parts[ptr]); err != nil { + if ptr >= len(parts) { + return nil, fmt.Errorf("too few fields %v", len(parts)) + } + r.Src, err = parseFlowDescAddrs(parts[ptr]) + if err != nil { return nil, err } ptr++ - if err := r.SetSourcePorts(parts[ptr]); err != nil { - } else { + // source port + if ptr >= len(parts) { + return nil, fmt.Errorf("too few fields %v", len(parts)) + } + sp, err := ParsePorts(parts[ptr]) + if err == nil { + r.SrcPorts = sp ptr++ } // to + if ptr >= len(parts) { + return nil, fmt.Errorf("too few fields %v", len(parts)) + } if to := parts[ptr]; to != "to" { return nil, flowDescErrorf("parse faild: must have 'to'") } ptr++ // dst - if err := r.SetDestinationIP(parts[ptr]); err != nil { + if ptr >= len(parts) { + return nil, fmt.Errorf("too few fields %v", len(parts)) + } + r.Dst, err = parseFlowDescAddrs(parts[ptr]) + if err != nil { return nil, err } ptr++ @@ -387,11 +277,127 @@ func Decode(s string) (*IPFilterRule, error) { return r, nil } - if err := r.SetDestinationPorts(parts[ptr]); err != nil { - return nil, err - } // else { - //ptr++ - //} + // destination port + dp, err := ParsePorts(parts[ptr]) + if err == nil { + r.DstPorts = dp + } return r, nil } + +func validAddrsFormat(addrs string) bool { + if addrs == "" { + return false + } + if addrs[0] == '!' { + return false + } + if addrs == "any" || addrs == "assigned" { + return true + } + _, _, err := net.ParseCIDR(addrs) + if err == nil { + return true + } + ip := net.ParseIP(addrs) + return ip != nil +} + +func parseFlowDescAddrs(addrs string) (string, error) { + if addrs == "" { + return "", flowDescErrorf("Empty string") + } + if addrs == "any" || addrs == "assigned" { + return addrs, nil + } + if addrs[0] == '!' { + return "", flowDescErrorf("Base on TS 29.212, ! expression shall not be used") + } + _, ipnet, err := net.ParseCIDR(addrs) + if err == nil { + return ipnet.String(), nil + } + ip := net.ParseIP(addrs) + if ip != nil { + return ip.String(), nil + } + return "", fmt.Errorf("invalid addresses %v", addrs) +} + +func parseAction(act string) (Action, error) { + action := Action(act) + switch action { + case Permit, Deny: + return action, nil + default: + return "", flowDescErrorf("'%s' is not allow, action only accept 'permit' or 'deny'", action) + } +} + +func parseDirection(dir string) (Direction, error) { + direction := Direction(dir) + switch direction { + case Out: + return direction, nil + case In: + return "", flowDescErrorf("dir cannot be 'in' in core-network") + default: + return "", flowDescErrorf("'%s' is not allow, dir only accept 'out'", dir) + } +} + +func parseProto(proto string) (uint8, error) { + if proto == "ip" { + return ProtocolNumberAny, nil + } else { + if proto, err := strconv.Atoi(proto); err != nil { + return 0, flowDescErrorf("parse proto failed: %s", err) + } else { + return uint8(proto), nil + } + } +} + +func validPortsFormat(ports string) bool { + if match, err := regexp.MatchString("([ ][0-9]{1,5}([,-][0-9]{1,5})*)?", ports); err != nil || !match { + return false + } + return true +} + +func ParsePorts(ports string) (PortRanges, error) { + if !validPortsFormat(ports) { + return nil, flowDescErrorf("not valid format of port number") + } + ranges := strings.Split(ports, ",") + PortRanges := []PortRange{} + for _, r := range ranges { + var pRange PortRange + rangeStr := strings.Split(r, "-") + if len(rangeStr) == 1 { + p, err := strconv.ParseUint(r, 10, 16) + if err != nil { + return nil, err + } + pUint16 := uint16(p) + pRange.Start = pUint16 + pRange.End = pUint16 + } else { + p0, err := strconv.ParseUint(rangeStr[0], 10, 16) + if err != nil { + return nil, err + } + p0Uint16 := uint16(p0) + p1, err := strconv.ParseUint(rangeStr[1], 10, 16) + if err != nil { + return nil, err + } + p1Uint16 := uint16(p1) + pRange.Start = p0Uint16 + pRange.End = p1Uint16 + } + PortRanges = append(PortRanges, pRange) + } + return PortRanges, nil +} diff --git a/flowdesc/ip_filter_rule_field.go b/flowdesc/ip_filter_rule_field.go deleted file mode 100644 index 3cda18f..0000000 --- a/flowdesc/ip_filter_rule_field.go +++ /dev/null @@ -1,77 +0,0 @@ -package flowdesc - -type IPFilterRuleFieldList []IPFilterRuleField - -type IPFilterRuleField interface { - Set(*IPFilterRule) error -} - -type IPFilterAction struct { - Action Action -} - -func (i *IPFilterAction) Set(r *IPFilterRule) error { - return r.SetAction(i.Action) -} - -type IPFilterDirection struct { - Direction Direction -} - -func (i *IPFilterDirection) Set(r *IPFilterRule) error { - return r.SetDirection(i.Direction) -} - -type IPFilterProto struct { - Proto uint8 -} - -func (i *IPFilterProto) Set(r *IPFilterRule) error { - return r.SetProtocol(i.Proto) -} - -type IPFilterSourceIP struct { - Src string -} - -func (i *IPFilterSourceIP) Set(r *IPFilterRule) error { - return r.SetSourceIP(i.Src) -} - -type IPFilterSourcePorts struct { - Ports string -} - -func (i *IPFilterSourcePorts) Set(r *IPFilterRule) error { - return r.SetSourcePorts(i.Ports) -} - -type IPFilterDestinationIP struct { - Src string -} - -func (i *IPFilterDestinationIP) Set(r *IPFilterRule) error { - return r.SetDestinationIP(i.Src) -} - -type IPFilterDestinationPorts struct { - Ports string -} - -func (i *IPFilterDestinationPorts) Set(r *IPFilterRule) error { - return r.SetDestinationPorts(i.Ports) -} - -func BuildIPFilterRuleFromField(cl IPFilterRuleFieldList) (*IPFilterRule, error) { - rule := NewIPFilterRule() - - var err error - for _, config := range cl { - err = config.Set(rule) - if err != nil { - return nil, flowDescErrorf("build ip filter rule failed by %s", err) - } - } - - return rule, nil -} diff --git a/flowdesc/ip_filter_rule_field_test.go b/flowdesc/ip_filter_rule_field_test.go deleted file mode 100644 index 5c514e8..0000000 --- a/flowdesc/ip_filter_rule_field_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package flowdesc - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestBuildIPFilterRuleFromField(t *testing.T) { - t.Parallel() - - testCases := []struct { - name string - configList IPFilterRuleFieldList - ipFilterRule string - }{ - { - name: "default", - configList: IPFilterRuleFieldList{}, - ipFilterRule: "permit out ip from any to any", - }, - { - name: "srcIP", - configList: IPFilterRuleFieldList{ - &IPFilterProto{ - Proto: 17, - }, - &IPFilterSourceIP{ - Src: "192.168.0.0/24", - }, - }, - ipFilterRule: "permit out 17 from 192.168.0.0/24 to any", - }, - { - name: "dstIP", - configList: IPFilterRuleFieldList{ - &IPFilterProto{ - Proto: 17, - }, - &IPFilterSourceIP{ - Src: "192.168.0.0/24", - }, - &IPFilterDestinationIP{ - Src: "10.60.0.0/16", - }, - }, - ipFilterRule: "permit out 17 from 192.168.0.0/24 to 10.60.0.0/16", - }, - { - name: "SinglePort", - configList: IPFilterRuleFieldList{ - &IPFilterProto{ - Proto: 17, - }, - &IPFilterSourceIP{ - Src: "192.168.0.0/24", - }, - &IPFilterSourcePorts{ - Ports: "3000", - }, - &IPFilterDestinationIP{ - Src: "10.60.0.0/16", - }, - }, - ipFilterRule: "permit out 17 from 192.168.0.0/24 3000 to 10.60.0.0/16", - }, - { - name: "PortRange", - configList: IPFilterRuleFieldList{ - &IPFilterProto{ - Proto: ProtocolNumberAny, - }, - &IPFilterSourceIP{ - Src: "192.168.0.0/24", - }, - &IPFilterSourcePorts{ - Ports: "3000", - }, - &IPFilterDestinationIP{ - Src: "10.60.0.0/16", - }, - &IPFilterDestinationPorts{ - Ports: "10000,65535", - }, - }, - ipFilterRule: "permit out ip from 192.168.0.0/24 3000 to 10.60.0.0/16 10000,65535", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - ipFilterRule, err := BuildIPFilterRuleFromField(tc.configList) - require.NoError(t, err) - filterRuleContent, err := Encode(ipFilterRule) - require.NoError(t, err) - require.Equal(t, tc.ipFilterRule, filterRuleContent) - }) - } -} diff --git a/flowdesc/ip_filter_rule_test.go b/flowdesc/ip_filter_rule_test.go index 64caded..0496331 100644 --- a/flowdesc/ip_filter_rule_test.go +++ b/flowdesc/ip_filter_rule_test.go @@ -8,6 +8,7 @@ import ( ) func TestIPFilterRuleEncode(t *testing.T) { + var err error testStr1 := "permit out ip from any to assigned 655" rule := NewIPFilterRule() @@ -15,27 +16,13 @@ func TestIPFilterRuleEncode(t *testing.T) { t.Fatal("IP Filter Rule Create Error") } - if err := rule.SetAction(Permit); err != nil { - assert.Nil(t, err) - } - - if err := rule.SetDirection(Out); err != nil { - assert.Nil(t, err) - } - - if err := rule.SetProtocol(0xfc); err != nil { - assert.Nil(t, err) - } - - if err := rule.SetSourceIP("any"); err != nil { - assert.Nil(t, err) - } - - if err := rule.SetDestinationIP("assigned"); err != nil { - assert.Nil(t, err) - } - - if err := rule.SetDestinationPorts("655"); err != nil { + rule.Action = Permit + rule.Dir = Out + rule.Proto = 0xfc + rule.Src = "any" + rule.Dst = "assigned" + rule.DstPorts, err = ParsePorts("655") + if err != nil { assert.Nil(t, err) } @@ -125,13 +112,13 @@ func TestIPFilterRuleDecode(t *testing.T) { t.Run(testName, func(t *testing.T) { r, err := Decode(expected.filterRule) - require.Equal(t, expected.action, r.GetAction()) - require.Equal(t, expected.dir, r.GetDirection()) - require.Equal(t, expected.proto, r.GetProtocol()) - require.Equal(t, expected.src, r.GetSourceIP()) - require.Equal(t, expected.srcPorts, r.GetSourcePorts()) - require.Equal(t, expected.dst, r.GetDestinationIP()) - require.Equal(t, expected.dstPorts, r.GetDestinationPorts()) + require.Equal(t, expected.action, r.Action) + require.Equal(t, expected.dir, r.Dir) + require.Equal(t, expected.proto, r.Proto) + require.Equal(t, expected.src, r.Src) + require.Equal(t, expected.srcPorts, r.SrcPorts.String()) + require.Equal(t, expected.dst, r.Dst) + require.Equal(t, expected.dstPorts, r.DstPorts.String()) require.NoError(t, err) }) @@ -214,14 +201,14 @@ func TestIPFilterRuleSwapSourceAndDestination(t *testing.T) { for testName, expected := range testCases { t.Run(testName, func(t *testing.T) { r, err := Decode(expected.filterRule) - r.SwapSourceAndDestination() - require.Equal(t, expected.action, r.GetAction()) - require.Equal(t, expected.dir, r.GetDirection()) - require.Equal(t, expected.proto, r.GetProtocol()) - require.Equal(t, expected.src, r.GetSourceIP()) - require.Equal(t, expected.srcPorts, r.GetSourcePorts()) - require.Equal(t, expected.dst, r.GetDestinationIP()) - require.Equal(t, expected.dstPorts, r.GetDestinationPorts()) + r.SwapSrcAndDst() + require.Equal(t, expected.action, r.Action) + require.Equal(t, expected.dir, r.Dir) + require.Equal(t, expected.proto, r.Proto) + require.Equal(t, expected.src, r.Src) + require.Equal(t, expected.srcPorts, r.SrcPorts.String()) + require.Equal(t, expected.dst, r.Dst) + require.Equal(t, expected.dstPorts, r.DstPorts.String()) require.NoError(t, err) })