1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <folly/MacAddress.h>
18
19#include <cassert>
20#include <ostream>
21
22#include <folly/Exception.h>
23#include <folly/Format.h>
24#include <folly/IPAddressV6.h>
25#include <folly/String.h>
26
27using std::invalid_argument;
28using std::string;
29
30namespace folly {
31
32const MacAddress MacAddress::BROADCAST{Endian::big(uint64_t(0xffffffffffffU))};
33const MacAddress MacAddress::ZERO;
34
35MacAddress::MacAddress(StringPiece str) {
36 memset(&bytes_, 0, 8);
37 parse(str);
38}
39
40MacAddress MacAddress::createMulticast(IPAddressV6 v6addr) {
41 // This method should only be used for multicast addresses.
42 assert(v6addr.isMulticast());
43
44 uint8_t bytes[SIZE];
45 bytes[0] = 0x33;
46 bytes[1] = 0x33;
47 memcpy(bytes + 2, v6addr.bytes() + 12, 4);
48 return fromBinary(ByteRange(bytes, SIZE));
49}
50
51string MacAddress::toString() const {
52 static const char hexValues[] = "0123456789abcdef";
53 string result;
54 result.resize(17);
55 result[0] = hexValues[getByte(0) >> 4];
56 result[1] = hexValues[getByte(0) & 0xf];
57 result[2] = ':';
58 result[3] = hexValues[getByte(1) >> 4];
59 result[4] = hexValues[getByte(1) & 0xf];
60 result[5] = ':';
61 result[6] = hexValues[getByte(2) >> 4];
62 result[7] = hexValues[getByte(2) & 0xf];
63 result[8] = ':';
64 result[9] = hexValues[getByte(3) >> 4];
65 result[10] = hexValues[getByte(3) & 0xf];
66 result[11] = ':';
67 result[12] = hexValues[getByte(4) >> 4];
68 result[13] = hexValues[getByte(4) & 0xf];
69 result[14] = ':';
70 result[15] = hexValues[getByte(5) >> 4];
71 result[16] = hexValues[getByte(5) & 0xf];
72 return result;
73}
74
75void MacAddress::parse(StringPiece str) {
76 // Helper function to convert a single hex char into an integer
77 auto isSeparatorChar = [](char c) { return c == ':' || c == '-'; };
78
79 uint8_t parsed[SIZE];
80 auto p = str.begin();
81 for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) {
82 if (p == str.end()) {
83 throw invalid_argument(
84 sformat("invalid MAC address '{}': not enough digits", str));
85 }
86
87 // Skip over ':' or '-' separators between bytes
88 if (byteIndex != 0 && isSeparatorChar(*p)) {
89 ++p;
90 if (p == str.end()) {
91 throw invalid_argument(
92 sformat("invalid MAC address '{}': not enough digits", str));
93 }
94 }
95
96 // Parse the upper nibble
97 uint8_t upper = detail::hexTable[static_cast<uint8_t>(*p)];
98 if (upper & 0x10) {
99 throw invalid_argument(
100 sformat("invalid MAC address '{}': contains non-hex digit", str));
101 }
102 ++p;
103
104 // Parse the lower nibble
105 uint8_t lower;
106 if (p == str.end()) {
107 lower = upper;
108 upper = 0;
109 } else {
110 lower = detail::hexTable[static_cast<uint8_t>(*p)];
111 if (lower & 0x10) {
112 // Also accept ':', '-', or '\0', to handle the case where one
113 // of the bytes was represented by just a single digit.
114 if (isSeparatorChar(*p)) {
115 lower = upper;
116 upper = 0;
117 } else {
118 throw invalid_argument(
119 sformat("invalid MAC address '{}': contains non-hex digit", str));
120 }
121 }
122 ++p;
123 }
124
125 // Update parsed with the newly parsed byte
126 parsed[byteIndex] = (upper << 4) | lower;
127 }
128
129 if (p != str.end()) {
130 // String is too long to be a MAC address
131 throw invalid_argument(
132 sformat("invalid MAC address '{}': found trailing characters", str));
133 }
134
135 // Only update now that we have successfully parsed the entire
136 // string. This way we remain unchanged on error.
137 setFromBinary(ByteRange(parsed, SIZE));
138}
139
140void MacAddress::setFromBinary(ByteRange value) {
141 if (value.size() != SIZE) {
142 throw invalid_argument(
143 sformat("MAC address must be 6 bytes long, got ", value.size()));
144 }
145 memcpy(bytes_ + 2, value.begin(), SIZE);
146}
147
148std::ostream& operator<<(std::ostream& os, MacAddress address) {
149 os << address.toString();
150 return os;
151}
152
153} // namespace folly
154