1636 lines
62 KiB
Go
1636 lines
62 KiB
Go
// Copyright 2018 CNI authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"syscall"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/vishvananda/netlink"
|
|
|
|
"github.com/containernetworking/cni/pkg/skel"
|
|
types100 "github.com/containernetworking/cni/pkg/types/100"
|
|
"github.com/containernetworking/plugins/pkg/ns"
|
|
"github.com/containernetworking/plugins/pkg/testutils"
|
|
)
|
|
|
|
var _ = Describe("bandwidth test", func() {
|
|
var (
|
|
hostNs ns.NetNS
|
|
containerNs ns.NetNS
|
|
ifbDeviceName string
|
|
hostIfname string
|
|
containerIfname string
|
|
hostIP net.IP
|
|
containerIP net.IP
|
|
hostIfaceMTU int
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
|
|
hostIfname = "host-veth"
|
|
containerIfname = "container-veth"
|
|
|
|
hostNs, err = testutils.NewNS()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
containerNs, err = testutils.NewNS()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
hostIP = net.IP{169, 254, 0, 1}
|
|
containerIP = net.IP{10, 254, 0, 1}
|
|
hostIfaceMTU = 1024
|
|
ifbDeviceName = "bwpa8eda89404b7"
|
|
|
|
createVeth(hostNs, hostIfname, containerNs, containerIfname, hostIP, containerIP, hostIfaceMTU)
|
|
})
|
|
|
|
AfterEach(func() {
|
|
Expect(containerNs.Close()).To(Succeed())
|
|
Expect(testutils.UnmountNS(containerNs)).To(Succeed())
|
|
Expect(hostNs.Close()).To(Succeed())
|
|
Expect(testutils.UnmountNS(hostNs)).To(Succeed())
|
|
})
|
|
|
|
// Bandwidth requires host-side interface info, and thus only
|
|
// supports 0.3.0 and later CNI versions
|
|
for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
|
|
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
|
|
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
|
|
ver := ver
|
|
|
|
Describe("cmdADD", func() {
|
|
It(fmt.Sprintf("[%s] works with a Veth pair without any unbounded traffic", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 16,
|
|
"egressBurst": 12,
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
// Container egress (host ingress)
|
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
result, err := types100.GetResult(r)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(result.Interfaces).To(HaveLen(3))
|
|
Expect(result.Interfaces[2].Name).To(Equal(ifbDeviceName))
|
|
Expect(result.Interfaces[2].Sandbox).To(Equal(""))
|
|
|
|
ifbLink, err := netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU))
|
|
|
|
qdiscs, err := netlink.QdiscList(ifbLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(1))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(7812500)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(4)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Since we do not exclude anything from egress traffic shapping, we should not find any filter
|
|
filters, err := netlink.FilterList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(BeEmpty())
|
|
|
|
hostVethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscFilters).To(HaveLen(1))
|
|
Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
|
|
// Container ingress (host egress)
|
|
Expect(hostNs.Do(func(n ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
vethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscs, err := netlink.QdiscList(vethLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(2))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(vethLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(1)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(15625000)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Since we do not exclude anything from ingress traffic shapping, we should not find any filter
|
|
filters, err := netlink.FilterList(vethLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(BeEmpty())
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
|
|
It(fmt.Sprintf("[%s] works with a Veth pair with some ipv4 and ipv6 unbounded traffic", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 16,
|
|
"egressBurst": 12,
|
|
"unshapedSubnets": [
|
|
"10.0.0.0/8",
|
|
"fd00:db8:abcd:1234:e000::/68"
|
|
],
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
// Container egress (host ingress)
|
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
result, err := types100.GetResult(r)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(result.Interfaces).To(HaveLen(3))
|
|
Expect(result.Interfaces[2].Name).To(Equal(ifbDeviceName))
|
|
Expect(result.Interfaces[2].Sandbox).To(Equal(""))
|
|
|
|
ifbLink, err := netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU))
|
|
|
|
qdiscs, err := netlink.QdiscList(ifbLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(1))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(7812500)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(4)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
filters, err := netlink.FilterList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(HaveLen(2))
|
|
|
|
// traffic to fd00:db8:abcd:1234:e000::/68 redirected to uncapped class
|
|
Expect(filters[0]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[0].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[0].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IPV6)))
|
|
Expect(filters[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(filters[0].Attrs().Priority).To(Equal(uint16(15)))
|
|
Expect(filters[0].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[0].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, 1)))
|
|
|
|
filterSel := filters[0].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(3))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(3)))
|
|
|
|
// The filter should match to fd00:db8:abcd:1234:e000::/68 dst address in other words it should be:
|
|
// match 0xfd000db8/0xffffffff at 24
|
|
// match 0xabcd1234/0xffffffff at 28
|
|
// match 0xe0000000/0xf0000000 at 32
|
|
// (and last match discarded because it would be equivalent to a matchall/true condition at 36)
|
|
Expect(filterSel.Keys[0].Off).To(Equal(int32(24)))
|
|
Expect(filterSel.Keys[0].Val).To(Equal(uint32(4244639160)))
|
|
Expect(filterSel.Keys[0].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[1].Off).To(Equal(int32(28)))
|
|
Expect(filterSel.Keys[1].Val).To(Equal(uint32(2882343476)))
|
|
Expect(filterSel.Keys[1].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[2].Off).To(Equal(int32(32)))
|
|
Expect(filterSel.Keys[2].Val).To(Equal(uint32(3758096384)))
|
|
Expect(filterSel.Keys[2].Mask).To(Equal(uint32(4026531840)))
|
|
|
|
// traffic to 10.0.0.0/8 redirected to uncapped class
|
|
Expect(filters[1]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[1].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[1].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IP)))
|
|
Expect(filters[1].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(filters[1].Attrs().Priority).To(Equal(uint16(16)))
|
|
Expect(filters[1].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[1].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, 1)))
|
|
|
|
filterSel = filters[1].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(1))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(1)))
|
|
|
|
// The filter should match to 10.0.0.0/8 dst address in other words it should be:
|
|
// match 0a000000/ff000000 at 16
|
|
selKey := filterSel.Keys[0]
|
|
Expect(selKey.Val).To(Equal(uint32(10 * math.Pow(256, 3))))
|
|
Expect(selKey.Off).To(Equal(int32(16)))
|
|
Expect(selKey.Mask).To(Equal(uint32(255 * math.Pow(256, 3))))
|
|
|
|
hostVethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscFilters).To(HaveLen(1))
|
|
Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
|
|
// Container ingress (host egress)
|
|
Expect(hostNs.Do(func(n ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
vethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscs, err := netlink.QdiscList(vethLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(2))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(vethLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(1)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(15625000)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
filters, err := netlink.FilterList(vethLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(HaveLen(2))
|
|
|
|
// traffic to fd00:db8:abcd:1234:e000::/68 redirected to uncapped class
|
|
Expect(filters[0]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[0].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[0].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IPV6)))
|
|
Expect(filters[0].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(filters[0].Attrs().Priority).To(Equal(uint16(15)))
|
|
Expect(filters[0].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[0].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, 1)))
|
|
|
|
filterSel := filters[0].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(3))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(3)))
|
|
|
|
// The filter should match to fd00:db8:abcd:1234:e000::/68 dst address in other words it should be:
|
|
// match 0xfd000db8/0xffffffff at 24
|
|
// match 0xabcd1234/0xffffffff at 28
|
|
// match 0xe0000000/0xf0000000 at 32
|
|
// (and last match discarded because it would be equivalent to a matchall/true condition at 36)
|
|
Expect(filterSel.Keys[0].Off).To(Equal(int32(24)))
|
|
Expect(filterSel.Keys[0].Val).To(Equal(uint32(4244639160)))
|
|
Expect(filterSel.Keys[0].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[1].Off).To(Equal(int32(28)))
|
|
Expect(filterSel.Keys[1].Val).To(Equal(uint32(2882343476)))
|
|
Expect(filterSel.Keys[1].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[2].Off).To(Equal(int32(32)))
|
|
Expect(filterSel.Keys[2].Val).To(Equal(uint32(3758096384)))
|
|
Expect(filterSel.Keys[2].Mask).To(Equal(uint32(4026531840)))
|
|
|
|
// traffic to 10.0.0.0/8 redirected to uncapped class
|
|
Expect(filters[1]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[1].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[1].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IP)))
|
|
Expect(filters[1].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(filters[1].Attrs().Priority).To(Equal(uint16(16)))
|
|
Expect(filters[1].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[1].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, 1)))
|
|
|
|
filterSel = filters[1].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(1))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(1)))
|
|
|
|
// The filter should match to 10.0.0.0/8 dst address in other words it should be:
|
|
// match 0a000000/ff000000 at 16
|
|
selKey := filterSel.Keys[0]
|
|
Expect(selKey.Val).To(Equal(uint32(10 * math.Pow(256, 3))))
|
|
Expect(selKey.Off).To(Equal(int32(16)))
|
|
Expect(selKey.Mask).To(Equal(uint32(255 * math.Pow(256, 3))))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
})
|
|
|
|
It(fmt.Sprintf("[%s] works with a Veth pair with some ipv4 and ipv6 shaped traffic for specific subnets", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 16,
|
|
"egressBurst": 12,
|
|
"shapedSubnets": [
|
|
"10.0.0.0/8",
|
|
"fd00:db8:abcd:1234:e000::/68"
|
|
],
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
// Container egress (host ingress)
|
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
result, err := types100.GetResult(r)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(result.Interfaces).To(HaveLen(3))
|
|
Expect(result.Interfaces[2].Name).To(Equal(ifbDeviceName))
|
|
Expect(result.Interfaces[2].Sandbox).To(Equal(""))
|
|
|
|
ifbLink, err := netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU))
|
|
|
|
qdiscs, err := netlink.QdiscList(ifbLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(1))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(UnShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, UnShapedClassMinorID)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, ShapedClassMinorID)))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(7812500)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(4)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
filters, err := netlink.FilterList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(HaveLen(2))
|
|
|
|
// traffic to fd00:db8:abcd:1234:e000::/68 redirected to uncapped class
|
|
Expect(filters[0]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[0].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[0].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IPV6)))
|
|
Expect(filters[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(filters[0].Attrs().Priority).To(Equal(uint16(15)))
|
|
Expect(filters[0].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[0].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, ShapedClassMinorID)))
|
|
|
|
filterSel := filters[0].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(3))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(3)))
|
|
|
|
// The filter should match to fd00:db8:abcd:1234:e000::/68 dst address in other words it should be:
|
|
// match 0xfd000db8/0xffffffff at 24
|
|
// match 0xabcd1234/0xffffffff at 28
|
|
// match 0xe0000000/0xf0000000 at 32
|
|
// (and last match discarded because it would be equivalent to a matchall/true condition at 36)
|
|
Expect(filterSel.Keys[0].Off).To(Equal(int32(24)))
|
|
Expect(filterSel.Keys[0].Val).To(Equal(uint32(4244639160)))
|
|
Expect(filterSel.Keys[0].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[1].Off).To(Equal(int32(28)))
|
|
Expect(filterSel.Keys[1].Val).To(Equal(uint32(2882343476)))
|
|
Expect(filterSel.Keys[1].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[2].Off).To(Equal(int32(32)))
|
|
Expect(filterSel.Keys[2].Val).To(Equal(uint32(3758096384)))
|
|
Expect(filterSel.Keys[2].Mask).To(Equal(uint32(4026531840)))
|
|
|
|
// traffic to 10.0.0.0/8 redirected to uncapped class
|
|
Expect(filters[1]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[1].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[1].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IP)))
|
|
Expect(filters[1].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(filters[1].Attrs().Priority).To(Equal(uint16(16)))
|
|
Expect(filters[1].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[1].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, ShapedClassMinorID)))
|
|
|
|
filterSel = filters[1].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(1))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(1)))
|
|
|
|
// The filter should match to 10.0.0.0/8 dst address in other words it should be:
|
|
// match 0a000000/ff000000 at 16
|
|
selKey := filterSel.Keys[0]
|
|
Expect(selKey.Val).To(Equal(uint32(10 * math.Pow(256, 3))))
|
|
Expect(selKey.Off).To(Equal(int32(16)))
|
|
Expect(selKey.Mask).To(Equal(uint32(255 * math.Pow(256, 3))))
|
|
|
|
hostVethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscFilters).To(HaveLen(1))
|
|
Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
|
|
// Container ingress (host egress)
|
|
Expect(hostNs.Do(func(n ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
vethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscs, err := netlink.QdiscList(vethLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(2))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(UnShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(vethLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, UnShapedClassMinorID)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, ShapedClassMinorID)))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(1)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(15625000)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
filters, err := netlink.FilterList(vethLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(HaveLen(2))
|
|
|
|
// traffic to fd00:db8:abcd:1234:e000::/68 redirected to uncapped class
|
|
Expect(filters[0]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[0].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[0].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IPV6)))
|
|
Expect(filters[0].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(filters[0].Attrs().Priority).To(Equal(uint16(15)))
|
|
Expect(filters[0].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[0].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, ShapedClassMinorID)))
|
|
|
|
filterSel := filters[0].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(3))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(3)))
|
|
|
|
// The filter should match to fd00:db8:abcd:1234:e000::/68 dst address in other words it should be:
|
|
// match 0xfd000db8/0xffffffff at 24
|
|
// match 0xabcd1234/0xffffffff at 28
|
|
// match 0xe0000000/0xf0000000 at 32
|
|
// (and last match discarded because it would be equivalent to a matchall/true condition at 36)
|
|
Expect(filterSel.Keys[0].Off).To(Equal(int32(24)))
|
|
Expect(filterSel.Keys[0].Val).To(Equal(uint32(4244639160)))
|
|
Expect(filterSel.Keys[0].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[1].Off).To(Equal(int32(28)))
|
|
Expect(filterSel.Keys[1].Val).To(Equal(uint32(2882343476)))
|
|
Expect(filterSel.Keys[1].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[2].Off).To(Equal(int32(32)))
|
|
Expect(filterSel.Keys[2].Val).To(Equal(uint32(3758096384)))
|
|
Expect(filterSel.Keys[2].Mask).To(Equal(uint32(4026531840)))
|
|
|
|
// traffic to 10.0.0.0/8 redirected to uncapped class
|
|
Expect(filters[1]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[1].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[1].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IP)))
|
|
Expect(filters[1].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(filters[1].Attrs().Priority).To(Equal(uint16(16)))
|
|
Expect(filters[1].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[1].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, ShapedClassMinorID)))
|
|
|
|
filterSel = filters[1].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(1))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(1)))
|
|
|
|
// The filter should match to 10.0.0.0/8 dst address in other words it should be:
|
|
// match 0a000000/ff000000 at 16
|
|
selKey := filterSel.Keys[0]
|
|
Expect(selKey.Val).To(Equal(uint32(10 * math.Pow(256, 3))))
|
|
Expect(selKey.Off).To(Equal(int32(16)))
|
|
Expect(selKey.Mask).To(Equal(uint32(255 * math.Pow(256, 3))))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
|
|
It(fmt.Sprintf("[%s] does not apply ingress when disabled", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 0,
|
|
"ingressBurst": 0,
|
|
"egressRate": 8000,
|
|
"egressBurst": 80,
|
|
"unshapedSubnets": [
|
|
"10.0.0.0/8",
|
|
"fd00:db8:abcd:1234:e000::/68"
|
|
],
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
// check container egress side / host ingress side, we expect to get some QoS setup there
|
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
|
|
ifbLink, err := netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscs, err := netlink.QdiscList(ifbLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(1))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
classes, err := netlink.ClassList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, ShapedClassMinorID)))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(1000)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(7812500)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(2000)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
filters, err := netlink.FilterList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(HaveLen(2))
|
|
|
|
// traffic to fd00:db8:abcd:1234:e000::/68 redirected to uncapped class
|
|
Expect(filters[0]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[0].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[0].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IPV6)))
|
|
Expect(filters[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(filters[0].Attrs().Priority).To(Equal(uint16(15)))
|
|
Expect(filters[0].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[0].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, 1)))
|
|
|
|
filterSel := filters[0].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(3))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(3)))
|
|
|
|
// The filter should match to fd00:db8:abcd:1234:e000::/68 dst address in other words it should be:
|
|
// match 0xfd000db8/0xffffffff at 24
|
|
// match 0xabcd1234/0xffffffff at 28
|
|
// match 0xe0000000/0xf0000000 at 32
|
|
// (and last match discarded because it would be equivalent to a matchall/true condition at 36)
|
|
Expect(filterSel.Keys[0].Off).To(Equal(int32(24)))
|
|
Expect(filterSel.Keys[0].Val).To(Equal(uint32(4244639160)))
|
|
Expect(filterSel.Keys[0].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[1].Off).To(Equal(int32(28)))
|
|
Expect(filterSel.Keys[1].Val).To(Equal(uint32(2882343476)))
|
|
Expect(filterSel.Keys[1].Mask).To(Equal(uint32(4294967295)))
|
|
|
|
Expect(filterSel.Keys[2].Off).To(Equal(int32(32)))
|
|
Expect(filterSel.Keys[2].Val).To(Equal(uint32(3758096384)))
|
|
Expect(filterSel.Keys[2].Mask).To(Equal(uint32(4026531840)))
|
|
|
|
// traffic to 10.0.0.0/8 redirected to uncapped class
|
|
Expect(filters[1]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[1].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[1].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IP)))
|
|
Expect(filters[1].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(filters[1].Attrs().Priority).To(Equal(uint16(16)))
|
|
Expect(filters[1].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[1].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, 1)))
|
|
|
|
filterSel = filters[1].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(1))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(1)))
|
|
|
|
// The filter should match to 10.0.0.0/8 dst address in other words it should be:
|
|
// match 0a000000/ff000000 at 16
|
|
selKey := filterSel.Keys[0]
|
|
Expect(selKey.Val).To(Equal(uint32(10 * math.Pow(256, 3))))
|
|
Expect(selKey.Off).To(Equal(int32(16)))
|
|
Expect(selKey.Mask).To(Equal(uint32(255 * math.Pow(256, 3))))
|
|
|
|
// check traffic mirroring from veth to ifb
|
|
hostVethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscFilters).To(HaveLen(1))
|
|
Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
|
|
// check container ingress side / host egress side, we should not have any htb qdisc/classes/filters defined for the host veth
|
|
// only the qdisc ingress + a noqueue qdisc
|
|
Expect(hostNs.Do(func(n ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
containerIfLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscs, err := netlink.QdiscList(containerIfLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(2))
|
|
Expect(qdiscs[0]).NotTo(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[1]).NotTo(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
|
|
It(fmt.Sprintf("[%s] does not apply egress when disabled", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"egressRate": 0,
|
|
"egressBurst": 0,
|
|
"ingressRate": 8000,
|
|
"ingressBurst": 80,
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
|
|
// Since we do not setup any egress QoS, no ifb interface should be created at all
|
|
_, err = netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).To(HaveOccurred())
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
|
|
Expect(hostNs.Do(func(n ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
containerIfLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// Only one qdisc should be found this time, no ingress qdisc should be there
|
|
qdiscs, err := netlink.QdiscList(containerIfLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(1))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(containerIfLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(containerIfLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(1000)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(15625000)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(2000)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// No subnets are exluded in this example so we should not get any filter
|
|
filters, err := netlink.FilterList(containerIfLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(BeEmpty())
|
|
|
|
// Just check no mirroring is setup
|
|
qdiscFilters, err := netlink.FilterList(containerIfLink, netlink.MakeHandle(0xffff, 0))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(qdiscFilters).To(BeEmpty())
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
|
|
It(fmt.Sprintf("[%s] works with a Veth pair using runtime config", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"runtimeConfig": {
|
|
"bandWidth": {
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 16,
|
|
"egressBurst": 9,
|
|
"unshapedSubnets": ["192.168.0.0/24"]
|
|
}
|
|
},
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
result, err := types100.GetResult(r)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(result.Interfaces).To(HaveLen(3))
|
|
Expect(result.Interfaces[2].Name).To(Equal(ifbDeviceName))
|
|
Expect(result.Interfaces[2].Sandbox).To(Equal(""))
|
|
|
|
ifbLink, err := netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU))
|
|
|
|
qdiscs, err := netlink.QdiscList(ifbLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(1))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(7812500)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(4)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
filters, err := netlink.FilterList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(HaveLen(1))
|
|
|
|
// traffic to 192.168.0.0/24 redirected to uncapped class
|
|
Expect(filters[0]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[0].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[0].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IP)))
|
|
Expect(filters[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(filters[0].Attrs().Priority).To(Equal(uint16(16)))
|
|
Expect(filters[0].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[0].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, 1)))
|
|
|
|
filterSel := filters[0].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(1))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(1)))
|
|
|
|
// The filter should match to 192.168.0.0/24 dst address in other words it should be:
|
|
// match c0a80000/ffffff00 at 16
|
|
selKey := filterSel.Keys[0]
|
|
Expect(selKey.Val).To(Equal(uint32(192*math.Pow(256, 3) + 168*math.Pow(256, 2))))
|
|
Expect(selKey.Off).To(Equal(int32(16)))
|
|
Expect(selKey.Mask).To(Equal(uint32(255*math.Pow(256, 3) + 255*math.Pow(256, 2) + 255*256)))
|
|
|
|
hostVethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscFilters).To(HaveLen(1))
|
|
Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
|
|
// Container ingress (host egress)
|
|
Expect(hostNs.Do(func(n ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
vethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscs, err := netlink.QdiscList(vethLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(2))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(vethLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(1)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(15625000)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
filters, err := netlink.FilterList(vethLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(HaveLen(1))
|
|
|
|
// traffic to 192.168.0.0/24 redirected to uncapped class
|
|
Expect(filters[0]).To(BeAssignableToTypeOf(&netlink.U32{}))
|
|
Expect(filters[0].(*netlink.U32).Actions).To(BeEmpty())
|
|
Expect(filters[0].Attrs().Protocol).To(Equal(uint16(syscall.ETH_P_IP)))
|
|
Expect(filters[0].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(filters[0].Attrs().Priority).To(Equal(uint16(16)))
|
|
Expect(filters[0].Attrs().Parent).To(Equal(qdiscs[0].Attrs().Handle))
|
|
Expect(filters[0].(*netlink.U32).ClassId).To(Equal(netlink.MakeHandle(1, 1)))
|
|
|
|
filterSel := filters[0].(*netlink.U32).Sel
|
|
Expect(filterSel).To(BeAssignableToTypeOf(&netlink.TcU32Sel{}))
|
|
Expect(filterSel.Flags).To(Equal(uint8(netlink.TC_U32_TERMINAL)))
|
|
Expect(filterSel.Keys).To(HaveLen(1))
|
|
Expect(filterSel.Nkeys).To(Equal(uint8(1)))
|
|
|
|
// The filter should match to 192.168.0.0/24 dst address in other words it should be:
|
|
// match c0a80000/ffffff00 at 16
|
|
selKey := filterSel.Keys[0]
|
|
Expect(selKey.Val).To(Equal(uint32(192*math.Pow(256, 3) + 168*math.Pow(256, 2))))
|
|
Expect(selKey.Off).To(Equal(int32(16)))
|
|
Expect(selKey.Mask).To(Equal(uint32(255*math.Pow(256, 3) + 255*math.Pow(256, 2) + 255*256)))
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
|
|
Describe("cmdDEL", func() {
|
|
It(fmt.Sprintf("[%s] works with a Veth pair using 0.3.0 config", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 9,
|
|
"egressBurst": 9,
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
|
|
_, err = netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
_, err = netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
err = testutils.CmdDel(containerNs.Path(), args.ContainerID, "", func() error { return cmdDel(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
|
|
_, err = netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).To(HaveOccurred())
|
|
|
|
// The host veth peer should remain as it has not be created by this plugin
|
|
_, err = netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
})
|
|
|
|
Describe("cmdCHECK", func() {
|
|
It(fmt.Sprintf("[%s] works with a Veth pair", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 9,
|
|
"egressBurst": 9,
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
_, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
|
|
_, err = netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
_, err = netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
if testutils.SpecVersionHasCHECK(ver) {
|
|
// Do CNI Check
|
|
|
|
err = testutils.CmdCheck(containerNs.Path(), args.ContainerID, "", func() error { return cmdCheck(args) })
|
|
Expect(err).NotTo(HaveOccurred())
|
|
}
|
|
|
|
err = testutils.CmdDel(containerNs.Path(), args.ContainerID, "", func() error { return cmdDel(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
|
|
_, err = netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).To(HaveOccurred())
|
|
|
|
// The host veth peer should remain as it has not be created by this plugin
|
|
_, err = netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
})
|
|
|
|
Describe("Getting the host interface which plugin should work on from veth peer of container interface", func() {
|
|
It(fmt.Sprintf("[%s] should work with multiple host veth interfaces", ver), func() {
|
|
// create veth peer in host ns
|
|
vethName, peerName := "host-veth-peer1", "host-veth-peer2"
|
|
createVethInOneNs(hostNs, vethName, peerName)
|
|
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 16,
|
|
"egressBurst": 8,
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, vethName, peerName, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).NotTo(HaveOccurred(), string(out))
|
|
result, err := types100.GetResult(r)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(result.Interfaces).To(HaveLen(5))
|
|
Expect(result.Interfaces[4].Name).To(Equal(ifbDeviceName))
|
|
Expect(result.Interfaces[4].Sandbox).To(Equal(""))
|
|
|
|
ifbLink, err := netlink.LinkByName(ifbDeviceName)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU))
|
|
|
|
qdiscs, err := netlink.QdiscList(ifbLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(1))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(7812500)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(4)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Since we do not exclude anything from egress traffic shapping, we should not find any filter
|
|
filters, err := netlink.FilterList(ifbLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(BeEmpty())
|
|
|
|
hostVethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscFilters).To(HaveLen(1))
|
|
Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index))
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
|
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
vethLink, err := netlink.LinkByName(hostIfname)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
qdiscs, err := netlink.QdiscList(vethLink)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
Expect(qdiscs).To(HaveLen(2))
|
|
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(vethLink.Attrs().Index))
|
|
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Htb{}))
|
|
Expect(qdiscs[0].(*netlink.Htb).Defcls).To(Equal(uint32(ShapedClassMinorID)))
|
|
|
|
classes, err := netlink.ClassList(vethLink, qdiscs[0].Attrs().Handle)
|
|
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(classes).To(HaveLen(2))
|
|
|
|
// Uncapped class
|
|
Expect(classes[0]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[0].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, 1)))
|
|
Expect(classes[0].(*netlink.HtbClass).Rate).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Buffer).To(Equal(uint32(0)))
|
|
Expect(classes[0].(*netlink.HtbClass).Ceil).To(Equal(UncappedRate))
|
|
Expect(classes[0].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Class with traffic shapping settings
|
|
Expect(classes[1]).To(BeAssignableToTypeOf(&netlink.HtbClass{}))
|
|
Expect(classes[1].(*netlink.HtbClass).Handle).To(Equal(netlink.MakeHandle(1, uint16(qdiscs[0].(*netlink.Htb).Defcls))))
|
|
Expect(classes[1].(*netlink.HtbClass).Rate).To(Equal(uint64(1)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Buffer).To(Equal(uint32(15625000)))
|
|
Expect(classes[1].(*netlink.HtbClass).Ceil).To(Equal(uint64(2)))
|
|
// Expect(classes[1].(*netlink.HtbClass).Cbuffer).To(Equal(uint32(0)))
|
|
|
|
// Since we do not exclude anything from ingress traffic shapping, we should not find any filter
|
|
filters, err := netlink.FilterList(vethLink, qdiscs[0].Attrs().Handle)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(filters).To(BeEmpty())
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
|
|
It(fmt.Sprintf("[%s] should fail when container interface has no veth peer", ver), func() {
|
|
// create a macvlan device to be container interface
|
|
macvlanContainerIfname := "container-macv"
|
|
createMacvlan(containerNs, containerIfname, macvlanContainerIfname)
|
|
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 16,
|
|
"egressBurst": 8,
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, hostIfname, macvlanContainerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: macvlanContainerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).To(HaveOccurred())
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
|
|
It(fmt.Sprintf("[%s] should fail when preResult has no interfaces", ver), func() {
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 16,
|
|
"egressBurst": 8,
|
|
"prevResult": {
|
|
"interfaces": [],
|
|
"ips": [],
|
|
"routes": []
|
|
}
|
|
}`, ver)
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: "eth0",
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).To(HaveOccurred())
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
|
|
It(fmt.Sprintf("[%s] should fail when veth peer of container interface does not match any of host interfaces in preResult", ver), func() {
|
|
// fake a non-exist host interface name
|
|
fakeHostIfname := fmt.Sprintf("%s-fake", hostIfname)
|
|
|
|
conf := fmt.Sprintf(`{
|
|
"cniVersion": "%s",
|
|
"name": "cni-plugin-bandwidth-test",
|
|
"type": "bandwidth",
|
|
"ingressRate": 8,
|
|
"ingressBurst": 8,
|
|
"egressRate": 16,
|
|
"egressBurst": 8,
|
|
"prevResult": {
|
|
"interfaces": [
|
|
{
|
|
"name": "%s",
|
|
"sandbox": ""
|
|
},
|
|
{
|
|
"name": "%s",
|
|
"sandbox": "%s"
|
|
}
|
|
],
|
|
"ips": [
|
|
{
|
|
"version": "4",
|
|
"address": "%s/24",
|
|
"gateway": "10.0.0.1",
|
|
"interface": 1
|
|
}
|
|
],
|
|
"routes": []
|
|
}
|
|
}`, ver, fakeHostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
|
|
|
args := &skel.CmdArgs{
|
|
ContainerID: "dummy",
|
|
Netns: containerNs.Path(),
|
|
IfName: containerIfname,
|
|
StdinData: []byte(conf),
|
|
}
|
|
|
|
Expect(hostNs.Do(func(_ ns.NetNS) error {
|
|
defer GinkgoRecover()
|
|
|
|
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
|
Expect(err).To(HaveOccurred())
|
|
|
|
return nil
|
|
})).To(Succeed())
|
|
})
|
|
})
|
|
}
|
|
})
|