From b2e53181d1af8587b82704374e6979eac4a5dd50 Mon Sep 17 00:00:00 2001 From: Minhan Xia Date: Tue, 3 May 2016 12:03:05 -0700 Subject: [PATCH 01/11] configure mac address based on assigned ip --- pkg/ipam/ipam.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index d9fbff74..7777e6f6 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -23,6 +23,17 @@ import ( "github.com/containernetworking/cni/pkg/types" "github.com/vishvananda/netlink" + "net" + "strconv" + "strings" +) + +const ( + // private mac prefix safe to use + privateMACPrefix = "0a:58" + + // veth link dev type + vethLinkType = "veth" ) func ExecAdd(plugin string, netconf []byte) (*types.Result, error) { @@ -45,6 +56,17 @@ func ConfigureIface(ifName string, res *types.Result) error { return fmt.Errorf("failed to set %q UP: %v", ifName, err) } + // only set hardware address to veth when using ipv4 + if link.Type() == vethLinkType && res.IP4 != nil { + hwAddr, err := generateHardwareAddr(res.IP4.IP.IP) + if err != nil { + return fmt.Errorf("failed to generate hardware addr: %v", err) + } + if err = netlink.LinkSetHardwareAddr(link, hwAddr); err != nil { + return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err) + } + } + // TODO(eyakubovich): IPv6 addr := &netlink.Addr{IPNet: &res.IP4.IP, Label: ""} if err = netlink.AddrAdd(link, addr); err != nil { @@ -66,3 +88,21 @@ func ConfigureIface(ifName string, res *types.Result) error { return nil } + +// generateHardwareAddr generates 48 bit virtual mac addresses based on the IP input. +func generateHardwareAddr(ip net.IP) (net.HardwareAddr, error) { + if ip.To4() == nil { + return nil, fmt.Errorf("generateHardwareAddr only support valid ipv4 address as input") + } + mac := privateMACPrefix + sections := strings.Split(ip.String(), ".") + for _, s := range sections { + i, _ := strconv.Atoi(s) + mac = mac + ":" + fmt.Sprintf("%02x", i) + } + hwAddr, err := net.ParseMAC(mac) + if err != nil { + return nil, fmt.Errorf("Failed to parse mac address %s generated based on ip %s due to: %v", mac, ip, err) + } + return hwAddr, nil +} From 1c1424c4726bbb35fa76d0f8a9e3d18504f920bd Mon Sep 17 00:00:00 2001 From: Minhan Xia Date: Tue, 3 May 2016 15:47:28 -0700 Subject: [PATCH 02/11] add tests for generateHardwardAddr --- pkg/ipam/ipam_suite_test.go | 27 ++++++++++++++++ pkg/ipam/ipam_test.go | 62 +++++++++++++++++++++++++++++++++++++ test | 2 +- 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 pkg/ipam/ipam_suite_test.go create mode 100644 pkg/ipam/ipam_test.go diff --git a/pkg/ipam/ipam_suite_test.go b/pkg/ipam/ipam_suite_test.go new file mode 100644 index 00000000..e80c8675 --- /dev/null +++ b/pkg/ipam/ipam_suite_test.go @@ -0,0 +1,27 @@ +// Copyright 2016 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 ipam_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestIpam(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Ipam Suite") +} diff --git a/pkg/ipam/ipam_test.go b/pkg/ipam/ipam_test.go new file mode 100644 index 00000000..0cacd08f --- /dev/null +++ b/pkg/ipam/ipam_test.go @@ -0,0 +1,62 @@ +// Copyright 2016 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 ipam + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "net" +) + +var _ = Describe("ipam utils", func() { + Context("Generate Hardware Addrress", func() { + It("generate hardware address based on ipv4 address", func() { + testCases := []struct { + ip net.IP + expectedMAC string + }{ + { + ip: net.ParseIP("10.0.0.2"), + expectedMAC: privateMACPrefix + ":0a:00:00:02", + }, + { + ip: net.ParseIP("10.250.0.244"), + expectedMAC: privateMACPrefix + ":0a:fa:00:f4", + }, + { + ip: net.ParseIP("172.17.0.2"), + expectedMAC: privateMACPrefix + ":ac:11:00:02", + }, + } + + for _, tc := range testCases { + mac, err := generateHardwareAddr(tc.ip) + Expect(err).NotTo(HaveOccurred()) + Expect(mac.String()).To(Equal(tc.expectedMAC)) + } + }) + + It("return error if input is not ipv4 address", func() { + testCases := []net.IP{ + net.ParseIP(""), + net.ParseIP("2001:db8:0:1:1:1:1:1"), + } + for _, tc := range testCases { + _, err := generateHardwareAddr(tc) + Expect(err.Error()).To(Equal("generateHardwareAddr only support valid ipv4 address as input")) + } + }) + }) +}) diff --git a/test b/test index 91220800..9cd43e03 100755 --- a/test +++ b/test @@ -12,7 +12,7 @@ set -e source ./build TESTABLE="libcni plugins/ipam/dhcp plugins/ipam/host-local plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/test/noop" -FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ipam pkg/testutils plugins/meta/flannel plugins/meta/tuning" +FORMATTABLE="$TESTABLE pkg/ip pkg/testutils plugins/meta/flannel plugins/meta/tuning" # user has not provided PKG override if [ -z "$PKG" ]; then From 96867eae05dcb47679aea2777fa72193fcb7b9f2 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Tue, 21 Jun 2016 16:28:38 -0700 Subject: [PATCH 03/11] pkg/ipam{,test}: improve error handling and tests * _suite.go and _test.go file should be in the same package, using the _test package for that, which requires some fields and methods to be exported * Introduce error type for cleaner error handling * test adaptions for error type checking --- pkg/ipam/ipam.go | 39 +++++++++++++++++++++++++++++++-------- pkg/ipam/ipam_test.go | 23 +++++++++++++++-------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 7777e6f6..74f937d7 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -30,7 +30,7 @@ import ( const ( // private mac prefix safe to use - privateMACPrefix = "0a:58" + PrivateMACPrefix = "0a:58" // veth link dev type vethLinkType = "veth" @@ -58,7 +58,7 @@ func ConfigureIface(ifName string, res *types.Result) error { // only set hardware address to veth when using ipv4 if link.Type() == vethLinkType && res.IP4 != nil { - hwAddr, err := generateHardwareAddr(res.IP4.IP.IP) + hwAddr, err := GenerateHardwareAddr4(res.IP4.IP.IP, PrivateMACPrefix) if err != nil { return fmt.Errorf("failed to generate hardware addr: %v", err) } @@ -89,20 +89,43 @@ func ConfigureIface(ifName string, res *types.Result) error { return nil } -// generateHardwareAddr generates 48 bit virtual mac addresses based on the IP input. -func generateHardwareAddr(ip net.IP) (net.HardwareAddr, error) { - if ip.To4() == nil { - return nil, fmt.Errorf("generateHardwareAddr only support valid ipv4 address as input") +type SupportIp4OnlyErr struct{ msg string } + +func (e SupportIp4OnlyErr) Error() string { return e.msg } + +type MacParseErr struct{ msg string } + +func (e MacParseErr) Error() string { return e.msg } + +type InvalidPrefixLengthErr struct{ msg string } + +func (e InvalidPrefixLengthErr) Error() string { return e.msg } + +// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input. +func GenerateHardwareAddr4(ip net.IP, prefix string) (net.HardwareAddr, error) { + switch { + + case ip.To4() == nil: + return nil, SupportIp4OnlyErr{msg: "GenerateHardwareAddr4 only supports valid IPv4 address as input"} + + case len(prefix) != len(PrivateMACPrefix): + return nil, InvalidPrefixLengthErr{msg: fmt.Sprintf( + "Prefix has length %d instead of %d", len(prefix), len(PrivateMACPrefix)), + } } - mac := privateMACPrefix + + mac := prefix sections := strings.Split(ip.String(), ".") for _, s := range sections { i, _ := strconv.Atoi(s) mac = mac + ":" + fmt.Sprintf("%02x", i) } + hwAddr, err := net.ParseMAC(mac) if err != nil { - return nil, fmt.Errorf("Failed to parse mac address %s generated based on ip %s due to: %v", mac, ip, err) + return nil, MacParseErr{msg: fmt.Sprintf( + "Failed to parse mac address %q generated based on IP %q due to: %v", mac, ip, err), + } } return hwAddr, nil } diff --git a/pkg/ipam/ipam_test.go b/pkg/ipam/ipam_test.go index 0cacd08f..1390ccec 100644 --- a/pkg/ipam/ipam_test.go +++ b/pkg/ipam/ipam_test.go @@ -12,16 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ipam +package ipam_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "net" + + "github.com/containernetworking/cni/pkg/ipam" ) var _ = Describe("ipam utils", func() { - Context("Generate Hardware Addrress", func() { + Context("Generate Hardware Address", func() { It("generate hardware address based on ipv4 address", func() { testCases := []struct { ip net.IP @@ -29,20 +31,20 @@ var _ = Describe("ipam utils", func() { }{ { ip: net.ParseIP("10.0.0.2"), - expectedMAC: privateMACPrefix + ":0a:00:00:02", + expectedMAC: ipam.PrivateMACPrefix + ":0a:00:00:02", }, { ip: net.ParseIP("10.250.0.244"), - expectedMAC: privateMACPrefix + ":0a:fa:00:f4", + expectedMAC: ipam.PrivateMACPrefix + ":0a:fa:00:f4", }, { ip: net.ParseIP("172.17.0.2"), - expectedMAC: privateMACPrefix + ":ac:11:00:02", + expectedMAC: ipam.PrivateMACPrefix + ":ac:11:00:02", }, } for _, tc := range testCases { - mac, err := generateHardwareAddr(tc.ip) + mac, err := ipam.GenerateHardwareAddr4(tc.ip, ipam.PrivateMACPrefix) Expect(err).NotTo(HaveOccurred()) Expect(mac.String()).To(Equal(tc.expectedMAC)) } @@ -54,9 +56,14 @@ var _ = Describe("ipam utils", func() { net.ParseIP("2001:db8:0:1:1:1:1:1"), } for _, tc := range testCases { - _, err := generateHardwareAddr(tc) - Expect(err.Error()).To(Equal("generateHardwareAddr only support valid ipv4 address as input")) + _, err := ipam.GenerateHardwareAddr4(tc, ipam.PrivateMACPrefix) + Expect(err).To(BeAssignableToTypeOf(ipam.SupportIp4OnlyErr{})) } }) + + It("return error if prefix is invalid", func() { + _, err := ipam.GenerateHardwareAddr4(net.ParseIP("10.0.0.2"), "") + Expect(err).To(BeAssignableToTypeOf(ipam.InvalidPrefixLengthErr{})) + }) }) }) From 3a1354cff644b16e7975f7c0c49fddb9090c63d5 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Tue, 28 Jun 2016 12:51:58 -0700 Subject: [PATCH 04/11] pkg/utils/hwaddr: migrate code from IPAM pkg --- pkg/ipam/ipam.go | 50 +------------- pkg/utils/hwaddr/hwaddr.go | 68 +++++++++++++++++++ pkg/utils/hwaddr/hwaddr_suite_test.go | 27 ++++++++ .../hwaddr/hwaddr_test.go} | 27 ++++---- 4 files changed, 111 insertions(+), 61 deletions(-) create mode 100644 pkg/utils/hwaddr/hwaddr.go create mode 100644 pkg/utils/hwaddr/hwaddr_suite_test.go rename pkg/{ipam/ipam_test.go => utils/hwaddr/hwaddr_test.go} (68%) diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 74f937d7..62bf15d5 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -21,17 +21,12 @@ import ( "github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/ip" "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/utils/hwaddr" "github.com/vishvananda/netlink" - "net" - "strconv" - "strings" ) const ( - // private mac prefix safe to use - PrivateMACPrefix = "0a:58" - // veth link dev type vethLinkType = "veth" ) @@ -58,7 +53,7 @@ func ConfigureIface(ifName string, res *types.Result) error { // only set hardware address to veth when using ipv4 if link.Type() == vethLinkType && res.IP4 != nil { - hwAddr, err := GenerateHardwareAddr4(res.IP4.IP.IP, PrivateMACPrefix) + hwAddr, err := hwaddr.GenerateHardwareAddr4(res.IP4.IP.IP, hwaddr.PrivateMACPrefix) if err != nil { return fmt.Errorf("failed to generate hardware addr: %v", err) } @@ -88,44 +83,3 @@ func ConfigureIface(ifName string, res *types.Result) error { return nil } - -type SupportIp4OnlyErr struct{ msg string } - -func (e SupportIp4OnlyErr) Error() string { return e.msg } - -type MacParseErr struct{ msg string } - -func (e MacParseErr) Error() string { return e.msg } - -type InvalidPrefixLengthErr struct{ msg string } - -func (e InvalidPrefixLengthErr) Error() string { return e.msg } - -// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input. -func GenerateHardwareAddr4(ip net.IP, prefix string) (net.HardwareAddr, error) { - switch { - - case ip.To4() == nil: - return nil, SupportIp4OnlyErr{msg: "GenerateHardwareAddr4 only supports valid IPv4 address as input"} - - case len(prefix) != len(PrivateMACPrefix): - return nil, InvalidPrefixLengthErr{msg: fmt.Sprintf( - "Prefix has length %d instead of %d", len(prefix), len(PrivateMACPrefix)), - } - } - - mac := prefix - sections := strings.Split(ip.String(), ".") - for _, s := range sections { - i, _ := strconv.Atoi(s) - mac = mac + ":" + fmt.Sprintf("%02x", i) - } - - hwAddr, err := net.ParseMAC(mac) - if err != nil { - return nil, MacParseErr{msg: fmt.Sprintf( - "Failed to parse mac address %q generated based on IP %q due to: %v", mac, ip, err), - } - } - return hwAddr, nil -} diff --git a/pkg/utils/hwaddr/hwaddr.go b/pkg/utils/hwaddr/hwaddr.go new file mode 100644 index 00000000..f03b7aca --- /dev/null +++ b/pkg/utils/hwaddr/hwaddr.go @@ -0,0 +1,68 @@ +// Copyright 2016 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 hwaddr + +import ( + "fmt" + "net" + "strconv" + "strings" +) + +const ( + // private mac prefix safe to use + PrivateMACPrefix = "0a:58" +) + +type SupportIp4OnlyErr struct{ msg string } + +func (e SupportIp4OnlyErr) Error() string { return e.msg } + +type MacParseErr struct{ msg string } + +func (e MacParseErr) Error() string { return e.msg } + +type InvalidPrefixLengthErr struct{ msg string } + +func (e InvalidPrefixLengthErr) Error() string { return e.msg } + +// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input. +func GenerateHardwareAddr4(ip net.IP, prefix string) (net.HardwareAddr, error) { + switch { + + case ip.To4() == nil: + return nil, SupportIp4OnlyErr{msg: "GenerateHardwareAddr4 only supports valid IPv4 address as input"} + + case len(prefix) != len(PrivateMACPrefix): + return nil, InvalidPrefixLengthErr{msg: fmt.Sprintf( + "Prefix has length %d instead of %d", len(prefix), len(PrivateMACPrefix)), + } + } + + mac := prefix + sections := strings.Split(ip.String(), ".") + for _, s := range sections { + i, _ := strconv.Atoi(s) + mac += fmt.Sprintf(":%02x", i) + } + + hwAddr, err := net.ParseMAC(mac) + if err != nil { + return nil, MacParseErr{msg: fmt.Sprintf( + "Failed to parse mac address %q generated based on IP %q due to: %v", mac, ip, err), + } + } + return hwAddr, nil +} diff --git a/pkg/utils/hwaddr/hwaddr_suite_test.go b/pkg/utils/hwaddr/hwaddr_suite_test.go new file mode 100644 index 00000000..e3bbfe97 --- /dev/null +++ b/pkg/utils/hwaddr/hwaddr_suite_test.go @@ -0,0 +1,27 @@ +// Copyright 2016 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 hwaddr_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestHwaddr(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Hwaddr Suite") +} diff --git a/pkg/ipam/ipam_test.go b/pkg/utils/hwaddr/hwaddr_test.go similarity index 68% rename from pkg/ipam/ipam_test.go rename to pkg/utils/hwaddr/hwaddr_test.go index 1390ccec..8a201a25 100644 --- a/pkg/ipam/ipam_test.go +++ b/pkg/utils/hwaddr/hwaddr_test.go @@ -12,17 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ipam_test +package hwaddr_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "net" - "github.com/containernetworking/cni/pkg/ipam" + "github.com/containernetworking/cni/pkg/utils/hwaddr" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" ) -var _ = Describe("ipam utils", func() { +var _ = Describe("Hwaddr", func() { Context("Generate Hardware Address", func() { It("generate hardware address based on ipv4 address", func() { testCases := []struct { @@ -31,20 +32,20 @@ var _ = Describe("ipam utils", func() { }{ { ip: net.ParseIP("10.0.0.2"), - expectedMAC: ipam.PrivateMACPrefix + ":0a:00:00:02", + expectedMAC: hwaddr.PrivateMACPrefix + ":0a:00:00:02", }, { ip: net.ParseIP("10.250.0.244"), - expectedMAC: ipam.PrivateMACPrefix + ":0a:fa:00:f4", + expectedMAC: hwaddr.PrivateMACPrefix + ":0a:fa:00:f4", }, { ip: net.ParseIP("172.17.0.2"), - expectedMAC: ipam.PrivateMACPrefix + ":ac:11:00:02", + expectedMAC: hwaddr.PrivateMACPrefix + ":ac:11:00:02", }, } for _, tc := range testCases { - mac, err := ipam.GenerateHardwareAddr4(tc.ip, ipam.PrivateMACPrefix) + mac, err := hwaddr.GenerateHardwareAddr4(tc.ip, hwaddr.PrivateMACPrefix) Expect(err).NotTo(HaveOccurred()) Expect(mac.String()).To(Equal(tc.expectedMAC)) } @@ -56,14 +57,14 @@ var _ = Describe("ipam utils", func() { net.ParseIP("2001:db8:0:1:1:1:1:1"), } for _, tc := range testCases { - _, err := ipam.GenerateHardwareAddr4(tc, ipam.PrivateMACPrefix) - Expect(err).To(BeAssignableToTypeOf(ipam.SupportIp4OnlyErr{})) + _, err := hwaddr.GenerateHardwareAddr4(tc, hwaddr.PrivateMACPrefix) + Expect(err).To(BeAssignableToTypeOf(hwaddr.SupportIp4OnlyErr{})) } }) It("return error if prefix is invalid", func() { - _, err := ipam.GenerateHardwareAddr4(net.ParseIP("10.0.0.2"), "") - Expect(err).To(BeAssignableToTypeOf(ipam.InvalidPrefixLengthErr{})) + _, err := hwaddr.GenerateHardwareAddr4(net.ParseIP("10.0.0.2"), "") + Expect(err).To(BeAssignableToTypeOf(hwaddr.InvalidPrefixLengthErr{})) }) }) }) From 924b30b57d8752bb4d375cc0aeb5ead515903f79 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Tue, 28 Jun 2016 17:46:17 -0700 Subject: [PATCH 05/11] plugins: set MAC addresses based on IP This will give deterministic MAC addresses for all interfaces CNI creates and manages the IP for: * bridge: container veth and host bridge * macvlan: container veth * ptp: container veth and host veth --- pkg/ip/link.go | 17 +++++++++++++++++ pkg/ipam/ipam.go | 17 ----------------- pkg/utils/hwaddr/hwaddr.go | 31 +++++++++++++------------------ pkg/utils/hwaddr/hwaddr_test.go | 12 ++++++------ plugins/main/bridge/bridge.go | 18 +++++++++++++++++- plugins/main/macvlan/macvlan.go | 8 ++++++++ plugins/main/ptp/ptp.go | 8 ++++++++ 7 files changed, 69 insertions(+), 42 deletions(-) diff --git a/pkg/ip/link.go b/pkg/ip/link.go index 1b785672..6d9d1cc2 100644 --- a/pkg/ip/link.go +++ b/pkg/ip/link.go @@ -21,6 +21,7 @@ import ( "os" "github.com/containernetworking/cni/pkg/ns" + "github.com/containernetworking/cni/pkg/utils/hwaddr" "github.com/vishvananda/netlink" ) @@ -151,3 +152,19 @@ func DelLinkByNameAddr(ifName string, family int) (*net.IPNet, error) { return addrs[0].IPNet, nil } + +func SetHWAddrByIP(link netlink.Link, ip4 net.IP, ip6 net.IP) error { + if ip4 != nil { + hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix) + if err != nil { + return fmt.Errorf("failed to generate hardware addr: %v", err) + } + if err = netlink.LinkSetHardwareAddr(link, hwAddr); err != nil { + return fmt.Errorf("failed to add hardware addr to %q: %v", link.Attrs().Name, err) + } + } + + // TODO: IPv6 + + return nil +} diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 62bf15d5..d9fbff74 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -21,16 +21,10 @@ import ( "github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/ip" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/utils/hwaddr" "github.com/vishvananda/netlink" ) -const ( - // veth link dev type - vethLinkType = "veth" -) - func ExecAdd(plugin string, netconf []byte) (*types.Result, error) { return invoke.DelegateAdd(plugin, netconf) } @@ -51,17 +45,6 @@ func ConfigureIface(ifName string, res *types.Result) error { return fmt.Errorf("failed to set %q UP: %v", ifName, err) } - // only set hardware address to veth when using ipv4 - if link.Type() == vethLinkType && res.IP4 != nil { - hwAddr, err := hwaddr.GenerateHardwareAddr4(res.IP4.IP.IP, hwaddr.PrivateMACPrefix) - if err != nil { - return fmt.Errorf("failed to generate hardware addr: %v", err) - } - if err = netlink.LinkSetHardwareAddr(link, hwAddr); err != nil { - return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err) - } - } - // TODO(eyakubovich): IPv6 addr := &netlink.Addr{IPNet: &res.IP4.IP, Label: ""} if err = netlink.AddrAdd(link, addr); err != nil { diff --git a/pkg/utils/hwaddr/hwaddr.go b/pkg/utils/hwaddr/hwaddr.go index f03b7aca..aaf3b8a0 100644 --- a/pkg/utils/hwaddr/hwaddr.go +++ b/pkg/utils/hwaddr/hwaddr.go @@ -17,13 +17,16 @@ package hwaddr import ( "fmt" "net" - "strconv" - "strings" ) const ( + ipRelevantByteLen = 4 + PrivateMACPrefixString = "0a:58" +) + +var ( // private mac prefix safe to use - PrivateMACPrefix = "0a:58" + PrivateMACPrefix = []byte{0x0a, 0x58} ) type SupportIp4OnlyErr struct{ msg string } @@ -39,7 +42,7 @@ type InvalidPrefixLengthErr struct{ msg string } func (e InvalidPrefixLengthErr) Error() string { return e.msg } // GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input. -func GenerateHardwareAddr4(ip net.IP, prefix string) (net.HardwareAddr, error) { +func GenerateHardwareAddr4(ip net.IP, prefix []byte) (net.HardwareAddr, error) { switch { case ip.To4() == nil: @@ -51,18 +54,10 @@ func GenerateHardwareAddr4(ip net.IP, prefix string) (net.HardwareAddr, error) { } } - mac := prefix - sections := strings.Split(ip.String(), ".") - for _, s := range sections { - i, _ := strconv.Atoi(s) - mac += fmt.Sprintf(":%02x", i) - } - - hwAddr, err := net.ParseMAC(mac) - if err != nil { - return nil, MacParseErr{msg: fmt.Sprintf( - "Failed to parse mac address %q generated based on IP %q due to: %v", mac, ip, err), - } - } - return hwAddr, nil + ipByteLen := len(ip) + return (net.HardwareAddr)( + append( + prefix, + ip[ipByteLen-ipRelevantByteLen:ipByteLen]...), + ), nil } diff --git a/pkg/utils/hwaddr/hwaddr_test.go b/pkg/utils/hwaddr/hwaddr_test.go index 8a201a25..51c4e498 100644 --- a/pkg/utils/hwaddr/hwaddr_test.go +++ b/pkg/utils/hwaddr/hwaddr_test.go @@ -28,26 +28,26 @@ var _ = Describe("Hwaddr", func() { It("generate hardware address based on ipv4 address", func() { testCases := []struct { ip net.IP - expectedMAC string + expectedMAC net.HardwareAddr }{ { ip: net.ParseIP("10.0.0.2"), - expectedMAC: hwaddr.PrivateMACPrefix + ":0a:00:00:02", + expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0x0a, 0x00, 0x00, 0x02)), }, { ip: net.ParseIP("10.250.0.244"), - expectedMAC: hwaddr.PrivateMACPrefix + ":0a:fa:00:f4", + expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0x0a, 0xfa, 0x00, 0xf4)), }, { ip: net.ParseIP("172.17.0.2"), - expectedMAC: hwaddr.PrivateMACPrefix + ":ac:11:00:02", + expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0xac, 0x11, 0x00, 0x02)), }, } for _, tc := range testCases { mac, err := hwaddr.GenerateHardwareAddr4(tc.ip, hwaddr.PrivateMACPrefix) Expect(err).NotTo(HaveOccurred()) - Expect(mac.String()).To(Equal(tc.expectedMAC)) + Expect(mac).To(Equal(tc.expectedMAC)) } }) @@ -63,7 +63,7 @@ var _ = Describe("Hwaddr", func() { }) It("return error if prefix is invalid", func() { - _, err := hwaddr.GenerateHardwareAddr4(net.ParseIP("10.0.0.2"), "") + _, err := hwaddr.GenerateHardwareAddr4(net.ParseIP("10.0.0.2"), []byte{0x58}) Expect(err).To(BeAssignableToTypeOf(hwaddr.InvalidPrefixLengthErr{})) }) }) diff --git a/plugins/main/bridge/bridge.go b/plugins/main/bridge/bridge.go index d4fc89c3..34f6c0a9 100644 --- a/plugins/main/bridge/bridge.go +++ b/plugins/main/bridge/bridge.go @@ -247,7 +247,19 @@ func cmdAdd(args *skel.CmdArgs) error { // TODO: IPV6 } - return ipam.ConfigureIface(args.IfName, result) + if err := ipam.ConfigureIface(args.IfName, result); err != nil { + return err + } + + contVethLink, err := netlink.LinkByName(args.IfName) + if err != nil { + return fmt.Errorf("failed to lookup %q: %v", args.IfName, err) + } + if err := ip.SetHWAddrByIP(contVethLink, result.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + return err + } + + return nil }); err != nil { return err } @@ -262,6 +274,10 @@ func cmdAdd(args *skel.CmdArgs) error { return err } + if err := ip.SetHWAddrByIP(br, gwn.IP, nil /* TODO IPv6 */); err != nil { + return err + } + if err := ip.EnableIP4Forward(); err != nil { return fmt.Errorf("failed to enable forwarding: %v", err) } diff --git a/plugins/main/macvlan/macvlan.go b/plugins/main/macvlan/macvlan.go index 7739d7b8..d1a40703 100644 --- a/plugins/main/macvlan/macvlan.go +++ b/plugins/main/macvlan/macvlan.go @@ -149,6 +149,14 @@ func cmdAdd(args *skel.CmdArgs) error { } err = netns.Do(func(_ ns.NetNS) error { + contVethLink, err := netlink.LinkByName(args.IfName) + if err != nil { + return fmt.Errorf("failed to lookup %q: %v", args.IfName, err) + } + if err := ip.SetHWAddrByIP(contVethLink, result.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + return err + } + return ipam.ConfigureIface(args.IfName, result) }) if err != nil { diff --git a/plugins/main/ptp/ptp.go b/plugins/main/ptp/ptp.go index aa695e39..e6c085e4 100644 --- a/plugins/main/ptp/ptp.go +++ b/plugins/main/ptp/ptp.go @@ -64,6 +64,10 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string return err } + if err := ip.SetHWAddrByIP(hostVeth, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + return fmt.Errorf("failed to set hardware addr by IP: %v", err) + } + if err = ipam.ConfigureIface(ifName, pr); err != nil { return err } @@ -73,6 +77,10 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string return fmt.Errorf("failed to look up %q: %v", ifName, err) } + if err := ip.SetHWAddrByIP(contVeth, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + return fmt.Errorf("failed to set hardware addr by IP: %v", err) + } + // Delete the route that was automatically added route := netlink.Route{ LinkIndex: contVeth.Attrs().Index, From 8e1c21511643931f94493d7c101221070daf2784 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Tue, 28 Jun 2016 19:52:08 -0700 Subject: [PATCH 06/11] pkg/ip tests: cover SetupVeth and DelLinkByName* --- pkg/ip/ip_suite_test.go | 27 ++++++++ pkg/ip/link_test.go | 132 ++++++++++++++++++++++++++++++++++++++++ test | 4 +- 3 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 pkg/ip/ip_suite_test.go create mode 100644 pkg/ip/link_test.go diff --git a/pkg/ip/ip_suite_test.go b/pkg/ip/ip_suite_test.go new file mode 100644 index 00000000..3fdd57e4 --- /dev/null +++ b/pkg/ip/ip_suite_test.go @@ -0,0 +1,27 @@ +// Copyright 2016 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 ip_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestIp(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Ip Suite") +} diff --git a/pkg/ip/link_test.go b/pkg/ip/link_test.go new file mode 100644 index 00000000..cdf861a6 --- /dev/null +++ b/pkg/ip/link_test.go @@ -0,0 +1,132 @@ +// Copyright 2016 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 ip_test + +import ( + "fmt" + "net" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/containernetworking/cni/pkg/ip" + "github.com/containernetworking/cni/pkg/ns" + + "github.com/vishvananda/netlink" + "github.com/vishvananda/netlink/nl" +) + +var _ = Describe("Link", func() { + const ( + ifaceFormatString string = "i%d" + mtu int = 1400 + ) + var ( + hostNetNS ns.NetNS + containerNetNS ns.NetNS + ifaceCounter int = 0 + hostVethName string + containerVethName string + ) + + BeforeEach(func() { + var err error + + hostNetNS, err = ns.NewNS() + Expect(err).NotTo(HaveOccurred()) + + containerNetNS, err = ns.NewNS() + Expect(err).NotTo(HaveOccurred()) + + _ = containerNetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + hostVeth, containerVeth, err := ip.SetupVeth(fmt.Sprintf(ifaceFormatString, ifaceCounter), mtu, hostNetNS) + if err != nil { + return err + } + Expect(err).NotTo(HaveOccurred()) + + hostVethName = hostVeth.Attrs().Name + containerVethName = containerVeth.Attrs().Name + + return nil + }) + }) + + AfterEach(func() { + Expect(containerNetNS.Close()).To(Succeed()) + Expect(hostNetNS.Close()).To(Succeed()) + ifaceCounter++ + }) + + It("SetupVeth must put the veth endpoints into the separate namespaces", func() { + _ = containerNetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + _, err := netlink.LinkByName(containerVethName) + Expect(err).NotTo(HaveOccurred()) + + return nil + }) + + _ = hostNetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + _, err := netlink.LinkByName(hostVethName) + Expect(err).NotTo(HaveOccurred()) + + return nil + }) + }) + + It("DelLinkByName must delete the veth endpoints", func() { + _ = containerNetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + // this will delete the host endpoint too + err := ip.DelLinkByName(containerVethName) + Expect(err).NotTo(HaveOccurred()) + + _, err = netlink.LinkByName(containerVethName) + Expect(err).To(HaveOccurred()) + + return nil + }) + + _ = hostNetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + _, err := netlink.LinkByName(hostVethName) + Expect(err).To(HaveOccurred()) + + return nil + }) + }) + + It("DelLinkByNameAddr must throw an error for configured interfaces", func() { + _ = containerNetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + // this will delete the host endpoint too + addr, err := ip.DelLinkByNameAddr(containerVethName, nl.FAMILY_V4) + Expect(err).To(HaveOccurred()) + + var ipNetNil *net.IPNet + Expect(addr).To(Equal(ipNetNil)) + return nil + }) + }) +}) diff --git a/test b/test index 9cd43e03..2f1a4b21 100755 --- a/test +++ b/test @@ -11,8 +11,8 @@ set -e source ./build -TESTABLE="libcni plugins/ipam/dhcp plugins/ipam/host-local plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/test/noop" -FORMATTABLE="$TESTABLE pkg/ip pkg/testutils plugins/meta/flannel plugins/meta/tuning" +TESTABLE="libcni plugins/ipam/dhcp plugins/ipam/host-local plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge plugins/main/ptp plugins/test/noop pkg/utils/hwaddr pkg/ip" +FORMATTABLE="$TESTABLE pkg/testutils plugins/meta/flannel plugins/meta/tuning" # user has not provided PKG override if [ -z "$PKG" ]; then From 7d19c0181812f208b88e232180c217df242f31d9 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Wed, 29 Jun 2016 12:13:05 -0700 Subject: [PATCH 07/11] pkg/ip: use iface name in SetHWAddrByIP --- pkg/ip/link.go | 31 +++++++++++++++++++++---------- plugins/main/bridge/bridge.go | 8 ++------ plugins/main/macvlan/macvlan.go | 6 +----- plugins/main/ptp/ptp.go | 8 ++++---- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/pkg/ip/link.go b/pkg/ip/link.go index 6d9d1cc2..c9662d98 100644 --- a/pkg/ip/link.go +++ b/pkg/ip/link.go @@ -153,18 +153,29 @@ func DelLinkByNameAddr(ifName string, family int) (*net.IPNet, error) { return addrs[0].IPNet, nil } -func SetHWAddrByIP(link netlink.Link, ip4 net.IP, ip6 net.IP) error { - if ip4 != nil { - hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix) - if err != nil { - return fmt.Errorf("failed to generate hardware addr: %v", err) - } - if err = netlink.LinkSetHardwareAddr(link, hwAddr); err != nil { - return fmt.Errorf("failed to add hardware addr to %q: %v", link.Attrs().Name, err) - } +func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error { + iface, err := netlink.LinkByName(ifName) + if err != nil { + return fmt.Errorf("failed to lookup %q: %v", ifName, err) } - // TODO: IPv6 + switch { + case ip4 == nil && ip6 == nil: + return fmt.Errorf("neither ip4 or ip6 specified") + + case ip4 != nil: + { + hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix) + if err != nil { + return fmt.Errorf("failed to generate hardware addr: %v", err) + } + if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil { + return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err) + } + } + case ip6 != nil: + // TODO: IPv6 + } return nil } diff --git a/plugins/main/bridge/bridge.go b/plugins/main/bridge/bridge.go index 34f6c0a9..5f9659d7 100644 --- a/plugins/main/bridge/bridge.go +++ b/plugins/main/bridge/bridge.go @@ -251,11 +251,7 @@ func cmdAdd(args *skel.CmdArgs) error { return err } - contVethLink, err := netlink.LinkByName(args.IfName) - if err != nil { - return fmt.Errorf("failed to lookup %q: %v", args.IfName, err) - } - if err := ip.SetHWAddrByIP(contVethLink, result.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + if err := ip.SetHWAddrByIP(args.IfName, result.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { return err } @@ -274,7 +270,7 @@ func cmdAdd(args *skel.CmdArgs) error { return err } - if err := ip.SetHWAddrByIP(br, gwn.IP, nil /* TODO IPv6 */); err != nil { + if err := ip.SetHWAddrByIP(n.BrName, gwn.IP, nil /* TODO IPv6 */); err != nil { return err } diff --git a/plugins/main/macvlan/macvlan.go b/plugins/main/macvlan/macvlan.go index d1a40703..63c9f7f0 100644 --- a/plugins/main/macvlan/macvlan.go +++ b/plugins/main/macvlan/macvlan.go @@ -149,11 +149,7 @@ func cmdAdd(args *skel.CmdArgs) error { } err = netns.Do(func(_ ns.NetNS) error { - contVethLink, err := netlink.LinkByName(args.IfName) - if err != nil { - return fmt.Errorf("failed to lookup %q: %v", args.IfName, err) - } - if err := ip.SetHWAddrByIP(contVethLink, result.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + if err := ip.SetHWAddrByIP(args.IfName, result.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { return err } diff --git a/plugins/main/ptp/ptp.go b/plugins/main/ptp/ptp.go index e6c085e4..cdb35366 100644 --- a/plugins/main/ptp/ptp.go +++ b/plugins/main/ptp/ptp.go @@ -64,7 +64,9 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string return err } - if err := ip.SetHWAddrByIP(hostVeth, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + hostVethName = hostVeth.Attrs().Name + + if err := ip.SetHWAddrByIP(hostVethName, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { return fmt.Errorf("failed to set hardware addr by IP: %v", err) } @@ -77,7 +79,7 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string return fmt.Errorf("failed to look up %q: %v", ifName, err) } - if err := ip.SetHWAddrByIP(contVeth, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + if err := ip.SetHWAddrByIP(contVeth.Attrs().Name, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { return fmt.Errorf("failed to set hardware addr by IP: %v", err) } @@ -121,8 +123,6 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string } } - hostVethName = hostVeth.Attrs().Name - return nil }) return hostVethName, err From 55fd81f7756a1d7cd9afb5257f974e0807aa5cee Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Wed, 29 Jun 2016 15:15:30 -0700 Subject: [PATCH 08/11] plugins/ptp: set the host veth hwaddr correctly --- plugins/main/ptp/ptp.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/main/ptp/ptp.go b/plugins/main/ptp/ptp.go index cdb35366..014b472c 100644 --- a/plugins/main/ptp/ptp.go +++ b/plugins/main/ptp/ptp.go @@ -64,11 +64,14 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string return err } - hostVethName = hostVeth.Attrs().Name + hostNS.Do(func(_ ns.NetNS) error { + hostVethName = hostVeth.Attrs().Name + if err := ip.SetHWAddrByIP(hostVethName, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { + return fmt.Errorf("failed to set hardware addr by IP: %v", err) + } - if err := ip.SetHWAddrByIP(hostVethName, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { - return fmt.Errorf("failed to set hardware addr by IP: %v", err) - } + return nil + }) if err = ipam.ConfigureIface(ifName, pr); err != nil { return err From f68cea27bc0be36faaa1560b39ccf9f869c51ce7 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Wed, 29 Jun 2016 12:13:37 -0700 Subject: [PATCH 09/11] pkg/ip link_test: ensure SetHWAddrByIP has an effect --- pkg/ip/link_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/pkg/ip/link_test.go b/pkg/ip/link_test.go index cdf861a6..5c139d98 100644 --- a/pkg/ip/link_test.go +++ b/pkg/ip/link_test.go @@ -28,10 +28,17 @@ import ( "github.com/vishvananda/netlink/nl" ) +func getHwAddr(linkname string) string { + veth, err := netlink.LinkByName(linkname) + Expect(err).NotTo(HaveOccurred()) + return fmt.Sprintf("%s", veth.Attrs().HardwareAddr) +} + var _ = Describe("Link", func() { const ( ifaceFormatString string = "i%d" mtu int = 1400 + ip4onehwaddr = "0a:58:01:01:01:01" ) var ( hostNetNS ns.NetNS @@ -39,6 +46,9 @@ var _ = Describe("Link", func() { ifaceCounter int = 0 hostVethName string containerVethName string + + ip4one = net.ParseIP("1.1.1.1") + ip4two = net.ParseIP("1.1.1.2") ) BeforeEach(func() { @@ -129,4 +139,41 @@ var _ = Describe("Link", func() { return nil }) }) + + It("SetHWAddrByIP must change the interface hwaddr and be predictable", func() { + + _ = containerNetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + var err error + hwaddrBefore := getHwAddr(containerVethName) + + err = ip.SetHWAddrByIP(containerVethName, ip4one, nil) + Expect(err).NotTo(HaveOccurred()) + hwaddrAfter1 := getHwAddr(containerVethName) + + Expect(hwaddrBefore).NotTo(Equal(hwaddrAfter1)) + Expect(hwaddrAfter1).To(Equal(ip4onehwaddr)) + + return nil + }) + }) + + It("SetHWAddrByIP must be injective", func() { + + _ = containerNetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := ip.SetHWAddrByIP(containerVethName, ip4one, nil) + Expect(err).NotTo(HaveOccurred()) + hwaddrAfter1 := getHwAddr(containerVethName) + + err = ip.SetHWAddrByIP(containerVethName, ip4two, nil) + Expect(err).NotTo(HaveOccurred()) + hwaddrAfter2 := getHwAddr(containerVethName) + + Expect(hwaddrAfter1).NotTo(Equal(hwaddrAfter2)) + return nil + }) + }) }) From 5a52316ab571b429392b6d082bd8e1bde0793a32 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Wed, 29 Jun 2016 16:03:25 -0700 Subject: [PATCH 10/11] plugins/{bridge,macvlan} test: ensure hardware addr * bridge: Test the following interface's hardware address for the CNI specific prefix: - bridge with IP address - container veth * plugins/macvlan test: ensure hardware addr --- plugins/main/bridge/bridge_test.go | 21 ++++++++++++++++++--- plugins/main/macvlan/macvlan_test.go | 5 +++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/plugins/main/bridge/bridge_test.go b/plugins/main/bridge/bridge_test.go index 14fef56b..7c0e8132 100644 --- a/plugins/main/bridge/bridge_test.go +++ b/plugins/main/bridge/bridge_test.go @@ -24,6 +24,8 @@ import ( "github.com/containernetworking/cni/pkg/testutils" "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/utils/hwaddr" + "github.com/vishvananda/netlink" . "github.com/onsi/ginkgo" @@ -179,9 +181,19 @@ var _ = Describe("bridge Operations", func() { Expect(err).NotTo(HaveOccurred()) Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback for _, l := range links { - if l.Attrs().Name != BRNAME && l.Attrs().Name != "lo" { - _, isVeth := l.(*netlink.Veth) - Expect(isVeth).To(Equal(true)) + switch { + case l.Attrs().Name == BRNAME: + { + _, isBridge := l.(*netlink.Bridge) + Expect(isBridge).To(Equal(true)) + hwAddr := fmt.Sprintf("%s", l.Attrs().HardwareAddr) + Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString)) + } + case l.Attrs().Name != BRNAME && l.Attrs().Name != "lo": + { + _, isVeth := l.(*netlink.Veth) + Expect(isVeth).To(Equal(true)) + } } } Expect(err).NotTo(HaveOccurred()) @@ -197,6 +209,9 @@ var _ = Describe("bridge Operations", func() { Expect(err).NotTo(HaveOccurred()) Expect(link.Attrs().Name).To(Equal(IFNAME)) + hwAddr := fmt.Sprintf("%s", link.Attrs().HardwareAddr) + Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString)) + // Ensure the default route routes, err := netlink.RouteList(link, 0) Expect(err).NotTo(HaveOccurred()) diff --git a/plugins/main/macvlan/macvlan_test.go b/plugins/main/macvlan/macvlan_test.go index 90cac2c1..a0a14863 100644 --- a/plugins/main/macvlan/macvlan_test.go +++ b/plugins/main/macvlan/macvlan_test.go @@ -21,6 +21,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/testutils" "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/utils/hwaddr" "github.com/vishvananda/netlink" @@ -139,6 +140,10 @@ var _ = Describe("macvlan Operations", func() { link, err := netlink.LinkByName(IFNAME) Expect(err).NotTo(HaveOccurred()) Expect(link.Attrs().Name).To(Equal(IFNAME)) + + hwAddr := fmt.Sprintf("%s", link.Attrs().HardwareAddr) + Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString)) + return nil }) Expect(err).NotTo(HaveOccurred()) From c0ac3913a129ab6a60f1445edcac271c63ff7d29 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Mon, 1 Aug 2016 16:50:40 -0700 Subject: [PATCH 11/11] pkg/utils/hwaddr tests: cover v4 in v6 addr --- pkg/utils/hwaddr/hwaddr_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/utils/hwaddr/hwaddr_test.go b/pkg/utils/hwaddr/hwaddr_test.go index 51c4e498..b77ccd89 100644 --- a/pkg/utils/hwaddr/hwaddr_test.go +++ b/pkg/utils/hwaddr/hwaddr_test.go @@ -42,6 +42,10 @@ var _ = Describe("Hwaddr", func() { ip: net.ParseIP("172.17.0.2"), expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0xac, 0x11, 0x00, 0x02)), }, + { + ip: net.IPv4(byte(172), byte(17), byte(0), byte(2)), + expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0xac, 0x11, 0x00, 0x02)), + }, } for _, tc := range testCases {