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