• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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