From 86b1836b83dc4a5ebdbc6a57f5a0d756c747651e Mon Sep 17 00:00:00 2001 From: Damyan Yordanov Date: Tue, 17 Sep 2024 14:40:34 +0200 Subject: [PATCH] Add IPv4 tests Add IP address family validator --- plugins/metal/plugin.go | 19 ++++-- plugins/metal/plugin_test.go | 112 +++++++++++++++++++++++++++++++---- plugins/metal/suite_test.go | 1 + 3 files changed, 116 insertions(+), 16 deletions(-) diff --git a/plugins/metal/plugin.go b/plugins/metal/plugin.go index d2663bf..e52f880 100644 --- a/plugins/metal/plugin.go +++ b/plugins/metal/plugin.go @@ -96,7 +96,7 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) { return nil, true } - if err := applyEndpointForMACAddress(mac); err != nil { + if err := applyEndpointForMACAddress(mac, ipamv1alpha1.CIPv6SubnetType); err != nil { log.Errorf("Could not apply endpoint for mac %s: %s", mac.String(), err) return resp, false } @@ -110,7 +110,7 @@ func handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) { mac := req.ClientHWAddr - if err := applyEndpointForMACAddress(mac); err != nil { + if err := applyEndpointForMACAddress(mac, ipamv1alpha1.CIPv4SubnetType); err != nil { log.Errorf("Could not apply peer address: %s", err) return resp, false } @@ -119,14 +119,14 @@ func handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) { return resp, false } -func applyEndpointForMACAddress(mac net.HardwareAddr) error { +func applyEndpointForMACAddress(mac net.HardwareAddr, subnetFamily ipamv1alpha1.SubnetAddressType) error { machineName, ok := machineMap[mac.String()] if !ok { // done here, next plugin return fmt.Errorf("unknown machine MAC address: %s", mac.String()) } - ip, err := GetIPForMACAddress(mac) + ip, err := GetIPForMACAddress(mac, subnetFamily) if err != nil { return fmt.Errorf("could not get IP for MAC address %s: %s", mac.String(), err) } @@ -173,7 +173,7 @@ func ApplyEndpointForMachine(name string, mac net.HardwareAddr, ip *netip.Addr) return nil } -func GetIPForMACAddress(mac net.HardwareAddr) (*netip.Addr, error) { +func GetIPForMACAddress(mac net.HardwareAddr, subnetFamily ipamv1alpha1.SubnetAddressType) (*netip.Addr, error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -189,10 +189,17 @@ func GetIPForMACAddress(mac net.HardwareAddr) (*netip.Addr, error) { sanitizedMAC := strings.Replace(mac.String(), ":", "", -1) for _, ip := range ips.Items { - if ip.Labels["mac"] == sanitizedMAC { + if ip.Labels["mac"] == sanitizedMAC && ipFamilyMatches(ip, subnetFamily) { return &ip.Status.Reserved.Net, nil } } return nil, nil } + +func ipFamilyMatches(ip ipamv1alpha1.IP, subnetFamily ipamv1alpha1.SubnetAddressType) bool { + ipAddr := ip.Status.Reserved.String() + + return strings.Contains(ipAddr, ":") && subnetFamily == "IPv6" || + strings.Contains(ipAddr, ".") && subnetFamily == "IPv4" +} diff --git a/plugins/metal/plugin_test.go b/plugins/metal/plugin_test.go index 90c5ce0..1d046ba 100644 --- a/plugins/metal/plugin_test.go +++ b/plugins/metal/plugin_test.go @@ -3,6 +3,7 @@ package metal import ( "encoding/json" "fmt" + "github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv6" ipamv1alpha1 "github.com/ironcore-dev/ipam/api/ipam/v1alpha1" metalv1alpha1 "github.com/ironcore-dev/metal-operator/api/v1alpha1" @@ -23,7 +24,7 @@ var _ = Describe("Endpoint", func() { ns := SetupTest() BeforeEach(func(ctx SpecContext) { - By("Creating an IPAM IP object") + By("Creating an IPAM IP objects") mac := machineWithIPAddressMACAddress m, err := net.ParseMAC(mac) Expect(err).NotTo(HaveOccurred()) @@ -32,9 +33,9 @@ var _ = Describe("Endpoint", func() { Expect(err).NotTo(HaveOccurred()) sanitizedMAC := strings.Replace(mac, ":", "", -1) - ipaddr, err := ipamv1alpha1.IPAddrFromString(linkLocalIPV6Addr.String()) + ipv6Addr, err := ipamv1alpha1.IPAddrFromString(linkLocalIPV6Addr.String()) Expect(err).NotTo(HaveOccurred()) - ip := &ipamv1alpha1.IP{ + ipv6 := &ipamv1alpha1.IP{ ObjectMeta: metav1.ObjectMeta{ Namespace: ns.Name, GenerateName: "test-", @@ -46,14 +47,39 @@ var _ = Describe("Endpoint", func() { Subnet: corev1.LocalObjectReference{ Name: "foo", }, - IP: ipaddr, + IP: ipv6Addr, }, } - Expect(k8sClient.Create(ctx, ip)).To(Succeed()) - DeferCleanup(k8sClient.Delete, ip) + Expect(k8sClient.Create(ctx, ipv6)).To(Succeed()) + DeferCleanup(k8sClient.Delete, ipv6) - Eventually(UpdateStatus(ip, func() { - ip.Status.Reserved = ipaddr + Eventually(UpdateStatus(ipv6, func() { + ipv6.Status.Reserved = ipv6Addr + })).Should(Succeed()) + + ipv4Addr, err := ipamv1alpha1.IPAddrFromString(privateIPV4Address) + Expect(err).NotTo(HaveOccurred()) + ipv4 := &ipamv1alpha1.IP{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + GenerateName: "test-", + Labels: map[string]string{ + "mac": sanitizedMAC, + }, + }, + Spec: ipamv1alpha1.IPSpec{ + Subnet: corev1.LocalObjectReference{ + Name: "bar", + }, + IP: ipv4Addr, + }, + } + + Expect(k8sClient.Create(ctx, ipv4)).To(Succeed()) + DeferCleanup(k8sClient.Delete, ipv4) + + Eventually(UpdateStatus(ipv4, func() { + ipv4.Status.Reserved = ipv4Addr })).Should(Succeed()) }) @@ -138,7 +164,7 @@ var _ = Describe("Endpoint", func() { It("Should not return an IP address for a known machine without IP address", func(ctx SpecContext) { mac, _ := net.ParseMAC(machineWithoutIPAddressMACAddress) - ip, err := GetIPForMACAddress(mac) + ip, err := GetIPForMACAddress(mac, ipamv1alpha1.CIPv6SubnetType) Eventually(err).Should(BeNil()) Eventually(ip).Should(BeNil()) }) @@ -184,7 +210,7 @@ var _ = Describe("Endpoint", func() { _, _ = handler6(relayedRequest, stub) epName := types.NamespacedName{ - Name: "foo", + Name: machineWithIPAddressName, } endpoint := &metalv1alpha1.Endpoint{} @@ -208,4 +234,70 @@ var _ = Describe("Endpoint", func() { Eventually(resp).Should(BeNil()) Eventually(breakChain).Should(BeTrue()) }) + + /* IPv4 */ + It("Should create an endpoint for IPv4 DHCP request from a known machine with IP address", func(ctx SpecContext) { + mac, _ := net.ParseMAC(machineWithIPAddressMACAddress) + + req, _ := dhcpv4.NewDiscovery(mac) + stub, _ := dhcpv4.NewReplyFromRequest(req) + + _, _ = handler4(req, stub) + + endpoint := &metalv1alpha1.Endpoint{ + ObjectMeta: metav1.ObjectMeta{ + Name: machineWithIPAddressName, + }, + } + + Eventually(Object(endpoint)).Should(SatisfyAll( + HaveField("Spec.MACAddress", machineWithIPAddressMACAddress), + HaveField("Spec.IP", metalv1alpha1.MustParseIP(privateIPV4Address)))) + + DeferCleanup(k8sClient.Delete, endpoint) + }) + + It("Should not create an endpoint for IPv4 DHCP request from a known machine without IP address", func(ctx SpecContext) { + mac, _ := net.ParseMAC(machineWithoutIPAddressMACAddress) + + req, _ := dhcpv4.NewDiscovery(mac) + stub, _ := dhcpv4.NewReplyFromRequest(req) + + _, _ = handler4(req, stub) + + epName := types.NamespacedName{ + Name: machineWithoutIPAddressName, + } + endpoint := &metalv1alpha1.Endpoint{} + + Eventually(func() error { + return k8sClient.Get(ctx, epName, endpoint) + }).ShouldNot(Succeed()) + Eventually(func() bool { + err := k8sClient.Get(ctx, epName, endpoint) + return apierrors.IsNotFound(err) + }).Should(BeTrue()) + }) + + It("Should not create an endpoint for IPv6 DHCP request from a unknown machine", func(ctx SpecContext) { + mac, _ := net.ParseMAC(unknownMachineMACAddress) + + req, _ := dhcpv4.NewDiscovery(mac) + stub, _ := dhcpv4.NewReplyFromRequest(req) + + _, _ = handler4(req, stub) + + epName := types.NamespacedName{ + Name: machineWithIPAddressName, + } + endpoint := &metalv1alpha1.Endpoint{} + + Eventually(func() error { + return k8sClient.Get(ctx, epName, endpoint) + }).ShouldNot(Succeed()) + Eventually(func() bool { + err := k8sClient.Get(ctx, epName, endpoint) + return apierrors.IsNotFound(err) + }).Should(BeTrue()) + }) }) diff --git a/plugins/metal/suite_test.go b/plugins/metal/suite_test.go index baed12a..e5b31ed 100644 --- a/plugins/metal/suite_test.go +++ b/plugins/metal/suite_test.go @@ -40,6 +40,7 @@ const ( machineWithoutIPAddressMACAddress = "47:11:47:11:47:11" unknownMachineMACAddress = "11:11:11:11:11:11" linkLocalIPV6Prefix = "fe80::" + privateIPV4Address = "192.168.47.11" ) var (