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