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 /**
30  * @file
31  *   This file implements the infrastructure interface for posix.
32  */
33 
34 #include "platform-posix.h"
35 
36 #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
37 
38 #ifdef __APPLE__
39 #define __APPLE_USE_RFC_3542
40 #endif
41 
42 #include <errno.h>
43 #include <ifaddrs.h>
44 #include <netdb.h>
45 // clang-format off
46 #include <netinet/in.h>
47 #include <netinet/icmp6.h>
48 // clang-format on
49 #include <signal.h>
50 #include <sys/ioctl.h>
51 #include <sys/types.h>
52 #include <unistd.h>
53 #ifdef __linux__
54 #include <linux/rtnetlink.h>
55 #endif
56 
57 #include <openthread/border_router.h>
58 #include <openthread/platform/infra_if.h>
59 
60 #include "common/code_utils.hpp"
61 #include "common/debug.hpp"
62 #include "lib/platform/exit_code.h"
63 #include "posix/platform/infra_if.hpp"
64 
otPlatInfraIfHasAddress(uint32_t aInfraIfIndex,const otIp6Address * aAddress)65 bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress)
66 {
67     bool            ret     = false;
68     struct ifaddrs *ifAddrs = nullptr;
69 
70     VerifyOrDie(getifaddrs(&ifAddrs) != -1, OT_EXIT_ERROR_ERRNO);
71 
72     for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next)
73     {
74         struct sockaddr_in6 *ip6Addr;
75 
76         if (if_nametoindex(addr->ifa_name) != aInfraIfIndex || addr->ifa_addr == nullptr ||
77             addr->ifa_addr->sa_family != AF_INET6)
78         {
79             continue;
80         }
81 
82         ip6Addr = reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr);
83         if (memcmp(&ip6Addr->sin6_addr, aAddress, sizeof(*aAddress)) == 0)
84         {
85             ExitNow(ret = true);
86         }
87     }
88 
89 exit:
90     freeifaddrs(ifAddrs);
91     return ret;
92 }
93 
94 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,const otIp6Address * aDestAddress,const uint8_t * aBuffer,uint16_t aBufferLength)95 otError otPlatInfraIfSendIcmp6Nd(uint32_t            aInfraIfIndex,
96                                  const otIp6Address *aDestAddress,
97                                  const uint8_t      *aBuffer,
98                                  uint16_t            aBufferLength)
99 {
100     return ot::Posix::InfraNetif::Get().SendIcmp6Nd(aInfraIfIndex, *aDestAddress, aBuffer, aBufferLength);
101 }
102 #endif
103 
104 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE && OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE
otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)105 otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)
106 {
107     return ot::Posix::InfraNetif::Get().DiscoverNat64Prefix(aInfraIfIndex);
108 }
109 #endif
110 
otSysInfraIfIsRunning(void)111 bool otSysInfraIfIsRunning(void) { return ot::Posix::InfraNetif::Get().IsRunning(); }
112 
otSysGetInfraNetifName(void)113 const char *otSysGetInfraNetifName(void) { return ot::Posix::InfraNetif::Get().GetNetifName(); }
114 
otSysGetInfraNetifIndex(void)115 uint32_t otSysGetInfraNetifIndex(void) { return ot::Posix::InfraNetif::Get().GetNetifIndex(); }
116 
otSysGetInfraNetifFlags(void)117 uint32_t otSysGetInfraNetifFlags(void) { return ot::Posix::InfraNetif::Get().GetFlags(); }
118 
otSysCountInfraNetifAddresses(otSysInfraNetIfAddressCounters * aAddressCounters)119 void otSysCountInfraNetifAddresses(otSysInfraNetIfAddressCounters *aAddressCounters)
120 {
121     ot::Posix::InfraNetif::Get().CountAddresses(*aAddressCounters);
122 }
123 
124 namespace ot {
125 namespace Posix {
126 
127 const char InfraNetif::kLogModuleName[] = "InfraNetif";
128 
CreateIcmp6Socket(const char * aInfraIfName)129 int InfraNetif::CreateIcmp6Socket(const char *aInfraIfName)
130 {
131     int                 sock;
132     int                 rval;
133     struct icmp6_filter filter;
134     const int           kEnable             = 1;
135     const int           kIpv6ChecksumOffset = 2;
136     const int           kHopLimit           = 255;
137 
138     // Initializes the ICMPv6 socket.
139     sock = SocketWithCloseExec(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, kSocketBlock);
140     VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
141 
142     // Only accept Router Advertisements, Router Solicitations and Neighbor Advertisements.
143     ICMP6_FILTER_SETBLOCKALL(&filter);
144     ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
145     ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
146     ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
147 
148     rval = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
149     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
150 
151     // We want a source address and interface index.
152     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &kEnable, sizeof(kEnable));
153     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
154 
155 #ifdef __linux__
156     rval = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &kIpv6ChecksumOffset, sizeof(kIpv6ChecksumOffset));
157 #else
158     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &kIpv6ChecksumOffset, sizeof(kIpv6ChecksumOffset));
159 #endif
160     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
161 
162     // We need to be able to reject RAs arriving from off-link.
163     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &kEnable, sizeof(kEnable));
164     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
165 
166     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
167     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
168 
169     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
170     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
171 
172 #ifdef __linux__
173     rval = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, aInfraIfName, strlen(aInfraIfName));
174 #else  // __NetBSD__ || __FreeBSD__ || __APPLE__
175     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_BOUND_IF, aInfraIfName, strlen(aInfraIfName));
176 #endif // __linux__
177     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
178 
179     return sock;
180 }
181 
IsAddressLinkLocal(const in6_addr & aAddress)182 bool IsAddressLinkLocal(const in6_addr &aAddress)
183 {
184     return ((aAddress.s6_addr[0] & 0xff) == 0xfe) && ((aAddress.s6_addr[1] & 0xc0) == 0x80);
185 }
186 
IsAddressUniqueLocal(const in6_addr & aAddress)187 bool IsAddressUniqueLocal(const in6_addr &aAddress) { return (aAddress.s6_addr[0] & 0xfe) == 0xfc; }
188 
IsAddressGlobalUnicast(const in6_addr & aAddress)189 bool IsAddressGlobalUnicast(const in6_addr &aAddress) { return (aAddress.s6_addr[0] & 0xe0) == 0x20; }
190 
191 #ifdef __linux__
192 // Create a net-link socket that subscribes to link & addresses events.
CreateNetLinkSocket(void)193 int CreateNetLinkSocket(void)
194 {
195     int                sock;
196     int                rval;
197     struct sockaddr_nl addr;
198 
199     sock = SocketWithCloseExec(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, kSocketBlock);
200     VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
201 
202     memset(&addr, 0, sizeof(addr));
203     addr.nl_family = AF_NETLINK;
204     addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR;
205 
206     rval = bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
207     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
208 
209     return sock;
210 }
211 #endif // #ifdef __linux__
212 
213 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
SendIcmp6Nd(uint32_t aInfraIfIndex,const otIp6Address & aDestAddress,const uint8_t * aBuffer,uint16_t aBufferLength)214 otError InfraNetif::SendIcmp6Nd(uint32_t            aInfraIfIndex,
215                                 const otIp6Address &aDestAddress,
216                                 const uint8_t      *aBuffer,
217                                 uint16_t            aBufferLength)
218 {
219     otError error = OT_ERROR_NONE;
220 
221     struct iovec        iov;
222     struct in6_pktinfo *packetInfo;
223 
224     int                 hopLimit = 255;
225     uint8_t             cmsgBuffer[CMSG_SPACE(sizeof(*packetInfo)) + CMSG_SPACE(sizeof(hopLimit))];
226     struct msghdr       msgHeader;
227     struct cmsghdr     *cmsgPointer;
228     ssize_t             rval;
229     struct sockaddr_in6 dest;
230 
231     VerifyOrExit(mInfraIfIcmp6Socket >= 0, error = OT_ERROR_FAILED);
232     VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP);
233 
234     memset(cmsgBuffer, 0, sizeof(cmsgBuffer));
235 
236     // Send the message
237     memset(&dest, 0, sizeof(dest));
238     dest.sin6_family = AF_INET6;
239     memcpy(&dest.sin6_addr, &aDestAddress, sizeof(aDestAddress));
240     if (IN6_IS_ADDR_LINKLOCAL(&dest.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&dest.sin6_addr))
241     {
242         dest.sin6_scope_id = mInfraIfIndex;
243     }
244 
245     iov.iov_base = const_cast<uint8_t *>(aBuffer);
246     iov.iov_len  = aBufferLength;
247 
248     msgHeader.msg_namelen    = sizeof(dest);
249     msgHeader.msg_name       = &dest;
250     msgHeader.msg_iov        = &iov;
251     msgHeader.msg_iovlen     = 1;
252     msgHeader.msg_control    = cmsgBuffer;
253     msgHeader.msg_controllen = sizeof(cmsgBuffer);
254 
255     // Specify the interface.
256     cmsgPointer             = CMSG_FIRSTHDR(&msgHeader);
257     cmsgPointer->cmsg_level = IPPROTO_IPV6;
258     cmsgPointer->cmsg_type  = IPV6_PKTINFO;
259     cmsgPointer->cmsg_len   = CMSG_LEN(sizeof(*packetInfo));
260     packetInfo              = (struct in6_pktinfo *)CMSG_DATA(cmsgPointer);
261     memset(packetInfo, 0, sizeof(*packetInfo));
262     packetInfo->ipi6_ifindex = mInfraIfIndex;
263 
264     // Per section 6.1.2 of RFC 4861, we need to send the ICMPv6 message with IP Hop Limit 255.
265     cmsgPointer             = CMSG_NXTHDR(&msgHeader, cmsgPointer);
266     cmsgPointer->cmsg_level = IPPROTO_IPV6;
267     cmsgPointer->cmsg_type  = IPV6_HOPLIMIT;
268     cmsgPointer->cmsg_len   = CMSG_LEN(sizeof(hopLimit));
269     memcpy(CMSG_DATA(cmsgPointer), &hopLimit, sizeof(hopLimit));
270 
271     rval = sendmsg(mInfraIfIcmp6Socket, &msgHeader, 0);
272 
273     if (rval < 0)
274     {
275         LogWarn("failed to send ICMPv6 message: %s", strerror(errno));
276         ExitNow(error = OT_ERROR_FAILED);
277     }
278 
279     if (static_cast<size_t>(rval) != iov.iov_len)
280     {
281         LogWarn("failed to send ICMPv6 message: partially sent");
282         ExitNow(error = OT_ERROR_FAILED);
283     }
284 
285 exit:
286     return error;
287 }
288 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
289 
IsRunning(void) const290 bool InfraNetif::IsRunning(void) const
291 {
292     return mInfraIfIndex ? ((GetFlags() & IFF_RUNNING) && HasLinkLocalAddress()) : false;
293 }
294 
GetFlags(void) const295 uint32_t InfraNetif::GetFlags(void) const
296 {
297     int          sock;
298     struct ifreq ifReq;
299     uint32_t     flags = 0;
300 
301     OT_ASSERT(mInfraIfIndex != 0);
302 
303     sock = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketBlock);
304     VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
305 
306     memset(&ifReq, 0, sizeof(ifReq));
307     static_assert(sizeof(ifReq.ifr_name) >= sizeof(mInfraIfName), "mInfraIfName is not of appropriate size.");
308     strcpy(ifReq.ifr_name, mInfraIfName);
309 
310     if (ioctl(sock, SIOCGIFFLAGS, &ifReq) == -1)
311     {
312 #if OPENTHREAD_POSIX_CONFIG_EXIT_ON_INFRA_NETIF_LOST_ENABLE
313         LogCrit("The infra link %s may be lost. Exiting.", mInfraIfName);
314         DieNow(OT_EXIT_ERROR_ERRNO);
315 #endif
316         ExitNow();
317     }
318     flags = static_cast<uint32_t>(ifReq.ifr_flags);
319 
320 exit:
321     close(sock);
322 
323     return flags;
324 }
325 
CountAddresses(otSysInfraNetIfAddressCounters & aAddressCounters) const326 void InfraNetif::CountAddresses(otSysInfraNetIfAddressCounters &aAddressCounters) const
327 {
328     struct ifaddrs *ifAddrs = nullptr;
329 
330     aAddressCounters.mLinkLocalAddresses     = 0;
331     aAddressCounters.mUniqueLocalAddresses   = 0;
332     aAddressCounters.mGlobalUnicastAddresses = 0;
333 
334     if (getifaddrs(&ifAddrs) < 0)
335     {
336         LogWarn("failed to get netif addresses: %s", strerror(errno));
337         ExitNow();
338     }
339 
340     for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next)
341     {
342         in6_addr *in6Addr;
343 
344         if (strncmp(addr->ifa_name, mInfraIfName, sizeof(mInfraIfName)) != 0 || addr->ifa_addr == nullptr ||
345             addr->ifa_addr->sa_family != AF_INET6)
346         {
347             continue;
348         }
349 
350         in6Addr = &(reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr)->sin6_addr);
351         aAddressCounters.mLinkLocalAddresses += IsAddressLinkLocal(*in6Addr);
352         aAddressCounters.mUniqueLocalAddresses += IsAddressUniqueLocal(*in6Addr);
353         aAddressCounters.mGlobalUnicastAddresses += IsAddressGlobalUnicast(*in6Addr);
354     }
355 
356     freeifaddrs(ifAddrs);
357 
358 exit:
359     return;
360 }
361 
362 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
HandleBackboneStateChange(otInstance * aInstance,otChangedFlags aFlags)363 void InfraNetif::HandleBackboneStateChange(otInstance *aInstance, otChangedFlags aFlags)
364 {
365     OT_ASSERT(gInstance == aInstance);
366 
367     OT_UNUSED_VARIABLE(aInstance);
368     OT_UNUSED_VARIABLE(aFlags);
369 
370 #if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
371     mMulticastRoutingManager.HandleStateChange(aInstance, aFlags);
372 #endif
373 }
374 #endif
375 
HasLinkLocalAddress(void) const376 bool InfraNetif::HasLinkLocalAddress(void) const
377 {
378     bool            hasLla  = false;
379     struct ifaddrs *ifAddrs = nullptr;
380 
381     if (getifaddrs(&ifAddrs) < 0)
382     {
383         LogCrit("failed to get netif addresses: %s", strerror(errno));
384         DieNow(OT_EXIT_ERROR_ERRNO);
385     }
386 
387     for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next)
388     {
389         struct sockaddr_in6 *ip6Addr;
390 
391         if (strncmp(addr->ifa_name, mInfraIfName, sizeof(mInfraIfName)) != 0 || addr->ifa_addr == nullptr ||
392             addr->ifa_addr->sa_family != AF_INET6)
393         {
394             continue;
395         }
396 
397         ip6Addr = reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr);
398         if (IN6_IS_ADDR_LINKLOCAL(&ip6Addr->sin6_addr))
399         {
400             hasLla = true;
401             break;
402         }
403     }
404 
405     freeifaddrs(ifAddrs);
406     return hasLla;
407 }
408 
Init(void)409 void InfraNetif::Init(void)
410 {
411 #ifdef __linux__
412     mNetLinkSocket = CreateNetLinkSocket();
413 #endif
414 }
415 
SetInfraNetif(const char * aIfName,int aIcmp6Socket)416 void InfraNetif::SetInfraNetif(const char *aIfName, int aIcmp6Socket)
417 {
418     uint32_t ifIndex = 0;
419 
420     OT_UNUSED_VARIABLE(aIcmp6Socket);
421 
422     OT_ASSERT(gInstance != nullptr);
423 #ifdef __linux__
424     VerifyOrDie(mNetLinkSocket != -1, OT_EXIT_INVALID_STATE);
425 #endif
426 
427 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
428     SetInfraNetifIcmp6SocketForBorderRouting(aIcmp6Socket);
429 #endif
430 #if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
431     VerifyOrDie(!mMulticastRoutingManager.IsEnabled(), OT_EXIT_INVALID_STATE);
432 #endif
433 
434     if (aIfName == nullptr || aIfName[0] == '\0')
435     {
436         LogWarn("Border Routing/Backbone Router feature is disabled: infra interface is missing");
437         ExitNow();
438     }
439 
440     VerifyOrDie(strnlen(aIfName, sizeof(mInfraIfName)) <= sizeof(mInfraIfName) - 1, OT_EXIT_INVALID_ARGUMENTS);
441     strcpy(mInfraIfName, aIfName);
442 
443     // Initializes the infra interface.
444     ifIndex = if_nametoindex(aIfName);
445     if (ifIndex == 0)
446     {
447         LogCrit("Failed to get the index for infra interface %s", aIfName);
448         DieNow(OT_EXIT_INVALID_ARGUMENTS);
449     }
450 
451     mInfraIfIndex = ifIndex;
452 
453 exit:
454     return;
455 }
456 
SetUp(void)457 void InfraNetif::SetUp(void)
458 {
459     OT_ASSERT(gInstance != nullptr);
460 #ifdef __linux__
461     VerifyOrExit(mNetLinkSocket != -1);
462 #endif
463 
464 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
465     SuccessOrDie(otBorderRoutingInit(gInstance, mInfraIfIndex, otSysInfraIfIsRunning()));
466     SuccessOrDie(otBorderRoutingSetEnabled(gInstance, /* aEnabled */ true));
467 #endif
468 
469 #if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
470     mMulticastRoutingManager.SetUp();
471 #endif
472 
473     Mainloop::Manager::Get().Add(*this);
474 
475     ExitNow(); // To silence unused `exit` label warning.
476 
477 exit:
478     return;
479 }
480 
TearDown(void)481 void InfraNetif::TearDown(void)
482 {
483 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
484     IgnoreError(otBorderRoutingSetEnabled(gInstance, false));
485 #endif
486 
487 #if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
488     mMulticastRoutingManager.TearDown();
489 #endif
490 
491     Mainloop::Manager::Get().Remove(*this);
492 }
493 
Deinit(void)494 void InfraNetif::Deinit(void)
495 {
496 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
497     if (mInfraIfIcmp6Socket != -1)
498     {
499         close(mInfraIfIcmp6Socket);
500         mInfraIfIcmp6Socket = -1;
501     }
502 #endif
503 
504 #ifdef __linux__
505     if (mNetLinkSocket != -1)
506     {
507         close(mNetLinkSocket);
508         mNetLinkSocket = -1;
509     }
510 #endif
511 
512     mInfraIfName[0] = '\0';
513     mInfraIfIndex   = 0;
514 }
515 
Update(otSysMainloopContext & aContext)516 void InfraNetif::Update(otSysMainloopContext &aContext)
517 {
518 #ifdef __linux__
519     VerifyOrExit(mNetLinkSocket != -1);
520 #endif
521 
522 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
523     VerifyOrExit(mInfraIfIcmp6Socket != -1);
524 
525     FD_SET(mInfraIfIcmp6Socket, &aContext.mReadFdSet);
526     aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mInfraIfIcmp6Socket);
527 #endif
528 
529 #ifdef __linux__
530     FD_SET(mNetLinkSocket, &aContext.mReadFdSet);
531     aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mNetLinkSocket);
532 #endif
533 
534 exit:
535     return;
536 }
537 
538 #ifdef __linux__
539 
ReceiveNetLinkMessage(void)540 void InfraNetif::ReceiveNetLinkMessage(void)
541 {
542     const size_t kMaxNetLinkBufSize = 8192;
543     ssize_t      len;
544     union
545     {
546         nlmsghdr mHeader;
547         uint8_t  mBuffer[kMaxNetLinkBufSize];
548     } msgBuffer;
549 
550     len = recv(mNetLinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0);
551     if (len < 0)
552     {
553         LogCrit("Failed to receive netlink message: %s", strerror(errno));
554         ExitNow();
555     }
556 
557     for (struct nlmsghdr *header = &msgBuffer.mHeader; NLMSG_OK(header, static_cast<size_t>(len));
558          header                  = NLMSG_NEXT(header, len))
559     {
560         switch (header->nlmsg_type)
561         {
562         // There are no effective netlink message types to get us notified
563         // of interface RUNNING state changes. But addresses events are
564         // usually associated with interface state changes.
565         case RTM_NEWADDR:
566         case RTM_DELADDR:
567         case RTM_NEWLINK:
568         case RTM_DELLINK:
569 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
570             SuccessOrDie(otPlatInfraIfStateChanged(gInstance, mInfraIfIndex, otSysInfraIfIsRunning()));
571 #endif
572             break;
573         case NLMSG_ERROR:
574         {
575             struct nlmsgerr *errMsg = reinterpret_cast<struct nlmsgerr *>(NLMSG_DATA(header));
576 
577             OT_UNUSED_VARIABLE(errMsg);
578             LogWarn("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error);
579             break;
580         }
581         default:
582             break;
583         }
584     }
585 
586 exit:
587     return;
588 }
589 
590 #endif // #ifdef __linux__
591 
592 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
ReceiveIcmp6Message(void)593 void InfraNetif::ReceiveIcmp6Message(void)
594 {
595     otError  error = OT_ERROR_NONE;
596     uint8_t  buffer[1500];
597     uint16_t bufferLength;
598 
599     ssize_t         rval;
600     struct msghdr   msg;
601     struct iovec    bufp;
602     char            cmsgbuf[128];
603     struct cmsghdr *cmh;
604     uint32_t        ifIndex  = 0;
605     int             hopLimit = -1;
606 
607     struct sockaddr_in6 srcAddr;
608     struct in6_addr     dstAddr;
609 
610     memset(&srcAddr, 0, sizeof(srcAddr));
611     memset(&dstAddr, 0, sizeof(dstAddr));
612 
613     bufp.iov_base      = buffer;
614     bufp.iov_len       = sizeof(buffer);
615     msg.msg_iov        = &bufp;
616     msg.msg_iovlen     = 1;
617     msg.msg_name       = &srcAddr;
618     msg.msg_namelen    = sizeof(srcAddr);
619     msg.msg_control    = cmsgbuf;
620     msg.msg_controllen = sizeof(cmsgbuf);
621 
622     rval = recvmsg(mInfraIfIcmp6Socket, &msg, 0);
623     if (rval < 0)
624     {
625         LogWarn("Failed to receive ICMPv6 message: %s", strerror(errno));
626         ExitNow(error = OT_ERROR_DROP);
627     }
628 
629     bufferLength = static_cast<uint16_t>(rval);
630 
631     for (cmh = CMSG_FIRSTHDR(&msg); cmh; cmh = CMSG_NXTHDR(&msg, cmh))
632     {
633         if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO &&
634             cmh->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
635         {
636             struct in6_pktinfo pktinfo;
637 
638             memcpy(&pktinfo, CMSG_DATA(cmh), sizeof pktinfo);
639             ifIndex = pktinfo.ipi6_ifindex;
640             dstAddr = pktinfo.ipi6_addr;
641         }
642         else if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_HOPLIMIT &&
643                  cmh->cmsg_len == CMSG_LEN(sizeof(int)))
644         {
645             hopLimit = *(int *)CMSG_DATA(cmh);
646         }
647     }
648 
649     VerifyOrExit(ifIndex == mInfraIfIndex, error = OT_ERROR_DROP);
650 
651     // We currently accept only RA & RS messages for the Border Router and it requires that
652     // the hoplimit must be 255 and the source address must be a link-local address.
653     VerifyOrExit(hopLimit == 255 && IN6_IS_ADDR_LINKLOCAL(&srcAddr.sin6_addr), error = OT_ERROR_DROP);
654 
655     otPlatInfraIfRecvIcmp6Nd(gInstance, ifIndex, reinterpret_cast<otIp6Address *>(&srcAddr.sin6_addr), buffer,
656                              bufferLength);
657 
658 exit:
659     if (error != OT_ERROR_NONE)
660     {
661         LogDebg("Failed to handle ICMPv6 message: %s", otThreadErrorToString(error));
662     }
663 }
664 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
665 
666 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE && OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE
667 const char         InfraNetif::kWellKnownIpv4OnlyName[]   = "ipv4only.arpa";
668 const otIp4Address InfraNetif::kWellKnownIpv4OnlyAddress1 = {{{192, 0, 0, 170}}};
669 const otIp4Address InfraNetif::kWellKnownIpv4OnlyAddress2 = {{{192, 0, 0, 171}}};
670 const uint8_t      InfraNetif::kValidNat64PrefixLength[]  = {96, 64, 56, 48, 40, 32};
671 
672 #ifdef __linux__
DiscoverNat64PrefixDone(union sigval sv)673 void InfraNetif::DiscoverNat64PrefixDone(union sigval sv)
674 {
675     struct gaicb    *req = (struct gaicb *)sv.sival_ptr;
676     struct addrinfo *res = (struct addrinfo *)req->ar_result;
677 
678     otIp6Prefix prefix = {};
679 
680     VerifyOrExit((char *)req->ar_name == kWellKnownIpv4OnlyName);
681 
682     LogInfo("Handling host address response for %s", kWellKnownIpv4OnlyName);
683 
684     // We extract the first valid NAT64 prefix from the address look-up response.
685     for (struct addrinfo *rp = res; rp != NULL && prefix.mLength == 0; rp = rp->ai_next)
686     {
687         struct sockaddr_in6 *ip6Addr;
688         otIp6Address         ip6Address;
689 
690         if (rp->ai_family != AF_INET6)
691         {
692             continue;
693         }
694 
695         ip6Addr = reinterpret_cast<sockaddr_in6 *>(rp->ai_addr);
696         memcpy(&ip6Address.mFields.m8, &ip6Addr->sin6_addr.s6_addr, OT_IP6_ADDRESS_SIZE);
697         for (uint8_t length : kValidNat64PrefixLength)
698         {
699             otIp4Address ip4Address;
700 
701             otIp4ExtractFromIp6Address(length, &ip6Address, &ip4Address);
702             if (otIp4IsAddressEqual(&ip4Address, &kWellKnownIpv4OnlyAddress1) ||
703                 otIp4IsAddressEqual(&ip4Address, &kWellKnownIpv4OnlyAddress2))
704             {
705                 // We check that the well-known IPv4 address is present only once in the IPv6 address.
706                 // In case another instance of the value is found for another prefix length, we ignore this address
707                 // and search for the other well-known IPv4 address (per RFC 7050 section 3).
708                 bool foundDuplicate = false;
709 
710                 for (uint8_t dupLength : kValidNat64PrefixLength)
711                 {
712                     otIp4Address dupIp4Address;
713 
714                     if (dupLength == length)
715                     {
716                         continue;
717                     }
718 
719                     otIp4ExtractFromIp6Address(dupLength, &ip6Address, &dupIp4Address);
720                     if (otIp4IsAddressEqual(&dupIp4Address, &ip4Address))
721                     {
722                         foundDuplicate = true;
723                         break;
724                     }
725                 }
726 
727                 if (!foundDuplicate)
728                 {
729                     otIp6GetPrefix(&ip6Address, length, &prefix);
730                     break;
731                 }
732             }
733 
734             if (prefix.mLength != 0)
735             {
736                 break;
737             }
738         }
739     }
740 
741 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
742     otPlatInfraIfDiscoverNat64PrefixDone(gInstance, Get().mInfraIfIndex, &prefix);
743 #endif
744 
745 exit:
746     freeaddrinfo(res);
747     freeaddrinfo((struct addrinfo *)req->ar_request);
748     free(req);
749 }
750 #endif // #ifdef __linux__
751 
DiscoverNat64Prefix(uint32_t aInfraIfIndex)752 otError InfraNetif::DiscoverNat64Prefix(uint32_t aInfraIfIndex)
753 {
754 #ifdef __linux__
755     otError          error   = OT_ERROR_NONE;
756     struct addrinfo *hints   = nullptr;
757     struct gaicb    *reqs[1] = {nullptr};
758     struct sigevent  sig;
759     int              status;
760 
761     VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP);
762     hints = (struct addrinfo *)malloc(sizeof(struct addrinfo));
763     VerifyOrExit(hints != nullptr, error = OT_ERROR_NO_BUFS);
764     memset(hints, 0, sizeof(struct addrinfo));
765     hints->ai_family   = AF_INET6;
766     hints->ai_socktype = SOCK_STREAM;
767 
768     reqs[0] = (struct gaicb *)malloc(sizeof(struct gaicb));
769     VerifyOrExit(reqs[0] != nullptr, error = OT_ERROR_NO_BUFS);
770     memset(reqs[0], 0, sizeof(struct gaicb));
771     reqs[0]->ar_name    = kWellKnownIpv4OnlyName;
772     reqs[0]->ar_request = hints;
773 
774     memset(&sig, 0, sizeof(struct sigevent));
775     sig.sigev_notify          = SIGEV_THREAD;
776     sig.sigev_value.sival_ptr = reqs[0];
777     sig.sigev_notify_function = &InfraNetif::DiscoverNat64PrefixDone;
778 
779     status = getaddrinfo_a(GAI_NOWAIT, reqs, 1, &sig);
780 
781     if (status != 0)
782     {
783         LogNote("getaddrinfo_a failed: %s", gai_strerror(status));
784         ExitNow(error = OT_ERROR_FAILED);
785     }
786     LogInfo("getaddrinfo_a requested for %s", kWellKnownIpv4OnlyName);
787 exit:
788     if (error != OT_ERROR_NONE)
789     {
790         if (hints)
791         {
792             freeaddrinfo(hints);
793         }
794         free(reqs[0]);
795     }
796     return error;
797 #else
798     OT_UNUSED_VARIABLE(aInfraIfIndex);
799 
800     return OT_ERROR_NOT_IMPLEMENTED;
801 #endif // #ifdef __linux__
802 }
803 #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE && OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE
804 
805 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
SetInfraNetifIcmp6SocketForBorderRouting(int aIcmp6Socket)806 void InfraNetif::SetInfraNetifIcmp6SocketForBorderRouting(int aIcmp6Socket)
807 {
808     otBorderRoutingState state = otBorderRoutingGetState(gInstance);
809 
810     VerifyOrDie(state == OT_BORDER_ROUTING_STATE_UNINITIALIZED || state == OT_BORDER_ROUTING_STATE_DISABLED,
811                 OT_EXIT_INVALID_STATE);
812 
813     if (mInfraIfIcmp6Socket != -1)
814     {
815         close(mInfraIfIcmp6Socket);
816     }
817     mInfraIfIcmp6Socket = aIcmp6Socket;
818 }
819 #endif
820 
Process(const otSysMainloopContext & aContext)821 void InfraNetif::Process(const otSysMainloopContext &aContext)
822 {
823 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
824     VerifyOrExit(mInfraIfIcmp6Socket != -1);
825 #endif
826 
827 #ifdef __linux__
828     VerifyOrExit(mNetLinkSocket != -1);
829 #endif
830 
831 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
832     if (FD_ISSET(mInfraIfIcmp6Socket, &aContext.mReadFdSet))
833     {
834         ReceiveIcmp6Message();
835     }
836 #endif
837 
838 #ifdef __linux__
839     if (FD_ISSET(mNetLinkSocket, &aContext.mReadFdSet))
840     {
841         ReceiveNetLinkMessage();
842     }
843 #endif
844 
845 exit:
846     return;
847 }
848 
Get(void)849 InfraNetif &InfraNetif::Get(void)
850 {
851     static InfraNetif sInstance;
852 
853     return sInstance;
854 }
855 
856 } // namespace Posix
857 } // namespace ot
858 #endif // OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
859