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