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