From af1812e92f5a4f6b24c34629069d2b20fdc8df16 Mon Sep 17 00:00:00 2001 From: "Robert H. Engelhardt" Date: Sat, 9 Nov 2019 10:49:59 -0700 Subject: [PATCH] MAC Address to EUI-64 mapping #45 There is confusion related to this issue --- src/Arcus.Tests/MacAddressTests.cs | 38 +++++++++++++++++++ src/Arcus/MacAddress.cs | 59 ++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/src/Arcus.Tests/MacAddressTests.cs b/src/Arcus.Tests/MacAddressTests.cs index c444b69..128ff2f 100644 --- a/src/Arcus.Tests/MacAddressTests.cs +++ b/src/Arcus.Tests/MacAddressTests.cs @@ -8,12 +8,24 @@ using System.Text.RegularExpressions; using Gulliver; using Xunit; +using Xunit.Abstractions; namespace Arcus.Tests { /// Tests for public class MacAddressTests { + #region Setup / Teardown + + public MacAddressTests(ITestOutputHelper testOutputHelper) + { + this._testOutputHelper = testOutputHelper; + } + + private readonly ITestOutputHelper _testOutputHelper; + + #endregion + #region IComparable [Theory] @@ -97,6 +109,32 @@ public void GetCidBytes_Test() #endregion end: GetCidBytes + public static IEnumerable 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(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)); + } + #region GetHashCode [Theory] diff --git a/src/Arcus/MacAddress.cs b/src/Arcus/MacAddress.cs index 7234285..b7e2e48 100644 --- a/src/Arcus/MacAddress.cs +++ b/src/Arcus/MacAddress.cs @@ -346,6 +346,65 @@ public byte[] GetCidBytes() .ToArray(); } + #region MapEui64 + + /// Gets a mapping of the Mac Address as a EUI-64 + /// Note that this mapping is considered deprecated, and is implemented for legacy needs + /// The implementation of this method is under question + /// an array of bytes representing the EUI-64 of the MAC Address + [NotNull] + [Obsolete("This method is obsolete until the desired output can be verified")] + public byte[] GetEui64AddressBytes() + { + /* there are several sources that say several things about this mapping; it is unclear which is "most" correct, or what the vairants impy. + * + * the Networking Engineering StackExchange (current implementation) https://networkengineering.stackexchange.com/a/30864/22751 + * states that the insertion bytes should be [0xFF, 0xFE] and 7th bit should be inverted. + * + * Wikipedia entry for "Mac Address" (https://en.wikipedia.org/wiki/MAC_address#cite_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) + * + * 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) + * (https://standards.ieee.org/content/dam/ieee-standards/standards/web/documents/tutorials/eui.pdf) + * states that the insertion bytes should be [0xFF, 0xFE] or [0xFF, 0xFF] + * (no inversion of any bits mentioned) + * + * It seems odd that each of the 3 above mentioned very specifically outlining differing rules for what would seem to be the same opeation. + * 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 🤷 + */ + + // 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; + } + + #endregion MapEui64 + /// public override string ToString() {