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