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/locator_getters.hpp"
41 #include "common/log.hpp"
42 #include "common/numeric_limits.hpp"
43 #include "common/random.hpp"
44 #include "common/settings.hpp"
45 #include "crypto/sha256.hpp"
46 #include "instance/instance.hpp"
47 #include "net/ip6_address.hpp"
48
49 namespace ot {
50 namespace Utils {
51
52 RegisterLogModule("Slaac");
53
Slaac(Instance & aInstance)54 Slaac::Slaac(Instance &aInstance)
55 : InstanceLocator(aInstance)
56 , mEnabled(true)
57 , mFilter(nullptr)
58 , mTimer(aInstance)
59 {
60 ClearAllBytes(mSlaacAddresses);
61 }
62
Enable(void)63 void Slaac::Enable(void)
64 {
65 VerifyOrExit(!mEnabled);
66
67 mEnabled = true;
68 LogInfo("Enabled");
69
70 AddAddresses();
71
72 exit:
73 return;
74 }
75
Disable(void)76 void Slaac::Disable(void)
77 {
78 VerifyOrExit(mEnabled);
79
80 RemoveAllAddresses();
81 mTimer.Stop();
82
83 LogInfo("Disabled");
84 mEnabled = false;
85
86 exit:
87 return;
88 }
89
SetFilter(PrefixFilter aFilter)90 void Slaac::SetFilter(PrefixFilter aFilter)
91 {
92 VerifyOrExit(aFilter != mFilter);
93
94 mFilter = aFilter;
95 LogInfo("Filter %s", (mFilter != nullptr) ? "updated" : "disabled");
96
97 VerifyOrExit(mEnabled);
98 RemoveOrDeprecateAddresses();
99 AddAddresses();
100
101 exit:
102 return;
103 }
104
FindDomainIdFor(const Ip6::Address & aAddress,uint8_t & aDomainId) const105 Error Slaac::FindDomainIdFor(const Ip6::Address &aAddress, uint8_t &aDomainId) const
106 {
107 Error error = kErrorNotFound;
108
109 for (const SlaacAddress &slaacAddr : mSlaacAddresses)
110 {
111 if (!slaacAddr.IsInUse() || !slaacAddr.IsDeprecating())
112 {
113 continue;
114 }
115
116 if (aAddress.PrefixMatch(slaacAddr.GetAddress()) >= Ip6::NetworkPrefix::kLength)
117 {
118 aDomainId = slaacAddr.GetDomainId();
119 error = kErrorNone;
120 break;
121 }
122 }
123
124 return error;
125 }
126
IsSlaac(const NetworkData::OnMeshPrefixConfig & aConfig) const127 bool Slaac::IsSlaac(const NetworkData::OnMeshPrefixConfig &aConfig) const
128 {
129 return aConfig.mSlaac && !aConfig.mDp && (aConfig.GetPrefix().GetLength() == Ip6::NetworkPrefix::kLength);
130 }
131
IsFiltered(const NetworkData::OnMeshPrefixConfig & aConfig) const132 bool Slaac::IsFiltered(const NetworkData::OnMeshPrefixConfig &aConfig) const
133 {
134 return (mFilter != nullptr) ? mFilter(&GetInstance(), &aConfig.GetPrefix()) : false;
135 }
136
HandleNotifierEvents(Events aEvents)137 void Slaac::HandleNotifierEvents(Events aEvents)
138 {
139 VerifyOrExit(mEnabled);
140
141 if (aEvents.Contains(kEventThreadNetdataChanged))
142 {
143 RemoveOrDeprecateAddresses();
144 AddAddresses();
145 ExitNow();
146 }
147
148 if (aEvents.Contains(kEventIp6AddressRemoved))
149 {
150 // When an IPv6 address is removed, we ensure to check if a SLAAC address
151 // needs to be added (replacing the removed address).
152 //
153 // Note that if an address matching a newly added on-mesh prefix (with
154 // SLAAC flag) is already present (e.g., user previously added an address
155 // with same prefix), the SLAAC module will not add a SLAAC address with same
156 // prefix. So on IPv6 address removal event, we check if SLAAC module need
157 // to add any addresses.
158
159 AddAddresses();
160 }
161
162 exit:
163 return;
164 }
165
DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig & aConfig,const Ip6::Netif::UnicastAddress & aAddr)166 bool Slaac::DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig &aConfig,
167 const Ip6::Netif::UnicastAddress &aAddr)
168 {
169 return (((aConfig.mOnMesh && (aAddr.mPrefixLength == aConfig.mPrefix.mLength)) ||
170 (!aConfig.mOnMesh && (aAddr.mPrefixLength == 128))) &&
171 (aAddr.GetAddress().MatchesPrefix(aConfig.GetPrefix())));
172 }
173
RemoveOrDeprecateAddresses(void)174 void Slaac::RemoveOrDeprecateAddresses(void)
175 {
176 // Remove or deprecate any SLAAC addresses with no matching on-mesh
177 // prefix in Network Data.
178
179 for (SlaacAddress &slaacAddr : mSlaacAddresses)
180 {
181 NetworkData::Iterator iterator;
182 NetworkData::OnMeshPrefixConfig config;
183 bool found = false;
184
185 if (!slaacAddr.IsInUse())
186 {
187 continue;
188 }
189
190 iterator = NetworkData::kIteratorInit;
191
192 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
193 {
194 if (IsSlaac(config) && DoesConfigMatchNetifAddr(config, slaacAddr))
195 {
196 found = true;
197 break;
198 }
199 }
200
201 if (found)
202 {
203 if (IsFiltered(config))
204 {
205 RemoveAddress(slaacAddr);
206 }
207 }
208 else if (!slaacAddr.IsDeprecating())
209 {
210 if (slaacAddr.mPreferred)
211 {
212 DeprecateAddress(slaacAddr);
213 }
214 else
215 {
216 RemoveAddress(slaacAddr);
217 }
218 }
219 }
220 }
221
DeprecateAddress(SlaacAddress & aAddress)222 void Slaac::DeprecateAddress(SlaacAddress &aAddress)
223 {
224 LogAddress(kDeprecating, aAddress);
225
226 aAddress.SetExpirationTime(TimerMilli::GetNow() + kDeprecationInterval);
227 mTimer.FireAtIfEarlier(aAddress.GetExpirationTime());
228
229 Get<ThreadNetif>().UpdatePreferredFlagOn(aAddress, false);
230 }
231
RemoveAllAddresses(void)232 void Slaac::RemoveAllAddresses(void)
233 {
234 for (SlaacAddress &slaacAddr : mSlaacAddresses)
235 {
236 if (slaacAddr.IsInUse())
237 {
238 RemoveAddress(slaacAddr);
239 }
240 }
241 }
242
RemoveAddress(SlaacAddress & aAddress)243 void Slaac::RemoveAddress(SlaacAddress &aAddress)
244 {
245 LogAddress(kRemoving, aAddress);
246
247 Get<ThreadNetif>().RemoveUnicastAddress(aAddress);
248 aAddress.MarkAsNotInUse();
249 }
250
AddAddresses(void)251 void Slaac::AddAddresses(void)
252 {
253 NetworkData::Iterator iterator;
254 NetworkData::OnMeshPrefixConfig config;
255
256 // Generate and add SLAAC addresses for any newly added on-mesh prefixes.
257
258 iterator = NetworkData::kIteratorInit;
259
260 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
261 {
262 bool found = false;
263
264 if (!IsSlaac(config) || IsFiltered(config))
265 {
266 continue;
267 }
268
269 for (SlaacAddress &slaacAddr : mSlaacAddresses)
270 {
271 if (slaacAddr.IsInUse() && DoesConfigMatchNetifAddr(config, slaacAddr))
272 {
273 if (slaacAddr.IsDeprecating() && config.mPreferred)
274 {
275 slaacAddr.MarkAsNotDeprecating();
276 Get<ThreadNetif>().UpdatePreferredFlagOn(slaacAddr, true);
277 }
278
279 found = true;
280 break;
281 }
282 }
283
284 if (found)
285 {
286 continue;
287 }
288
289 for (const Ip6::Netif::UnicastAddress &netifAddr : Get<ThreadNetif>().GetUnicastAddresses())
290 {
291 if (DoesConfigMatchNetifAddr(config, netifAddr))
292 {
293 found = true;
294 break;
295 }
296 }
297
298 if (!found)
299 {
300 AddAddressFor(config);
301 }
302 }
303 }
304
AddAddressFor(const NetworkData::OnMeshPrefixConfig & aConfig)305 void Slaac::AddAddressFor(const NetworkData::OnMeshPrefixConfig &aConfig)
306 {
307 SlaacAddress *newAddress = nullptr;
308 uint8_t dadCounter = 0;
309 uint8_t domainId = 0;
310
311 for (SlaacAddress &slaacAddr : mSlaacAddresses)
312 {
313 // If all address entries are in-use, and we have any
314 // deprecating addresses, we select one with earliest
315 // expiration time.
316
317 if (!slaacAddr.IsInUse())
318 {
319 newAddress = &slaacAddr;
320 break;
321 }
322
323 if (slaacAddr.IsDeprecating())
324 {
325 if ((newAddress == nullptr) || slaacAddr.GetExpirationTime() < newAddress->GetExpirationTime())
326 {
327 newAddress = &slaacAddr;
328 }
329 }
330 }
331
332 if (newAddress == nullptr)
333 {
334 LogWarn("Failed to add - already have max %u addresses", kNumSlaacAddresses);
335 ExitNow();
336 }
337
338 if (newAddress->IsInUse())
339 {
340 RemoveAddress(*newAddress);
341 }
342
343 newAddress->MarkAsNotDeprecating();
344 newAddress->InitAsSlaacOrigin(aConfig.mOnMesh ? aConfig.GetPrefix().mLength : 128, aConfig.mPreferred);
345 newAddress->GetAddress().SetPrefix(aConfig.GetPrefix());
346
347 IgnoreError(Get<NetworkData::Leader>().FindDomainIdFor(aConfig.GetPrefix(), domainId));
348 newAddress->SetDomainId(domainId);
349
350 IgnoreError(GenerateIid(*newAddress, dadCounter));
351
352 LogAddress(kAdding, *newAddress);
353
354 Get<ThreadNetif>().AddUnicastAddress(*newAddress);
355
356 exit:
357 return;
358 }
359
HandleTimer(void)360 void Slaac::HandleTimer(void)
361 {
362 NextFireTime nextTime;
363
364 for (SlaacAddress &slaacAddr : mSlaacAddresses)
365 {
366 if (!slaacAddr.IsInUse() || !slaacAddr.IsDeprecating())
367 {
368 continue;
369 }
370
371 if (slaacAddr.GetExpirationTime() <= nextTime.GetNow())
372 {
373 RemoveAddress(slaacAddr);
374 }
375 else
376 {
377 nextTime.UpdateIfEarlier(slaacAddr.GetExpirationTime());
378 }
379 }
380
381 mTimer.FireAtIfEarlier(nextTime);
382 }
383
GenerateIid(Ip6::Netif::UnicastAddress & aAddress,uint8_t & aDadCounter) const384 Error Slaac::GenerateIid(Ip6::Netif::UnicastAddress &aAddress, uint8_t &aDadCounter) const
385 {
386 /*
387 * This method generates a semantically opaque IID per RFC 7217.
388 *
389 * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key)
390 *
391 * - RID is random (but stable) Identifier.
392 * - For pseudo-random function `F()` SHA-256 is used in this method.
393 * - `Net_Iface` is set to constant string "wpan".
394 * - `Network_ID` is not used (optional per RFC 7217).
395 * - The `secret_key` is randomly generated on first use (using true
396 * random number generator) and saved in non-volatile settings for
397 * future use.
398 *
399 */
400
401 Error error = kErrorFailed;
402 const uint8_t netIface[] = {'w', 'p', 'a', 'n'};
403 IidSecretKey secretKey;
404 Crypto::Sha256 sha256;
405 Crypto::Sha256::Hash hash;
406
407 static_assert(sizeof(hash) >= Ip6::InterfaceIdentifier::kSize,
408 "SHA-256 hash size is too small to use as IPv6 address IID");
409
410 GetIidSecretKey(secretKey);
411
412 for (uint16_t count = 0; count < kMaxIidCreationAttempts; count++, aDadCounter++)
413 {
414 sha256.Start();
415 sha256.Update(aAddress.mAddress.mFields.m8, BytesForBitSize(aAddress.mPrefixLength));
416
417 sha256.Update(netIface);
418 sha256.Update(aDadCounter);
419 sha256.Update(secretKey);
420 sha256.Finish(hash);
421
422 aAddress.GetAddress().GetIid().SetBytes(hash.GetBytes());
423
424 // If the IID is reserved, try again with a new dadCounter
425 if (aAddress.GetAddress().GetIid().IsReserved())
426 {
427 continue;
428 }
429
430 // Exit and return the address if the IID is not reserved,
431 ExitNow(error = kErrorNone);
432 }
433
434 LogWarn("Failed to generate a non-reserved IID after %d attempts", kMaxIidCreationAttempts);
435
436 exit:
437 return error;
438 }
439
440 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
LogAddress(Action aAction,const SlaacAddress & aAddress)441 void Slaac::LogAddress(Action aAction, const SlaacAddress &aAddress)
442 {
443 static const char *const kActionStrings[] = {
444 "Adding", // (0) kAdding
445 "Removing", // (1) kRemoving
446 "Deprecating", // (2) kDeprecating
447 };
448
449 static_assert(kAdding == 0, "kAdding value is incorrect");
450 static_assert(kRemoving == 1, "kRemoving value is incorrect");
451 static_assert(kDeprecating == 2, "kDeprecating value is incorrect");
452
453 LogInfo("%s %s", kActionStrings[aAction], aAddress.GetAddress().ToString().AsCString());
454 }
455 #else
LogAddress(Action,const SlaacAddress &)456 void Slaac::LogAddress(Action, const SlaacAddress &) {}
457 #endif
458
GetIidSecretKey(IidSecretKey & aKey) const459 void Slaac::GetIidSecretKey(IidSecretKey &aKey) const
460 {
461 Error error;
462
463 error = Get<Settings>().Read<Settings::SlaacIidSecretKey>(aKey);
464 VerifyOrExit(error != kErrorNone);
465
466 // If there is no previously saved secret key, generate
467 // a random one and save it.
468
469 error = Random::Crypto::Fill(aKey);
470
471 if (error != kErrorNone)
472 {
473 IgnoreError(Random::Crypto::Fill(aKey));
474 }
475
476 IgnoreError(Get<Settings>().Save<Settings::SlaacIidSecretKey>(aKey));
477
478 LogInfo("Generated and saved secret key");
479
480 exit:
481 return;
482 }
483
484 } // namespace Utils
485 } // namespace ot
486
487 #endif // OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
488