Skip to content

Commit

Permalink
masquerade: Don't configure IPv4 on single stack IPv6
Browse files Browse the repository at this point in the history
- iptables/nftables
- k6t-eth0 bridge gateway
- dhcp configutation

TODO - add single stack ipv6 unit tests!!!

Signed-off-by: Alona Kaplan <alkaplan@redhat.com>
  • Loading branch information
AlonaKaplan committed Feb 3, 2022
1 parent c367489 commit 4b9dac3
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 78 deletions.
15 changes: 11 additions & 4 deletions pkg/network/dhcp/masquerade.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,20 @@ func (d *MasqueradeConfigGenerator) Generate() (*cache.DHCPConfig, error) {
dhcpConfig.Subdomain = d.subdomain
dhcpConfig.Mtu = uint16(podNicLink.Attrs().MTU)

ipv4Gateway, ipv4, err := virtnetlink.GenerateMasqueradeGatewayAndVmIPAddrs(d.vmiSpecNetwork, iptables.ProtocolIPv4)
ipv4Enabled, err := d.handler.IsIpv4Enabled(d.podInterfaceName)
if err != nil {
log.Log.Reason(err).Errorf("failed to verify whether ipv4 is configured on %s", d.podInterfaceName)
return nil, err
}
dhcpConfig.IP = *ipv4
dhcpConfig.AdvertisingIPAddr = ipv4Gateway.IP.To4()
dhcpConfig.Gateway = ipv4Gateway.IP.To4()
if ipv4Enabled {
ipv4Gateway, ipv4, err := virtnetlink.GenerateMasqueradeGatewayAndVmIPAddrs(d.vmiSpecNetwork, iptables.ProtocolIPv4)
if err != nil {
return nil, err
}
dhcpConfig.IP = *ipv4
dhcpConfig.AdvertisingIPAddr = ipv4Gateway.IP.To4()
dhcpConfig.Gateway = ipv4Gateway.IP.To4()
}

ipv6Enabled, err := d.handler.IsIpv6Enabled(d.podInterfaceName)
if err != nil {
Expand Down
81 changes: 58 additions & 23 deletions pkg/network/dhcp/masquerade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,10 @@ var _ = Describe("Masquerade DHCP configurator", func() {
expectedIpv6 = "fd10:0:2::2/120"
)

generateExpectedConfigIPv6Disabled := func(vmiSpecNetwork *v1.Network, macString *string, mtu int, ifaceName string, subdomain string) cache.DHCPConfig {
ipv4, _ := netlink.ParseAddr(expectedIpv4)
ipv4Gateway, _ := netlink.ParseAddr(expectedIpv4Gateway)

generateExpectedConfig := func(vmiSpecNetwork *v1.Network, macString *string, mtu int, ifaceName string, subdomain string) cache.DHCPConfig {
expectedConfig := cache.DHCPConfig{Name: ifaceName,
IP: *ipv4,
Mtu: uint16(mtu),
AdvertisingIPAddr: ipv4Gateway.IP.To4(),
Gateway: ipv4Gateway.IP.To4(),
Subdomain: subdomain,
Mtu: uint16(mtu),
Subdomain: subdomain,
}

if macString != nil {
Expand All @@ -85,8 +79,20 @@ var _ = Describe("Masquerade DHCP configurator", func() {
return expectedConfig
}

generateExpectedConfigIPv6Enabled := func(vmiSpecNetwork *v1.Network, macString *string, mtu int, ifaceName string, subdomain string) cache.DHCPConfig {
expectedConfig := generateExpectedConfigIPv6Disabled(vmiSpecNetwork, macString, mtu, ifaceName, subdomain)
generateExpectedConfigOnlyIPv4Enabled := func(vmiSpecNetwork *v1.Network, macString *string, mtu int, ifaceName string, subdomain string) cache.DHCPConfig {
expectedConfig := generateExpectedConfig(vmiSpecNetwork, macString, mtu, ifaceName, subdomain)
ipv4, _ := netlink.ParseAddr(expectedIpv4)
ipv4Gateway, _ := netlink.ParseAddr(expectedIpv4Gateway)

expectedConfig.IP = *ipv4
expectedConfig.AdvertisingIPAddr = ipv4Gateway.IP.To4()
expectedConfig.Gateway = ipv4Gateway.IP.To4()

return expectedConfig
}

generateExpectedConfigOnlyIPv6Enabled := func(vmiSpecNetwork *v1.Network, macString *string, mtu int, ifaceName string, subdomain string) cache.DHCPConfig {
expectedConfig := generateExpectedConfig(vmiSpecNetwork, macString, mtu, ifaceName, subdomain)
ipv6, _ := netlink.ParseAddr(expectedIpv6)
ipv6Gateway, _ := netlink.ParseAddr(expectedIpv6Gateway)

Expand All @@ -96,6 +102,16 @@ var _ = Describe("Masquerade DHCP configurator", func() {
return expectedConfig
}

generateExpectedConfigOnlyIPv4AndIPv6Enabled := func(vmiSpecNetwork *v1.Network, macString *string, mtu int, ifaceName string, subdomain string) cache.DHCPConfig {
expectedConfig := generateExpectedConfigOnlyIPv4Enabled(vmiSpecNetwork, macString, mtu, ifaceName, subdomain)
ipv6ExpectedConfig := generateExpectedConfigOnlyIPv6Enabled(vmiSpecNetwork, macString, mtu, ifaceName, subdomain)

expectedConfig.IPv6 = ipv6ExpectedConfig.IPv6
expectedConfig.AdvertisingIPv6Addr = ipv6ExpectedConfig.AdvertisingIPv6Addr

return expectedConfig
}

BeforeEach(func() {
vmiSpecNetwork = v1.DefaultPodNetwork()
vmiSpecIface = &v1.Interface{Name: "default", InterfaceBindingMethod: v1.InterfaceBindingMethod{Masquerade: &v1.InterfaceMasquerade{}}}
Expand All @@ -117,34 +133,53 @@ var _ = Describe("Masquerade DHCP configurator", func() {
mockHandler.EXPECT().LinkByName(ifaceName).Return(iface, nil)
})

When("IPv6 is enabled", func() {
When("Only Ipv4 is enabled", func() {
BeforeEach(func() {
mockHandler.EXPECT().IsIpv4Enabled(ifaceName).Return(true, nil)
mockHandler.EXPECT().IsIpv6Enabled(ifaceName).Return(false, nil)
})
It("Should return the dhcp configuration with IPv4 only", func() {
config, err := generator.Generate()
Expect(err).ToNot(HaveOccurred())
Expect(*config).To(Equal(generateExpectedConfigOnlyIPv4Enabled(vmiSpecNetwork, nil, mtu, ifaceName, subdomain)))
})
})

When("Only IPv6 is enabled", func() {
BeforeEach(func() {
mockHandler.EXPECT().IsIpv4Enabled(ifaceName).Return(false, nil)
mockHandler.EXPECT().IsIpv6Enabled(ifaceName).Return(true, nil)
})
It("Should return the dhcp configuration", func() {
It("Should return the dhcp configuration with IPv6 only", func() {
config, err := generator.Generate()
Expect(err).ToNot(HaveOccurred())
Expect(*config).To(Equal(generateExpectedConfigIPv6Enabled(vmiSpecNetwork, nil, mtu, ifaceName, subdomain)))
Expect(*config).To(Equal(generateExpectedConfigOnlyIPv6Enabled(vmiSpecNetwork, nil, mtu, ifaceName, subdomain)))
})
})

When("IPv6 is disabled", func() {
When("Both Ipv4 and IPv6 are enabled", func() {
BeforeEach(func() {
mockHandler.EXPECT().IsIpv6Enabled(ifaceName).Return(false, nil)
mockHandler.EXPECT().IsIpv4Enabled(ifaceName).Return(true, nil)
mockHandler.EXPECT().IsIpv6Enabled(ifaceName).Return(true, nil)
})
It("Should return the dhcp configuration without IPv6", func() {
It("Should return the dhcp configuration with both IPv4 and IPv6", func() {
config, err := generator.Generate()
Expect(err).ToNot(HaveOccurred())
Expect(*config).To(Equal(generateExpectedConfigIPv6Disabled(vmiSpecNetwork, nil, mtu, ifaceName, subdomain)))
Expect(*config).To(Equal(generateExpectedConfigOnlyIPv4AndIPv6Enabled(vmiSpecNetwork, nil, mtu, ifaceName, subdomain)))
})
})

It("Should return an error if the config discovering fails", func() {
vmiSpecNetwork.Pod.VMNetworkCIDR = "abc"
When("Config discovering fails", func() {
BeforeEach(func() {
mockHandler.EXPECT().IsIpv4Enabled(ifaceName).Return(true, nil)
})
It("Should return an error", func() {
vmiSpecNetwork.Pod.VMNetworkCIDR = "abc"

config, err := generator.Generate()
Expect(err).To(HaveOccurred())
Expect(config).To(BeNil())
config, err := generator.Generate()
Expect(err).To(HaveOccurred())
Expect(config).To(BeNil())
})
})
})
})
19 changes: 19 additions & 0 deletions pkg/network/driver/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type NetworkHandler interface {
LinkSetMaster(link netlink.Link, master *netlink.Bridge) error
StartDHCP(nic *cache.DHCPConfig, bridgeInterfaceName string, dhcpOptions *v1.DHCPOptions) error
HasNatIptables(proto iptables.Protocol) bool
IsIpv4Enabled(interfaceName string) (bool, error)
IsIpv6Enabled(interfaceName string) (bool, error)
IsIpv4Primary() (bool, error)
ConfigureIpForwarding(proto iptables.Protocol) error
Expand Down Expand Up @@ -161,6 +162,24 @@ func (h *NetworkUtilsHandler) ConfigureIpForwarding(proto iptables.Protocol) err
return err
}

func (h *NetworkUtilsHandler) IsIpv4Enabled(interfaceName string) (bool, error) {
link, err := h.LinkByName(interfaceName)
if err != nil {
return false, err
}
addrList, err := h.AddrList(link, netlink.FAMILY_V4)
if err != nil {
return false, err
}

for _, addr := range addrList {
if addr.IP.IsGlobalUnicast() {
return true, nil
}
}
return false, nil
}

func (h *NetworkUtilsHandler) IsIpv6Enabled(interfaceName string) (bool, error) {
link, err := h.LinkByName(interfaceName)
if err != nil {
Expand Down
11 changes: 11 additions & 0 deletions pkg/network/driver/generated_mock_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,17 @@ func (_mr *_MockNetworkHandlerRecorder) HasNatIptables(arg0 interface{}) *gomock
return _mr.mock.ctrl.RecordCall(_mr.mock, "HasNatIptables", arg0)
}

func (_m *MockNetworkHandler) IsIpv4Enabled(interfaceName string) (bool, error) {
ret := _m.ctrl.Call(_m, "IsIpv4Enabled", interfaceName)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}

func (_mr *_MockNetworkHandlerRecorder) IsIpv4Enabled(arg0 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCall(_mr.mock, "IsIpv4Enabled", arg0)
}

func (_m *MockNetworkHandler) IsIpv6Enabled(interfaceName string) (bool, error) {
ret := _m.ctrl.Call(_m, "IsIpv6Enabled", interfaceName)
ret0, _ := ret[0].(bool)
Expand Down
46 changes: 29 additions & 17 deletions pkg/network/infraconfigurators/masquerade.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
)

const (
ipVerifyFailFmt = "failed to verify whether ipv6 is configured on %s"
ipVerifyFailFmt = "failed to verify whether ipv%s is configured on %s"
toDest = "--to-destination"
src = "--source"
dport = "--dport"
Expand Down Expand Up @@ -63,17 +63,24 @@ func (b *MasqueradePodNetworkConfigurator) DiscoverPodNetworkInterface(podIfaceN
}
b.podNicLink = link

if err := b.computeIPv4GatewayAndVmIp(); err != nil {
ipv4Enabled, err := b.handler.IsIpv4Enabled(podIfaceName)
if err != nil {
log.Log.Reason(err).Errorf(ipVerifyFailFmt, "4", podIfaceName)
return err
}
if ipv4Enabled {
if err := b.computeIPv4GatewayAndVmIp(); err != nil {
return err
}
}

ipv6Enabled, err := b.handler.IsIpv6Enabled(podIfaceName)
if err != nil {
log.Log.Reason(err).Errorf(ipVerifyFailFmt, podIfaceName)
log.Log.Reason(err).Errorf(ipVerifyFailFmt, "6", podIfaceName)
return err
}
if ipv6Enabled {
if err := b.discoverIPv6GatewayAndVmIp(); err != nil {
if err := b.computeIPv6GatewayAndVmIp(); err != nil {
return err
}
}
Expand All @@ -92,7 +99,7 @@ func (b *MasqueradePodNetworkConfigurator) computeIPv4GatewayAndVmIp() error {
return nil
}

func (b *MasqueradePodNetworkConfigurator) discoverIPv6GatewayAndVmIp() error {
func (b *MasqueradePodNetworkConfigurator) computeIPv6GatewayAndVmIp() error {
ipv6Gateway, ipv6, err := virtnetlink.GenerateMasqueradeGatewayAndVmIPAddrs(b.vmiSpecNetwork, iptables.ProtocolIPv6)
if err != nil {
return err
Expand Down Expand Up @@ -122,15 +129,22 @@ func (b *MasqueradePodNetworkConfigurator) PreparePodNetworkInterface() error {
return err
}

err = b.createNatRules(iptables.ProtocolIPv4)
ipv4Enabled, err := b.handler.IsIpv4Enabled(b.podNicLink.Attrs().Name)
if err != nil {
log.Log.Reason(err).Errorf("failed to create ipv4 nat rules for vm error: %v", err)
log.Log.Reason(err).Errorf(ipVerifyFailFmt, "4", b.podNicLink.Attrs().Name)
return err
}
if ipv4Enabled {
err = b.createNatRules(iptables.ProtocolIPv4)
if err != nil {
log.Log.Reason(err).Errorf("failed to create ipv4 nat rules for vm error: %v", err)
return err
}
}

ipv6Enabled, err := b.handler.IsIpv6Enabled(b.podNicLink.Attrs().Name)
if err != nil {
log.Log.Reason(err).Errorf(ipVerifyFailFmt, b.podNicLink.Attrs().Name)
log.Log.Reason(err).Errorf(ipVerifyFailFmt, "6", b.podNicLink.Attrs().Name)
return err
}
if ipv6Enabled {
Expand Down Expand Up @@ -172,16 +186,14 @@ func (b *MasqueradePodNetworkConfigurator) createBridge() error {
return err
}

if err := b.handler.AddrAdd(bridge, b.vmGatewayAddr); err != nil {
log.Log.Reason(err).Errorf("failed to set bridge IP")
return err
}
ipv6Enabled, err := b.handler.IsIpv6Enabled(b.podNicLink.Attrs().Name)
if err != nil {
log.Log.Reason(err).Errorf(ipVerifyFailFmt, b.podNicLink.Attrs().Name)
return err
if b.vmGatewayAddr != nil {
if err := b.handler.AddrAdd(bridge, b.vmGatewayAddr); err != nil {
log.Log.Reason(err).Errorf("failed to set bridge IP")
return err
}
}
if ipv6Enabled {

if b.vmGatewayIpv6Addr != nil {
if err := b.handler.AddrAdd(bridge, b.vmGatewayIpv6Addr); err != nil {
log.Log.Reason(err).Errorf("failed to set bridge IPv6")
return err
Expand Down

0 comments on commit 4b9dac3

Please sign in to comment.