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/code_utils.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/logging.hpp"
42 #include "common/random.hpp"
43 #include "common/settings.hpp"
44 #include "crypto/sha256.hpp"
45 #include "net/ip6_address.hpp"
46 
47 namespace ot {
48 namespace Utils {
49 
Slaac(Instance & aInstance)50 Slaac::Slaac(Instance &aInstance)
51     : InstanceLocator(aInstance)
52     , mEnabled(true)
53     , mFilter(nullptr)
54 {
55     memset(mAddresses, 0, sizeof(mAddresses));
56 }
57 
Enable(void)58 void Slaac::Enable(void)
59 {
60     VerifyOrExit(!mEnabled);
61 
62     otLogInfoUtil("SLAAC:: Enabling");
63     mEnabled = true;
64     Update(kModeAdd);
65 
66 exit:
67     return;
68 }
69 
Disable(void)70 void Slaac::Disable(void)
71 {
72     VerifyOrExit(mEnabled);
73 
74     otLogInfoUtil("SLAAC:: Disabling");
75     mEnabled = false;
76     Update(kModeRemove);
77 
78 exit:
79     return;
80 }
81 
SetFilter(otIp6SlaacPrefixFilter aFilter)82 void Slaac::SetFilter(otIp6SlaacPrefixFilter aFilter)
83 {
84     VerifyOrExit(aFilter != mFilter);
85 
86     mFilter = aFilter;
87     otLogInfoUtil("SLAAC: Filter %s", (mFilter != nullptr) ? "updated" : "disabled");
88 
89     VerifyOrExit(mEnabled);
90     Update(kModeAdd | kModeRemove);
91 
92 exit:
93     return;
94 }
95 
ShouldFilter(const Ip6::Prefix & aPrefix) const96 bool Slaac::ShouldFilter(const Ip6::Prefix &aPrefix) const
97 {
98     return (mFilter != nullptr) && mFilter(&GetInstance(), &aPrefix);
99 }
100 
HandleNotifierEvents(Events aEvents)101 void Slaac::HandleNotifierEvents(Events aEvents)
102 {
103     UpdateMode mode = kModeNone;
104 
105     VerifyOrExit(mEnabled);
106 
107     if (aEvents.Contains(kEventThreadNetdataChanged))
108     {
109         mode |= kModeAdd | kModeRemove;
110     }
111 
112     if (aEvents.Contains(kEventIp6AddressRemoved))
113     {
114         // When an IPv6 address is removed, we ensure to check if a SLAAC address
115         // needs to be added (replacing the removed address).
116         //
117         // Note that if an address matching a newly added on-mesh prefix (with
118         // SLAAC flag) is already present (e.g., user previously added an address
119         // with same prefix), the SLAAC module will not add a SLAAC address with same
120         // prefix. So on IPv6 address removal event, we check if SLAAC module need
121         // to add any addresses.
122 
123         mode |= kModeAdd;
124     }
125 
126     if (mode != kModeNone)
127     {
128         Update(mode);
129     }
130 
131 exit:
132     return;
133 }
134 
DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig & aConfig,const Ip6::Netif::UnicastAddress & aAddr)135 bool Slaac::DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig &aConfig,
136                                      const Ip6::Netif::UnicastAddress &     aAddr)
137 {
138     return (((aConfig.mOnMesh && (aAddr.mPrefixLength == aConfig.mPrefix.mLength)) ||
139              (!aConfig.mOnMesh && (aAddr.mPrefixLength == 128))) &&
140             (aAddr.GetAddress().MatchesPrefix(aConfig.GetPrefix())));
141 }
142 
Update(UpdateMode aMode)143 void Slaac::Update(UpdateMode aMode)
144 {
145     NetworkData::Iterator           iterator;
146     NetworkData::OnMeshPrefixConfig config;
147     bool                            found;
148 
149     if (aMode & kModeRemove)
150     {
151         // If enabled, remove any SLAAC addresses with no matching on-mesh prefix,
152         // otherwise (when disabled) remove all previously added SLAAC addresses.
153 
154         for (Ip6::Netif::UnicastAddress &slaacAddr : mAddresses)
155         {
156             if (!slaacAddr.mValid)
157             {
158                 continue;
159             }
160 
161             found = false;
162 
163             if (mEnabled)
164             {
165                 iterator = NetworkData::kIteratorInit;
166 
167                 while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
168                 {
169                     if (config.mDp)
170                     {
171                         // Skip domain prefix which is processed in MLE.
172                         continue;
173                     }
174 
175                     if (config.mSlaac && !ShouldFilter(config.GetPrefix()) &&
176                         DoesConfigMatchNetifAddr(config, slaacAddr))
177                     {
178                         found = true;
179                         break;
180                     }
181                 }
182             }
183 
184             if (!found)
185             {
186                 otLogInfoUtil("SLAAC: Removing address %s", slaacAddr.GetAddress().ToString().AsCString());
187 
188                 Get<ThreadNetif>().RemoveUnicastAddress(slaacAddr);
189                 slaacAddr.mValid = false;
190             }
191         }
192     }
193 
194     if ((aMode & kModeAdd) && mEnabled)
195     {
196         // Generate and add SLAAC addresses for any newly added on-mesh prefixes.
197 
198         iterator = NetworkData::kIteratorInit;
199 
200         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
201         {
202             Ip6::Prefix &prefix = config.GetPrefix();
203 
204             if (config.mDp || !config.mSlaac || ShouldFilter(prefix))
205             {
206                 continue;
207             }
208 
209             found = false;
210 
211             for (const Ip6::Netif::UnicastAddress &netifAddr : Get<ThreadNetif>().GetUnicastAddresses())
212             {
213                 if (DoesConfigMatchNetifAddr(config, netifAddr))
214                 {
215                     found = true;
216                     break;
217                 }
218             }
219 
220             if (!found)
221             {
222                 bool added = false;
223 
224                 for (Ip6::Netif::UnicastAddress &slaacAddr : mAddresses)
225                 {
226                     if (slaacAddr.mValid)
227                     {
228                         continue;
229                     }
230 
231                     slaacAddr.InitAsSlaacOrigin(config.mOnMesh ? prefix.mLength : 128, config.mPreferred);
232                     slaacAddr.GetAddress().SetPrefix(prefix);
233 
234                     IgnoreError(GenerateIid(slaacAddr));
235 
236                     otLogInfoUtil("SLAAC: Adding address %s", slaacAddr.GetAddress().ToString().AsCString());
237 
238                     Get<ThreadNetif>().AddUnicastAddress(slaacAddr);
239 
240                     added = true;
241                     break;
242                 }
243 
244                 if (!added)
245                 {
246                     otLogWarnUtil("SLAAC: Failed to add - max %d addresses supported and already in use",
247                                   OT_ARRAY_LENGTH(mAddresses));
248                 }
249             }
250         }
251     }
252 }
253 
GenerateIid(Ip6::Netif::UnicastAddress & aAddress,uint8_t * aNetworkId,uint8_t aNetworkIdLength,uint8_t * aDadCounter) const254 Error Slaac::GenerateIid(Ip6::Netif::UnicastAddress &aAddress,
255                          uint8_t *                   aNetworkId,
256                          uint8_t                     aNetworkIdLength,
257                          uint8_t *                   aDadCounter) const
258 {
259     /*
260      *  This method generates a semantically opaque IID per RFC 7217.
261      *
262      * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key)
263      *
264      *  - RID is random (but stable) Identifier.
265      *  - For pseudo-random function `F()` SHA-256 is used in this method.
266      *  - `Net_Iface` is set to constant string "wpan".
267      *  - `Network_ID` is not used if `aNetworkId` is nullptr (optional per RF-7217).
268      *  - The `secret_key` is randomly generated on first use (using true
269      *    random number generator) and saved in non-volatile settings for
270      *    future use.
271      *
272      */
273 
274     Error                error      = kErrorFailed;
275     const uint8_t        netIface[] = {'w', 'p', 'a', 'n'};
276     uint8_t              dadCounter = aDadCounter ? *aDadCounter : 0;
277     IidSecretKey         secretKey;
278     Crypto::Sha256       sha256;
279     Crypto::Sha256::Hash hash;
280 
281     static_assert(sizeof(hash) >= Ip6::InterfaceIdentifier::kSize,
282                   "SHA-256 hash size is too small to use as IPv6 address IID");
283 
284     GetIidSecretKey(secretKey);
285 
286     for (uint16_t count = 0; count < kMaxIidCreationAttempts; count++, dadCounter++)
287     {
288         sha256.Start();
289         sha256.Update(aAddress.mAddress.mFields.m8, BitVectorBytes(aAddress.mPrefixLength));
290 
291         if (aNetworkId)
292         {
293             sha256.Update(aNetworkId, aNetworkIdLength);
294         }
295 
296         sha256.Update(netIface);
297         sha256.Update(dadCounter);
298         sha256.Update(secretKey);
299         sha256.Finish(hash);
300 
301         aAddress.GetAddress().GetIid().SetBytes(hash.GetBytes());
302 
303         // If the IID is reserved, try again with a new dadCounter
304         if (aAddress.GetAddress().GetIid().IsReserved())
305         {
306             continue;
307         }
308 
309         if (aDadCounter)
310         {
311             *aDadCounter = dadCounter;
312         }
313 
314         // Exit and return the address if the IID is not reserved,
315         ExitNow(error = kErrorNone);
316     }
317 
318     otLogWarnUtil("SLAAC: Failed to generate a non-reserved IID after %d attempts", kMaxIidCreationAttempts);
319 
320 exit:
321     return error;
322 }
323 
GetIidSecretKey(IidSecretKey & aKey) const324 void Slaac::GetIidSecretKey(IidSecretKey &aKey) const
325 {
326     Error error;
327 
328     error = Get<Settings>().Read<Settings::SlaacIidSecretKey>(aKey);
329     VerifyOrExit(error != kErrorNone);
330 
331     // If there is no previously saved secret key, generate
332     // a random one and save it.
333 
334     error = Random::Crypto::FillBuffer(aKey.m8, sizeof(IidSecretKey));
335 
336     if (error != kErrorNone)
337     {
338         IgnoreError(Random::Crypto::FillBuffer(aKey.m8, sizeof(IidSecretKey)));
339     }
340 
341     IgnoreError(Get<Settings>().Save<Settings::SlaacIidSecretKey>(aKey));
342 
343     otLogInfoUtil("SLAAC: Generated and saved secret key");
344 
345 exit:
346     return;
347 }
348 
349 } // namespace Utils
350 } // namespace ot
351 
352 #endif // OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
353