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