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