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