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