From 950e689637b8094800a6989b7aa9efd012166c4a Mon Sep 17 00:00:00 2001 From: Minhan Xia Date: Fri, 19 Aug 2016 10:18:00 -0700 Subject: [PATCH] kubenet set cbr0 mac address --- pkg/kubelet/network/kubenet/kubenet_linux.go | 45 +++++++++++++++++++ .../network/kubenet/kubenet_linux_test.go | 31 +++++++++++++ 2 files changed, 76 insertions(+) diff --git a/pkg/kubelet/network/kubenet/kubenet_linux.go b/pkg/kubelet/network/kubenet/kubenet_linux.go index 1385be099f6..ab2314792ac 100644 --- a/pkg/kubelet/network/kubenet/kubenet_linux.go +++ b/pkg/kubelet/network/kubenet/kubenet_linux.go @@ -45,6 +45,7 @@ import ( utilsysctl "k8s.io/kubernetes/pkg/util/sysctl" "k8s.io/kubernetes/pkg/kubelet/network/hostport" + "strconv" ) const ( @@ -56,6 +57,9 @@ const ( // fallbackMTU is used if an MTU is not specified, and we cannot determine the MTU fallbackMTU = 1460 + + // private mac prefix safe to use + privateMACPrefix = "0a:58" ) type kubenetNetworkPlugin struct { @@ -79,6 +83,8 @@ type kubenetNetworkPlugin struct { // kubenet will search for cni binaries in DefaultCNIDir first, then continue to vendorDir. vendorDir string nonMasqueradeCIDR string + podCidr string + gateway net.IP } func NewPlugin(networkPluginDir string) network.NetworkPlugin { @@ -243,6 +249,8 @@ func (plugin *kubenetNetworkPlugin) Event(name string, details map[string]interf // plugin will bail out if the bridge has an unexpected one plugin.clearBridgeAddressesExcept(cidr) } + plugin.podCidr = podCIDR + plugin.gateway = cidr.IP } if err != nil { @@ -327,6 +335,23 @@ func (plugin *kubenetNetworkPlugin) setup(namespace string, name string, id kube return fmt.Errorf("CNI plugin reported an invalid IPv4 address for container %v: %+v.", id, res.IP4) } + + // Explicitly assign mac address to cbr0. If bridge mac address is not explicitly set will adopt the lowest MAC address of the attached veths. + // TODO: Remove this once upstream cni bridge plugin handles this + link, err := netlink.LinkByName(BridgeName) + if err != nil { + return fmt.Errorf("failed to lookup %q: %v", BridgeName, err) + } + macAddr, err := generateHardwareAddr(plugin.gateway) + if err != nil { + return err + } + glog.V(3).Infof("Configure %q mac address to %v", BridgeName, macAddr) + err = netlink.LinkSetHardwareAddr(link, macAddr) + if err != nil { + return fmt.Errorf("Failed to configure %q mac address to %q: %v", BridgeName, macAddr, err) + } + // Put the container bridge into promiscuous mode to force it to accept hairpin packets. // TODO: Remove this once the kernel bug (#20096) is fixed. // TODO: check and set promiscuous mode with netlink once vishvananda/netlink supports it @@ -338,6 +363,8 @@ func (plugin *kubenetNetworkPlugin) setup(namespace string, name string, id kube return fmt.Errorf("Error setting promiscuous mode on %s: %v", BridgeName, err) } } + + } // The first SetUpPod call creates the bridge; get a shaper for the sake of @@ -583,3 +610,21 @@ func (plugin *kubenetNetworkPlugin) shaper() bandwidth.BandwidthShaper { } return plugin.bandwidthShaper } + +// 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 +} \ No newline at end of file diff --git a/pkg/kubelet/network/kubenet/kubenet_linux_test.go b/pkg/kubelet/network/kubenet/kubenet_linux_test.go index 72fe194bb7d..e3d129b06ce 100644 --- a/pkg/kubelet/network/kubenet/kubenet_linux_test.go +++ b/pkg/kubelet/network/kubenet/kubenet_linux_test.go @@ -18,6 +18,7 @@ package kubenet import ( "fmt" + "net" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -198,4 +199,34 @@ func TestInit_MTU(t *testing.T) { assert.Equal(t, 1, sysctl.Settings["net/bridge/bridge-nf-call-iptables"], "net/bridge/bridge-nf-call-iptables sysctl should have been set") } +func TestGenerateMacAddress(t *testing.T) { + 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) + if err != nil { + t.Errorf("Did not expect error: %v", err) + } + if mac.String() != tc.expectedMAC { + t.Errorf("generated mac: %q, expecting: %q", mac.String(), tc.expectedMAC) + } + } +} + //TODO: add unit test for each implementation of network plugin interface