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 #pragma once
18
19 #include <iosfwd>
20
21 #include <folly/Conv.h>
22 #include <folly/Expected.h>
23 #include <folly/Range.h>
24 #include <folly/Unit.h>
25 #include <folly/lang/Bits.h>
26
27 namespace folly {
28
29 class IPAddressV6;
30
31 enum class MacAddressFormatError {
32 Invalid,
33 };
34
35 /*
36 * MacAddress represents an IEEE 802 MAC address.
37 */
38 class MacAddress {
39 public:
40 static constexpr size_t SIZE = 6;
41 static const MacAddress BROADCAST;
42 static const MacAddress ZERO;
43
44 /*
45 * Construct a zero-initialized MacAddress.
46 */
MacAddress()47 MacAddress() { memset(&bytes_, 0, 8); }
48
49 /*
50 * Parse a MacAddress from a human-readable string.
51 * The string must contain 6 one- or two-digit hexadecimal
52 * numbers, separated by dashes or colons.
53 * Examples: 00:02:C9:C8:F9:68 or 0-2-c9-c8-f9-68
54 */
55 explicit MacAddress(StringPiece str);
56
tryFromString(StringPiece value)57 static Expected<MacAddress, MacAddressFormatError> tryFromString(
58 StringPiece value) {
59 MacAddress ret;
60 auto ok = ret.trySetFromString(value);
61 if (!ok) {
62 return makeUnexpected(ok.error());
63 }
64 return ret;
65 }
fromString(StringPiece value)66 static MacAddress fromString(StringPiece value) {
67 MacAddress ret;
68 ret.setFromString(value);
69 return ret;
70 }
71
72 /*
73 * Construct a MAC address from its 6-byte binary value
74 */
tryFromBinary(ByteRange value)75 static Expected<MacAddress, MacAddressFormatError> tryFromBinary(
76 ByteRange value) {
77 MacAddress ret;
78 auto ok = ret.trySetFromBinary(value);
79 if (!ok) {
80 return makeUnexpected(ok.error());
81 }
82 return ret;
83 }
fromBinary(ByteRange value)84 static MacAddress fromBinary(ByteRange value) {
85 MacAddress ret;
86 ret.setFromBinary(value);
87 return ret;
88 }
89
90 /*
91 * Construct a MacAddress from a uint64_t in network byte order.
92 *
93 * The first two bytes are ignored, and the MAC address is taken from the
94 * latter 6 bytes.
95 *
96 * This is a static method rather than a constructor to avoid confusion
97 * between host and network byte order constructors.
98 */
fromNBO(uint64_t value)99 static MacAddress fromNBO(uint64_t value) { return MacAddress(value); }
100
101 /*
102 * Construct a MacAddress from a uint64_t in host byte order.
103 *
104 * The most significant two bytes are ignored, and the MAC address is taken
105 * from the least significant 6 bytes.
106 *
107 * This is a static method rather than a constructor to avoid confusion
108 * between host and network byte order constructors.
109 */
fromHBO(uint64_t value)110 static MacAddress fromHBO(uint64_t value) {
111 return MacAddress(Endian::big(value));
112 }
113
114 /*
115 * Construct the multicast MacAddress for the specified multicast IPv6
116 * address.
117 */
118 static MacAddress createMulticast(IPAddressV6 addr);
119
120 /*
121 * Get a pointer to the MAC address' binary value.
122 *
123 * The returned value points to internal storage inside the MacAddress
124 * object. It is only valid as long as the MacAddress, and its contents may
125 * change if the MacAddress is updated.
126 */
bytes()127 const uint8_t* bytes() const { return bytes_ + 2; }
128
129 /*
130 * Return the address as a uint64_t, in network byte order.
131 *
132 * The first two bytes will be 0, and the subsequent 6 bytes will contain
133 * the address in network byte order.
134 */
u64NBO()135 uint64_t u64NBO() const { return packedBytes(); }
136
137 /*
138 * Return the address as a uint64_t, in host byte order.
139 *
140 * The two most significant bytes will be 0, and the remaining 6 bytes will
141 * contain the address. The most significant of these 6 bytes will contain
142 * the first byte that appear on the wire, and the least significant byte
143 * will contain the last byte.
144 */
u64HBO()145 uint64_t u64HBO() const {
146 // Endian::big() does what we want here, even though we are converting
147 // from big-endian to host byte order. This swaps if and only if
148 // the host byte order is little endian.
149 return Endian::big(packedBytes());
150 }
151
152 /*
153 * Return a human-readable representation of the MAC address.
154 */
155 std::string toString() const;
156
157 /*
158 * Update the current MacAddress object from a human-readable string.
159 */
160 Expected<Unit, MacAddressFormatError> trySetFromString(StringPiece value);
161 void setFromString(StringPiece value);
parse(StringPiece str)162 void parse(StringPiece str) { setFromString(str); }
163
164 /*
165 * Update the current MacAddress object from a 6-byte binary representation.
166 */
167 Expected<Unit, MacAddressFormatError> trySetFromBinary(ByteRange value);
168 void setFromBinary(ByteRange value);
169
isBroadcast()170 bool isBroadcast() const { return *this == BROADCAST; }
isMulticast()171 bool isMulticast() const { return getByte(0) & 0x1; }
isUnicast()172 bool isUnicast() const { return !isMulticast(); }
173
174 /*
175 * Return true if this MAC address is locally administered.
176 *
177 * Locally administered addresses are assigned by the local network
178 * administrator, and are not guaranteed to be globally unique. (It is
179 * similar to IPv4's private address space.)
180 *
181 * Note that isLocallyAdministered() will return true for the broadcast
182 * address, since it has the locally administered bit set.
183 */
isLocallyAdministered()184 bool isLocallyAdministered() const { return getByte(0) & 0x2; }
185
186 // Comparison operators.
187
188 bool operator==(const MacAddress& other) const {
189 // All constructors and modifying methods make sure padding is 0,
190 // so we don't need to mask these bytes out when comparing here.
191 return packedBytes() == other.packedBytes();
192 }
193
194 bool operator<(const MacAddress& other) const {
195 return u64HBO() < other.u64HBO();
196 }
197
198 bool operator!=(const MacAddress& other) const { return !(*this == other); }
199
200 bool operator>(const MacAddress& other) const { return other < *this; }
201
202 bool operator>=(const MacAddress& other) const { return !(*this < other); }
203
204 bool operator<=(const MacAddress& other) const { return !(*this > other); }
205
206 private:
MacAddress(uint64_t valueNBO)207 explicit MacAddress(uint64_t valueNBO) {
208 memcpy(&bytes_, &valueNBO, 8);
209 // Set the pad bytes to 0.
210 // This allows us to easily compare two MacAddresses,
211 // without having to worry about differences in the padding.
212 bytes_[0] = 0;
213 bytes_[1] = 0;
214 }
215
216 template <typename OnError>
217 Expected<Unit, MacAddressFormatError> setFromString(
218 StringPiece value, OnError err);
219
220 template <typename OnError>
221 Expected<Unit, MacAddressFormatError> setFromBinary(
222 ByteRange value, OnError err);
223
224 /* We store the 6 bytes starting at bytes_[2] (most significant)
225 through bytes_[7] (least).
226 bytes_[0] and bytes_[1] are always equal to 0 to simplify comparisons.
227 */
228 unsigned char bytes_[8];
229
getByte(size_t index)230 inline uint64_t getByte(size_t index) const { return bytes_[index + 2]; }
231
packedBytes()232 uint64_t packedBytes() const {
233 uint64_t u64;
234 memcpy(&u64, bytes_, 8);
235 return u64;
236 }
237 };
238
239 /* Define toAppend() so to<string> will work */
240 template <class Tgt>
toAppend(MacAddress address,Tgt * result)241 typename std::enable_if<IsSomeString<Tgt>::value>::type toAppend(
242 MacAddress address, Tgt* result) {
243 toAppend(address.toString(), result);
244 }
245
246 std::ostream& operator<<(std::ostream& os, MacAddress address);
247
248 } // namespace folly
249
250 namespace std {
251
252 // Provide an implementation for std::hash<MacAddress>
253 template <>
254 struct hash<folly::MacAddress> {
255 size_t operator()(const folly::MacAddress& address) const {
256 return std::hash<uint64_t>()(address.u64HBO());
257 }
258 };
259
260 } // namespace std
261