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