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 MPL.
32 */
33
34 #include "ip6_mpl.hpp"
35
36 #include "common/code_utils.hpp"
37 #include "common/instance.hpp"
38 #include "common/locator_getters.hpp"
39 #include "common/message.hpp"
40 #include "common/random.hpp"
41 #include "common/serial_number.hpp"
42 #include "net/ip6.hpp"
43
44 namespace ot {
45 namespace Ip6 {
46
Mpl(Instance & aInstance)47 Mpl::Mpl(Instance &aInstance)
48 : InstanceLocator(aInstance)
49 , mMatchingAddress(nullptr)
50 , mSeedSetTimer(aInstance, Mpl::HandleSeedSetTimer)
51 , mSeedId(0)
52 , mSequence(0)
53 #if OPENTHREAD_FTD
54 , mRetransmissionTimer(aInstance, Mpl::HandleRetransmissionTimer)
55 , mTimerExpirations(0)
56 #endif
57 {
58 memset(mSeedSet, 0, sizeof(mSeedSet));
59 }
60
InitOption(OptionMpl & aOption,const Address & aAddress)61 void Mpl::InitOption(OptionMpl &aOption, const Address &aAddress)
62 {
63 aOption.Init();
64 aOption.SetSequence(mSequence++);
65
66 // Check if Seed Id can be elided.
67 if (mMatchingAddress && aAddress == *mMatchingAddress)
68 {
69 aOption.SetSeedIdLength(OptionMpl::kSeedIdLength0);
70
71 // Decrease default option length.
72 aOption.SetLength(aOption.GetLength() - sizeof(mSeedId));
73 }
74 else
75 {
76 aOption.SetSeedIdLength(OptionMpl::kSeedIdLength2);
77 aOption.SetSeedId(mSeedId);
78 }
79 }
80
ProcessOption(Message & aMessage,const Address & aAddress,bool aIsOutbound,bool & aReceive)81 Error Mpl::ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound, bool &aReceive)
82 {
83 Error error;
84 OptionMpl option;
85
86 VerifyOrExit(aMessage.ReadBytes(aMessage.GetOffset(), &option, sizeof(option)) >= OptionMpl::kMinLength &&
87 (option.GetSeedIdLength() == OptionMpl::kSeedIdLength0 ||
88 option.GetSeedIdLength() == OptionMpl::kSeedIdLength2),
89 error = kErrorParse);
90
91 if (option.GetSeedIdLength() == OptionMpl::kSeedIdLength0)
92 {
93 // Retrieve MPL Seed Id from the IPv6 Source Address.
94 option.SetSeedId(HostSwap16(aAddress.mFields.m16[7]));
95 }
96
97 // Check if the MPL Data Message is new.
98 error = UpdateSeedSet(option.GetSeedId(), option.GetSequence());
99
100 if (error == kErrorNone)
101 {
102 #if OPENTHREAD_FTD
103 AddBufferedMessage(aMessage, option.GetSeedId(), option.GetSequence(), aIsOutbound);
104 #endif
105 }
106 else if (aIsOutbound)
107 {
108 aReceive = false;
109 // In case MPL Data Message is generated locally, ignore potential error of the MPL Seed Set
110 // to allow subsequent retransmissions with the same sequence number.
111 ExitNow(error = kErrorNone);
112 }
113
114 exit:
115 return error;
116 }
117
118 /*
119 * mSeedSet stores recently received (Seed ID, Sequence) values.
120 * - (Seed ID, Sequence) values are grouped by Seed ID.
121 * - (Seed ID, Sequence) groups are not sorted by Seed ID relative to other groups.
122 * - (Seed ID, Sequence) values within a group are sorted by Sequence.
123 * - All unused entries (marked by 0 lifetime) are grouped at the end.
124 *
125 * Update process:
126 *
127 * - Eviction selection:
128 * - If there are unused entries, mark the first unused entry for "eviction"
129 * - Otherwise, pick the first entry of the group that has the most entries.
130 *
131 * - Insert selection:
132 * - If there exists a group matching the Seed ID, select insert entry based on Sequence ordering.
133 * - Otherwise, set insert entry equal to evict entry.
134 *
135 * - If evicting a valid entry (lifetime non-zero):
136 * - Require group size to have >=2 entries.
137 * - If inserting into existing group, require Sequence to be larger than oldest stored Sequence in group.
138 */
UpdateSeedSet(uint16_t aSeedId,uint8_t aSequence)139 Error Mpl::UpdateSeedSet(uint16_t aSeedId, uint8_t aSequence)
140 {
141 Error error = kErrorNone;
142 SeedEntry *insert = nullptr;
143 SeedEntry *group = mSeedSet;
144 SeedEntry *evict = mSeedSet;
145 uint8_t curCount = 0;
146 uint8_t maxCount = 0;
147
148 for (uint32_t i = 0; i < kNumSeedEntries; i++, curCount++)
149 {
150 if (mSeedSet[i].mLifetime == 0)
151 {
152 // unused entries exist
153
154 if (insert == nullptr)
155 {
156 // no existing group, set insert and evict entry to be the same
157 insert = &mSeedSet[i];
158 }
159
160 // mark first unused entry for eviction
161 evict = &mSeedSet[i];
162 break;
163 }
164
165 if (mSeedSet[i].mSeedId != group->mSeedId)
166 {
167 // processing new group
168
169 if (aSeedId == group->mSeedId && insert == nullptr)
170 {
171 // insert at end of existing group
172 insert = &mSeedSet[i];
173 curCount++;
174 }
175
176 if (maxCount < curCount)
177 {
178 // look to evict an entry from the seed with the most entries
179 evict = group;
180 maxCount = curCount;
181 }
182
183 group = &mSeedSet[i];
184 curCount = 0;
185 }
186
187 if (aSeedId == mSeedSet[i].mSeedId)
188 {
189 // have existing entries for aSeedId
190
191 if (aSequence == mSeedSet[i].mSequence)
192 {
193 // already received, drop message
194 ExitNow(error = kErrorDrop);
195 }
196 else if (insert == nullptr && SerialNumber::IsLess(aSequence, mSeedSet[i].mSequence))
197 {
198 // insert in order of sequence
199 insert = &mSeedSet[i];
200 curCount++;
201 }
202 }
203 }
204
205 if (evict->mLifetime != 0)
206 {
207 // no free entries available, look to evict an existing entry
208 OT_ASSERT(curCount != 0);
209
210 if (aSeedId == group->mSeedId && insert == nullptr)
211 {
212 // insert at end of existing group
213 insert = &mSeedSet[kNumSeedEntries];
214 curCount++;
215 }
216
217 if (maxCount < curCount)
218 {
219 // look to evict an entry from the seed with the most entries
220 evict = group;
221 maxCount = curCount;
222 }
223
224 // require evict group size to have >= 2 entries
225 VerifyOrExit(maxCount > 1, error = kErrorDrop);
226
227 if (insert == nullptr)
228 {
229 // no existing entries for aSeedId
230 insert = evict;
231 }
232 else
233 {
234 // require Sequence to be larger than oldest stored Sequence in group
235 VerifyOrExit(insert > mSeedSet && aSeedId == (insert - 1)->mSeedId, error = kErrorDrop);
236 }
237 }
238
239 if (evict > insert)
240 {
241 OT_ASSERT(insert >= mSeedSet);
242 memmove(insert + 1, insert, static_cast<size_t>(evict - insert) * sizeof(SeedEntry));
243 }
244 else if (evict < insert)
245 {
246 OT_ASSERT(evict >= mSeedSet);
247 memmove(evict, evict + 1, static_cast<size_t>(insert - 1 - evict) * sizeof(SeedEntry));
248 insert--;
249 }
250
251 insert->mSeedId = aSeedId;
252 insert->mSequence = aSequence;
253 insert->mLifetime = kSeedEntryLifetime;
254
255 if (!mSeedSetTimer.IsRunning())
256 {
257 mSeedSetTimer.Start(kSeedEntryLifetimeDt);
258 }
259
260 exit:
261 return error;
262 }
263
HandleSeedSetTimer(Timer & aTimer)264 void Mpl::HandleSeedSetTimer(Timer &aTimer)
265 {
266 aTimer.Get<Mpl>().HandleSeedSetTimer();
267 }
268
HandleSeedSetTimer(void)269 void Mpl::HandleSeedSetTimer(void)
270 {
271 bool startTimer = false;
272 int j = 0;
273
274 for (int i = 0; i < kNumSeedEntries && mSeedSet[i].mLifetime; i++)
275 {
276 mSeedSet[i].mLifetime--;
277
278 if (mSeedSet[i].mLifetime > 0)
279 {
280 mSeedSet[j++] = mSeedSet[i];
281 startTimer = true;
282 }
283 }
284
285 for (; j < kNumSeedEntries && mSeedSet[j].mLifetime; j++)
286 {
287 mSeedSet[j].mLifetime = 0;
288 }
289
290 if (startTimer)
291 {
292 mSeedSetTimer.Start(kSeedEntryLifetimeDt);
293 }
294 }
295
296 #if OPENTHREAD_FTD
297
AddBufferedMessage(Message & aMessage,uint16_t aSeedId,uint8_t aSequence,bool aIsOutbound)298 void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSequence, bool aIsOutbound)
299 {
300 Error error = kErrorNone;
301 Message *messageCopy = nullptr;
302 Metadata metadata;
303 uint8_t hopLimit = 0;
304
305 #if OPENTHREAD_CONFIG_MPL_DYNAMIC_INTERVAL_ENABLE
306 // adjust the first MPL forward interval dynamically according to the network scale
307 uint8_t interval = (kDataMessageInterval / Mle::kMaxRouters) * Get<RouterTable>().GetNeighborCount();
308 #else
309 uint8_t interval = kDataMessageInterval;
310 #endif
311
312 VerifyOrExit(GetTimerExpirations() > 0);
313 VerifyOrExit((messageCopy = aMessage.Clone()) != nullptr, error = kErrorNoBufs);
314
315 if (!aIsOutbound)
316 {
317 IgnoreError(aMessage.Read(Header::kHopLimitFieldOffset, hopLimit));
318 VerifyOrExit(hopLimit-- > 1, error = kErrorDrop);
319 messageCopy->Write(Header::kHopLimitFieldOffset, hopLimit);
320 }
321
322 metadata.mSeedId = aSeedId;
323 metadata.mSequence = aSequence;
324 metadata.mTransmissionCount = aIsOutbound ? 1 : 0;
325 metadata.mIntervalOffset = 0;
326 metadata.GenerateNextTransmissionTime(TimerMilli::GetNow(), interval);
327
328 SuccessOrExit(error = metadata.AppendTo(*messageCopy));
329 mBufferedMessageSet.Enqueue(*messageCopy);
330
331 mRetransmissionTimer.FireAtIfEarlier(metadata.mTransmissionTime);
332
333 exit:
334 FreeMessageOnError(messageCopy, error);
335 }
336
HandleRetransmissionTimer(Timer & aTimer)337 void Mpl::HandleRetransmissionTimer(Timer &aTimer)
338 {
339 aTimer.Get<Mpl>().HandleRetransmissionTimer();
340 }
341
HandleRetransmissionTimer(void)342 void Mpl::HandleRetransmissionTimer(void)
343 {
344 TimeMilli now = TimerMilli::GetNow();
345 TimeMilli nextTime = now.GetDistantFuture();
346 Metadata metadata;
347
348 for (Message &message : mBufferedMessageSet)
349 {
350 metadata.ReadFrom(message);
351
352 if (now < metadata.mTransmissionTime)
353 {
354 if (nextTime > metadata.mTransmissionTime)
355 {
356 nextTime = metadata.mTransmissionTime;
357 }
358 }
359 else
360 {
361 // Update the number of transmission timer expirations.
362 metadata.mTransmissionCount++;
363
364 if (metadata.mTransmissionCount < GetTimerExpirations())
365 {
366 Message *messageCopy = message.Clone(message.GetLength() - sizeof(Metadata));
367
368 if (messageCopy != nullptr)
369 {
370 if (metadata.mTransmissionCount > 1)
371 {
372 messageCopy->SetSubType(Message::kSubTypeMplRetransmission);
373 }
374
375 Get<Ip6>().EnqueueDatagram(*messageCopy);
376 }
377
378 metadata.GenerateNextTransmissionTime(now, kDataMessageInterval);
379 metadata.UpdateIn(message);
380
381 if (nextTime > metadata.mTransmissionTime)
382 {
383 nextTime = metadata.mTransmissionTime;
384 }
385 }
386 else
387 {
388 mBufferedMessageSet.Dequeue(message);
389
390 if (metadata.mTransmissionCount == GetTimerExpirations())
391 {
392 if (metadata.mTransmissionCount > 1)
393 {
394 message.SetSubType(Message::kSubTypeMplRetransmission);
395 }
396
397 metadata.RemoveFrom(message);
398 Get<Ip6>().EnqueueDatagram(message);
399 }
400 else
401 {
402 // Stop retransmitting if the number of timer expirations is already exceeded.
403 message.Free();
404 }
405 }
406 }
407 }
408
409 if (nextTime < now.GetDistantFuture())
410 {
411 mRetransmissionTimer.FireAt(nextTime);
412 }
413 }
414
ReadFrom(const Message & aMessage)415 void Mpl::Metadata::ReadFrom(const Message &aMessage)
416 {
417 uint16_t length = aMessage.GetLength();
418
419 OT_ASSERT(length >= sizeof(*this));
420 IgnoreError(aMessage.Read(length - sizeof(*this), *this));
421 }
422
RemoveFrom(Message & aMessage) const423 void Mpl::Metadata::RemoveFrom(Message &aMessage) const
424 {
425 SuccessOrAssert(aMessage.SetLength(aMessage.GetLength() - sizeof(*this)));
426 }
427
UpdateIn(Message & aMessage) const428 void Mpl::Metadata::UpdateIn(Message &aMessage) const
429 {
430 aMessage.Write(aMessage.GetLength() - sizeof(*this), *this);
431 }
432
GenerateNextTransmissionTime(TimeMilli aCurrentTime,uint8_t aInterval)433 void Mpl::Metadata::GenerateNextTransmissionTime(TimeMilli aCurrentTime, uint8_t aInterval)
434 {
435 // Emulate Trickle timer behavior and set up the next retransmission within [0,I) range.
436 uint8_t t = (aInterval == 0) ? aInterval : Random::NonCrypto::GetUint8InRange(0, aInterval);
437
438 // Set transmission time at the beginning of the next interval.
439 mTransmissionTime = aCurrentTime + static_cast<uint32_t>(mIntervalOffset + t);
440 mIntervalOffset = aInterval - t;
441 }
442
443 #endif // OPENTHREAD_FTD
444
445 } // namespace Ip6
446 } // namespace ot
447