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