• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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