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