1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements the Thread IPv6 global addresses configuration utilities.
32 */
33
34 #include "slaac_address.hpp"
35
36 #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
37
38 #include "common/array.hpp"
39 #include "common/code_utils.hpp"
40 #include "common/instance.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/log.hpp"
43 #include "common/random.hpp"
44 #include "common/settings.hpp"
45 #include "crypto/sha256.hpp"
46 #include "net/ip6_address.hpp"
47
48 namespace ot {
49 namespace Utils {
50
51 RegisterLogModule("Slaac");
52
Slaac(Instance & aInstance)53 Slaac::Slaac(Instance &aInstance)
54 : InstanceLocator(aInstance)
55 , mEnabled(true)
56 , mFilter(nullptr)
57 {
58 memset(mAddresses, 0, sizeof(mAddresses));
59 }
60
Enable(void)61 void Slaac::Enable(void)
62 {
63 VerifyOrExit(!mEnabled);
64
65 LogInfo("Enabling");
66 mEnabled = true;
67 Update(kModeAdd);
68
69 exit:
70 return;
71 }
72
Disable(void)73 void Slaac::Disable(void)
74 {
75 VerifyOrExit(mEnabled);
76
77 LogInfo("Disabling");
78 mEnabled = false;
79 Update(kModeRemove);
80
81 exit:
82 return;
83 }
84
SetFilter(otIp6SlaacPrefixFilter aFilter)85 void Slaac::SetFilter(otIp6SlaacPrefixFilter aFilter)
86 {
87 VerifyOrExit(aFilter != mFilter);
88
89 mFilter = aFilter;
90 LogInfo("Filter %s", (mFilter != nullptr) ? "updated" : "disabled");
91
92 VerifyOrExit(mEnabled);
93 Update(kModeAdd | kModeRemove);
94
95 exit:
96 return;
97 }
98
ShouldFilter(const Ip6::Prefix & aPrefix) const99 bool Slaac::ShouldFilter(const Ip6::Prefix &aPrefix) const
100 {
101 return (mFilter != nullptr) && mFilter(&GetInstance(), &aPrefix);
102 }
103
HandleNotifierEvents(Events aEvents)104 void Slaac::HandleNotifierEvents(Events aEvents)
105 {
106 UpdateMode mode = kModeNone;
107
108 VerifyOrExit(mEnabled);
109
110 if (aEvents.Contains(kEventThreadNetdataChanged))
111 {
112 mode |= kModeAdd | kModeRemove;
113 }
114
115 if (aEvents.Contains(kEventIp6AddressRemoved))
116 {
117 // When an IPv6 address is removed, we ensure to check if a SLAAC address
118 // needs to be added (replacing the removed address).
119 //
120 // Note that if an address matching a newly added on-mesh prefix (with
121 // SLAAC flag) is already present (e.g., user previously added an address
122 // with same prefix), the SLAAC module will not add a SLAAC address with same
123 // prefix. So on IPv6 address removal event, we check if SLAAC module need
124 // to add any addresses.
125
126 mode |= kModeAdd;
127 }
128
129 if (mode != kModeNone)
130 {
131 Update(mode);
132 }
133
134 exit:
135 return;
136 }
137
DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig & aConfig,const Ip6::Netif::UnicastAddress & aAddr)138 bool Slaac::DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig &aConfig,
139 const Ip6::Netif::UnicastAddress & aAddr)
140 {
141 return (((aConfig.mOnMesh && (aAddr.mPrefixLength == aConfig.mPrefix.mLength)) ||
142 (!aConfig.mOnMesh && (aAddr.mPrefixLength == 128))) &&
143 (aAddr.GetAddress().MatchesPrefix(aConfig.GetPrefix())));
144 }
145
Update(UpdateMode aMode)146 void Slaac::Update(UpdateMode aMode)
147 {
148 NetworkData::Iterator iterator;
149 NetworkData::OnMeshPrefixConfig config;
150 bool found;
151
152 if (aMode & kModeRemove)
153 {
154 // If enabled, remove any SLAAC addresses with no matching on-mesh prefix,
155 // otherwise (when disabled) remove all previously added SLAAC addresses.
156
157 for (Ip6::Netif::UnicastAddress &slaacAddr : mAddresses)
158 {
159 if (!slaacAddr.mValid)
160 {
161 continue;
162 }
163
164 found = false;
165
166 if (mEnabled)
167 {
168 iterator = NetworkData::kIteratorInit;
169
170 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
171 {
172 if (config.mDp)
173 {
174 // Skip domain prefix which is processed in MLE.
175 continue;
176 }
177
178 if (config.mSlaac && !ShouldFilter(config.GetPrefix()) &&
179 DoesConfigMatchNetifAddr(config, slaacAddr))
180 {
181 found = true;
182 break;
183 }
184 }
185 }
186
187 if (!found)
188 {
189 LogInfo("Removing address %s", slaacAddr.GetAddress().ToString().AsCString());
190
191 Get<ThreadNetif>().RemoveUnicastAddress(slaacAddr);
192 slaacAddr.mValid = false;
193 }
194 }
195 }
196
197 if ((aMode & kModeAdd) && mEnabled)
198 {
199 // Generate and add SLAAC addresses for any newly added on-mesh prefixes.
200
201 iterator = NetworkData::kIteratorInit;
202
203 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
204 {
205 Ip6::Prefix &prefix = config.GetPrefix();
206
207 if (config.mDp || !config.mSlaac || ShouldFilter(prefix))
208 {
209 continue;
210 }
211
212 found = false;
213
214 for (const Ip6::Netif::UnicastAddress &netifAddr : Get<ThreadNetif>().GetUnicastAddresses())
215 {
216 if (DoesConfigMatchNetifAddr(config, netifAddr))
217 {
218 found = true;
219 break;
220 }
221 }
222
223 if (!found)
224 {
225 bool added = false;
226
227 for (Ip6::Netif::UnicastAddress &slaacAddr : mAddresses)
228 {
229 if (slaacAddr.mValid)
230 {
231 continue;
232 }
233
234 slaacAddr.InitAsSlaacOrigin(config.mOnMesh ? prefix.mLength : 128, config.mPreferred);
235 slaacAddr.GetAddress().SetPrefix(prefix);
236
237 IgnoreError(GenerateIid(slaacAddr));
238
239 LogInfo("Adding address %s", slaacAddr.GetAddress().ToString().AsCString());
240
241 Get<ThreadNetif>().AddUnicastAddress(slaacAddr);
242
243 added = true;
244 break;
245 }
246
247 if (!added)
248 {
249 LogWarn("Failed to add - max %d addresses supported and already in use",
250 GetArrayLength(mAddresses));
251 }
252 }
253 }
254 }
255 }
256
GenerateIid(Ip6::Netif::UnicastAddress & aAddress,uint8_t * aNetworkId,uint8_t aNetworkIdLength,uint8_t * aDadCounter) const257 Error Slaac::GenerateIid(Ip6::Netif::UnicastAddress &aAddress,
258 uint8_t * aNetworkId,
259 uint8_t aNetworkIdLength,
260 uint8_t * aDadCounter) const
261 {
262 /*
263 * This method generates a semantically opaque IID per RFC 7217.
264 *
265 * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key)
266 *
267 * - RID is random (but stable) Identifier.
268 * - For pseudo-random function `F()` SHA-256 is used in this method.
269 * - `Net_Iface` is set to constant string "wpan".
270 * - `Network_ID` is not used if `aNetworkId` is `nullptr` (optional per RF-7217).
271 * - The `secret_key` is randomly generated on first use (using true
272 * random number generator) and saved in non-volatile settings for
273 * future use.
274 *
275 */
276
277 Error error = kErrorFailed;
278 const uint8_t netIface[] = {'w', 'p', 'a', 'n'};
279 uint8_t dadCounter = aDadCounter ? *aDadCounter : 0;
280 IidSecretKey secretKey;
281 Crypto::Sha256 sha256;
282 Crypto::Sha256::Hash hash;
283
284 static_assert(sizeof(hash) >= Ip6::InterfaceIdentifier::kSize,
285 "SHA-256 hash size is too small to use as IPv6 address IID");
286
287 GetIidSecretKey(secretKey);
288
289 for (uint16_t count = 0; count < kMaxIidCreationAttempts; count++, dadCounter++)
290 {
291 sha256.Start();
292 sha256.Update(aAddress.mAddress.mFields.m8, BitVectorBytes(aAddress.mPrefixLength));
293
294 if (aNetworkId)
295 {
296 sha256.Update(aNetworkId, aNetworkIdLength);
297 }
298
299 sha256.Update(netIface);
300 sha256.Update(dadCounter);
301 sha256.Update(secretKey);
302 sha256.Finish(hash);
303
304 aAddress.GetAddress().GetIid().SetBytes(hash.GetBytes());
305
306 // If the IID is reserved, try again with a new dadCounter
307 if (aAddress.GetAddress().GetIid().IsReserved())
308 {
309 continue;
310 }
311
312 if (aDadCounter)
313 {
314 *aDadCounter = dadCounter;
315 }
316
317 // Exit and return the address if the IID is not reserved,
318 ExitNow(error = kErrorNone);
319 }
320
321 LogWarn("Failed to generate a non-reserved IID after %d attempts", kMaxIidCreationAttempts);
322
323 exit:
324 return error;
325 }
326
GetIidSecretKey(IidSecretKey & aKey) const327 void Slaac::GetIidSecretKey(IidSecretKey &aKey) const
328 {
329 Error error;
330
331 error = Get<Settings>().Read<Settings::SlaacIidSecretKey>(aKey);
332 VerifyOrExit(error != kErrorNone);
333
334 // If there is no previously saved secret key, generate
335 // a random one and save it.
336
337 error = Random::Crypto::FillBuffer(aKey.m8, sizeof(IidSecretKey));
338
339 if (error != kErrorNone)
340 {
341 IgnoreError(Random::Crypto::FillBuffer(aKey.m8, sizeof(IidSecretKey)));
342 }
343
344 IgnoreError(Get<Settings>().Save<Settings::SlaacIidSecretKey>(aKey));
345
346 LogInfo("Generated and saved secret key");
347
348 exit:
349 return;
350 }
351
352 } // namespace Utils
353 } // namespace ot
354
355 #endif // OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
356