Skip to content

Commit 10887f6

Browse files
authored
Enhance MAC address detection (#375)
Improve MAC detection to use ClientLinkLayerAddress after RFC6939
1 parent 60adafd commit 10887f6

File tree

5 files changed

+56
-23
lines changed

5 files changed

+56
-23
lines changed

internal/helper/helper.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ import (
1010
"net"
1111
"time"
1212

13+
"github.com/insomniacslk/dhcp/dhcpv6"
14+
"github.com/insomniacslk/dhcp/iana"
1315
"github.com/ironcore-dev/fedhcp/internal/kubernetes"
1416
ipamv1alpha1 "github.com/ironcore-dev/ipam/api/ipam/v1alpha1"
17+
"github.com/mdlayher/netx/eui64"
1518
"github.com/sirupsen/logrus"
1619
apierrors "k8s.io/apimachinery/pkg/api/errors"
1720
"k8s.io/apimachinery/pkg/util/wait"
@@ -89,3 +92,19 @@ func CheckIPInCIDR(ip net.IP, cidrStr string, log *logrus.Entry) bool {
8992
// Check if the CIDR contains the IP
9093
return cidrNet.Contains(ip)
9194
}
95+
96+
func GetMAC(relayMsg *dhcpv6.RelayMessage, log *logrus.Entry) (net.HardwareAddr, error) {
97+
hwType, mac := relayMsg.Options.ClientLinkLayerAddress()
98+
if hwType == iana.HWTypeEthernet {
99+
return mac, nil
100+
}
101+
102+
log.Infof("failed to retrieve client link layer address, falling back to EUI64 (%s)", relayMsg.PeerAddr.String())
103+
_, mac, err := eui64.ParseIP(relayMsg.PeerAddr)
104+
if err != nil {
105+
log.Errorf("Could not parse peer address: %s", err)
106+
return nil, err
107+
}
108+
109+
return mac, nil
110+
}

plugins/metal/plugin.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"strings"
1313

14+
"github.com/ironcore-dev/fedhcp/internal/helper"
1415
"github.com/ironcore-dev/fedhcp/internal/printer"
1516

1617
"k8s.io/apimachinery/pkg/api/errors"
@@ -29,7 +30,6 @@ import (
2930
"github.com/ironcore-dev/fedhcp/internal/kubernetes"
3031
ipamv1alpha1 "github.com/ironcore-dev/ipam/api/ipam/v1alpha1"
3132
metalv1alpha1 "github.com/ironcore-dev/metal-operator/api/v1alpha1"
32-
"github.com/mdlayher/netx/eui64"
3333
"gopkg.in/yaml.v3"
3434
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3535
)
@@ -159,9 +159,9 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
159159
}
160160

161161
relayMsg := req.(*dhcpv6.RelayMessage)
162-
_, mac, err := eui64.ParseIP(relayMsg.PeerAddr)
162+
mac, err := helper.GetMAC(relayMsg, log)
163163
if err != nil {
164-
log.Errorf("Could not parse peer address %s: %s", relayMsg.PeerAddr.String(), err)
164+
log.Errorf("Failed to obtain MAC, dropping.")
165165
return nil, true
166166
}
167167

plugins/metal/plugin_test.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99
"strings"
1010

11+
"github.com/insomniacslk/dhcp/iana"
1112
"github.com/ironcore-dev/fedhcp/internal/api"
1213

1314
"gopkg.in/yaml.v2"
@@ -29,7 +30,7 @@ var _ = Describe("Endpoint", func() {
2930
ns := SetupTest()
3031

3132
BeforeEach(func(ctx SpecContext) {
32-
By("Creating an IPAM IP objects")
33+
By("Creating IPAM IP objects")
3334
mac := machineWithIPAddressMACAddress
3435
m, err := net.ParseMAC(mac)
3536
Expect(err).NotTo(HaveOccurred())
@@ -356,6 +357,35 @@ var _ = Describe("Endpoint", func() {
356357
Eventually(Get(endpoint)).Should(Satisfy(apierrors.IsNotFound))
357358
})
358359

360+
It("Should create an endpoint for IPv6 DHCP request from a unknown machine, if ClientLinkLayer is set to allowed MAC (RFC6939) ", func(ctx SpecContext) {
361+
mac, _ := net.ParseMAC(unknownMachineMACAddress)
362+
ip := net.ParseIP(linkLocalIPV6Prefix)
363+
linkLocalIPV6Addr, _ := eui64.ParseMAC(ip, mac)
364+
365+
req, _ := dhcpv6.NewMessage()
366+
req.MessageType = dhcpv6.MessageTypeRequest
367+
relayedRequest, _ := dhcpv6.EncapsulateRelay(req, dhcpv6.MessageTypeRelayForward, net.IPv6loopback, linkLocalIPV6Addr)
368+
369+
knownMac, _ := net.ParseMAC(machineWithIPAddressMACAddress)
370+
relayedRequest.AddOption(dhcpv6.OptClientLinkLayerAddress(iana.HWTypeEthernet, knownMac))
371+
372+
stub, _ := dhcpv6.NewMessage()
373+
stub.MessageType = dhcpv6.MessageTypeReply
374+
_, _ = handler6(relayedRequest, stub)
375+
376+
endpoint := &metalv1alpha1.Endpoint{
377+
ObjectMeta: metav1.ObjectMeta{
378+
Name: machineWithIPAddressName,
379+
},
380+
}
381+
382+
knownLinkLocalIPV6Addr, _ := eui64.ParseMAC(ip, knownMac)
383+
Eventually(Object(endpoint)).Should(SatisfyAll(
384+
HaveField("Spec.MACAddress", machineWithIPAddressMACAddress),
385+
HaveField("Spec.IP", metalv1alpha1.MustParseIP(knownLinkLocalIPV6Addr.String()))))
386+
DeferCleanup(k8sClient.Delete, endpoint)
387+
})
388+
359389
It("Should return and break plugin chain, if getting an IPv6 DHCP request directly (no relay)", func(ctx SpecContext) {
360390
req, _ := dhcpv6.NewMessage()
361391
req.MessageType = dhcpv6.MessageTypeRequest

plugins/metal/suite_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ var _ = BeforeSuite(func() {
8282
// Note that you must have the required binaries setup under the bin directory to perform
8383
// the tests directly. When we run make test it will be setup and used automatically.
8484
BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s",
85-
fmt.Sprintf("1.30.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
85+
fmt.Sprintf("1.34.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
8686
}
8787

8888
var err error

plugins/oob/plugin.go

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"time"
1313

14+
"github.com/ironcore-dev/fedhcp/internal/helper"
1415
"github.com/ironcore-dev/fedhcp/internal/printer"
1516

1617
"github.com/ironcore-dev/fedhcp/internal/api"
@@ -23,7 +24,6 @@ import (
2324
"github.com/coredhcp/coredhcp/plugins"
2425
"github.com/insomniacslk/dhcp/dhcpv4"
2526
"github.com/insomniacslk/dhcp/dhcpv6"
26-
"github.com/insomniacslk/dhcp/iana"
2727

2828
"github.com/mdlayher/netx/eui64"
2929
)
@@ -105,7 +105,7 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
105105

106106
relayMsg := req.(*dhcpv6.RelayMessage)
107107

108-
mac, err := getMAC(relayMsg)
108+
mac, err := helper.GetMAC(relayMsg, log)
109109
if err != nil {
110110
log.Errorf("Failed to obtain MAC, dropping.")
111111
return nil, true
@@ -160,22 +160,6 @@ func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
160160
return resp, false
161161
}
162162

163-
func getMAC(relayMsg *dhcpv6.RelayMessage) (net.HardwareAddr, error) {
164-
hwType, mac := relayMsg.Options.ClientLinkLayerAddress()
165-
if hwType == iana.HWTypeEthernet {
166-
return mac, nil
167-
}
168-
169-
log.Infof("failed to retrieve client link layer address, falling back to EUI64 (%s)", relayMsg.PeerAddr.String())
170-
_, mac, err := eui64.ParseIP(relayMsg.PeerAddr)
171-
if err != nil {
172-
log.Errorf("Could not parse peer address: %s", err)
173-
return nil, err
174-
}
175-
176-
return mac, nil
177-
}
178-
179163
func setup4(args ...string) (handler.Handler4, error) {
180164
oobConfig, err := loadConfig(args...)
181165
if err != nil {

0 commit comments

Comments
 (0)