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