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