Merge pull request #636 from EdDev/bridge-mac-specification

bridge: Add mac field to specify container iface mac
This commit is contained in:
Dan Williams
2021-06-30 10:57:09 -05:00
committed by GitHub
5 changed files with 199 additions and 24 deletions

View File

@ -55,6 +55,25 @@ type NetConf struct {
HairpinMode bool `json:"hairpinMode"`
PromiscMode bool `json:"promiscMode"`
Vlan int `json:"vlan"`
Args struct {
Cni BridgeArgs `json:"cni,omitempty"`
} `json:"args,omitempty"`
RuntimeConfig struct {
Mac string `json:"mac,omitempty"`
} `json:"runtimeConfig,omitempty"`
mac string
}
type BridgeArgs struct {
Mac string `json:"mac,omitempty"`
}
// MacEnvArgs represents CNI_ARGS
type MacEnvArgs struct {
types.CommonArgs
MAC types.UnmarshallableString `json:"mac,omitempty"`
}
type gwInfo struct {
@ -70,7 +89,7 @@ func init() {
runtime.LockOSThread()
}
func loadNetConf(bytes []byte) (*NetConf, string, error) {
func loadNetConf(bytes []byte, envArgs string) (*NetConf, string, error) {
n := &NetConf{
BrName: defaultBrName,
}
@ -80,6 +99,26 @@ func loadNetConf(bytes []byte) (*NetConf, string, error) {
if n.Vlan < 0 || n.Vlan > 4094 {
return nil, "", fmt.Errorf("invalid VLAN ID %d (must be between 0 and 4094)", n.Vlan)
}
if envArgs != "" {
e := MacEnvArgs{}
if err := types.LoadArgs(envArgs, &e); err != nil {
return nil, "", err
}
if e.MAC != "" {
n.mac = string(e.MAC)
}
}
if mac := n.Args.Cni.Mac; mac != "" {
n.mac = mac
}
if mac := n.RuntimeConfig.Mac; mac != "" {
n.mac = mac
}
return n, n.CNIVersion, nil
}
@ -273,7 +312,7 @@ func ensureVlanInterface(br *netlink.Bridge, vlanId int) (netlink.Link, error) {
return nil, fmt.Errorf("faild to find host namespace: %v", err)
}
_, brGatewayIface, err := setupVeth(hostNS, br, name, br.MTU, false, vlanId)
_, brGatewayIface, err := setupVeth(hostNS, br, name, br.MTU, false, vlanId, "")
if err != nil {
return nil, fmt.Errorf("faild to create vlan gateway %q: %v", name, err)
}
@ -287,13 +326,13 @@ func ensureVlanInterface(br *netlink.Bridge, vlanId int) (netlink.Link, error) {
return brGatewayVeth, nil
}
func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool, vlanID int) (*current.Interface, *current.Interface, error) {
func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool, vlanID int, mac string) (*current.Interface, *current.Interface, error) {
contIface := &current.Interface{}
hostIface := &current.Interface{}
err := netns.Do(func(hostNS ns.NetNS) error {
// create the veth pair in the container and move host end into host netns
hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)
hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, mac, hostNS)
if err != nil {
return err
}
@ -380,7 +419,7 @@ func enableIPForward(family int) error {
func cmdAdd(args *skel.CmdArgs) error {
var success bool = false
n, cniVersion, err := loadNetConf(args.StdinData)
n, cniVersion, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
return err
}
@ -406,7 +445,7 @@ func cmdAdd(args *skel.CmdArgs) error {
}
defer netns.Close()
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan)
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan, n.mac)
if err != nil {
return err
}
@ -585,7 +624,7 @@ func cmdAdd(args *skel.CmdArgs) error {
}
func cmdDel(args *skel.CmdArgs) error {
n, _, err := loadNetConf(args.StdinData)
n, _, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
return err
}
@ -776,7 +815,7 @@ func validateCniContainerInterface(intf current.Interface) (cniBridgeIf, error)
func cmdCheck(args *skel.CmdArgs) error {
n, _, err := loadNetConf(args.StdinData)
n, _, err := loadNetConf(args.StdinData, args.Args)
if err != nil {
return err
}

View File

@ -78,6 +78,21 @@ type testCase struct {
DelErr020 string
AddErr010 string
DelErr010 string
envArgs string // CNI_ARGS
runtimeConfig struct {
mac string
}
args struct {
cni struct {
mac string
}
}
// Unlike the parameters above, the following parameters
// are expected values to be checked against.
// e.g. the mac address has several sources: CNI_ARGS, Args and RuntimeConfig.
expectedMac string
}
// Range definition for each entry in the ranges list
@ -148,6 +163,18 @@ const (
ipamEndStr = `
}`
argsFormat = `,
"args": {
"cni": {
"mac": %q
}
}`
runtimeConfig = `,
"RuntimeConfig": {
"mac": %q
}`
)
// netConfJSON() generates a JSON network configuration string
@ -160,6 +187,12 @@ func (tc testCase) netConfJSON(dataDir string) string {
if tc.ipMasq {
conf += tc.ipMasqConfig()
}
if tc.args.cni.mac != "" {
conf += fmt.Sprintf(argsFormat, tc.args.cni.mac)
}
if tc.runtimeConfig.mac != "" {
conf += fmt.Sprintf(runtimeConfig, tc.runtimeConfig.mac)
}
if !tc.isLayer2 {
conf += netDefault
@ -223,6 +256,7 @@ func (tc testCase) createCmdArgs(targetNS ns.NetNS, dataDir string) *skel.CmdArg
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
Args: tc.envArgs,
}
}
@ -428,7 +462,10 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
Expect(result.Interfaces[1].Mac).To(HaveLen(17))
Expect(result.Interfaces[2].Name).To(Equal(IFNAME))
Expect(result.Interfaces[2].Mac).To(HaveLen(17)) //mac is random
Expect(result.Interfaces[2].Mac).To(HaveLen(17))
if tc.expectedMac != "" {
Expect(result.Interfaces[2].Mac).To(Equal(tc.expectedMac))
}
Expect(result.Interfaces[2].Sandbox).To(Equal(tester.targetNS.Path()))
// Make sure bridge link exists
@ -725,7 +762,10 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
Expect(result.Interfaces[1].Mac).To(HaveLen(17))
Expect(result.Interfaces[2].Name).To(Equal(IFNAME))
Expect(result.Interfaces[2].Mac).To(HaveLen(17)) //mac is random
Expect(result.Interfaces[2].Mac).To(HaveLen(17))
if tc.expectedMac != "" {
Expect(result.Interfaces[2].Mac).To(Equal(tc.expectedMac))
}
Expect(result.Interfaces[2].Sandbox).To(Equal(tester.targetNS.Path()))
// Make sure bridge link exists
@ -1022,7 +1062,10 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result,
Expect(result.Interfaces[1].Mac).To(HaveLen(17))
Expect(result.Interfaces[2].Name).To(Equal(IFNAME))
Expect(result.Interfaces[2].Mac).To(HaveLen(17)) //mac is random
Expect(result.Interfaces[2].Mac).To(HaveLen(17))
if tc.expectedMac != "" {
Expect(result.Interfaces[2].Mac).To(Equal(tc.expectedMac))
}
Expect(result.Interfaces[2].Sandbox).To(Equal(tester.targetNS.Path()))
// Make sure bridge link exists
@ -1967,6 +2010,66 @@ var _ = Describe("bridge Operations", func() {
})
}
It(fmt.Sprintf("[%s] uses an explicit MAC addresses for the container iface (from CNI_ARGS)", ver), func() {
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
const expectedMac = "02:00:00:00:00:00"
tc := testCase{
cniVersion: ver,
subnet: "10.1.2.0/24",
envArgs: "MAC=" + expectedMac,
expectedMac: expectedMac,
}
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] uses an explicit MAC addresses for the container iface (from Args)", ver), func() {
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
const expectedMac = "02:00:00:00:00:00"
tc := testCase{
cniVersion: ver,
subnet: "10.1.2.0/24",
envArgs: "MAC=" + "02:00:00:00:04:56",
expectedMac: expectedMac,
}
tc.args.cni.mac = expectedMac
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] uses an explicit MAC addresses for the container iface (from RuntimeConfig)", ver), func() {
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
const expectedMac = "02:00:00:00:00:00"
tc := testCase{
cniVersion: ver,
subnet: "10.1.2.0/24",
envArgs: "MAC=" + "02:00:00:00:04:56",
expectedMac: expectedMac,
}
tc.args.cni.mac = "02:00:00:00:07:89"
tc.runtimeConfig.mac = expectedMac
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] checks ip release in case of error", ver), func() {
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
@ -2099,7 +2202,7 @@ var _ = Describe("bridge Operations", func() {
tests = append(tests, createCaseFn("0.4.0", 5000, fmt.Errorf("invalid VLAN ID 5000 (must be between 0 and 4094)")))
for _, test := range tests {
_, _, err := loadNetConf([]byte(test.netConfJSON("")))
_, _, err := loadNetConf([]byte(test.netConfJSON("")), "")
if test.err == nil {
Expect(err).To(BeNil())
} else {

View File

@ -66,7 +66,7 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu
containerInterface := &current.Interface{}
err := netns.Do(func(hostNS ns.NetNS) error {
hostVeth, contVeth0, err := ip.SetupVeth(ifName, mtu, hostNS)
hostVeth, contVeth0, err := ip.SetupVeth(ifName, mtu, "", hostNS)
if err != nil {
return err
}