• 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 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