Skip to content

Commit

Permalink
ipv6 for manual networking on ubuntu
Browse files Browse the repository at this point in the history
Signed-off-by: dmitriy kalinin <[email protected]>
  • Loading branch information
Brian Cunnie authored and pivotal committed Nov 9, 2017
1 parent 7abe8b9 commit 91acda4
Show file tree
Hide file tree
Showing 17 changed files with 819 additions and 213 deletions.
3 changes: 2 additions & 1 deletion agent/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -830,8 +830,9 @@ func init() {
interfaceAddrsProvider = &fakeip.FakeInterfaceAddressesProvider{}
interfaceAddressesValidator := boship.NewInterfaceAddressesValidator(interfaceAddrsProvider)
dnsValidator := boshnet.NewDNSValidator(fs)
kernelIPv6 := boshnet.NewKernelIPv6Impl(fs, runner, logger)
fs.WriteFileString("/etc/resolv.conf", "8.8.8.8 4.4.4.4")
ubuntuNetManager := boshnet.NewUbuntuNetManager(fs, runner, ipResolver, interfaceConfigurationCreator, interfaceAddressesValidator, dnsValidator, arping, logger)
ubuntuNetManager := boshnet.NewUbuntuNetManager(fs, runner, ipResolver, interfaceConfigurationCreator, interfaceAddressesValidator, dnsValidator, arping, kernelIPv6, logger)

ubuntuCertManager := boshcert.NewUbuntuCertManager(fs, runner, 1, logger)

Expand Down
9 changes: 9 additions & 0 deletions platform/net/dns_validator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net

import (
gonet "net"
"strings"

bosherr "github.com/cloudfoundry/bosh-utils/errors"
Expand Down Expand Up @@ -35,6 +36,14 @@ func (d *dnsValidator) Validate(dnsServers []string) error {
if strings.Contains(resolvConfContents, dnsServer) {
return nil
}

canonicalIP := gonet.ParseIP(dnsServer)

if canonicalIP != nil {
if strings.Contains(resolvConfContents, canonicalIP.String()) {
return nil
}
}
}

return bosherr.WrapError(err, "None of the DNS servers that were specified in the manifest were found in /etc/resolv.conf.")
Expand Down
11 changes: 11 additions & 0 deletions platform/net/fakes/fake_kernel_ipv6.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package fakes

type FakeKernelIPv6 struct {
Enabled bool
EnableErr error
}

func (net *FakeKernelIPv6) Enable(stopCh <-chan struct{}) error {
net.Enabled = true
return net.EnableErr
}
66 changes: 62 additions & 4 deletions platform/net/interface_configuration_creator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package net

import (
"net"
"strconv"

boshsettings "github.com/cloudfoundry/bosh-agent/settings"
bosherr "github.com/cloudfoundry/bosh-utils/errors"
boshlog "github.com/cloudfoundry/bosh-utils/logger"
Expand All @@ -18,6 +21,25 @@ type StaticInterfaceConfiguration struct {
Gateway string
}

func (c StaticInterfaceConfiguration) Version6() string {
if c.IsVersion6() {
return "6"
}
return ""
}

func (c StaticInterfaceConfiguration) IsVersion6() bool {
return len(c.Network) == 0 && len(c.Broadcast) == 0
}

func (c StaticInterfaceConfiguration) NetmaskOrLen() string {
if c.IsVersion6() {
ones, _ := net.IPMask(net.ParseIP(c.Netmask)).Size()
return strconv.Itoa(ones)
}
return c.Netmask
}

type StaticInterfaceConfigurations []StaticInterfaceConfiguration

func (configs StaticInterfaceConfigurations) Len() int {
Expand All @@ -32,8 +54,33 @@ func (configs StaticInterfaceConfigurations) Swap(i, j int) {
configs[i], configs[j] = configs[j], configs[i]
}

func (configs StaticInterfaceConfigurations) HasVersion6() bool {
for _, config := range configs {
if config.IsVersion6() {
return true
}
}
return false
}

type DHCPInterfaceConfiguration struct {
Name string
Name string
Address string
}

func (c DHCPInterfaceConfiguration) Version6() string {
if c.IsVersion6() {
return "6"
}
return ""
}

func (c DHCPInterfaceConfiguration) IsVersion6() bool {
ip := net.ParseIP(c.Address)
if ip == nil || ip.To4() != nil {
return false
}
return true
}

type DHCPInterfaceConfigurations []DHCPInterfaceConfiguration
Expand All @@ -50,6 +97,15 @@ func (configs DHCPInterfaceConfigurations) Swap(i, j int) {
configs[i], configs[j] = configs[j], configs[i]
}

func (configs DHCPInterfaceConfigurations) HasVersion6() bool {
for _, config := range configs {
if len(config.Version6()) > 0 {
return true
}
}
return false
}

type InterfaceConfigurationCreator interface {
CreateInterfaceConfigurations(boshsettings.Networks, map[string]string) ([]StaticInterfaceConfiguration, []DHCPInterfaceConfiguration, error)
}
Expand All @@ -72,7 +128,8 @@ func (creator interfaceConfigurationCreator) createInterfaceConfiguration(static
if networkSettings.IsDHCP() || networkSettings.Mac == "" {
creator.logger.Debug(creator.logTag, "Using dhcp networking")
dhcpConfigs = append(dhcpConfigs, DHCPInterfaceConfiguration{
Name: ifaceName,
Name: ifaceName,
Address: networkSettings.IP,
})
} else {
creator.logger.Debug(creator.logTag, "Using static networking")
Expand All @@ -81,7 +138,7 @@ func (creator interfaceConfigurationCreator) createInterfaceConfiguration(static
return nil, nil, bosherr.WrapError(err, "Calculating Network and Broadcast")
}

staticConfigs = append(staticConfigs, StaticInterfaceConfiguration{
conf := StaticInterfaceConfiguration{
Name: ifaceName,
Address: networkSettings.IP,
Netmask: networkSettings.Netmask,
Expand All @@ -90,7 +147,8 @@ func (creator interfaceConfigurationCreator) createInterfaceConfiguration(static
Broadcast: broadcastAddress,
Mac: networkSettings.Mac,
Gateway: networkSettings.Gateway,
})
}
staticConfigs = append(staticConfigs, conf)
}
return staticConfigs, dhcpConfigs, nil
}
Expand Down
96 changes: 94 additions & 2 deletions platform/net/interface_configuration_creator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,100 @@ func describeInterfaceConfigurationCreator() {
}

_, _, err := interfaceConfigurationCreator.CreateInterfaceConfigurations(boshsettings.Networks{"foo": invalidNetwork}, interfacesByMAC)

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("Creating interface configuration: Calculating Network and Broadcast:"))
Expect(err.Error()).To(ContainSubstring("Invalid IP 'not an ip'"))
})
}

var _ = Describe("StaticInterfaceConfiguration", func() {
Describe("Version6", func() {
It("returns '6' when network and broadcast are empty", func() {
Expect(StaticInterfaceConfiguration{}.Version6()).To(Equal("6"))
})

It("returns '' when network and/or broadcast are not empty", func() {
Expect(StaticInterfaceConfiguration{Network: "network"}.Version6()).To(Equal(""))
Expect(StaticInterfaceConfiguration{Broadcast: "broadcast"}.Version6()).To(Equal(""))
})
})

Describe("IsVersion6", func() {
It("returns '6' when network and broadcast are empty", func() {
Expect(StaticInterfaceConfiguration{}.IsVersion6()).To(BeTrue())
})

It("returns '' when network and/or broadcast are not empty", func() {
Expect(StaticInterfaceConfiguration{Network: "network"}.IsVersion6()).To(BeFalse())
Expect(StaticInterfaceConfiguration{Broadcast: "broadcast"}.IsVersion6()).To(BeFalse())
})
})

Describe("NetmaskOrLen", func() {
It("returns number of ones in IPv6 netmask when network and broadcast are empty", func() {
Expect(StaticInterfaceConfiguration{Netmask: "ffff:ffff:ff00::"}.NetmaskOrLen()).To(Equal("40"))
})

It("returns provided netmask when network and/or broadcast are not empty", func() {
Expect(StaticInterfaceConfiguration{Netmask: "netmask", Network: "network"}.NetmaskOrLen()).To(Equal("netmask"))
Expect(StaticInterfaceConfiguration{Netmask: "netmask", Broadcast: "broadcast"}.NetmaskOrLen()).To(Equal("netmask"))
})
})
})

var _ = Describe("StaticInterfaceConfigurations", func() {
Describe("HasVersion6", func() {
It("returns true if there is at least one IPv6 static config", func() {
Expect(StaticInterfaceConfigurations{}.HasVersion6()).To(BeFalse())

Expect(StaticInterfaceConfigurations{
StaticInterfaceConfiguration{Network: "network"},
StaticInterfaceConfiguration{},
}.HasVersion6()).To(BeTrue())

Expect(StaticInterfaceConfigurations{
StaticInterfaceConfiguration{Network: "network"},
}.HasVersion6()).To(BeFalse())
})
})
})

var _ = Describe("DHCPInterfaceConfiguration", func() {
Describe("Version6", func() {
It("returns '6' when address is not IPv4 and not empty", func() {
Expect(DHCPInterfaceConfiguration{Address: "ff00::"}.Version6()).To(Equal("6"))
})

It("returns '' when address is empty or IPv4", func() {
Expect(DHCPInterfaceConfiguration{}.Version6()).To(Equal(""))
Expect(DHCPInterfaceConfiguration{Address: "1.2.3.4"}.Version6()).To(Equal(""))
})
})

Describe("IsVersion6", func() {
It("returns '6' when address is not IPv4 and not empty", func() {
Expect(DHCPInterfaceConfiguration{Address: "ff00::"}.IsVersion6()).To(BeTrue())
})

It("returns '' when address is empty or IPv4", func() {
Expect(DHCPInterfaceConfiguration{}.IsVersion6()).To(BeFalse())
Expect(DHCPInterfaceConfiguration{Address: "1.2.3.4"}.IsVersion6()).To(BeFalse())
})
})
})

var _ = Describe("DHCPInterfaceConfigurations", func() {
Describe("HasVersion6", func() {
It("returns true if there is at least one IPv6 DHCP config", func() {
Expect(DHCPInterfaceConfigurations{}.HasVersion6()).To(BeFalse())

Expect(DHCPInterfaceConfigurations{
DHCPInterfaceConfiguration{Address: "ff00::"},
DHCPInterfaceConfiguration{},
}.HasVersion6()).To(BeTrue())

Expect(DHCPInterfaceConfigurations{
DHCPInterfaceConfiguration{Address: "1.2.3.4"},
}.HasVersion6()).To(BeFalse())
})
})
})
25 changes: 23 additions & 2 deletions platform/net/ip/interface_address.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ip

import (
"fmt"
"net"

bosherr "github.com/cloudfoundry/bosh-utils/errors"
)

Expand All @@ -20,7 +23,15 @@ func NewSimpleInterfaceAddress(interfaceName string, ip string) InterfaceAddress
}

