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