/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/MacAddress.h>
#include <fmt/core.h>
#include <folly/IPAddressV6.h>
#include <folly/portability/GTest.h>
using folly::IPAddressV6;
using folly::MacAddress;
using folly::StringPiece;
void testMAC(const std::string& str, uint64_t expectedHBO) {
SCOPED_TRACE(str);
MacAddress addr(str);
// Make sure parsing returned the expected value.
EXPECT_EQ(expectedHBO, addr.u64HBO());
// Perform additional checks on the MacAddress
// Check using operator==()
EXPECT_EQ(MacAddress::fromHBO(expectedHBO), addr);
// Check using operator==() when passing in non-zero padding bytes
EXPECT_EQ(MacAddress::fromHBO(expectedHBO | 0xa5a5000000000000), addr);
// Similar checks after converting to network byte order
uint64_t expectedNBO = folly::Endian::big(expectedHBO);
EXPECT_EQ(expectedNBO, addr.u64NBO());
EXPECT_EQ(MacAddress::fromNBO(expectedNBO), addr);
uint64_t nboWithPad = folly::Endian::big(expectedHBO | 0xa5a5000000000000);
EXPECT_EQ(MacAddress::fromNBO(nboWithPad), addr);
// Check they value returned by bytes()
uint8_t expectedBytes[8];
memcpy(expectedBytes, &expectedNBO, 8);
for (int n = 0; n < 6; ++n) {
EXPECT_EQ(expectedBytes[n + 2], addr.bytes()[n]);
}
}
TEST(MacAddress, parse) {
testMAC("12:34:56:78:9a:bc", 0x123456789abc);
testMAC("00-11-22-33-44-55", 0x1122334455);
testMAC("abcdef123456", 0xabcdef123456);
testMAC("1:2:3:4:5:6", 0x010203040506);
testMAC("0:0:0:0:0:0", 0);
testMAC("0:0:5e:0:1:1", 0x00005e000101);
EXPECT_THROW(MacAddress(""), std::invalid_argument);
EXPECT_THROW(MacAddress("0"), std::invalid_argument);
EXPECT_THROW(MacAddress("12:34"), std::invalid_argument);
EXPECT_THROW(MacAddress("12:3"), std::invalid_argument);
EXPECT_THROW(MacAddress("12:"), std::invalid_argument);
EXPECT_THROW(MacAddress("12:x4:56:78:9a:bc"), std::invalid_argument);
EXPECT_THROW(MacAddress("12x34:56:78:9a:bc"), std::invalid_argument);
EXPECT_THROW(MacAddress("12:34:56:78:9a:bc:de"), std::invalid_argument);
EXPECT_THROW(MacAddress("12:34:56:78:9a:bcde"), std::invalid_argument);
EXPECT_THROW(MacAddress("12:34:56:78:9a:bc "), std::invalid_argument);
EXPECT_THROW(MacAddress(" 12:34:56:78:9a:bc"), std::invalid_argument);
EXPECT_THROW(MacAddress("12:34:56:78:-1:bc"), std::invalid_argument);
}
void testFromBinary(const char* str, uint64_t expectedHBO) {
StringPiece bin(str, 6);
auto mac = MacAddress::fromBinary(bin);
SCOPED_TRACE(mac.toString());
EXPECT_EQ(expectedHBO, mac.u64HBO());
}
TEST(MacAddress, fromBinary) {
testFromBinary("\0\0\0\0\0\0", 0);
testFromBinary("\x12\x34\x56\x78\x9a\xbc", 0x123456789abc);
testFromBinary("\x11\x22\x33\x44\x55\x66", 0x112233445566);
StringPiece empty("");
EXPECT_THROW(MacAddress::fromBinary(empty), std::invalid_argument);
StringPiece tooShort("\x11", 1);
EXPECT_THROW(MacAddress::fromBinary(tooShort), std::invalid_argument);
StringPiece tooLong("\x11\x22\x33\x44\x55\x66\x77", 7);
EXPECT_THROW(MacAddress::fromBinary(tooLong), std::invalid_argument);
}
TEST(MacAddress, toString) {
EXPECT_EQ(
"12:34:56:78:9a:bc", MacAddress::fromHBO(0x123456789abc).toString());
EXPECT_EQ("12:34:56:78:9a:bc", MacAddress("12:34:56:78:9a:bc").toString());
EXPECT_EQ("01:23:45:67:89:ab", MacAddress("01-23-45-67-89-ab").toString());
EXPECT_EQ("01:23:45:67:89:ab", MacAddress("0123456789ab").toString());
}
TEST(MacAddress, attributes) {
EXPECT_TRUE(MacAddress("ff:ff:ff:ff:ff:ff").isBroadcast());
EXPECT_FALSE(MacAddress("7f:ff:ff:ff:ff:ff").isBroadcast());
EXPECT_FALSE(MacAddress("7f:ff:ff:ff:ff:fe").isBroadcast());
EXPECT_FALSE(MacAddress("00:00:00:00:00:00").isBroadcast());
EXPECT_TRUE(MacAddress::fromNBO(0xffffffffffffffffU).isBroadcast());
EXPECT_TRUE(MacAddress("ff:ff:ff:ff:ff:ff").isMulticast());
EXPECT_TRUE(MacAddress("01:00:00:00:00:00").isMulticast());
EXPECT_FALSE(MacAddress("00:00:00:00:00:00").isMulticast());
EXPECT_FALSE(MacAddress("fe:ff:ff:ff:ff:ff").isMulticast());
EXPECT_FALSE(MacAddress("00:00:5e:00:01:01").isMulticast());
EXPECT_FALSE(MacAddress("ff:ff:ff:ff:ff:ff").isUnicast());
EXPECT_FALSE(MacAddress("01:00:00:00:00:00").isUnicast());
EXPECT_TRUE(MacAddress("00:00:00:00:00:00").isUnicast());
EXPECT_TRUE(MacAddress("fe:ff:ff:ff:ff:ff").isUnicast());
EXPECT_TRUE(MacAddress("00:00:5e:00:01:01").isUnicast());
EXPECT_TRUE(MacAddress("ff:ff:ff:ff:ff:ff").isLocallyAdministered());
EXPECT_TRUE(MacAddress("02:00:00:00:00:00").isLocallyAdministered());
EXPECT_FALSE(MacAddress("01:00:00:00:00:00").isLocallyAdministered());
EXPECT_FALSE(MacAddress("00:00:00:00:00:00").isLocallyAdministered());
EXPECT_FALSE(MacAddress("fd:ff:ff:ff:ff:ff").isLocallyAdministered());
EXPECT_TRUE(MacAddress("fe:ff:ff:ff:ff:ff").isLocallyAdministered());
EXPECT_FALSE(MacAddress("00:00:5e:00:01:01").isLocallyAdministered());
EXPECT_TRUE(MacAddress("02:12:34:56:78:9a").isLocallyAdministered());
}
TEST(MacAddress, createMulticast) {
EXPECT_EQ(
MacAddress("33:33:00:01:00:03"),
MacAddress::createMulticast(IPAddressV6("ff02:dead:beef::1:3")));
EXPECT_EQ(
MacAddress("33:33:12:34:56:78"),
MacAddress::createMulticast(IPAddressV6("ff02::abcd:1234:5678")));
}
void testCmp(const char* str1, const char* str2) {
SCOPED_TRACE(fmt::format("{} < {}", str1, str2));
MacAddress m1(str1);
MacAddress m2(str2);
// Test the comparison operators
EXPECT_TRUE(m1 < m2);
EXPECT_FALSE(m1 < m1);
EXPECT_TRUE(m1 <= m2);
EXPECT_TRUE(m2 > m1);
EXPECT_TRUE(m2 >= m1);
EXPECT_TRUE(m1 != m2);
EXPECT_TRUE(m1 == (m1));
EXPECT_FALSE(m1 == m2);
// Also test the copy constructor and assignment operator
MacAddress copy(m1);
EXPECT_EQ(copy, m1);
copy = m2;
EXPECT_EQ(copy, m2);
}
TEST(MacAddress, ordering) {
testCmp("00:00:00:00:00:00", "00:00:00:00:00:01");
testCmp("00:00:00:00:00:01", "00:00:00:00:00:02");
testCmp("01:00:00:00:00:00", "02:00:00:00:00:00");
testCmp("00:00:00:00:00:01", "00:00:00:00:01:00");
}
TEST(MacAddress, hash) {
EXPECT_EQ(
std::hash<MacAddress>()(MacAddress("00:11:22:33:44:55")),
std::hash<MacAddress>()(MacAddress("00-11-22-33-44-55")));
EXPECT_NE(
std::hash<MacAddress>()(MacAddress("00:11:22:33:44:55")),
std::hash<MacAddress>()(MacAddress("00:11:22:33:44:56")));
}