diff --git a/internal/helper/helper.go b/internal/helper/helper.go index c95796d..a6c90f5 100644 --- a/internal/helper/helper.go +++ b/internal/helper/helper.go @@ -10,8 +10,11 @@ import ( "net" "time" + "github.com/insomniacslk/dhcp/dhcpv6" + "github.com/insomniacslk/dhcp/iana" "github.com/ironcore-dev/fedhcp/internal/kubernetes" ipamv1alpha1 "github.com/ironcore-dev/ipam/api/ipam/v1alpha1" + "github.com/mdlayher/netx/eui64" "github.com/sirupsen/logrus" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/util/wait" @@ -89,3 +92,19 @@ func CheckIPInCIDR(ip net.IP, cidrStr string, log *logrus.Entry) bool { // Check if the CIDR contains the IP return cidrNet.Contains(ip) } + +func GetMAC(relayMsg *dhcpv6.RelayMessage, log *logrus.Entry) (net.HardwareAddr, error) { + hwType, mac := relayMsg.Options.ClientLinkLayerAddress() + if hwType == iana.HWTypeEthernet { + return mac, nil + } + + log.Infof("failed to retrieve client link layer address, falling back to EUI64 (%s)", relayMsg.PeerAddr.String()) + _, mac, err := eui64.ParseIP(relayMsg.PeerAddr) + if err != nil { + log.Errorf("Could not parse peer address: %s", err) + return nil, err + } + + return mac, nil +} diff --git a/plugins/metal/plugin.go b/plugins/metal/plugin.go index 6b037b4..50ba96b 100644 --- a/plugins/metal/plugin.go +++ b/plugins/metal/plugin.go @@ -11,6 +11,7 @@ import ( "os" "strings" + "github.com/ironcore-dev/fedhcp/internal/helper" "github.com/ironcore-dev/fedhcp/internal/printer" "k8s.io/apimachinery/pkg/api/errors" @@ -29,7 +30,6 @@ import ( "github.com/ironcore-dev/fedhcp/internal/kubernetes" ipamv1alpha1 "github.com/ironcore-dev/ipam/api/ipam/v1alpha1" metalv1alpha1 "github.com/ironcore-dev/metal-operator/api/v1alpha1" - "github.com/mdlayher/netx/eui64" "gopkg.in/yaml.v3" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -159,9 +159,9 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) { } relayMsg := req.(*dhcpv6.RelayMessage) - _, mac, err := eui64.ParseIP(relayMsg.PeerAddr) + mac, err := helper.GetMAC(relayMsg, log) if err != nil { - log.Errorf("Could not parse peer address %s: %s", relayMsg.PeerAddr.String(), err) + log.Errorf("Failed to obtain MAC, dropping.") return nil, true } diff --git a/plugins/metal/plugin_test.go b/plugins/metal/plugin_test.go index fb7b9a0..3db7335 100644 --- a/plugins/metal/plugin_test.go +++ b/plugins/metal/plugin_test.go @@ -8,6 +8,7 @@ import ( "os" "strings" + "github.com/insomniacslk/dhcp/iana" "github.com/ironcore-dev/fedhcp/internal/api" "gopkg.in/yaml.v2" @@ -29,7 +30,7 @@ var _ = Describe("Endpoint", func() { ns := SetupTest() BeforeEach(func(ctx SpecContext) { - By("Creating an IPAM IP objects") + By("Creating IPAM IP objects") mac := machineWithIPAddressMACAddress m, err := net.ParseMAC(mac) Expect(err).NotTo(HaveOccurred()) @@ -356,6 +357,35 @@ var _ = Describe("Endpoint", func() { Eventually(Get(endpoint)).Should(Satisfy(apierrors.IsNotFound)) }) + It("Should create an endpoint for IPv6 DHCP request from a unknown machine, if ClientLinkLayer is set to allowed MAC (RFC6939) ", func(ctx SpecContext) { + mac, _ := net.ParseMAC(unknownMachineMACAddress) + ip := net.ParseIP(linkLocalIPV6Prefix) + linkLocalIPV6Addr, _ := eui64.ParseMAC(ip, mac) + + req, _ := dhcpv6.NewMessage() + req.MessageType = dhcpv6.MessageTypeRequest + relayedRequest, _ := dhcpv6.EncapsulateRelay(req, dhcpv6.MessageTypeRelayForward, net.IPv6loopback, linkLocalIPV6Addr) + + knownMac, _ := net.ParseMAC(machineWithIPAddressMACAddress) + relayedRequest.AddOption(dhcpv6.OptClientLinkLayerAddress(iana.HWTypeEthernet, knownMac)) + + stub, _ := dhcpv6.NewMessage() + stub.MessageType = dhcpv6.MessageTypeReply + _, _ = handler6(relayedRequest, stub) + + endpoint := &metalv1alpha1.Endpoint{ + ObjectMeta: metav1.ObjectMeta{ + Name: machineWithIPAddressName, + }, + } + + knownLinkLocalIPV6Addr, _ := eui64.ParseMAC(ip, knownMac) + Eventually(Object(endpoint)).Should(SatisfyAll( + HaveField("Spec.MACAddress", machineWithIPAddressMACAddress), + HaveField("Spec.IP", metalv1alpha1.MustParseIP(knownLinkLocalIPV6Addr.String())))) + DeferCleanup(k8sClient.Delete, endpoint) + }) + It("Should return and break plugin chain, if getting an IPv6 DHCP request directly (no relay)", func(ctx SpecContext) { req, _ := dhcpv6.NewMessage() req.MessageType = dhcpv6.MessageTypeRequest diff --git a/plugins/metal/suite_test.go b/plugins/metal/suite_test.go index 8352bdd..c2c072f 100644 --- a/plugins/metal/suite_test.go +++ b/plugins/metal/suite_test.go @@ -82,7 +82,7 @@ var _ = BeforeSuite(func() { // Note that you must have the required binaries setup under the bin directory to perform // the tests directly. When we run make test it will be setup and used automatically. BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", - fmt.Sprintf("1.30.0-%s-%s", runtime.GOOS, runtime.GOARCH)), + fmt.Sprintf("1.34.0-%s-%s", runtime.GOOS, runtime.GOARCH)), } var err error diff --git a/plugins/oob/plugin.go b/plugins/oob/plugin.go index 53f87da..a376ebf 100644 --- a/plugins/oob/plugin.go +++ b/plugins/oob/plugin.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/ironcore-dev/fedhcp/internal/helper" "github.com/ironcore-dev/fedhcp/internal/printer" "github.com/ironcore-dev/fedhcp/internal/api" @@ -23,7 +24,6 @@ import ( "github.com/coredhcp/coredhcp/plugins" "github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv6" - "github.com/insomniacslk/dhcp/iana" "github.com/mdlayher/netx/eui64" ) @@ -105,7 +105,7 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) { relayMsg := req.(*dhcpv6.RelayMessage) - mac, err := getMAC(relayMsg) + mac, err := helper.GetMAC(relayMsg, log) if err != nil { log.Errorf("Failed to obtain MAC, dropping.") return nil, true @@ -160,22 +160,6 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) { return resp, false } -func getMAC(relayMsg *dhcpv6.RelayMessage) (net.HardwareAddr, error) { - hwType, mac := relayMsg.Options.ClientLinkLayerAddress() - if hwType == iana.HWTypeEthernet { - return mac, nil - } - - log.Infof("failed to retrieve client link layer address, falling back to EUI64 (%s)", relayMsg.PeerAddr.String()) - _, mac, err := eui64.ParseIP(relayMsg.PeerAddr) - if err != nil { - log.Errorf("Could not parse peer address: %s", err) - return nil, err - } - - return mac, nil -} - func setup4(args ...string) (handler.Handler4, error) { oobConfig, err := loadConfig(args...) if err != nil {