diff --git a/cns/imds/client.go b/cns/imds/client.go index ac06e6d8a3..57ec60303d 100644 --- a/cns/imds/client.go +++ b/cns/imds/client.go @@ -9,6 +9,7 @@ import ( "net" "net/http" "net/url" + "strings" "github.com/avast/retry-go/v4" "github.com/pkg/errors" @@ -45,17 +46,18 @@ func RetryAttempts(attempts uint) ClientOption { } const ( - vmUniqueIDProperty = "vmId" - imdsComputePath = "/metadata/instance/compute" - imdsNetworkPath = "/metadata/instance/network" - imdsVersionsPath = "/metadata/versions" - imdsDefaultAPIVersion = "api-version=2021-01-01" - imdsNCDetailsVersion = "api-version=2025-07-24" - imdsFormatJSON = "format=json" - metadataHeaderKey = "Metadata" - metadataHeaderValue = "true" - defaultRetryAttempts = 3 - defaultIMDSEndpoint = "http://169.254.169.254" + vmUniqueIDProperty = "vmId" + imdsComputePath = "/metadata/instance/compute" + imdsNetworkPath = "/metadata/instance/network" + imdsVersionsPath = "/metadata/versions" + imdsDefaultAPIVersion = "api-version=2021-01-01" + imdsNCDetailsVersion = "api-version=2025-07-24" + imdsMACAddressStringLength = 12 // 6 bytes in hex equals 12 characters + imdsFormatJSON = "format=json" + metadataHeaderKey = "Metadata" + metadataHeaderValue = "true" + defaultRetryAttempts = 3 + defaultIMDSEndpoint = "http://169.254.169.254" ) var ( @@ -218,7 +220,8 @@ func (h *HardwareAddr) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &s); err != nil { return errors.Wrap(err, "failed to unmarshal JSON data") } - mac, err := net.ParseMAC(s) + + mac, err := parseMacAddress(s) if err != nil { return errors.Wrap(err, "failed to parse MAC address") } @@ -226,6 +229,27 @@ func (h *HardwareAddr) UnmarshalJSON(data []byte) error { return nil } +// parseMacAddress is a wrapper around net.ParseMAC to handle Windows MAC address. Windows MAC address is a pure hex +// dump without delimiter, so we need to add delimiters. This happens when CNS gets MAC address from IMDS. +func parseMacAddress(s string) (net.HardwareAddr, error) { + if !strings.ContainsAny(s, ":-.") && len(s) == imdsMACAddressStringLength { + var sb strings.Builder + for i := 0; i < len(s); i += 2 { + if i > 0 { + sb.WriteByte(':') + } + sb.WriteString(s[i : i+2]) + } + s = sb.String() + } + + mac, err := net.ParseMAC(s) + if err != nil { + return nil, errors.Wrap(err, "failed to parse MAC address") + } + return mac, nil +} + func (h *HardwareAddr) String() string { return net.HardwareAddr(*h).String() } diff --git a/cns/imds/client_test.go b/cns/imds/client_test.go index ac97ba5251..263a5276f0 100644 --- a/cns/imds/client_test.go +++ b/cns/imds/client_test.go @@ -111,7 +111,7 @@ func TestGetNetworkInterfaces(t *testing.T) { }, { "interfaceCompartmentID": "", - "macAddress": "00:00:5e:00:53:02" + "macAddress": "00005e005302" } ] }`) @@ -160,6 +160,37 @@ func TestGetNetworkInterfaces(t *testing.T) { assert.NotEqual(t, firstMAC.String(), secondMAC.String(), "MAC addresses should be different") } +func TestGetNetworkInterfacesInvalidMAC(t *testing.T) { + networkInterfaces := []byte(`{ + "interface": [ + { + "interfaceCompartmentID": "nc-12345-67890", + "macAddress": "00005e00530" // incorrect windows MAC address length + }, + ] + }`) + + mockIMDSServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + metadataHeader := r.Header.Get("Metadata") + assert.Equal(t, "true", metadataHeader) + + assert.Contains(t, r.URL.Path, "/metadata/instance/network") + + w.WriteHeader(http.StatusOK) + _, writeErr := w.Write(networkInterfaces) + if writeErr != nil { + t.Errorf("error writing response: %v", writeErr) + return + } + })) + defer mockIMDSServer.Close() + + imdsClient := imds.NewClient(imds.Endpoint(mockIMDSServer.URL)) + interfaces, err := imdsClient.GetNetworkInterfaces(context.Background()) + require.Error(t, err, "expected error for invalid MAC address") + require.Nil(t, interfaces, "expected nil interfaces on error") +} + func TestGetNetworkInterfacesInvalidEndpoint(t *testing.T) { imdsClient := imds.NewClient(imds.Endpoint(string([]byte{0x7f})), imds.RetryAttempts(1)) _, err := imdsClient.GetNetworkInterfaces(context.Background())