diff --git a/internal/netlink/interface.go b/internal/netlink/interface.go index c2239eb0..39bfcfd9 100644 --- a/internal/netlink/interface.go +++ b/internal/netlink/interface.go @@ -1,5 +1,7 @@ package netlink +//go:generate mockgen -destination=mock_$GOPACKAGE/$GOFILE . NetLinker + var _ NetLinker = (*NetLink)(nil) type NetLinker interface { diff --git a/internal/netlink/mock_netlink/interface.go b/internal/netlink/mock_netlink/interface.go new file mode 100644 index 00000000..4896283c --- /dev/null +++ b/internal/netlink/mock_netlink/interface.go @@ -0,0 +1,251 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/qdm12/gluetun/internal/netlink (interfaces: NetLinker) + +// Package mock_netlink is a generated GoMock package. +package mock_netlink + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + netlink "github.com/vishvananda/netlink" +) + +// MockNetLinker is a mock of NetLinker interface. +type MockNetLinker struct { + ctrl *gomock.Controller + recorder *MockNetLinkerMockRecorder +} + +// MockNetLinkerMockRecorder is the mock recorder for MockNetLinker. +type MockNetLinkerMockRecorder struct { + mock *MockNetLinker +} + +// NewMockNetLinker creates a new mock instance. +func NewMockNetLinker(ctrl *gomock.Controller) *MockNetLinker { + mock := &MockNetLinker{ctrl: ctrl} + mock.recorder = &MockNetLinkerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockNetLinker) EXPECT() *MockNetLinkerMockRecorder { + return m.recorder +} + +// AddrAdd mocks base method. +func (m *MockNetLinker) AddrAdd(arg0 netlink.Link, arg1 *netlink.Addr) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddrAdd", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddrAdd indicates an expected call of AddrAdd. +func (mr *MockNetLinkerMockRecorder) AddrAdd(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrAdd", reflect.TypeOf((*MockNetLinker)(nil).AddrAdd), arg0, arg1) +} + +// AddrList mocks base method. +func (m *MockNetLinker) AddrList(arg0 netlink.Link, arg1 int) ([]netlink.Addr, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddrList", arg0, arg1) + ret0, _ := ret[0].([]netlink.Addr) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddrList indicates an expected call of AddrList. +func (mr *MockNetLinkerMockRecorder) AddrList(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrList", reflect.TypeOf((*MockNetLinker)(nil).AddrList), arg0, arg1) +} + +// LinkAdd mocks base method. +func (m *MockNetLinker) LinkAdd(arg0 netlink.Link) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkAdd", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// LinkAdd indicates an expected call of LinkAdd. +func (mr *MockNetLinkerMockRecorder) LinkAdd(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), arg0) +} + +// LinkByIndex mocks base method. +func (m *MockNetLinker) LinkByIndex(arg0 int) (netlink.Link, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkByIndex", arg0) + ret0, _ := ret[0].(netlink.Link) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LinkByIndex indicates an expected call of LinkByIndex. +func (mr *MockNetLinkerMockRecorder) LinkByIndex(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByIndex", reflect.TypeOf((*MockNetLinker)(nil).LinkByIndex), arg0) +} + +// LinkByName mocks base method. +func (m *MockNetLinker) LinkByName(arg0 string) (netlink.Link, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkByName", arg0) + ret0, _ := ret[0].(netlink.Link) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LinkByName indicates an expected call of LinkByName. +func (mr *MockNetLinkerMockRecorder) LinkByName(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), arg0) +} + +// LinkDel mocks base method. +func (m *MockNetLinker) LinkDel(arg0 netlink.Link) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkDel", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// LinkDel indicates an expected call of LinkDel. +func (mr *MockNetLinkerMockRecorder) LinkDel(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), arg0) +} + +// LinkList mocks base method. +func (m *MockNetLinker) LinkList() ([]netlink.Link, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkList") + ret0, _ := ret[0].([]netlink.Link) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LinkList indicates an expected call of LinkList. +func (mr *MockNetLinkerMockRecorder) LinkList() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkList", reflect.TypeOf((*MockNetLinker)(nil).LinkList)) +} + +// LinkSetUp mocks base method. +func (m *MockNetLinker) LinkSetUp(arg0 netlink.Link) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LinkSetUp", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// LinkSetUp indicates an expected call of LinkSetUp. +func (mr *MockNetLinkerMockRecorder) LinkSetUp(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), arg0) +} + +// RouteAdd mocks base method. +func (m *MockNetLinker) RouteAdd(arg0 *netlink.Route) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RouteAdd", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RouteAdd indicates an expected call of RouteAdd. +func (mr *MockNetLinkerMockRecorder) RouteAdd(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), arg0) +} + +// RouteDel mocks base method. +func (m *MockNetLinker) RouteDel(arg0 *netlink.Route) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RouteDel", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RouteDel indicates an expected call of RouteDel. +func (mr *MockNetLinkerMockRecorder) RouteDel(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteDel", reflect.TypeOf((*MockNetLinker)(nil).RouteDel), arg0) +} + +// RouteList mocks base method. +func (m *MockNetLinker) RouteList(arg0 netlink.Link, arg1 int) ([]netlink.Route, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RouteList", arg0, arg1) + ret0, _ := ret[0].([]netlink.Route) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RouteList indicates an expected call of RouteList. +func (mr *MockNetLinkerMockRecorder) RouteList(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), arg0, arg1) +} + +// RouteReplace mocks base method. +func (m *MockNetLinker) RouteReplace(arg0 *netlink.Route) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RouteReplace", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RouteReplace indicates an expected call of RouteReplace. +func (mr *MockNetLinkerMockRecorder) RouteReplace(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteReplace", reflect.TypeOf((*MockNetLinker)(nil).RouteReplace), arg0) +} + +// RuleAdd mocks base method. +func (m *MockNetLinker) RuleAdd(arg0 *netlink.Rule) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RuleAdd", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RuleAdd indicates an expected call of RuleAdd. +func (mr *MockNetLinkerMockRecorder) RuleAdd(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), arg0) +} + +// RuleDel mocks base method. +func (m *MockNetLinker) RuleDel(arg0 *netlink.Rule) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RuleDel", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RuleDel indicates an expected call of RuleDel. +func (mr *MockNetLinkerMockRecorder) RuleDel(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), arg0) +} + +// RuleList mocks base method. +func (m *MockNetLinker) RuleList(arg0 int) ([]netlink.Rule, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RuleList", arg0) + ret0, _ := ret[0].([]netlink.Rule) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RuleList indicates an expected call of RuleList. +func (mr *MockNetLinkerMockRecorder) RuleList(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleList", reflect.TypeOf((*MockNetLinker)(nil).RuleList), arg0) +} diff --git a/internal/routing/inbound.go b/internal/routing/inbound.go index 8c71aad2..dfe8a888 100644 --- a/internal/routing/inbound.go +++ b/internal/routing/inbound.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "net" + + "github.com/qdm12/gluetun/internal/netlink" ) const ( @@ -53,8 +55,11 @@ func (r *Routing) addRuleInboundFromDefault(table int) (err error) { return fmt.Errorf("%w: %s", errDefaultIP, err) } - if err := r.addIPRule(defaultIP, table, inboundPriority); err != nil { - return fmt.Errorf("%w: %s", errIPRuleAdd, err) + defaultIPMasked32 := netlink.NewIPNet(defaultIP) + ruleDstNet := (*net.IPNet)(nil) + err = r.addIPRule(defaultIPMasked32, ruleDstNet, table, inboundPriority) + if err != nil { + return fmt.Errorf("%w: %s", errRuleAdd, err) } return nil @@ -66,8 +71,11 @@ func (r *Routing) delRuleInboundFromDefault(table int) (err error) { return fmt.Errorf("%w: %s", errDefaultIP, err) } - if err := r.deleteIPRule(defaultIP, table, inboundPriority); err != nil { - return fmt.Errorf("%w: %s", errIPRuleAdd, err) + defaultIPMasked32 := netlink.NewIPNet(defaultIP) + ruleDstNet := (*net.IPNet)(nil) + err = r.deleteIPRule(defaultIPMasked32, ruleDstNet, table, inboundPriority) + if err != nil { + return fmt.Errorf("%w: %s", errRuleDelete, err) } return nil diff --git a/internal/routing/rules.go b/internal/routing/rules.go index 879c42db..763ffa20 100644 --- a/internal/routing/rules.go +++ b/internal/routing/rules.go @@ -5,23 +5,21 @@ import ( "errors" "fmt" "net" - "strconv" "github.com/qdm12/gluetun/internal/netlink" ) var ( - errIPRuleAdd = errors.New("cannot add IP rule") errRulesList = errors.New("cannot list rules") ) -func (r *Routing) addIPRule(src net.IP, table, priority int) error { - r.logger.Debug("ip rule add from " + src.String() + - " lookup " + strconv.Itoa(table) + - " pref " + strconv.Itoa(priority)) +func (r *Routing) addIPRule(src, dst *net.IPNet, table, priority int) error { + const add = true + r.logger.Debug(ruleDbgMsg(add, src, dst, table, priority)) rule := netlink.NewRule() - rule.Src = netlink.NewIPNet(src) + rule.Src = src + rule.Dst = dst rule.Priority = priority rule.Table = table @@ -45,13 +43,13 @@ func (r *Routing) addIPRule(src net.IP, table, priority int) error { return nil } -func (r *Routing) deleteIPRule(src net.IP, table, priority int) error { - r.logger.Debug("ip rule del from " + src.String() + - " lookup " + strconv.Itoa(table) + - " pref " + strconv.Itoa(priority)) +func (r *Routing) deleteIPRule(src, dst *net.IPNet, table, priority int) error { + const add = false + r.logger.Debug(ruleDbgMsg(add, src, dst, table, priority)) rule := netlink.NewRule() - rule.Src = netlink.NewIPNet(src) + rule.Src = src + rule.Dst = dst rule.Priority = priority rule.Table = table @@ -72,3 +70,32 @@ func (r *Routing) deleteIPRule(src net.IP, table, priority int) error { } return nil } + +func ruleDbgMsg(add bool, src, dst *net.IPNet, + table, priority int) (debugMessage string) { + debugMessage = "ip rule" + + if add { + debugMessage += " add" + } else { + debugMessage += " del" + } + + if src != nil { + debugMessage += " from " + src.String() + } + + if dst != nil { + debugMessage += " to " + dst.String() + } + + if table != 0 { + debugMessage += " lookup " + fmt.Sprint(table) + } + + if priority != -1 { + debugMessage += " pref " + fmt.Sprint(priority) + } + + return debugMessage +} diff --git a/internal/routing/rules_test.go b/internal/routing/rules_test.go new file mode 100644 index 00000000..7a951803 --- /dev/null +++ b/internal/routing/rules_test.go @@ -0,0 +1,308 @@ +package routing + +import ( + "errors" + "net" + "testing" + + "github.com/golang/mock/gomock" + "github.com/qdm12/gluetun/internal/netlink" + "github.com/qdm12/gluetun/internal/netlink/mock_netlink" + "github.com/qdm12/golibs/logging/mock_logging" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func makeIPNet(t *testing.T, n byte) *net.IPNet { + t.Helper() + return &net.IPNet{ + IP: net.IPv4(n, n, n, 0), + Mask: net.IPv4Mask(255, 255, 255, 0), + } +} + +func makeIPRule(t *testing.T, src, dst *net.IPNet, + table, priority int) *netlink.Rule { + t.Helper() + rule := netlink.NewRule() + rule.Src = src + rule.Dst = dst + rule.Table = table + rule.Priority = priority + return rule +} + +func Test_Routing_addIPRule(t *testing.T) { + t.Parallel() + + errDummy := errors.New("dummy error") + + type ruleListCall struct { + rules []netlink.Rule + err error + } + + type ruleAddCall struct { + expected bool + ruleToAdd *netlink.Rule + err error + } + + testCases := map[string]struct { + src *net.IPNet + dst *net.IPNet + table int + priority int + dbgMsg string + ruleList ruleListCall + ruleAdd ruleAddCall + err error + }{ + "list error": { + dbgMsg: "ip rule add pref 0", + ruleList: ruleListCall{ + err: errDummy, + }, + err: errors.New("cannot list rules: dummy error"), + }, + "rule already exists": { + src: makeIPNet(t, 1), + dst: makeIPNet(t, 2), + table: 99, + priority: 99, + dbgMsg: "ip rule add from 1.1.1.0/24 to 2.2.2.0/24 lookup 99 pref 99", + ruleList: ruleListCall{ + rules: []netlink.Rule{ + *makeIPRule(t, makeIPNet(t, 2), makeIPNet(t, 2), 99, 99), + *makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 99, 99), + }, + }, + }, + "add rule error": { + src: makeIPNet(t, 1), + dst: makeIPNet(t, 2), + table: 99, + priority: 99, + dbgMsg: "ip rule add from 1.1.1.0/24 to 2.2.2.0/24 lookup 99 pref 99", + ruleAdd: ruleAddCall{ + expected: true, + ruleToAdd: makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 99, 99), + err: errDummy, + }, + err: errors.New("dummy error: for rule: ip rule 99: from 1.1.1.0/24 table 99"), + }, + "add rule success": { + src: makeIPNet(t, 1), + dst: makeIPNet(t, 2), + table: 99, + priority: 99, + dbgMsg: "ip rule add from 1.1.1.0/24 to 2.2.2.0/24 lookup 99 pref 99", + ruleList: ruleListCall{ + rules: []netlink.Rule{ + *makeIPRule(t, makeIPNet(t, 2), makeIPNet(t, 2), 99, 99), + *makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 101, 101), + }, + }, + ruleAdd: ruleAddCall{ + expected: true, + ruleToAdd: makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 99, 99), + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + + logger := mock_logging.NewMockLogger(ctrl) + logger.EXPECT().Debug(testCase.dbgMsg) + + netLinker := mock_netlink.NewMockNetLinker(ctrl) + netLinker.EXPECT().RuleList(netlink.FAMILY_ALL). + Return(testCase.ruleList.rules, testCase.ruleList.err) + if testCase.ruleAdd.expected { + netLinker.EXPECT().RuleAdd(testCase.ruleAdd.ruleToAdd). + Return(testCase.ruleAdd.err) + } + + r := Routing{ + logger: logger, + netLinker: netLinker, + } + + err := r.addIPRule(testCase.src, testCase.dst, + testCase.table, testCase.priority) + + if testCase.err != nil { + require.Error(t, err) + assert.Equal(t, testCase.err.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +func Test_Routing_deleteIPRule(t *testing.T) { + t.Parallel() + + errDummy := errors.New("dummy error") + + type ruleListCall struct { + rules []netlink.Rule + err error + } + + type ruleDelCall struct { + expected bool + ruleToDel *netlink.Rule + err error + } + + testCases := map[string]struct { + src *net.IPNet + dst *net.IPNet + table int + priority int + dbgMsg string + ruleList ruleListCall + ruleDel ruleDelCall + err error + }{ + "list error": { + dbgMsg: "ip rule del pref 0", + ruleList: ruleListCall{ + err: errDummy, + }, + err: errors.New("cannot list rules: dummy error"), + }, + "rule delete error": { + src: makeIPNet(t, 1), + dst: makeIPNet(t, 2), + table: 99, + priority: 99, + dbgMsg: "ip rule del from 1.1.1.0/24 to 2.2.2.0/24 lookup 99 pref 99", + ruleList: ruleListCall{ + rules: []netlink.Rule{ + *makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 99, 99), + }, + }, + ruleDel: ruleDelCall{ + expected: true, + ruleToDel: makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 99, 99), + err: errDummy, + }, + err: errors.New("dummy error: for rule: ip rule 99: from 1.1.1.0/24 table 99"), + }, + "rule deleted": { + src: makeIPNet(t, 1), + dst: makeIPNet(t, 2), + table: 99, + priority: 99, + dbgMsg: "ip rule del from 1.1.1.0/24 to 2.2.2.0/24 lookup 99 pref 99", + ruleList: ruleListCall{ + rules: []netlink.Rule{ + *makeIPRule(t, makeIPNet(t, 2), makeIPNet(t, 2), 99, 99), + *makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 99, 99), + }, + }, + ruleDel: ruleDelCall{ + expected: true, + ruleToDel: makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 99, 99), + }, + }, + "rule does not exist": { + src: makeIPNet(t, 1), + dst: makeIPNet(t, 2), + table: 99, + priority: 99, + dbgMsg: "ip rule del from 1.1.1.0/24 to 2.2.2.0/24 lookup 99 pref 99", + ruleList: ruleListCall{ + rules: []netlink.Rule{ + *makeIPRule(t, makeIPNet(t, 2), makeIPNet(t, 2), 99, 99), + *makeIPRule(t, makeIPNet(t, 1), makeIPNet(t, 2), 101, 101), + }, + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + + logger := mock_logging.NewMockLogger(ctrl) + logger.EXPECT().Debug(testCase.dbgMsg) + + netLinker := mock_netlink.NewMockNetLinker(ctrl) + netLinker.EXPECT().RuleList(netlink.FAMILY_ALL). + Return(testCase.ruleList.rules, testCase.ruleList.err) + if testCase.ruleDel.expected { + netLinker.EXPECT().RuleDel(testCase.ruleDel.ruleToDel). + Return(testCase.ruleDel.err) + } + + r := Routing{ + logger: logger, + netLinker: netLinker, + } + + err := r.deleteIPRule(testCase.src, testCase.dst, + testCase.table, testCase.priority) + + if testCase.err != nil { + require.Error(t, err) + assert.Equal(t, testCase.err.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +func Test_ruleDbgMsg(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + add bool + src *net.IPNet + dst *net.IPNet + table int + priority int + dbgMsg string + }{ + "default values": { + dbgMsg: "ip rule del pref 0", + }, + "add rule": { + add: true, + src: makeIPNet(t, 1), + dst: makeIPNet(t, 2), + table: 100, + priority: 101, + dbgMsg: "ip rule add from 1.1.1.0/24 to 2.2.2.0/24 lookup 100 pref 101", + }, + "del rule": { + src: makeIPNet(t, 1), + dst: makeIPNet(t, 2), + table: 100, + priority: 101, + dbgMsg: "ip rule del from 1.1.1.0/24 to 2.2.2.0/24 lookup 100 pref 101", + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + dbgMsg := ruleDbgMsg(testCase.add, testCase.src, + testCase.dst, testCase.table, testCase.priority) + + assert.Equal(t, testCase.dbgMsg, dbgMsg) + }) + } +}