45892826e955209d1d57cc63f2611088f5479d2d
[folly.git] / folly / MacAddress.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
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 <ostream>
20
21 #include <folly/Exception.h>
22 #include <folly/IPAddressV6.h>
23 #include <folly/String.h>
24
25 using std::invalid_argument;
26 using std::string;
27
28 namespace folly {
29
30 const MacAddress MacAddress::BROADCAST{Endian::big(uint64_t(0xffffffffffffU))};
31 const MacAddress MacAddress::ZERO;
32
33 MacAddress::MacAddress(StringPiece str) {
34   memset(&bytes_, 0, 8);
35   parse(str);
36 }
37
38 MacAddress MacAddress::createMulticast(IPAddressV6 v6addr) {
39   // This method should only be used for multicast addresses.
40   DCHECK(v6addr.isMulticast());
41
42   uint8_t bytes[SIZE];
43   bytes[0] = 0x33;
44   bytes[1] = 0x33;
45   memcpy(bytes + 2, v6addr.bytes() + 12, 4);
46   return fromBinary(ByteRange(bytes, SIZE));
47 }
48
49 string MacAddress::toString() const {
50   static const char hexValues[] = "0123456789abcdef";
51   string result;
52   result.resize(17);
53   result[0] = hexValues[getByte(0) >> 4];
54   result[1] = hexValues[getByte(0) & 0xf];
55   result[2] = ':';
56   result[3] = hexValues[getByte(1) >> 4];
57   result[4] = hexValues[getByte(1) & 0xf];
58   result[5] = ':';
59   result[6] = hexValues[getByte(2) >> 4];
60   result[7] = hexValues[getByte(2) & 0xf];
61   result[8] = ':';
62   result[9] = hexValues[getByte(3) >> 4];
63   result[10] = hexValues[getByte(3) & 0xf];
64   result[11] = ':';
65   result[12] = hexValues[getByte(4) >> 4];
66   result[13] = hexValues[getByte(4) & 0xf];
67   result[14] = ':';
68   result[15] = hexValues[getByte(5) >> 4];
69   result[16] = hexValues[getByte(5) & 0xf];
70   return result;
71 }
72
73 void MacAddress::parse(StringPiece str) {
74   // Helper function to convert a single hex char into an integer
75   auto isSeparatorChar = [](char c) {
76     return c == ':' || c == '-';
77   };
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(to<string>("invalid MAC address \"", str,
84                                         "\": not enough digits"));
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(to<string>("invalid MAC address \"", str,
92                                           "\": not enough digits"));
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(to<string>("invalid MAC address \"", str,
100                                         "\": contains non-hex digit"));
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(to<string>("invalid MAC address \"", str,
119                                             "\": contains non-hex digit"));
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(to<string>("invalid MAC address \"", str,
132                                       "\": found trailing characters"));
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
140 void MacAddress::setFromBinary(ByteRange value) {
141   if (value.size() != SIZE) {
142     throw invalid_argument(to<string>("MAC address must be 6 bytes "
143                                       "long, got ", value.size()));
144   }
145   memcpy(bytes_ + 2, value.begin(), SIZE);
146 }
147
148 std::ostream& operator<<(std::ostream& os, MacAddress address) {
149   os << address.toString();
150   return os;
151 }
152
153 } // folly