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