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 #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(otSysGetInfraNetifName());
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 = otSysGetInfraNetifIndex();
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         VerifyOrExit(!aSrcAddr.IsLinkLocal(), error = OT_ERROR_NONE);
313         VerifyOrExit(aSrcAddr.GetPrefix() != AsCoreType(otThreadGetMeshLocalPrefix(gInstance)), error = OT_ERROR_NONE);
314         // Forward multicast traffic from Thread to Backbone if multicast scope > kRealmLocalScope
315         // TODO: (MLR) allow scope configuration of outbound multicast routing
316         if (aGroupAddr.GetScope() > Ip6::Address::kRealmLocalScope)
317         {
318             forwardMif = kMifIndexBackbone;
319         }
320     }
321 
322     memset(&mf6cctl, 0, sizeof(mf6cctl));
323 
324     memcpy(mf6cctl.mf6cc_origin.sin6_addr.s6_addr, aSrcAddr.GetBytes(), sizeof(mf6cctl.mf6cc_origin.sin6_addr.s6_addr));
325     memcpy(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr, aGroupAddr.GetBytes(),
326            sizeof(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr));
327     mf6cctl.mf6cc_parent = aIif;
328 
329     if (forwardMif != kMifIndexNone)
330     {
331         IF_SET(forwardMif, &mf6cctl.mf6cc_ifset);
332     }
333 
334     // Note that kernel reports repetitive `MRT6MSG_NOCACHE` upcalls with a rate limit (e.g. once per 10s for Linux).
335     // Because of it, we need to add a "blocking" MFC even if there is no forwarding for this group address.
336     // When a  Multicast Listener is later added, the "blocking" MFC will be altered to be a "forwarding" MFC so that
337     // corresponding multicast traffic can be forwarded instantly.
338     VerifyOrExit(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MFC, &mf6cctl, sizeof(mf6cctl)),
339                  error = OT_ERROR_FAILED);
340 
341     SaveMulticastForwardingCache(aSrcAddr, aGroupAddr, aIif, forwardMif);
342 exit:
343     LogResult(error, "MulticastRoutingManager: %s: add dynamic route: %s %s => %s %s", __FUNCTION__,
344               MifIndexToString(aIif), aSrcAddr.ToString().AsCString(), aGroupAddr.ToString().AsCString(),
345               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, "MulticastRoutingManager: %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 * 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 * 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         otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s: bytecnt=%lu, pktcnt=%lu, wrong_if=%lu",
443                       __FUNCTION__, aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
444                       sioc_sg_req6.bytecnt, 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         otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__,
457                       aMfc.mSrcAddr.ToString().AsCString(), 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     otLogDebgPlat("MulticastRoutingManager: ==================== MFC ENTRIES ====================");
487 
488     for (const MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
489     {
490         if (mfc.IsValid())
491         {
492             otLogDebgPlat("MulticastRoutingManager: %s %s => %s %s", MifIndexToString(mfc.mIif),
493                           mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
494                           MifIndexToString(mfc.mOif));
495         }
496     }
497 
498     otLogDebgPlat("MulticastRoutingManager: =====================================================");
499 #endif
500 }
501 
HandleStateChange(otInstance * aInstance,otChangedFlags aFlags)502 void MulticastRoutingManager::HandleStateChange(otInstance *aInstance, otChangedFlags aFlags)
503 {
504     if (aFlags & OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE)
505     {
506         otBackboneRouterState state = otBackboneRouterGetState(aInstance);
507 
508         switch (state)
509         {
510         case OT_BACKBONE_ROUTER_STATE_DISABLED:
511         case OT_BACKBONE_ROUTER_STATE_SECONDARY:
512             Disable();
513             break;
514         case OT_BACKBONE_ROUTER_STATE_PRIMARY:
515             Enable();
516             break;
517         }
518     }
519 }
520 
Set(MulticastRoutingManager::MifIndex aIif,MulticastRoutingManager::MifIndex aOif)521 void MulticastRoutingManager::MulticastForwardingCache::Set(MulticastRoutingManager::MifIndex aIif,
522                                                             MulticastRoutingManager::MifIndex aOif)
523 {
524     mIif         = aIif;
525     mOif         = aOif;
526     mValidPktCnt = 0;
527     mLastUseTime = otPlatTimeGet();
528 }
529 
Set(const Ip6::Address & aSrcAddr,const Ip6::Address & aGroupAddr,MifIndex aIif,MifIndex aOif)530 void MulticastRoutingManager::MulticastForwardingCache::Set(const Ip6::Address &aSrcAddr,
531                                                             const Ip6::Address &aGroupAddr,
532                                                             MifIndex            aIif,
533                                                             MifIndex            aOif)
534 {
535     mSrcAddr   = aSrcAddr;
536     mGroupAddr = aGroupAddr;
537     Set(aIif, aOif);
538 }
539 
SetValidPktCnt(unsigned long aValidPktCnt)540 void MulticastRoutingManager::MulticastForwardingCache::SetValidPktCnt(unsigned long aValidPktCnt)
541 {
542     mValidPktCnt = aValidPktCnt;
543     mLastUseTime = otPlatTimeGet();
544 }
545 
SaveMulticastForwardingCache(const Ip6::Address & aSrcAddr,const Ip6::Address & aGroupAddr,MulticastRoutingManager::MifIndex aIif,MulticastRoutingManager::MifIndex aOif)546 void MulticastRoutingManager::SaveMulticastForwardingCache(const Ip6::Address               &aSrcAddr,
547                                                            const Ip6::Address               &aGroupAddr,
548                                                            MulticastRoutingManager::MifIndex aIif,
549                                                            MulticastRoutingManager::MifIndex aOif)
550 {
551     MulticastForwardingCache *invalid = nullptr;
552     MulticastForwardingCache *oldest  = nullptr;
553 
554     for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
555     {
556         if (mfc.IsValid())
557         {
558             if (mfc.mSrcAddr == aSrcAddr && mfc.mGroupAddr == aGroupAddr)
559             {
560                 mfc.Set(aIif, aOif);
561                 ExitNow();
562             }
563 
564             if (oldest == nullptr || mfc.mLastUseTime < oldest->mLastUseTime)
565             {
566                 oldest = &mfc;
567             }
568         }
569         else if (invalid == nullptr)
570         {
571             invalid = &mfc;
572         }
573     }
574 
575     if (invalid != nullptr)
576     {
577         invalid->Set(aSrcAddr, aGroupAddr, aIif, aOif);
578     }
579     else
580     {
581         RemoveMulticastForwardingCache(*oldest);
582         oldest->Set(aSrcAddr, aGroupAddr, aIif, aOif);
583     }
584 
585 exit:
586     return;
587 }
588 
RemoveMulticastForwardingCache(MulticastRoutingManager::MulticastForwardingCache & aMfc) const589 void MulticastRoutingManager::RemoveMulticastForwardingCache(
590     MulticastRoutingManager::MulticastForwardingCache &aMfc) const
591 {
592     otError        error;
593     struct mf6cctl mf6cctl;
594 
595     memset(&mf6cctl, 0, sizeof(mf6cctl));
596 
597     memcpy(mf6cctl.mf6cc_origin.sin6_addr.s6_addr, aMfc.mSrcAddr.GetBytes(),
598            sizeof(mf6cctl.mf6cc_origin.sin6_addr.s6_addr));
599     memcpy(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr, aMfc.mGroupAddr.GetBytes(),
600            sizeof(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr));
601 
602     mf6cctl.mf6cc_parent = aMfc.mIif;
603 
604     error = (0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_DEL_MFC, &mf6cctl, sizeof(mf6cctl)))
605                 ? OT_ERROR_NONE
606                 : OT_ERROR_FAILED;
607 
608     LogResult(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(aMfc.mIif),
609               aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
610               MifIndexToString(aMfc.mOif));
611 
612     aMfc.Erase();
613 }
614 
615 } // namespace Posix
616 } // namespace ot
617 
618 #endif // OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
619