sandialabs/Arcus

[Feature Request] MacAddress shold be able to map to EUI-64 bytes for legacy support

rheone opened this issue · 1 comments

Description

A legacy use of a MAC Address is to convert it to a EUI-64.

This will allow us to also support the legacy modification of a EUI-64 as the least-significant 64 bits of a unicast network address or link-local address when stateless address autoconfiguration is used.

Eg. MAC Address to IPv6

Proposed solutions

There are several sources that say several things about this mapping; it is unclear which is "most" correct, or what the variants imply.

It seems odd that each of the 3 below mentioned very specifically outlining differing rules for what would seem to be the same operation. While IEEE is the definitive source, I'd like to determine the reasoning of the other implementations before moving forward. The differing schemes may be perfectly valid in the appropriate place.

All this for a known legacy operation 🤷

Networking Engineering StackExchange

(current implementation)
Answer to "What are EUI-48 and EUI-64?"
states that the insertion bytes should be [0xFF, 0xFE] and 7th bit should be inverted.

Wikipedia entry for "Mac Address"

Mac Address. Citation Note 6
states that the insertion bytes should be [0xFF, 0xFE] if the source is a EUI-48
and that the insertion bytes should be [0xFF, 0xFF] if the source is a MAC-48
(no inversion of any bits mentioned)

IEE

IEEE in "Guidelines for Use of Extended Unique Identifier (EUI), Organizationally Unique Identifier (OUI), and Company ID (CID)" section "Mapping an EUI-48 to an EUI-64" (pg 15)
states that the insertion bytes should be [0xFF, 0xFE] or [0xFF, 0xFF]
(no inversion of any bits mentioned)

Can you help?

Maybe? Gotta clear some things up and do some research

Code

What follows is the "StackExchange" implementation. This exists simply because it is what I first encountered before researching further. By no means is it the "correct" solution, it is simply correct as to the implied spec.

/// <summary>Gets a mapping of the Mac Address as a EUI-64</summary>
/// <remarks><para>Note that this mapping is considered deprecated, and is implemented for legacy needs</para></remarks>
/// <remarks><para>The implementation of this method is under question</para></remarks>
/// <returns>an array of bytes representing the EUI-64 of the MAC Address</returns>
[NotNull]
[Obsolete("This method is obsolete until the desired output can be verified")]
public byte[] GetEui64AddressBytes()
{
   // To generate a EUI-64 from the EUI-48
   // - divided EUI-48 int two 3-byte parts; OUI & CID
   // - insert the value 0xFFFE between the two parts (24th bit)
   // - Invert the 7th bit of the result

   var eui64 = new byte[8];
   Array.Copy(this._address, 0, eui64, 0, 3);  // first 3 bytes od address
   eui64[3] = 0xFF;
   eui64[4] = 0xFE;
   Array.Copy(this._address, 3, eui64, 5, 3);  // last 3 bytes of address

   // invert the 7th bit
   if ((eui64[0] & 0b0000_0010) != 0)  // 7th bit (big-endian) is set, it should be cleared
   {
         eui64[0] &= 0b1111_1101;    // clear the 7th bit
   }
   else  // 7th bit (big-endian) is not set, it should be set
   {
         eui64[0] |= 0b0000_0010;    // set the 7th bit
   }

   return eui64;
}

Tests

public static IEnumerable<object[]> GetEui64AddressBytes_Test_Values()
{
   yield return new object[] { new byte[] { 0x02, 0x21, 0x86, 0xFF, 0xFE, 0xB5, 0x6E, 0x10 }, MacAddress.Parse("00:21:86:B5:6E:10") };
   yield return new object[] { new byte[] { 0xFD, 0x21, 0x86, 0xFF, 0xFE, 0xB5, 0x6E, 0x10 }, MacAddress.Parse("FF:21:86:B5:6E:10") };
   yield return new object[] { new byte[] { 0xC2, 0xFF, 0xEE, 0xFF, 0xFE, 0xCA, 0xFE, 0x00 }, MacAddress.Parse("C0:FF:EE:CA:FE:00") };
}

[Theory]
[MemberData(nameof(GetEui64AddressBytes_Test_Values))]
public void GetEui64AddressBytes_Test(byte[] expected, MacAddress input)
{
   // Arrange
   // Act
   var result = input.GetEui64AddressBytes();

   // Assert
   Assert.IsType<byte[]>(result);
   var addressBytes = input.GetAddressBytes();

   Assert.Equal(8, result.Length);
   Assert.NotEqual(expected[0] & 0b10, addressBytes[0] & 0b10);
   Assert.Equal(0xFF, result[3]);
   Assert.Equal(0xFE, result[4]);
   Assert.Equal(0, ByteArrayUtils.CompareUnsignedBigEndian(expected, result));
}

Comment from user goulashsoup

"... I was also curious about the bit inversion. Actually most tutorials and descriptions are wrong about it. The bit inversion is used to get the "Modified EUI-64" as described in RFC 4291 to acquire the interface identifier. Therefor the correct "EUI-64" value does not include the inversion. See also Understanding IPv6 EUI-64 Bit Address"