func (s simpleInterfaceAddress) GetInterfaceName() string { return s.interfaceName }
func (s simpleInterfaceAddress) GetIP() (string, error) { return s.ip, nil }

func (s simpleInterfaceAddress) GetIP() (string, error) {
ip2 := net.ParseIP(s.ip)
if ip2 == nil {
return "", fmt.Errorf("Cannot parse IP '%s'", s.ip)
}

return fmtIP(ip2), nil
}

type resolvingInterfaceAddress struct {
interfaceName string
Expand Down Expand Up @@ -50,7 +61,17 @@ func (s *resolvingInterfaceAddress) GetIP() (string, error) {
return "", bosherr.WrapError(err, "Getting primary IPv4")
}

s.ip = ip.IP.String()
s.ip = fmtIP(ip.IP)

return s.ip, nil
}

func fmtIP(ip net.IP) string {
if p4 := ip.To4(); len(p4) == net.IPv4len {
return ip.String()
}

return fmt.Sprintf("%x:%x:%x:%x:%x:%x:%x:%x",
[]byte(ip[0:2]), []byte(ip[2:4]), []byte(ip[4:6]), []byte(ip[6:8]),
[]byte(ip[8:10]), []byte(ip[10:12]), []byte(ip[12:14]), []byte(ip[14:16]))
}
41 changes: 40 additions & 1 deletion platform/net/ip/interface_address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,32 @@ import (
fakeip "github.com/cloudfoundry/bosh-agent/platform/net/ip/fakes"
)

