1 /*
2 * Copyright (c) 2020, 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 Multicast Listeners Table.
32 */
33
34 #include "multicast_listeners_table.hpp"
35
36 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_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 "thread/mle_types.hpp"
45 #include "thread/thread_netif.hpp"
46 #include "thread/uri_paths.hpp"
47
48 namespace ot {
49
50 namespace BackboneRouter {
51
52 RegisterLogModule("BbrMlt");
53
Add(const Ip6::Address & aAddress,Time aExpireTime)54 Error MulticastListenersTable::Add(const Ip6::Address &aAddress, Time aExpireTime)
55 {
56 Error error = kErrorNone;
57
58 VerifyOrExit(aAddress.IsMulticastLargerThanRealmLocal(), error = kErrorInvalidArgs);
59
60 for (uint16_t i = 0; i < mNumValidListeners; i++)
61 {
62 Listener &listener = mListeners[i];
63
64 if (listener.GetAddress() == aAddress)
65 {
66 listener.SetExpireTime(aExpireTime);
67 FixHeap(i);
68 ExitNow();
69 }
70 }
71
72 VerifyOrExit(mNumValidListeners < GetArrayLength(mListeners), error = kErrorNoBufs);
73
74 mListeners[mNumValidListeners].SetAddress(aAddress);
75 mListeners[mNumValidListeners].SetExpireTime(aExpireTime);
76 mNumValidListeners++;
77
78 FixHeap(mNumValidListeners - 1);
79
80 if (mCallback != nullptr)
81 {
82 mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED, &aAddress);
83 }
84
85 exit:
86 LogMulticastListenersTable("Add", aAddress, aExpireTime, error);
87 CheckInvariants();
88 return error;
89 }
90
Remove(const Ip6::Address & aAddress)91 void MulticastListenersTable::Remove(const Ip6::Address &aAddress)
92 {
93 Error error = kErrorNotFound;
94
95 for (uint16_t i = 0; i < mNumValidListeners; i++)
96 {
97 Listener &listener = mListeners[i];
98
99 if (listener.GetAddress() == aAddress)
100 {
101 mNumValidListeners--;
102
103 if (i != mNumValidListeners)
104 {
105 listener = mListeners[mNumValidListeners];
106 FixHeap(i);
107 }
108
109 if (mCallback != nullptr)
110 {
111 mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &aAddress);
112 }
113
114 ExitNow(error = kErrorNone);
115 }
116 }
117
118 exit:
119 LogMulticastListenersTable("Remove", aAddress, TimeMilli(0), error);
120 CheckInvariants();
121 }
122
Expire(void)123 void MulticastListenersTable::Expire(void)
124 {
125 TimeMilli now = TimerMilli::GetNow();
126 Ip6::Address address;
127
128 while (mNumValidListeners > 0 && now >= mListeners[0].GetExpireTime())
129 {
130 LogMulticastListenersTable("Expire", mListeners[0].GetAddress(), mListeners[0].GetExpireTime(), kErrorNone);
131 address = mListeners[0].GetAddress();
132
133 mNumValidListeners--;
134
135 if (mNumValidListeners > 0)
136 {
137 mListeners[0] = mListeners[mNumValidListeners];
138 FixHeap(0);
139 }
140
141 if (mCallback != nullptr)
142 {
143 mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &address);
144 }
145 }
146
147 CheckInvariants();
148 }
149
LogMulticastListenersTable(const char * aAction,const Ip6::Address & aAddress,TimeMilli aExpireTime,Error aError)150 void MulticastListenersTable::LogMulticastListenersTable(const char * aAction,
151 const Ip6::Address &aAddress,
152 TimeMilli aExpireTime,
153 Error aError)
154 {
155 OT_UNUSED_VARIABLE(aAction);
156 OT_UNUSED_VARIABLE(aAddress);
157 OT_UNUSED_VARIABLE(aExpireTime);
158 OT_UNUSED_VARIABLE(aError);
159
160 LogDebg("%s %s expire %u: %s", aAction, aAddress.ToString().AsCString(), aExpireTime.GetValue(),
161 ErrorToString(aError));
162 }
163
FixHeap(uint16_t aIndex)164 void MulticastListenersTable::FixHeap(uint16_t aIndex)
165 {
166 if (!SiftHeapElemDown(aIndex))
167 {
168 SiftHeapElemUp(aIndex);
169 }
170 }
171
CheckInvariants(void) const172 void MulticastListenersTable::CheckInvariants(void) const
173 {
174 #if OPENTHREAD_EXAMPLES_SIMULATION && OPENTHREAD_CONFIG_ASSERT_ENABLE
175 for (uint16_t child = 1; child < mNumValidListeners; ++child)
176 {
177 uint16_t parent = (child - 1) / 2;
178
179 OT_ASSERT(!(mListeners[child] < mListeners[parent]));
180 }
181 #endif
182 }
183
SiftHeapElemDown(uint16_t aIndex)184 bool MulticastListenersTable::SiftHeapElemDown(uint16_t aIndex)
185 {
186 uint16_t index = aIndex;
187 Listener saveElem;
188
189 OT_ASSERT(aIndex < mNumValidListeners);
190
191 saveElem = mListeners[aIndex];
192
193 for (;;)
194 {
195 uint16_t child = 2 * index + 1;
196
197 if (child >= mNumValidListeners || child <= index) // child <= index after int overflow
198 {
199 break;
200 }
201
202 if (child + 1 < mNumValidListeners && mListeners[child + 1] < mListeners[child])
203 {
204 child++;
205 }
206
207 if (!(mListeners[child] < saveElem))
208 {
209 break;
210 }
211
212 mListeners[index] = mListeners[child];
213
214 index = child;
215 }
216
217 if (index > aIndex)
218 {
219 mListeners[index] = saveElem;
220 }
221
222 return index > aIndex;
223 }
224
SiftHeapElemUp(uint16_t aIndex)225 void MulticastListenersTable::SiftHeapElemUp(uint16_t aIndex)
226 {
227 uint16_t index = aIndex;
228 Listener saveElem;
229
230 OT_ASSERT(aIndex < mNumValidListeners);
231
232 saveElem = mListeners[aIndex];
233
234 for (;;)
235 {
236 uint16_t parent = (index - 1) / 2;
237
238 if (index == 0 || !(saveElem < mListeners[parent]))
239 {
240 break;
241 }
242
243 mListeners[index] = mListeners[parent];
244
245 index = parent;
246 }
247
248 if (index < aIndex)
249 {
250 mListeners[index] = saveElem;
251 }
252 }
253
begin(void)254 MulticastListenersTable::Listener *MulticastListenersTable::IteratorBuilder::begin(void)
255 {
256 return &Get<MulticastListenersTable>().mListeners[0];
257 }
258
end(void)259 MulticastListenersTable::Listener *MulticastListenersTable::IteratorBuilder::end(void)
260 {
261 return &Get<MulticastListenersTable>().mListeners[Get<MulticastListenersTable>().mNumValidListeners];
262 }
263
Clear(void)264 void MulticastListenersTable::Clear(void)
265 {
266 if (mCallback != nullptr)
267 {
268 for (uint16_t i = 0; i < mNumValidListeners; i++)
269 {
270 mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &mListeners[i].GetAddress());
271 }
272 }
273
274 mNumValidListeners = 0;
275
276 CheckInvariants();
277 }
278
SetCallback(otBackboneRouterMulticastListenerCallback aCallback,void * aContext)279 void MulticastListenersTable::SetCallback(otBackboneRouterMulticastListenerCallback aCallback, void *aContext)
280 {
281 mCallback = aCallback;
282 mCallbackContext = aContext;
283
284 if (mCallback != nullptr)
285 {
286 for (uint16_t i = 0; i < mNumValidListeners; i++)
287 {
288 mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED, &mListeners[i].GetAddress());
289 }
290 }
291 }
292
GetNext(otBackboneRouterMulticastListenerIterator & aIterator,otBackboneRouterMulticastListenerInfo & aListenerInfo)293 Error MulticastListenersTable::GetNext(otBackboneRouterMulticastListenerIterator &aIterator,
294 otBackboneRouterMulticastListenerInfo & aListenerInfo)
295 {
296 Error error = kErrorNone;
297 TimeMilli now;
298
299 VerifyOrExit(aIterator < mNumValidListeners, error = kErrorNotFound);
300
301 now = TimerMilli::GetNow();
302
303 aListenerInfo.mAddress = mListeners[aIterator].mAddress;
304 aListenerInfo.mTimeout =
305 Time::MsecToSec(mListeners[aIterator].mExpireTime > now ? mListeners[aIterator].mExpireTime - now : 0);
306
307 aIterator++;
308
309 exit:
310 return error;
311 }
312
313 } // namespace BackboneRouter
314
315 } // namespace ot
316
317 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
318