154 lines
4.4 KiB
C++
154 lines
4.4 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its 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 <cassert>
|
|
#include <ostream>
|
|
|
|
#include <folly/Exception.h>
|
|
#include <folly/Format.h>
|
|
#include <folly/IPAddressV6.h>
|
|
#include <folly/String.h>
|
|
|
|
using std::invalid_argument;
|
|
using std::string;
|
|
|
|
namespace folly {
|
|
|
|
const MacAddress MacAddress::BROADCAST{Endian::big(uint64_t(0xffffffffffffU))};
|
|
const MacAddress MacAddress::ZERO;
|
|
|
|
MacAddress::MacAddress(StringPiece str) {
|
|
memset(&bytes_, 0, 8);
|
|
parse(str);
|
|
}
|
|
|
|
MacAddress MacAddress::createMulticast(IPAddressV6 v6addr) {
|
|
// This method should only be used for multicast addresses.
|
|
assert(v6addr.isMulticast());
|
|
|
|
uint8_t bytes[SIZE];
|
|
bytes[0] = 0x33;
|
|
bytes[1] = 0x33;
|
|
memcpy(bytes + 2, v6addr.bytes() + 12, 4);
|
|
return fromBinary(ByteRange(bytes, SIZE));
|
|
}
|
|
|
|
string MacAddress::toString() const {
|
|
static const char hexValues[] = "0123456789abcdef";
|
|
string result;
|
|
result.resize(17);
|
|
result[0] = hexValues[getByte(0) >> 4];
|
|
result[1] = hexValues[getByte(0) & 0xf];
|
|
result[2] = ':';
|
|
result[3] = hexValues[getByte(1) >> 4];
|
|
result[4] = hexValues[getByte(1) & 0xf];
|
|
result[5] = ':';
|
|
result[6] = hexValues[getByte(2) >> 4];
|
|
result[7] = hexValues[getByte(2) & 0xf];
|
|
result[8] = ':';
|
|
result[9] = hexValues[getByte(3) >> 4];
|
|
result[10] = hexValues[getByte(3) & 0xf];
|
|
result[11] = ':';
|
|
result[12] = hexValues[getByte(4) >> 4];
|
|
result[13] = hexValues[getByte(4) & 0xf];
|
|
result[14] = ':';
|
|
result[15] = hexValues[getByte(5) >> 4];
|
|
result[16] = hexValues[getByte(5) & 0xf];
|
|
return result;
|
|
}
|
|
|
|
void MacAddress::parse(StringPiece str) {
|
|
// Helper function to convert a single hex char into an integer
|
|
auto isSeparatorChar = [](char c) { return c == ':' || c == '-'; };
|
|
|
|
uint8_t parsed[SIZE];
|
|
auto p = str.begin();
|
|
for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) {
|
|
if (p == str.end()) {
|
|
throw invalid_argument(
|
|
sformat("invalid MAC address '{}': not enough digits", str));
|
|
}
|
|
|
|
// Skip over ':' or '-' separators between bytes
|
|
if (byteIndex != 0 && isSeparatorChar(*p)) {
|
|
++p;
|
|
if (p == str.end()) {
|
|
throw invalid_argument(
|
|
sformat("invalid MAC address '{}': not enough digits", str));
|
|
}
|
|
}
|
|
|
|
// Parse the upper nibble
|
|
uint8_t upper = detail::hexTable[static_cast<uint8_t>(*p)];
|
|
if (upper & 0x10) {
|
|
throw invalid_argument(
|
|
sformat("invalid MAC address '{}': contains non-hex digit", str));
|
|
}
|
|
++p;
|
|
|
|
// Parse the lower nibble
|
|
uint8_t lower;
|
|
if (p == str.end()) {
|
|
lower = upper;
|
|
upper = 0;
|
|
} else {
|
|
lower = detail::hexTable[static_cast<uint8_t>(*p)];
|
|
if (lower & 0x10) {
|
|
// Also accept ':', '-', or '\0', to handle the case where one
|
|
// of the bytes was represented by just a single digit.
|
|
if (isSeparatorChar(*p)) {
|
|
lower = upper;
|
|
upper = 0;
|
|
} else {
|
|
throw invalid_argument(
|
|
sformat("invalid MAC address '{}': contains non-hex digit", str));
|
|
}
|
|
}
|
|
++p;
|
|
}
|
|
|
|
// Update parsed with the newly parsed byte
|
|
parsed[byteIndex] = (upper << 4) | lower;
|
|
}
|
|
|
|
if (p != str.end()) {
|
|
// String is too long to be a MAC address
|
|
throw invalid_argument(
|
|
sformat("invalid MAC address '{}': found trailing characters", str));
|
|
}
|
|
|
|
// Only update now that we have successfully parsed the entire
|
|
// string. This way we remain unchanged on error.
|
|
setFromBinary(ByteRange(parsed, SIZE));
|
|
}
|
|
|
|
void MacAddress::setFromBinary(ByteRange value) {
|
|
if (value.size() != SIZE) {
|
|
throw invalid_argument(
|
|
sformat("MAC address must be 6 bytes long, got ", value.size()));
|
|
}
|
|
memcpy(bytes_ + 2, value.begin(), SIZE);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, MacAddress address) {
|
|
os << address.toString();
|
|
return os;
|
|
}
|
|
|
|
} // namespace folly
|