Skip to content

Commit 5884bad

Browse files
ecigar13Keith Nguyen
andauthored
feat: add support for windows MAC hex dump (#4122)
* feat: add support for windows MAC hex dump * test: invalid MAC address length * fix: typo * fix: comment --------- Co-authored-by: Keith Nguyen <[email protected]>
1 parent faa9c44 commit 5884bad

File tree

2 files changed

+68
-13
lines changed

2 files changed

+68
-13
lines changed

cns/imds/client.go

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net"
1010
"net/http"
1111
"net/url"
12+
"strings"
1213

1314
"github.com/avast/retry-go/v4"
1415
"github.com/pkg/errors"
@@ -45,17 +46,18 @@ func RetryAttempts(attempts uint) ClientOption {
4546
}
4647

4748
const (
48-
vmUniqueIDProperty = "vmId"
49-
imdsComputePath = "/metadata/instance/compute"
50-
imdsNetworkPath = "/metadata/instance/network"
51-
imdsVersionsPath = "/metadata/versions"
52-
imdsDefaultAPIVersion = "api-version=2021-01-01"
53-
imdsNCDetailsVersion = "api-version=2025-07-24"
54-
imdsFormatJSON = "format=json"
55-
metadataHeaderKey = "Metadata"
56-
metadataHeaderValue = "true"
57-
defaultRetryAttempts = 3
58-
defaultIMDSEndpoint = "http://169.254.169.254"
49+
vmUniqueIDProperty = "vmId"
50+
imdsComputePath = "/metadata/instance/compute"
51+
imdsNetworkPath = "/metadata/instance/network"
52+
imdsVersionsPath = "/metadata/versions"
53+
imdsDefaultAPIVersion = "api-version=2021-01-01"
54+
imdsNCDetailsVersion = "api-version=2025-07-24"
55+
imdsMACAddressStringLength = 12 // 6 bytes in hex equals 12 characters
56+
imdsFormatJSON = "format=json"
57+
metadataHeaderKey = "Metadata"
58+
metadataHeaderValue = "true"
59+
defaultRetryAttempts = 3
60+
defaultIMDSEndpoint = "http://169.254.169.254"
5961
)
6062

6163
var (
@@ -218,14 +220,36 @@ func (h *HardwareAddr) UnmarshalJSON(data []byte) error {
218220
if err := json.Unmarshal(data, &s); err != nil {
219221
return errors.Wrap(err, "failed to unmarshal JSON data")
220222
}
221-
mac, err := net.ParseMAC(s)
223+
224+
mac, err := parseMacAddress(s)
222225
if err != nil {
223226
return errors.Wrap(err, "failed to parse MAC address")
224227
}
225228
*h = HardwareAddr(mac)
226229
return nil
227230
}
228231

232+
// parseMacAddress is a wrapper around net.ParseMAC to handle Windows MAC address. Windows MAC address is a pure hex
233+
// dump without delimiter, so we need to add delimiters. This happens when CNS gets MAC address from IMDS.
234+
func parseMacAddress(s string) (net.HardwareAddr, error) {
235+
if !strings.ContainsAny(s, ":-.") && len(s) == imdsMACAddressStringLength {
236+
var sb strings.Builder
237+
for i := 0; i < len(s); i += 2 {
238+
if i > 0 {
239+
sb.WriteByte(':')
240+
}
241+
sb.WriteString(s[i : i+2])
242+
}
243+
s = sb.String()
244+
}
245+
246+
mac, err := net.ParseMAC(s)
247+
if err != nil {
248+
return nil, errors.Wrap(err, "failed to parse MAC address")
249+
}
250+
return mac, nil
251+
}
252+
229253
func (h *HardwareAddr) String() string {
230254
return net.HardwareAddr(*h).String()
231255
}

cns/imds/client_test.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func TestGetNetworkInterfaces(t *testing.T) {
111111
},
112112
{
113113
"interfaceCompartmentID": "",
114-
"macAddress": "00:00:5e:00:53:02"
114+
"macAddress": "00005e005302"
115115
}
116116
]
117117
}`)
@@ -160,6 +160,37 @@ func TestGetNetworkInterfaces(t *testing.T) {
160160
assert.NotEqual(t, firstMAC.String(), secondMAC.String(), "MAC addresses should be different")
161161
}
162162

163+
func TestGetNetworkInterfacesInvalidMAC(t *testing.T) {
164+
networkInterfaces := []byte(`{
165+
"interface": [
166+
{
167+
"interfaceCompartmentID": "nc-12345-67890",
168+
"macAddress": "00005e00530" // incorrect windows MAC address length
169+
},
170+
]
171+
}`)
172+
173+
mockIMDSServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
174+
metadataHeader := r.Header.Get("Metadata")
175+
assert.Equal(t, "true", metadataHeader)
176+
177+
assert.Contains(t, r.URL.Path, "/metadata/instance/network")
178+
179+
w.WriteHeader(http.StatusOK)
180+
_, writeErr := w.Write(networkInterfaces)
181+
if writeErr != nil {
182+
t.Errorf("error writing response: %v", writeErr)
183+
return
184+
}
185+
}))
186+
defer mockIMDSServer.Close()
187+
188+
imdsClient := imds.NewClient(imds.Endpoint(mockIMDSServer.URL))
189+
interfaces, err := imdsClient.GetNetworkInterfaces(context.Background())
190+
require.Error(t, err, "expected error for invalid MAC address")
191+
require.Nil(t, interfaces, "expected nil interfaces on error")
192+
}
193+
163194
func TestGetNetworkInterfacesInvalidEndpoint(t *testing.T) {
164195
imdsClient := imds.NewClient(imds.Endpoint(string([]byte{0x7f})), imds.RetryAttempts(1))
165196
_, err := imdsClient.GetNetworkInterfaces(context.Background())

0 commit comments

Comments
 (0)