diff --git a/injector/network_disruption.go b/injector/network_disruption.go index 909fdae08..478976fc4 100644 --- a/injector/network_disruption.go +++ b/injector/network_disruption.go @@ -233,12 +233,14 @@ func (i *networkDisruptionInjector) Inject() error { return fmt.Errorf("error injecting packet marking iptables rule: %w", err) } } else { // cgroup v1 needs to mark packets through the net_cls cgroup controller of the container - if err := i.config.Cgroup.Write("net_cls", "net_cls.classid", types.InjectorCgroupClassID); err != nil { - return fmt.Errorf("error injecting packet marking in net_cls cgroup: %w", err) - } - - if err := i.config.IPTables.MarkClassID(types.InjectorCgroupClassID, types.InjectorCgroupClassID); err != nil { - return fmt.Errorf("error injecting packet marking iptables rule: %w", err) + if i.spec.HasHTTPFilters() { + if err := i.config.Cgroup.Write("net_cls", "net_cls.classid", types.InjectorBPFCgroupClassID); err != nil { + return fmt.Errorf("error injecting packet marking in net_cls cgroup: %w", err) + } + } else { + if err := i.config.Cgroup.Write("net_cls", "net_cls.classid", types.InjectorCgroupClassID); err != nil { + return fmt.Errorf("error injecting packet marking in net_cls cgroup: %w", err) + } } } } @@ -491,8 +493,14 @@ func (i *networkDisruptionInjector) applyOperations() error { } // create fw filter to classify packets based on their mark - if err := i.config.TrafficController.AddFwFilter(interfaces, "4:0", types.InjectorCgroupClassID, "4:2"); err != nil { - return fmt.Errorf("can't create the fw filter: %w", err) + if i.config.Cgroup.IsCgroupV2() { + if err := i.config.TrafficController.AddFwFilter(interfaces, "4:0", types.InjectorCgroupClassID, "4:2"); err != nil { + return fmt.Errorf("can't create the fw filter: %w", err) + } + } else { + if err := i.config.TrafficController.AddCgroupFilter(interfaces, "4:0", 2); err != nil { + return fmt.Errorf("can't create the cgroup filter: %w", err) + } } // parent 4:2 refers to the 3nd band of the 4th prio qdisc @@ -505,10 +513,18 @@ func (i *networkDisruptionInjector) applyOperations() error { return fmt.Errorf("can't create a new qdisc: %w", err) } - // create fw filter to classify packets based on their mark - if err := i.config.TrafficController.AddFwFilter(interfaces, "2:0", types.InjectorCgroupClassID, "2:2"); err != nil { - return fmt.Errorf("can't create the fw filter: %w", err) + if i.config.Cgroup.IsCgroupV2() { + // create fw filter to classify packets based on their mark + if err := i.config.TrafficController.AddFwFilter(interfaces, "2:0", types.InjectorCgroupClassID, "2:2"); err != nil { + return fmt.Errorf("can't create the fw filter: %w", err) + } + } else { + // create cgroup filter to classify packets + if err := i.config.TrafficController.AddCgroupFilter(interfaces, "2:0", 2); err != nil { + return fmt.Errorf("can't create the cgroup filter: %w", err) + } } + // parent 2:2 refers to the 2nd band of the 2nd prio qdisc // handle starts from 3 because 1 and 2 are used by the 2 prio qdiscs parent = "2:2" diff --git a/injector/network_disruption_test.go b/injector/network_disruption_test.go index 0dd0908b5..714d6bd2e 100644 --- a/injector/network_disruption_test.go +++ b/injector/network_disruption_test.go @@ -93,6 +93,7 @@ var _ = Describe("Failure", func() { tc.EXPECT().AddPrio(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() tc.EXPECT().AddFilter(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(0, nil).Maybe() tc.EXPECT().AddFwFilter(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() + tc.EXPECT().AddCgroupFilter(mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() tc.EXPECT().AddOutputLimit(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe() tc.EXPECT().DeleteFilter(mock.Anything, mock.Anything).Return(nil).Maybe() tc.EXPECT().ClearQdisc(mock.Anything).Return(nil).Maybe() @@ -293,10 +294,6 @@ var _ = Describe("Failure", func() { tc.AssertCalled(GinkgoT(), "AddPrio", []string{"lo", "eth0", "eth1"}, "1:4", "2:", uint32(2), mock.Anything) }) - It("should add an fw filter to classify packets according to their classid set by iptables mark", func() { - tc.AssertCalled(GinkgoT(), "AddFwFilter", []string{"lo", "eth0", "eth1"}, "2:0", "0x00020002", "2:2") - }) - It("should apply disruptions to main interfaces 2nd band", func() { tc.AssertCalled(GinkgoT(), "AddNetem", []string{"lo", "eth0", "eth1"}, "2:2", mock.Anything, time.Second, time.Second, spec.Drop, spec.Corrupt, spec.Duplicate) tc.AssertNumberOfCalls(GinkgoT(), "AddNetem", 1) @@ -304,9 +301,12 @@ var _ = Describe("Failure", func() { }) Context("packet marking with cgroups v1", func() { + It("should add a cgroup filter to mark packets going out from the identified (container or host) cgroup for the tc fw filter", func() { + tc.AssertCalled(GinkgoT(), "AddCgroupFilter", []string{"lo", "eth0", "eth1"}, "2:0", uint32(2)) + }) + It("should mark packets going out from the identified (container or host) cgroup for the tc fw filter", func() { cgroupManager.AssertCalled(GinkgoT(), "Write", "net_cls", "net_cls.classid", chaostypes.InjectorCgroupClassID) - iptables.AssertCalled(GinkgoT(), "MarkClassID", chaostypes.InjectorCgroupClassID, chaostypes.InjectorCgroupClassID) }) }) @@ -319,6 +319,10 @@ var _ = Describe("Failure", func() { Expect(err).ShouldNot(HaveOccurred()) }) + It("should add an fw filter to classify packets according to their classid set by iptables mark", func() { + tc.AssertCalled(GinkgoT(), "AddFwFilter", []string{"lo", "eth0", "eth1"}, "2:0", "0x00020002", "2:2") + }) + It("should mark packets going out from the identified (container or host) cgroup for the tc fw filter", func() { iptables.AssertCalled(GinkgoT(), "MarkCgroupPath", "/kubepod.slice/foo", chaostypes.InjectorCgroupClassID) }) @@ -719,8 +723,13 @@ var _ = Describe("Failure", func() { tc.AssertCalled(GinkgoT(), "AddPrio", interfaces, "2:2", "3:", uint32(2), mock.Anything) tc.AssertCalled(GinkgoT(), "AddPrio", interfaces, "3:2", "4:", uint32(2), mock.Anything) - By("adding an fw filter to classify packets according to their classid set by iptables mark") - tc.AssertCalled(GinkgoT(), "AddFwFilter", interfaces, "4:0", "0x00020002", "4:2") + if config.Cgroup.IsCgroupV2() { + By("adding an fw filter to classify packets according to their classid set by iptables mark") + tc.AssertCalled(GinkgoT(), "AddFwFilter", interfaces, "4:0", "0x00020002", "4:2") + } else { + By("should add a cgroup filter to mark packets going out from the identified (container or host) cgroup for the tc fw filter") + tc.AssertCalled(GinkgoT(), "AddCgroupFilter", interfaces, "4:0", uint32(2)) + } By("adding an BPF filter to classify packets according to their method") tc.AssertCalled(GinkgoT(), "AddBPFFilter", interfaces, "2:0", "/usr/local/bin/bpf-network-tc-filter.bpf.o", "2:2", "classifier_methods") diff --git a/network/tc.go b/network/tc.go index ad5d5e434..3828bd51a 100644 --- a/network/tc.go +++ b/network/tc.go @@ -61,6 +61,7 @@ type TrafficController interface { AddPrio(ifaces []string, parent string, handle string, bands uint32, priomap [16]uint32) error AddFilter(ifaces []string, parent string, handle string, srcIP, dstIP *net.IPNet, srcPort, dstPort int, prot protocol, state connState, flowid string) (uint32, error) DeleteFilter(iface string, priority uint32) error + AddCgroupFilter(ifaces []string, parent string, handle uint32) error AddFwFilter(ifaces []string, parent string, handle string, flowid string) error AddBPFFilter(ifaces []string, parent string, obj string, flowid string, section string) error ConfigBPFFilter(cmd executor, args ...string) error @@ -251,6 +252,16 @@ func (t *tc) DeleteFilter(iface string, priority uint32) error { return nil } +func (t *tc) AddCgroupFilter(ifaces []string, parent string, handle uint32) error { + for _, iface := range ifaces { + if _, _, err := t.executer.Run(buildCmd("filter", iface, parent, "", 0, fmt.Sprintf("%d", handle), "cgroup", "")); err != nil { + return err + } + } + + return nil +} + // AddFwFilter generates a cgroup filter func (t *tc) AddFwFilter(ifaces []string, parent string, handle string, flowid string) error { for _, iface := range ifaces { diff --git a/network/tc_test.go b/network/tc_test.go index e7cbd64e1..5c5a8e83b 100644 --- a/network/tc_test.go +++ b/network/tc_test.go @@ -212,6 +212,19 @@ var _ = Describe("Tc", func() { }) }) + Describe("AddCgroupFilter", func() { + JustBeforeEach(func() { + Expect(tcRunner.AddCgroupFilter(ifaces, parent, 12345)).Should(Succeed()) + }) + + Context("add a cgroup filter", func() { + It("should execute", func() { + tcExecuter.AssertCalled(GinkgoT(), "Run", []string{"filter", "add", "dev", "lo", "root", "handle", "12345", "cgroup"}) + tcExecuter.AssertCalled(GinkgoT(), "Run", []string{"filter", "add", "dev", "eth0", "root", "handle", "12345", "cgroup"}) + }) + }) + }) + Describe("AddFwFilter", func() { JustBeforeEach(func() { Expect(tcRunner.AddFwFilter(ifaces, parent, handle, flowid)).Should(Succeed()) diff --git a/network/traffic_controller_mock.go b/network/traffic_controller_mock.go index 4207a9472..d9befd3f1 100644 --- a/network/traffic_controller_mock.go +++ b/network/traffic_controller_mock.go @@ -77,6 +77,54 @@ func (_c *TrafficControllerMock_AddBPFFilter_Call) RunAndReturn(run func([]strin return _c } +// AddCgroupFilter provides a mock function with given fields: ifaces, parent, handle +func (_m *TrafficControllerMock) AddCgroupFilter(ifaces []string, parent string, handle uint32) error { + ret := _m.Called(ifaces, parent, handle) + + if len(ret) == 0 { + panic("no return value specified for AddCgroupFilter") + } + + var r0 error + if rf, ok := ret.Get(0).(func([]string, string, uint32) error); ok { + r0 = rf(ifaces, parent, handle) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// TrafficControllerMock_AddCgroupFilter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddCgroupFilter' +type TrafficControllerMock_AddCgroupFilter_Call struct { + *mock.Call +} + +// AddCgroupFilter is a helper method to define mock.On call +// - ifaces []string +// - parent string +// - handle uint32 +func (_e *TrafficControllerMock_Expecter) AddCgroupFilter(ifaces interface{}, parent interface{}, handle interface{}) *TrafficControllerMock_AddCgroupFilter_Call { + return &TrafficControllerMock_AddCgroupFilter_Call{Call: _e.mock.On("AddCgroupFilter", ifaces, parent, handle)} +} + +func (_c *TrafficControllerMock_AddCgroupFilter_Call) Run(run func(ifaces []string, parent string, handle uint32)) *TrafficControllerMock_AddCgroupFilter_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]string), args[1].(string), args[2].(uint32)) + }) + return _c +} + +func (_c *TrafficControllerMock_AddCgroupFilter_Call) Return(_a0 error) *TrafficControllerMock_AddCgroupFilter_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *TrafficControllerMock_AddCgroupFilter_Call) RunAndReturn(run func([]string, string, uint32) error) *TrafficControllerMock_AddCgroupFilter_Call { + _c.Call.Return(run) + return _c +} + // AddFilter provides a mock function with given fields: ifaces, parent, handle, srcIP, dstIP, srcPort, dstPort, prot, state, flowid func (_m *TrafficControllerMock) AddFilter(ifaces []string, parent string, handle string, srcIP *net.IPNet, dstIP *net.IPNet, srcPort int, dstPort int, prot protocol, state connState, flowid string) (uint32, error) { ret := _m.Called(ifaces, parent, handle, srcIP, dstIP, srcPort, dstPort, prot, state, flowid) diff --git a/types/types.go b/types/types.go index fe77da7c8..e30688e41 100644 --- a/types/types.go +++ b/types/types.go @@ -134,6 +134,9 @@ const ( // This value should NEVER be changed without changing the Network Disruption TC tree. InjectorCgroupClassID = "0x00020002" + // This should be used specifically for eBPF disruptions + InjectorBPFCgroupClassID = "0x00040002" + // DDMarkChaoslibPrefix allows to consistently name the chaos-imported API in ddmark. // It's arbitrary but needs to be consistent across multiple files. DDMarkChaoslibPrefix = "chaos-api"