var _ = Describe("simpleInterfaceAddress", func() {
Describe("GetIP", func() {
It("returns fully formatted IPv4", func() {
ipStr, err := NewSimpleInterfaceAddress("iface", "127.0.0.1").GetIP()
Expect(err).ToNot(HaveOccurred())
Expect(ipStr).To(Equal("127.0.0.1"))
})

It("returns fully formatted IPv6", func() {
ipStr, err := NewSimpleInterfaceAddress("iface", "ff00:f8::").GetIP()
Expect(err).ToNot(HaveOccurred())
Expect(ipStr).To(Equal("ff00:00f8:0000:0000:0000:0000:0000:0000"))

ipStr, err = NewSimpleInterfaceAddress("iface", "1101:2202:3303:4404:5505:6606:7707:8808").GetIP()
Expect(err).ToNot(HaveOccurred())
Expect(ipStr).To(Equal("1101:2202:3303:4404:5505:6606:7707:8808"))
})

It("returns error if IP cannot be parsed", func() {
_, err := NewSimpleInterfaceAddress("iface", "").GetIP()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("Cannot parse IP ''"))
})
})
})

var _ = Describe("resolvingInterfaceAddress", func() {
var (
ipResolver *fakeip.FakeResolver
Expand All @@ -31,14 +57,27 @@ var _ = Describe("resolvingInterfaceAddress", func() {
}
})

It("resolves the IP", func() {
It("resolves the IP and returns fully formatted IPv4", func() {
ip, err := interfaceAddress.GetIP()
Expect(err).ToNot(HaveOccurred())
Expect(ip).To(Equal("127.0.0.1"))

Expect(ipResolver.GetPrimaryIPv4InterfaceName).To(Equal("fake-iface-name"))
})

It("resolves the IP and returns fully formatted IPv6", func() {
ipResolver.GetPrimaryIPv4IPNet = &gonet.IPNet{
IP: gonet.ParseIP("ff00:f8::"),
Mask: gonet.CIDRMask(64, 128),
}

ip, err := interfaceAddress.GetIP()
Expect(err).ToNot(HaveOccurred())
Expect(ip).To(Equal("ff00:00f8:0000:0000:0000:0000:0000:0000"))

Expect(ipResolver.GetPrimaryIPv4InterfaceName).To(Equal("fake-iface-name"))
})

It("returns error if resolving IP fails", func() {
ipResolver.GetPrimaryIPv4Err = errors.New("fake-get-primary-ipv4-err")

Expand Down
5 changes: 2 additions & 3 deletions platform/net/ip/interface_addresses_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,10 @@ func (s *systemInterfaceAddrs) Get() ([]InterfaceAddress, error) {
return []InterfaceAddress{}, bosherr.WrapErrorf(err, "Parsing addresses of interface '%s'", iface.Name)
}

if ipv4 := ip.To4(); ipv4 != nil {
interfaceAddrs = append(interfaceAddrs, NewSimpleInterfaceAddress(iface.Name, ipv4.String()))
if ip.To4() != nil || ip.IsGlobalUnicast() {
interfaceAddrs = append(interfaceAddrs, NewSimpleInterfaceAddress(iface.Name, ip.String()))
}
}

}

return interfaceAddrs, nil
Expand Down
Loading

0 comments on commit 91acda4

Please sign in to comment.