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 #include "posix/platform/multicast_routing.hpp"
30
31 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
32
33 #include <assert.h>
34 #include <net/if.h>
35 #include <netinet/icmp6.h>
36 #include <netinet/in.h>
37 #include <stdio.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42 #if __linux__
43 #include <linux/mroute6.h>
44 #else
45 #error "Multicast Routing feature is not ported to non-Linux platforms yet."
46 #endif
47
48 #include <openthread/backbone_router_ftd.h>
49 #include <openthread/logging.h>
50
51 #include "core/common/arg_macros.hpp"
52 #include "core/common/debug.hpp"
53
54 namespace ot {
55 namespace Posix {
56
57 #define LogResult(aError, ...) \
58 do \
59 { \
60 otError _err = (aError); \
61 \
62 if (_err == OT_ERROR_NONE) \
63 { \
64 otLogInfoPlat(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
65 } \
66 else \
67 { \
68 otLogWarnPlat(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
69 } \
70 } while (false)
71
SetUp(void)72 void MulticastRoutingManager::SetUp(void)
73 {
74 OT_ASSERT(gInstance != nullptr);
75
76 otBackboneRouterSetMulticastListenerCallback(gInstance,
77 &MulticastRoutingManager::HandleBackboneMulticastListenerEvent, this);
78 Mainloop::Manager::Get().Add(*this);
79 }
80
TearDown(void)81 void MulticastRoutingManager::TearDown(void)
82 {
83 OT_ASSERT(gInstance != nullptr);
84
85 otBackboneRouterSetMulticastListenerCallback(gInstance, nullptr, nullptr);
86 Mainloop::Manager::Get().Remove(*this);
87 }
88
HandleBackboneMulticastListenerEvent(void * aContext,otBackboneRouterMulticastListenerEvent aEvent,const otIp6Address * aAddress)89 void MulticastRoutingManager::HandleBackboneMulticastListenerEvent(void * aContext,
90 otBackboneRouterMulticastListenerEvent aEvent,
91 const otIp6Address * aAddress)
92 {
93 static_cast<MulticastRoutingManager *>(aContext)->HandleBackboneMulticastListenerEvent(
94 aEvent, static_cast<const Ip6::Address &>(*aAddress));
95 }
96
HandleBackboneMulticastListenerEvent(otBackboneRouterMulticastListenerEvent aEvent,const Ip6::Address & aAddress)97 void MulticastRoutingManager::HandleBackboneMulticastListenerEvent(otBackboneRouterMulticastListenerEvent aEvent,
98 const Ip6::Address & aAddress)
99 {
100 switch (aEvent)
101 {
102 case OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED:
103 Add(aAddress);
104 break;
105 case OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED:
106 Remove(aAddress);
107 break;
108 }
109 }
110
Enable(void)111 void MulticastRoutingManager::Enable(void)
112 {
113 VerifyOrExit(!IsEnabled());
114
115 InitMulticastRouterSock();
116
117 LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
118 exit:
119 return;
120 }
121
Disable(void)122 void MulticastRoutingManager::Disable(void)
123 {
124 FinalizeMulticastRouterSock();
125
126 LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
127 }
128
Add(const Ip6::Address & aAddress)129 void MulticastRoutingManager::Add(const Ip6::Address &aAddress)
130 {
131 VerifyOrExit(IsEnabled());
132
133 UnblockInboundMulticastForwardingCache(aAddress);
134 UpdateMldReport(aAddress, true);
135
136 LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s: %s", __FUNCTION__, aAddress.ToString().AsCString());
137
138 exit:
139 return;
140 }
141
Remove(const Ip6::Address & aAddress)142 void MulticastRoutingManager::Remove(const Ip6::Address &aAddress)
143 {
144 otError error = OT_ERROR_NONE;
145
146 VerifyOrExit(IsEnabled());
147
148 RemoveInboundMulticastForwardingCache(aAddress);
149 UpdateMldReport(aAddress, false);
150
151 LogResult(error, "MulticastRoutingManager: %s: %s", __FUNCTION__, aAddress.ToString().AsCString());
152
153 exit:
154 return;
155 }
156
UpdateMldReport(const Ip6::Address & aAddress,bool isAdd)157 void MulticastRoutingManager::UpdateMldReport(const Ip6::Address &aAddress, bool isAdd)
158 {
159 struct ipv6_mreq ipv6mr;
160 otError error = OT_ERROR_NONE;
161
162 ipv6mr.ipv6mr_interface = if_nametoindex(gBackboneNetifName);
163 memcpy(&ipv6mr.ipv6mr_multiaddr, aAddress.GetBytes(), sizeof(ipv6mr.ipv6mr_multiaddr));
164 error = (setsockopt(mMulticastRouterSock, IPPROTO_IPV6, (isAdd ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP),
165 (void *)&ipv6mr, sizeof(ipv6mr))
166 ? OT_ERROR_FAILED
167 : OT_ERROR_NONE);
168
169 LogResult(error, "MulticastRoutingManager: %s: address %s %s", __FUNCTION__, aAddress.ToString().AsCString(),
170 (isAdd ? "Added" : "Removed"));
171 }
172
HasMulticastListener(const Ip6::Address & aAddress) const173 bool MulticastRoutingManager::HasMulticastListener(const Ip6::Address &aAddress) const
174 {
175 bool found = false;
176 otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
177 otBackboneRouterMulticastListenerInfo listenerInfo;
178
179 while (otBackboneRouterMulticastListenerGetNext(gInstance, &iter, &listenerInfo) == OT_ERROR_NONE)
180 {
181 VerifyOrExit(static_cast<const Ip6::Address &>(listenerInfo.mAddress) != aAddress, found = true);
182 }
183
184 exit:
185 return found;
186 }
187
Update(otSysMainloopContext & aContext)188 void MulticastRoutingManager::Update(otSysMainloopContext &aContext)
189 {
190 VerifyOrExit(IsEnabled());
191
192 FD_SET(mMulticastRouterSock, &aContext.mReadFdSet);
193 aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mMulticastRouterSock);
194
195 exit:
196 return;
197 }
198
Process(const otSysMainloopContext & aContext)199 void MulticastRoutingManager::Process(const otSysMainloopContext &aContext)
200 {
201 VerifyOrExit(IsEnabled());
202
203 ExpireMulticastForwardingCache();
204
205 if (FD_ISSET(mMulticastRouterSock, &aContext.mReadFdSet))
206 {
207 ProcessMulticastRouterMessages();
208 }
209
210 exit:
211 return;
212 }
213
InitMulticastRouterSock(void)214 void MulticastRoutingManager::InitMulticastRouterSock(void)
215 {
216 int one = 1;
217 struct icmp6_filter filter;
218 struct mif6ctl mif6ctl;
219
220 // Create a Multicast Routing socket
221 mMulticastRouterSock = SocketWithCloseExec(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, kSocketBlock);
222 VerifyOrDie(mMulticastRouterSock != -1, OT_EXIT_ERROR_ERRNO);
223
224 // Enable Multicast Forwarding in Kernel
225 VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_INIT, &one, sizeof(one)), OT_EXIT_ERROR_ERRNO);
226
227 // Filter all ICMPv6 messages
228 ICMP6_FILTER_SETBLOCKALL(&filter);
229 VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_ICMPV6, ICMP6_FILTER, (void *)&filter, sizeof(filter)),
230 OT_EXIT_ERROR_ERRNO);
231
232 memset(&mif6ctl, 0, sizeof(mif6ctl));
233 mif6ctl.mif6c_flags = 0;
234 mif6ctl.vifc_threshold = 1;
235 mif6ctl.vifc_rate_limit = 0;
236
237 // Add Thread network interface to MIF
238 mif6ctl.mif6c_mifi = kMifIndexThread;
239 mif6ctl.mif6c_pifi = if_nametoindex(gNetifName);
240 VerifyOrDie(mif6ctl.mif6c_pifi > 0, OT_EXIT_ERROR_ERRNO);
241 VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MIF, &mif6ctl, sizeof(mif6ctl)),
242 OT_EXIT_ERROR_ERRNO);
243
244 // Add Backbone network interface to MIF
245 mif6ctl.mif6c_mifi = kMifIndexBackbone;
246 mif6ctl.mif6c_pifi = if_nametoindex(gBackboneNetifName);
247 VerifyOrDie(mif6ctl.mif6c_pifi > 0, OT_EXIT_ERROR_ERRNO);
248 VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MIF, &mif6ctl, sizeof(mif6ctl)),
249 OT_EXIT_ERROR_ERRNO);
250 }
251
FinalizeMulticastRouterSock(void)252 void MulticastRoutingManager::FinalizeMulticastRouterSock(void)
253 {
254 VerifyOrExit(IsEnabled());
255
256 close(mMulticastRouterSock);
257 mMulticastRouterSock = -1;
258
259 exit:
260 return;
261 }
262
ProcessMulticastRouterMessages(void)263 void MulticastRoutingManager::ProcessMulticastRouterMessages(void)
264 {
265 otError error = OT_ERROR_NONE;
266 char buf[sizeof(struct mrt6msg)];
267 int nr;
268 struct mrt6msg *mrt6msg;
269 Ip6::Address src, dst;
270
271 nr = read(mMulticastRouterSock, buf, sizeof(buf));
272
273 VerifyOrExit(nr >= static_cast<int>(sizeof(struct mrt6msg)), error = OT_ERROR_FAILED);
274
275 mrt6msg = reinterpret_cast<struct mrt6msg *>(buf);
276
277 VerifyOrExit(mrt6msg->im6_mbz == 0);
278 VerifyOrExit(mrt6msg->im6_msgtype == MRT6MSG_NOCACHE);
279
280 src.SetBytes(mrt6msg->im6_src.s6_addr);
281 dst.SetBytes(mrt6msg->im6_dst.s6_addr);
282
283 error = AddMulticastForwardingCache(src, dst, static_cast<MifIndex>(mrt6msg->im6_mif));
284
285 exit:
286 LogResult(error, "MulticastRoutingManager: %s", __FUNCTION__);
287 }
288
AddMulticastForwardingCache(const Ip6::Address & aSrcAddr,const Ip6::Address & aGroupAddr,MifIndex aIif)289 otError MulticastRoutingManager::AddMulticastForwardingCache(const Ip6::Address &aSrcAddr,
290 const Ip6::Address &aGroupAddr,
291 MifIndex aIif)
292 {
293 otError error = OT_ERROR_NONE;
294 struct mf6cctl mf6cctl;
295 MifIndex forwardMif = kMifIndexNone;
296
297 VerifyOrExit(aIif == kMifIndexThread || aIif == kMifIndexBackbone, error = OT_ERROR_INVALID_ARGS);
298
299 ExpireMulticastForwardingCache();
300
301 if (aIif == kMifIndexBackbone)
302 {
303 // Forward multicast traffic from Backbone to Thread if the group address is subscribed by any Thread device via
304 // MLR.
305 if (HasMulticastListener(aGroupAddr))
306 {
307 forwardMif = kMifIndexThread;
308 }
309 }
310 else
311 {
312 // Forward multicast traffic from Thread to Backbone if multicast scope > kRealmLocalScope
313 // TODO: (MLR) allow scope configuration of outbound multicast routing
314 if (aGroupAddr.GetScope() > Ip6::Address::kRealmLocalScope)
315 {
316 forwardMif = kMifIndexBackbone;
317 }
318 }
319
320 memset(&mf6cctl, 0, sizeof(mf6cctl));
321
322 memcpy(mf6cctl.mf6cc_origin.sin6_addr.s6_addr, aSrcAddr.GetBytes(), sizeof(mf6cctl.mf6cc_origin.sin6_addr.s6_addr));
323 memcpy(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr, aGroupAddr.GetBytes(),
324 sizeof(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr));
325 mf6cctl.mf6cc_parent = aIif;
326
327 if (forwardMif != kMifIndexNone)
328 {
329 IF_SET(forwardMif, &mf6cctl.mf6cc_ifset);
330 }
331
332 // Note that kernel reports repetitive `MRT6MSG_NOCACHE` upcalls with a rate limit (e.g. once per 10s for Linux).
333 // Because of it, we need to add a "blocking" MFC even if there is no forwarding for this group address.
334 // When a Multicast Listener is later added, the "blocking" MFC will be altered to be a "forwarding" MFC so that
335 // corresponding multicast traffic can be forwarded instantly.
336 VerifyOrExit(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MFC, &mf6cctl, sizeof(mf6cctl)),
337 error = OT_ERROR_FAILED);
338
339 SaveMulticastForwardingCache(aSrcAddr, aGroupAddr, aIif, forwardMif);
340 exit:
341 LogResult(error, "MulticastRoutingManager: %s: add dynamic route: %s %s => %s %s", __FUNCTION__,
342 MifIndexToString(aIif), aSrcAddr.ToString().AsCString(), aGroupAddr.ToString().AsCString(),
343 MifIndexToString(forwardMif));
344
345 return error;
346 }
347
UnblockInboundMulticastForwardingCache(const Ip6::Address & aGroupAddr)348 void MulticastRoutingManager::UnblockInboundMulticastForwardingCache(const Ip6::Address &aGroupAddr)
349 {
350 struct mf6cctl mf6cctl;
351
352 memset(&mf6cctl, 0, sizeof(mf6cctl));
353 memcpy(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr, aGroupAddr.GetBytes(),
354 sizeof(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr));
355 mf6cctl.mf6cc_parent = kMifIndexBackbone;
356 IF_SET(kMifIndexThread, &mf6cctl.mf6cc_ifset);
357
358 for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
359 {
360 otError error;
361
362 if (!mfc.IsValid() || mfc.mIif != kMifIndexBackbone || mfc.mOif == kMifIndexThread ||
363 mfc.mGroupAddr != aGroupAddr)
364 {
365 continue;
366 }
367
368 // Unblock this inbound route
369 memcpy(mf6cctl.mf6cc_origin.sin6_addr.s6_addr, mfc.mSrcAddr.GetBytes(),
370 sizeof(mf6cctl.mf6cc_origin.sin6_addr.s6_addr));
371
372 error = (0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MFC, &mf6cctl, sizeof(mf6cctl)))
373 ? OT_ERROR_NONE
374 : OT_ERROR_FAILED;
375
376 mfc.Set(kMifIndexBackbone, kMifIndexThread);
377
378 LogResult(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(mfc.mIif),
379 mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
380 MifIndexToString(kMifIndexThread));
381 }
382 }
383
RemoveInboundMulticastForwardingCache(const Ip6::Address & aGroupAddr)384 void MulticastRoutingManager::RemoveInboundMulticastForwardingCache(const Ip6::Address &aGroupAddr)
385 {
386 for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
387 {
388 if (mfc.IsValid() && mfc.mIif == kMifIndexBackbone && mfc.mGroupAddr == aGroupAddr)
389 {
390 RemoveMulticastForwardingCache(mfc);
391 }
392 }
393 }
394
ExpireMulticastForwardingCache(void)395 void MulticastRoutingManager::ExpireMulticastForwardingCache(void)
396 {
397 struct sioc_sg_req6 sioc_sg_req6;
398 uint64_t now = otPlatTimeGet();
399 struct mf6cctl mf6cctl;
400
401 VerifyOrExit(now >= mLastExpireTime + kMulticastForwardingCacheExpiringInterval * US_PER_S);
402
403 mLastExpireTime = now;
404
405 memset(&mf6cctl, 0, sizeof(mf6cctl));
406 memset(&sioc_sg_req6, 0, sizeof(sioc_sg_req6));
407
408 for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
409 {
410 if (mfc.IsValid() && mfc.mLastUseTime + kMulticastForwardingCacheExpireTimeout * US_PER_S < now)
411 {
412 if (!UpdateMulticastRouteInfo(mfc))
413 {
414 // The multicast route is expired
415 RemoveMulticastForwardingCache(mfc);
416 }
417 }
418 }
419
420 DumpMulticastForwardingCache();
421
422 exit:
423 return;
424 }
425
UpdateMulticastRouteInfo(MulticastForwardingCache & aMfc) const426 bool MulticastRoutingManager::UpdateMulticastRouteInfo(MulticastForwardingCache &aMfc) const
427 {
428 bool updated = false;
429 struct sioc_sg_req6 sioc_sg_req6;
430
431 memset(&sioc_sg_req6, 0, sizeof(sioc_sg_req6));
432
433 memcpy(sioc_sg_req6.src.sin6_addr.s6_addr, aMfc.mSrcAddr.GetBytes(), sizeof(sioc_sg_req6.src.sin6_addr.s6_addr));
434 memcpy(sioc_sg_req6.grp.sin6_addr.s6_addr, aMfc.mGroupAddr.GetBytes(), sizeof(sioc_sg_req6.grp.sin6_addr.s6_addr));
435
436 if (ioctl(mMulticastRouterSock, SIOCGETSGCNT_IN6, &sioc_sg_req6) != -1)
437 {
438 unsigned long validPktCnt;
439
440 otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s: bytecnt=%lu, pktcnt=%lu, wrong_if=%lu",
441 __FUNCTION__, aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
442 sioc_sg_req6.bytecnt, sioc_sg_req6.pktcnt, sioc_sg_req6.wrong_if);
443
444 validPktCnt = sioc_sg_req6.pktcnt - sioc_sg_req6.wrong_if;
445 if (validPktCnt != aMfc.mValidPktCnt)
446 {
447 aMfc.SetValidPktCnt(validPktCnt);
448
449 updated = true;
450 }
451 }
452 else
453 {
454 otLogWarnPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__,
455 aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(), strerror(errno));
456 }
457
458 return updated;
459 }
460
MifIndexToString(MifIndex aMif)461 const char *MulticastRoutingManager::MifIndexToString(MifIndex aMif)
462 {
463 const char *string = "Unknown";
464
465 switch (aMif)
466 {
467 case kMifIndexNone:
468 string = "None";
469 break;
470 case kMifIndexThread:
471 string = "Thread";
472 break;
473 case kMifIndexBackbone:
474 string = "Backbone";
475 break;
476 }
477
478 return string;
479 }
480
DumpMulticastForwardingCache(void) const481 void MulticastRoutingManager::DumpMulticastForwardingCache(void) const
482 {
483 #if OPENTHREAD_CONFIG_LOG_PLATFORM && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG)
484 otLogDebgPlat("MulticastRoutingManager: ==================== MFC ENTRIES ====================");
485
486 for (const MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
487 {
488 if (mfc.IsValid())
489 {
490 otLogDebgPlat("MulticastRoutingManager: %s %s => %s %s", MifIndexToString(mfc.mIif),
491 mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
492 MifIndexToString(mfc.mOif));
493 }
494 }
495
496 otLogDebgPlat("MulticastRoutingManager: =====================================================");
497 #endif
498 }
499
HandleStateChange(otInstance * aInstance,otChangedFlags aFlags)500 void MulticastRoutingManager::HandleStateChange(otInstance *aInstance, otChangedFlags aFlags)
501 {
502 if (aFlags & OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE)
503 {
504 otBackboneRouterState state = otBackboneRouterGetState(aInstance);
505
506 switch (state)
507 {
508 case OT_BACKBONE_ROUTER_STATE_DISABLED:
509 case OT_BACKBONE_ROUTER_STATE_SECONDARY:
510 Disable();
511 break;
512 case OT_BACKBONE_ROUTER_STATE_PRIMARY:
513 Enable();
514 break;
515 }
516 }
517 }
518
Set(MulticastRoutingManager::MifIndex aIif,MulticastRoutingManager::MifIndex aOif)519 void MulticastRoutingManager::MulticastForwardingCache::Set(MulticastRoutingManager::MifIndex aIif,
520 MulticastRoutingManager::MifIndex aOif)
521 {
522 mIif = aIif;
523 mOif = aOif;
524 mValidPktCnt = 0;
525 mLastUseTime = otPlatTimeGet();
526 }
527
Set(const Ip6::Address & aSrcAddr,const Ip6::Address & aGroupAddr,MifIndex aIif,MifIndex aOif)528 void MulticastRoutingManager::MulticastForwardingCache::Set(const Ip6::Address &aSrcAddr,
529 const Ip6::Address &aGroupAddr,
530 MifIndex aIif,
531 MifIndex aOif)
532 {
533 mSrcAddr = aSrcAddr;
534 mGroupAddr = aGroupAddr;
535 Set(aIif, aOif);
536 }
537
SetValidPktCnt(unsigned long aValidPktCnt)538 void MulticastRoutingManager::MulticastForwardingCache::SetValidPktCnt(unsigned long aValidPktCnt)
539 {
540 mValidPktCnt = aValidPktCnt;
541 mLastUseTime = otPlatTimeGet();
542 }
543
SaveMulticastForwardingCache(const Ip6::Address & aSrcAddr,const Ip6::Address & aGroupAddr,MulticastRoutingManager::MifIndex aIif,MulticastRoutingManager::MifIndex aOif)544 void MulticastRoutingManager::SaveMulticastForwardingCache(const Ip6::Address & aSrcAddr,
545 const Ip6::Address & aGroupAddr,
546 MulticastRoutingManager::MifIndex aIif,
547 MulticastRoutingManager::MifIndex aOif)
548 {
549 MulticastForwardingCache *invalid = nullptr;
550 MulticastForwardingCache *oldest = nullptr;
551
552 for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
553 {
554 if (mfc.IsValid())
555 {
556 if (mfc.mSrcAddr == aSrcAddr && mfc.mGroupAddr == aGroupAddr)
557 {
558 mfc.Set(aIif, aOif);
559 ExitNow();
560 }
561
562 if (oldest == nullptr || mfc.mLastUseTime < oldest->mLastUseTime)
563 {
564 oldest = &mfc;
565 }
566 }
567 else if (invalid == nullptr)
568 {
569 invalid = &mfc;
570 }
571 }
572
573 if (invalid != nullptr)
574 {
575 invalid->Set(aSrcAddr, aGroupAddr, aIif, aOif);
576 }
577 else
578 {
579 RemoveMulticastForwardingCache(*oldest);
580 oldest->Set(aSrcAddr, aGroupAddr, aIif, aOif);
581 }
582
583 exit:
584 return;
585 }
586
RemoveMulticastForwardingCache(MulticastRoutingManager::MulticastForwardingCache & aMfc) const587 void MulticastRoutingManager::RemoveMulticastForwardingCache(
588 MulticastRoutingManager::MulticastForwardingCache &aMfc) const
589 {
590 otError error;
591 struct mf6cctl mf6cctl;
592
593 memset(&mf6cctl, 0, sizeof(mf6cctl));
594
595 memcpy(mf6cctl.mf6cc_origin.sin6_addr.s6_addr, aMfc.mSrcAddr.GetBytes(),
596 sizeof(mf6cctl.mf6cc_origin.sin6_addr.s6_addr));
597 memcpy(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr, aMfc.mGroupAddr.GetBytes(),
598 sizeof(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr));
599
600 mf6cctl.mf6cc_parent = aMfc.mIif;
601
602 error = (0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_DEL_MFC, &mf6cctl, sizeof(mf6cctl)))
603 ? OT_ERROR_NONE
604 : OT_ERROR_FAILED;
605
606 LogResult(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(aMfc.mIif),
607 aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
608 MifIndexToString(aMfc.mOif));
609
610 aMfc.Erase();
611 }
612
613 } // namespace Posix
614 } // namespace ot
615
616 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
617