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 
otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,const otIp6Address * aDestAddress,const uint8_t * aBuffer,uint16_t aBufferLength)94 otError otPlatInfraIfSendIcmp6Nd(uint32_t            aInfraIfIndex,
95                                  const otIp6Address *aDestAddress,
96                                  const uint8_t      *aBuffer,
97                                  uint16_t            aBufferLength)
98 {
99     return ot::Posix::InfraNetif::Get().SendIcmp6Nd(aInfraIfIndex, *aDestAddress, aBuffer, aBufferLength);
100 }
101 
otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)102 otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)
103 {
104     OT_UNUSED_VARIABLE(aInfraIfIndex);
105 
106 #if OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE
107     return ot::Posix::InfraNetif::Get().DiscoverNat64Prefix(aInfraIfIndex);
108 #else
109     return OT_ERROR_DROP;
110 #endif
111 }
112 
platformInfraIfIsRunning(void)113 bool platformInfraIfIsRunning(void) { return ot::Posix::InfraNetif::Get().IsRunning(); }
114 
otSysGetInfraNetifName(void)115 const char *otSysGetInfraNetifName(void) { return ot::Posix::InfraNetif::Get().GetNetifName(); }
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 namespace {
127 
CreateIcmp6Socket(void)128 int CreateIcmp6Socket(void)
129 {
130     int                 sock;
131     int                 rval;
132     struct icmp6_filter filter;
133     const int           kEnable             = 1;
134     const int           kIpv6ChecksumOffset = 2;
135     const int           kHopLimit           = 255;
136 
137     // Initializes the ICMPv6 socket.
138     sock = SocketWithCloseExec(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, kSocketBlock);
139     VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
140 
141     // Only accept Router Advertisements, Router Solicitations and Neighbor Advertisements.
142     ICMP6_FILTER_SETBLOCKALL(&filter);
143     ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
144     ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
145     ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
146 
147     rval = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
148     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
149 
150     // We want a source address and interface index.
151     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &kEnable, sizeof(kEnable));
152     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
153 
154 #ifdef __linux__
155     rval = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &kIpv6ChecksumOffset, sizeof(kIpv6ChecksumOffset));
156 #else
157     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &kIpv6ChecksumOffset, sizeof(kIpv6ChecksumOffset));
158 #endif
159     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
160 
161     // We need to be able to reject RAs arriving from off-link.
162     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &kEnable, sizeof(kEnable));
163     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
164 
165     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
166     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
167 
168     rval = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
169     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
170 
171     return sock;
172 }
173 
IsAddressLinkLocal(const in6_addr & aAddress)174 bool IsAddressLinkLocal(const in6_addr &aAddress)
175 {
176     return ((aAddress.s6_addr[0] & 0xff) == 0xfe) && ((aAddress.s6_addr[1] & 0xc0) == 0x80);
177 }
178 
IsAddressUniqueLocal(const in6_addr & aAddress)179 bool IsAddressUniqueLocal(const in6_addr &aAddress) { return (aAddress.s6_addr[0] & 0xfe) == 0xfc; }
180 
IsAddressGlobalUnicast(const in6_addr & aAddress)181 bool IsAddressGlobalUnicast(const in6_addr &aAddress) { return (aAddress.s6_addr[0] & 0xe0) == 0x20; }
182 
183 // Create a net-link socket that subscribes to link & addresses events.
CreateNetLinkSocket(void)184 int CreateNetLinkSocket(void)
185 {
186     int                sock;
187     int                rval;
188     struct sockaddr_nl addr;
189 
190     sock = SocketWithCloseExec(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, kSocketBlock);
191     VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
192 
193     memset(&addr, 0, sizeof(addr));
194     addr.nl_family = AF_NETLINK;
195     addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR;
196 
197     rval = bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
198     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
199 
200     return sock;
201 }
202 
203 } // namespace
204 
SendIcmp6Nd(uint32_t aInfraIfIndex,const otIp6Address & aDestAddress,const uint8_t * aBuffer,uint16_t aBufferLength)205 otError InfraNetif::SendIcmp6Nd(uint32_t            aInfraIfIndex,
206                                 const otIp6Address &aDestAddress,
207                                 const uint8_t      *aBuffer,
208                                 uint16_t            aBufferLength)
209 {
210     otError error = OT_ERROR_NONE;
211 
212     struct iovec        iov;
213     struct in6_pktinfo *packetInfo;
214 
215     int                 hopLimit = 255;
216     uint8_t             cmsgBuffer[CMSG_SPACE(sizeof(*packetInfo)) + CMSG_SPACE(sizeof(hopLimit))];
217     struct msghdr       msgHeader;
218     struct cmsghdr     *cmsgPointer;
219     ssize_t             rval;
220     struct sockaddr_in6 dest;
221 
222     VerifyOrExit(mInfraIfIcmp6Socket >= 0, error = OT_ERROR_FAILED);
223     VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP);
224 
225     memset(cmsgBuffer, 0, sizeof(cmsgBuffer));
226 
227     // Send the message
228     memset(&dest, 0, sizeof(dest));
229     dest.sin6_family = AF_INET6;
230     memcpy(&dest.sin6_addr, &aDestAddress, sizeof(aDestAddress));
231     if (IN6_IS_ADDR_LINKLOCAL(&dest.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&dest.sin6_addr))
232     {
233         dest.sin6_scope_id = mInfraIfIndex;
234     }
235 
236     iov.iov_base = const_cast<uint8_t *>(aBuffer);
237     iov.iov_len  = aBufferLength;
238 
239     msgHeader.msg_namelen    = sizeof(dest);
240     msgHeader.msg_name       = &dest;
241     msgHeader.msg_iov        = &iov;
242     msgHeader.msg_iovlen     = 1;
243     msgHeader.msg_control    = cmsgBuffer;
244     msgHeader.msg_controllen = sizeof(cmsgBuffer);
245 
246     // Specify the interface.
247     cmsgPointer             = CMSG_FIRSTHDR(&msgHeader);
248     cmsgPointer->cmsg_level = IPPROTO_IPV6;
249     cmsgPointer->cmsg_type  = IPV6_PKTINFO;
250     cmsgPointer->cmsg_len   = CMSG_LEN(sizeof(*packetInfo));
251     packetInfo              = (struct in6_pktinfo *)CMSG_DATA(cmsgPointer);
252     memset(packetInfo, 0, sizeof(*packetInfo));
253     packetInfo->ipi6_ifindex = mInfraIfIndex;
254 
255     // Per section 6.1.2 of RFC 4861, we need to send the ICMPv6 message with IP Hop Limit 255.
256     cmsgPointer             = CMSG_NXTHDR(&msgHeader, cmsgPointer);
257     cmsgPointer->cmsg_level = IPPROTO_IPV6;
258     cmsgPointer->cmsg_type  = IPV6_HOPLIMIT;
259     cmsgPointer->cmsg_len   = CMSG_LEN(sizeof(hopLimit));
260     memcpy(CMSG_DATA(cmsgPointer), &hopLimit, sizeof(hopLimit));
261 
262     rval = sendmsg(mInfraIfIcmp6Socket, &msgHeader, 0);
263     if (rval < 0)
264     {
265         otLogWarnPlat("failed to send ICMPv6 message: %s", strerror(errno));
266         ExitNow(error = OT_ERROR_FAILED);
267     }
268 
269     if (static_cast<size_t>(rval) != iov.iov_len)
270     {
271         otLogWarnPlat("failed to send ICMPv6 message: partially sent");
272         ExitNow(error = OT_ERROR_FAILED);
273     }
274 
275 exit:
276     return error;
277 }
278 
IsRunning(void) const279 bool InfraNetif::IsRunning(void) const { return (GetFlags() & IFF_RUNNING) && HasLinkLocalAddress(); }
280 
GetFlags(void) const281 uint32_t InfraNetif::GetFlags(void) const
282 {
283     int          sock;
284     struct ifreq ifReq;
285 
286     OT_ASSERT(mInfraIfIndex != 0);
287 
288     sock = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketBlock);
289     VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
290 
291     memset(&ifReq, 0, sizeof(ifReq));
292     static_assert(sizeof(ifReq.ifr_name) >= sizeof(mInfraIfName), "mInfraIfName is not of appropriate size.");
293     strcpy(ifReq.ifr_name, mInfraIfName);
294 
295     VerifyOrDie(ioctl(sock, SIOCGIFFLAGS, &ifReq) != -1, OT_EXIT_ERROR_ERRNO);
296 
297     close(sock);
298 
299     return static_cast<uint32_t>(ifReq.ifr_flags);
300 }
301 
CountAddresses(otSysInfraNetIfAddressCounters & aAddressCounters) const302 void InfraNetif::CountAddresses(otSysInfraNetIfAddressCounters &aAddressCounters) const
303 {
304     struct ifaddrs *ifAddrs = nullptr;
305 
306     aAddressCounters.mLinkLocalAddresses     = 0;
307     aAddressCounters.mUniqueLocalAddresses   = 0;
308     aAddressCounters.mGlobalUnicastAddresses = 0;
309 
310     if (getifaddrs(&ifAddrs) < 0)
311     {
312         otLogWarnPlat("failed to get netif addresses: %s", strerror(errno));
313         ExitNow();
314     }
315 
316     for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next)
317     {
318         in6_addr *in6Addr;
319 
320         if (strncmp(addr->ifa_name, mInfraIfName, sizeof(mInfraIfName)) != 0 || addr->ifa_addr == nullptr ||
321             addr->ifa_addr->sa_family != AF_INET6)
322         {
323             continue;
324         }
325 
326         in6Addr = &(reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr)->sin6_addr);
327         aAddressCounters.mLinkLocalAddresses += IsAddressLinkLocal(*in6Addr);
328         aAddressCounters.mUniqueLocalAddresses += IsAddressUniqueLocal(*in6Addr);
329         aAddressCounters.mGlobalUnicastAddresses += IsAddressGlobalUnicast(*in6Addr);
330     }
331 
332     freeifaddrs(ifAddrs);
333 
334 exit:
335     return;
336 }
337 
HasLinkLocalAddress(void) const338 bool InfraNetif::HasLinkLocalAddress(void) const
339 {
340     bool            hasLla  = false;
341     struct ifaddrs *ifAddrs = nullptr;
342 
343     if (getifaddrs(&ifAddrs) < 0)
344     {
345         otLogCritPlat("failed to get netif addresses: %s", strerror(errno));
346         DieNow(OT_EXIT_ERROR_ERRNO);
347     }
348 
349     for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next)
350     {
351         struct sockaddr_in6 *ip6Addr;
352 
353         if (strncmp(addr->ifa_name, mInfraIfName, sizeof(mInfraIfName)) != 0 || addr->ifa_addr == nullptr ||
354             addr->ifa_addr->sa_family != AF_INET6)
355         {
356             continue;
357         }
358 
359         ip6Addr = reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr);
360         if (IN6_IS_ADDR_LINKLOCAL(&ip6Addr->sin6_addr))
361         {
362             hasLla = true;
363             break;
364         }
365     }
366 
367     freeifaddrs(ifAddrs);
368     return hasLla;
369 }
370 
Init(const char * aIfName)371 void InfraNetif::Init(const char *aIfName)
372 {
373     ssize_t  rval;
374     uint32_t ifIndex = 0;
375 
376     if (aIfName == nullptr || aIfName[0] == '\0')
377     {
378         otLogWarnPlat("Border Routing feature is disabled: infra/backbone interface is missing");
379         ExitNow();
380     }
381 
382     VerifyOrDie(strnlen(aIfName, sizeof(mInfraIfName)) <= sizeof(mInfraIfName) - 1, OT_EXIT_INVALID_ARGUMENTS);
383     strcpy(mInfraIfName, aIfName);
384 
385     // Initializes the infra interface.
386     ifIndex = if_nametoindex(aIfName);
387     if (ifIndex == 0)
388     {
389         otLogCritPlat("Failed to get the index for infra interface %s", aIfName);
390         DieNow(OT_EXIT_INVALID_ARGUMENTS);
391     }
392     mInfraIfIndex = ifIndex;
393 
394     mInfraIfIcmp6Socket = CreateIcmp6Socket();
395 #ifdef __linux__
396     rval = setsockopt(mInfraIfIcmp6Socket, SOL_SOCKET, SO_BINDTODEVICE, mInfraIfName, strlen(mInfraIfName));
397 #else  // __NetBSD__ || __FreeBSD__ || __APPLE__
398     rval = setsockopt(mInfraIfIcmp6Socket, IPPROTO_IPV6, IPV6_BOUND_IF, &mInfraIfIndex, sizeof(mInfraIfIndex));
399 #endif // __linux__
400     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
401 
402     mNetLinkSocket = CreateNetLinkSocket();
403 
404 exit:
405     return;
406 }
407 
SetUp(void)408 void InfraNetif::SetUp(void)
409 {
410     OT_ASSERT(gInstance != nullptr);
411     VerifyOrExit(mInfraIfIndex != 0);
412 
413     SuccessOrDie(otBorderRoutingInit(gInstance, mInfraIfIndex, platformInfraIfIsRunning()));
414     SuccessOrDie(otBorderRoutingSetEnabled(gInstance, /* aEnabled */ true));
415     Mainloop::Manager::Get().Add(*this);
416 exit:
417     return;
418 }
419 
TearDown(void)420 void InfraNetif::TearDown(void)
421 {
422     VerifyOrExit(mInfraIfIndex != 0);
423 
424     Mainloop::Manager::Get().Remove(*this);
425 
426 exit:
427     return;
428 }
429 
Deinit(void)430 void InfraNetif::Deinit(void)
431 {
432     if (mInfraIfIcmp6Socket != -1)
433     {
434         close(mInfraIfIcmp6Socket);
435         mInfraIfIcmp6Socket = -1;
436     }
437 
438     if (mNetLinkSocket != -1)
439     {
440         close(mNetLinkSocket);
441         mNetLinkSocket = -1;
442     }
443 
444     mInfraIfIndex = 0;
445 }
446 
Update(otSysMainloopContext & aContext)447 void InfraNetif::Update(otSysMainloopContext &aContext)
448 {
449     VerifyOrExit(mInfraIfIcmp6Socket != -1);
450     VerifyOrExit(mNetLinkSocket != -1);
451 
452     FD_SET(mInfraIfIcmp6Socket, &aContext.mReadFdSet);
453     aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mInfraIfIcmp6Socket);
454 
455     FD_SET(mNetLinkSocket, &aContext.mReadFdSet);
456     aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mNetLinkSocket);
457 
458 exit:
459     return;
460 }
461 
ReceiveNetLinkMessage(void)462 void InfraNetif::ReceiveNetLinkMessage(void)
463 {
464     const size_t kMaxNetLinkBufSize = 8192;
465     ssize_t      len;
466     union
467     {
468         nlmsghdr mHeader;
469         uint8_t  mBuffer[kMaxNetLinkBufSize];
470     } msgBuffer;
471 
472     len = recv(mNetLinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0);
473     if (len < 0)
474     {
475         otLogCritPlat("Failed to receive netlink message: %s", strerror(errno));
476         ExitNow();
477     }
478 
479     for (struct nlmsghdr *header = &msgBuffer.mHeader; NLMSG_OK(header, static_cast<size_t>(len));
480          header                  = NLMSG_NEXT(header, len))
481     {
482         switch (header->nlmsg_type)
483         {
484         // There are no effective netlink message types to get us notified
485         // of interface RUNNING state changes. But addresses events are
486         // usually associated with interface state changes.
487         case RTM_NEWADDR:
488         case RTM_DELADDR:
489         case RTM_NEWLINK:
490         case RTM_DELLINK:
491             SuccessOrDie(otPlatInfraIfStateChanged(gInstance, mInfraIfIndex, platformInfraIfIsRunning()));
492             break;
493         case NLMSG_ERROR:
494         {
495             struct nlmsgerr *errMsg = reinterpret_cast<struct nlmsgerr *>(NLMSG_DATA(header));
496 
497             OT_UNUSED_VARIABLE(errMsg);
498             otLogWarnPlat("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error);
499             break;
500         }
501         default:
502             break;
503         }
504     }
505 
506 exit:
507     return;
508 }
509 
ReceiveIcmp6Message(void)510 void InfraNetif::ReceiveIcmp6Message(void)
511 {
512     otError  error = OT_ERROR_NONE;
513     uint8_t  buffer[1500];
514     uint16_t bufferLength;
515 
516     ssize_t         rval;
517     struct msghdr   msg;
518     struct iovec    bufp;
519     char            cmsgbuf[128];
520     struct cmsghdr *cmh;
521     uint32_t        ifIndex  = 0;
522     int             hopLimit = -1;
523 
524     struct sockaddr_in6 srcAddr;
525     struct in6_addr     dstAddr;
526 
527     memset(&srcAddr, 0, sizeof(srcAddr));
528     memset(&dstAddr, 0, sizeof(dstAddr));
529 
530     bufp.iov_base      = buffer;
531     bufp.iov_len       = sizeof(buffer);
532     msg.msg_iov        = &bufp;
533     msg.msg_iovlen     = 1;
534     msg.msg_name       = &srcAddr;
535     msg.msg_namelen    = sizeof(srcAddr);
536     msg.msg_control    = cmsgbuf;
537     msg.msg_controllen = sizeof(cmsgbuf);
538 
539     rval = recvmsg(mInfraIfIcmp6Socket, &msg, 0);
540     if (rval < 0)
541     {
542         otLogWarnPlat("Failed to receive ICMPv6 message: %s", strerror(errno));
543         ExitNow(error = OT_ERROR_DROP);
544     }
545 
546     bufferLength = static_cast<uint16_t>(rval);
547 
548     for (cmh = CMSG_FIRSTHDR(&msg); cmh; cmh = CMSG_NXTHDR(&msg, cmh))
549     {
550         if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO &&
551             cmh->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
552         {
553             struct in6_pktinfo pktinfo;
554 
555             memcpy(&pktinfo, CMSG_DATA(cmh), sizeof pktinfo);
556             ifIndex = pktinfo.ipi6_ifindex;
557             dstAddr = pktinfo.ipi6_addr;
558         }
559         else if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_HOPLIMIT &&
560                  cmh->cmsg_len == CMSG_LEN(sizeof(int)))
561         {
562             hopLimit = *(int *)CMSG_DATA(cmh);
563         }
564     }
565 
566     VerifyOrExit(ifIndex == mInfraIfIndex, error = OT_ERROR_DROP);
567 
568     // We currently accept only RA & RS messages for the Border Router and it requires that
569     // the hoplimit must be 255 and the source address must be a link-local address.
570     VerifyOrExit(hopLimit == 255 && IN6_IS_ADDR_LINKLOCAL(&srcAddr.sin6_addr), error = OT_ERROR_DROP);
571 
572     otPlatInfraIfRecvIcmp6Nd(gInstance, ifIndex, reinterpret_cast<otIp6Address *>(&srcAddr.sin6_addr), buffer,
573                              bufferLength);
574 
575 exit:
576     if (error != OT_ERROR_NONE)
577     {
578         otLogDebgPlat("Failed to handle ICMPv6 message: %s", otThreadErrorToString(error));
579     }
580 }
581 
582 #if OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE
583 const char         InfraNetif::kWellKnownIpv4OnlyName[]   = "ipv4only.arpa";
584 const otIp4Address InfraNetif::kWellKnownIpv4OnlyAddress1 = {{{192, 0, 0, 170}}};
585 const otIp4Address InfraNetif::kWellKnownIpv4OnlyAddress2 = {{{192, 0, 0, 171}}};
586 const uint8_t      InfraNetif::kValidNat64PrefixLength[]  = {96, 64, 56, 48, 40, 32};
587 
DiscoverNat64PrefixDone(union sigval sv)588 void InfraNetif::DiscoverNat64PrefixDone(union sigval sv)
589 {
590     struct gaicb    *req = (struct gaicb *)sv.sival_ptr;
591     struct addrinfo *res = (struct addrinfo *)req->ar_result;
592 
593     otIp6Prefix prefix = {};
594 
595     VerifyOrExit((char *)req->ar_name == kWellKnownIpv4OnlyName);
596 
597     otLogInfoPlat("Handling host address response for %s", kWellKnownIpv4OnlyName);
598 
599     // We extract the first valid NAT64 prefix from the address look-up response.
600     for (struct addrinfo *rp = res; rp != NULL && prefix.mLength == 0; rp = rp->ai_next)
601     {
602         struct sockaddr_in6 *ip6Addr;
603         otIp6Address         ip6Address;
604 
605         if (rp->ai_family != AF_INET6)
606         {
607             continue;
608         }
609 
610         ip6Addr = reinterpret_cast<sockaddr_in6 *>(rp->ai_addr);
611         memcpy(&ip6Address.mFields.m8, &ip6Addr->sin6_addr.s6_addr, OT_IP6_ADDRESS_SIZE);
612         for (uint8_t length : kValidNat64PrefixLength)
613         {
614             otIp4Address ip4Address;
615 
616             otIp4ExtractFromIp6Address(length, &ip6Address, &ip4Address);
617             if (otIp4IsAddressEqual(&ip4Address, &kWellKnownIpv4OnlyAddress1) ||
618                 otIp4IsAddressEqual(&ip4Address, &kWellKnownIpv4OnlyAddress2))
619             {
620                 // We check that the well-known IPv4 address is present only once in the IPv6 address.
621                 // In case another instance of the value is found for another prefix length, we ignore this address
622                 // and search for the other well-known IPv4 address (per RFC 7050 section 3).
623                 bool foundDuplicate = false;
624 
625                 for (uint8_t dupLength : kValidNat64PrefixLength)
626                 {
627                     otIp4Address dupIp4Address;
628 
629                     if (dupLength == length)
630                     {
631                         continue;
632                     }
633 
634                     otIp4ExtractFromIp6Address(dupLength, &ip6Address, &dupIp4Address);
635                     if (otIp4IsAddressEqual(&dupIp4Address, &ip4Address))
636                     {
637                         foundDuplicate = true;
638                         break;
639                     }
640                 }
641 
642                 if (!foundDuplicate)
643                 {
644                     otIp6GetPrefix(&ip6Address, length, &prefix);
645                     break;
646                 }
647             }
648 
649             if (prefix.mLength != 0)
650             {
651                 break;
652             }
653         }
654     }
655 
656     otPlatInfraIfDiscoverNat64PrefixDone(gInstance, Get().mInfraIfIndex, &prefix);
657 
658 exit:
659     freeaddrinfo(res);
660     freeaddrinfo((struct addrinfo *)req->ar_request);
661     free(req);
662 }
663 
DiscoverNat64Prefix(uint32_t aInfraIfIndex)664 otError InfraNetif::DiscoverNat64Prefix(uint32_t aInfraIfIndex)
665 {
666     otError          error = OT_ERROR_NONE;
667     struct addrinfo *hints = nullptr;
668     struct gaicb    *reqs[1];
669     struct sigevent  sig;
670     int              status;
671 
672     VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP);
673     hints = (struct addrinfo *)malloc(sizeof(struct addrinfo));
674     VerifyOrExit(hints != nullptr, error = OT_ERROR_NO_BUFS);
675     memset(hints, 0, sizeof(struct addrinfo));
676     hints->ai_family   = AF_INET6;
677     hints->ai_socktype = SOCK_STREAM;
678 
679     reqs[0] = (struct gaicb *)malloc(sizeof(struct gaicb));
680     VerifyOrExit(reqs[0] != nullptr, error = OT_ERROR_NO_BUFS);
681     memset(reqs[0], 0, sizeof(struct gaicb));
682     reqs[0]->ar_name    = kWellKnownIpv4OnlyName;
683     reqs[0]->ar_request = hints;
684 
685     memset(&sig, 0, sizeof(struct sigevent));
686     sig.sigev_notify          = SIGEV_THREAD;
687     sig.sigev_value.sival_ptr = reqs[0];
688     sig.sigev_notify_function = &InfraNetif::DiscoverNat64PrefixDone;
689 
690     status = getaddrinfo_a(GAI_NOWAIT, reqs, 1, &sig);
691 
692     if (status != 0)
693     {
694         otLogNotePlat("getaddrinfo_a failed: %s", gai_strerror(status));
695         ExitNow(error = OT_ERROR_FAILED);
696     }
697     otLogInfoPlat("getaddrinfo_a requested for %s", kWellKnownIpv4OnlyName);
698 exit:
699     if (error != OT_ERROR_NONE)
700     {
701         if (hints)
702         {
703             freeaddrinfo(hints);
704         }
705         free(reqs[0]);
706     }
707     return error;
708 }
709 #endif // OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE
710 
Process(const otSysMainloopContext & aContext)711 void InfraNetif::Process(const otSysMainloopContext &aContext)
712 {
713     VerifyOrExit(mInfraIfIcmp6Socket != -1);
714     VerifyOrExit(mNetLinkSocket != -1);
715 
716     if (FD_ISSET(mInfraIfIcmp6Socket, &aContext.mReadFdSet))
717     {
718         ReceiveIcmp6Message();
719     }
720 
721     if (FD_ISSET(mNetLinkSocket, &aContext.mReadFdSet))
722     {
723         ReceiveNetLinkMessage();
724     }
725 
726 exit:
727     return;
728 }
729 
Get(void)730 InfraNetif &InfraNetif::Get(void)
731 {
732     static InfraNetif sInstance;
733 
734     return sInstance;
735 }
736 
737 } // namespace Posix
738 } // namespace ot
739 #endif // OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
740