Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use cgroup filter for network disruptions #917

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions injector/network_disruption.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
}
Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand Down
23 changes: 16 additions & 7 deletions injector/network_disruption_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -293,20 +294,19 @@ 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)
tc.AssertCalled(GinkgoT(), "AddOutputLimit", []string{"lo", "eth0", "eth1"}, "3:", mock.Anything, uint(spec.BandwidthLimit))
})

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)
})
})

Expand All @@ -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)
})
Expand Down Expand Up @@ -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")
Expand Down
11 changes: 11 additions & 0 deletions network/tc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions network/tc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
48 changes: 48 additions & 0 deletions network/traffic_controller_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down