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_CONFIG_BORDER_ROUTING_ENABLE
37
38 #ifdef __APPLE__
39 #define __APPLE_USE_RFC_3542
40 #endif
41
42 #include <errno.h>
43 #include <ifaddrs.h>
44 // clang-format off
45 #include <netinet/in.h>
46 #include <netinet/icmp6.h>
47 // clang-format on
48 #include <sys/ioctl.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51 #ifdef __linux__
52 #include <linux/rtnetlink.h>
53 #endif
54
55 #include <openthread/border_router.h>
56 #include <openthread/platform/infra_if.h>
57
58 #include "common/code_utils.hpp"
59 #include "common/debug.hpp"
60 #include "lib/platform/exit_code.h"
61 #include "posix/platform/infra_if.hpp"
62
otPlatInfraIfHasAddress(uint32_t aInfraIfIndex,const otIp6Address * aAddress)63 bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress)
64 {
65 bool ret = false;
66 struct ifaddrs *ifAddrs = nullptr;
67
68 VerifyOrDie(getifaddrs(&ifAddrs) != -1, OT_EXIT_ERROR_ERRNO);
69
70 for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next)
71 {
72 struct sockaddr_in6 *ip6Addr;
73
74 if (if_nametoindex(addr->ifa_name) != aInfraIfIndex || addr->ifa_addr->sa_family != AF_INET6)
75 {
76 continue;
77 }
78
79 ip6Addr = reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr);
80 if (memcmp(&ip6Addr->sin6_addr, aAddress, sizeof(*aAddress)) == 0)
81 {
82 ExitNow(ret = true);
83 }
84 }
85
86 exit:
87 freeifaddrs(ifAddrs);
88 return ret;
89 }
90
otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,const otIp6Address * aDestAddress,const uint8_t * aBuffer,uint16_t aBufferLength)91 otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
92 const otIp6Address *aDestAddress,
93 const uint8_t * aBuffer,
94 uint16_t aBufferLength)
95 {
96 return ot::Posix::InfraNetif::Get().SendIcmp6Nd(aInfraIfIndex, *aDestAddress, aBuffer, aBufferLength);
97 }
98
platformInfraIfIsRunning(void)99 bool platformInfraIfIsRunning(void)
100 {
101 return ot::Posix::InfraNetif::Get().IsRunning();
102 }
103
otSysGetInfraNetifName(void)104 const char *otSysGetInfraNetifName(void)
105 {
106 return ot::Posix::InfraNetif::Get().GetNetifName();
107 }
108
109 namespace ot {
110 namespace Posix {
111 namespace {
112
CreateIcmp6Socket(void)113 int CreateIcmp6Socket(void)
114 {
115 int sock;
116 int rval;
117 struct icmp6_filter filter;
118 const int kEnable = 1;
119 const int kIpv6ChecksumOffset = 2;
120 const int kHopLimit = 255;
121
122 // Initializes the ICMPv6 socket.
123 sock = SocketWithCloseExec(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, kSocketBlock);
124 VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
125
126 // Only accept router advertisements and solicitations.
127 ICMP6_FILTER_SETBLOCKALL(&filter);
128 ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
129 ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
130
131 rval = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
132 VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
133
134 // We want a source address and interface index.
135 rval = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &kEnable, sizeof(kEnable));
136 VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
137
138 #ifdef __linux__
139 rval = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &kIpv6ChecksumOffset, sizeof(kIpv6ChecksumOffset));
140 #else
141 rval = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &kIpv6ChecksumOffset, sizeof(kIpv6ChecksumOffset));
142 #endif
143 VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
144
145 // We need to be able to reject RAs arriving from off-link.
146 rval = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &kEnable, sizeof(kEnable));
147 VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
148
149 rval = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
150 VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
151
152 rval = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
153 VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
154
155 return sock;
156 }
157
158 // Create a net-link socket that subscribes to link & addresses events.
CreateNetLinkSocket(void)159 int CreateNetLinkSocket(void)
160 {
161 int sock;
162 int rval;
163 struct sockaddr_nl addr;
164
165 sock = SocketWithCloseExec(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, kSocketBlock);
166 VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
167
168 memset(&addr, 0, sizeof(addr));
169 addr.nl_family = AF_NETLINK;
170 addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR;
171
172 rval = bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
173 VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
174
175 return sock;
176 }
177
178 } // namespace
179
SendIcmp6Nd(uint32_t aInfraIfIndex,const otIp6Address & aDestAddress,const uint8_t * aBuffer,uint16_t aBufferLength)180 otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex,
181 const otIp6Address &aDestAddress,
182 const uint8_t * aBuffer,
183 uint16_t aBufferLength)
184 {
185 otError error = OT_ERROR_NONE;
186
187 struct iovec iov;
188 struct in6_pktinfo *packetInfo;
189
190 int hopLimit = 255;
191 uint8_t cmsgBuffer[CMSG_SPACE(sizeof(*packetInfo)) + CMSG_SPACE(sizeof(hopLimit))];
192 struct msghdr msgHeader;
193 struct cmsghdr * cmsgPointer;
194 ssize_t rval;
195 struct sockaddr_in6 dest;
196
197 VerifyOrExit(mInfraIfIcmp6Socket >= 0, error = OT_ERROR_FAILED);
198 VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP);
199
200 memset(cmsgBuffer, 0, sizeof(cmsgBuffer));
201
202 // Send the message
203 memset(&dest, 0, sizeof(dest));
204 dest.sin6_family = AF_INET6;
205 memcpy(&dest.sin6_addr, &aDestAddress, sizeof(aDestAddress));
206 if (IN6_IS_ADDR_LINKLOCAL(&dest.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&dest.sin6_addr))
207 {
208 dest.sin6_scope_id = mInfraIfIndex;
209 }
210
211 iov.iov_base = const_cast<uint8_t *>(aBuffer);
212 iov.iov_len = aBufferLength;
213
214 msgHeader.msg_namelen = sizeof(dest);
215 msgHeader.msg_name = &dest;
216 msgHeader.msg_iov = &iov;
217 msgHeader.msg_iovlen = 1;
218 msgHeader.msg_control = cmsgBuffer;
219 msgHeader.msg_controllen = sizeof(cmsgBuffer);
220
221 // Specify the interface.
222 cmsgPointer = CMSG_FIRSTHDR(&msgHeader);
223 cmsgPointer->cmsg_level = IPPROTO_IPV6;
224 cmsgPointer->cmsg_type = IPV6_PKTINFO;
225 cmsgPointer->cmsg_len = CMSG_LEN(sizeof(*packetInfo));
226 packetInfo = (struct in6_pktinfo *)CMSG_DATA(cmsgPointer);
227 memset(packetInfo, 0, sizeof(*packetInfo));
228 packetInfo->ipi6_ifindex = mInfraIfIndex;
229
230 // Per section 6.1.2 of RFC 4861, we need to send the ICMPv6 message with IP Hop Limit 255.
231 cmsgPointer = CMSG_NXTHDR(&msgHeader, cmsgPointer);
232 cmsgPointer->cmsg_level = IPPROTO_IPV6;
233 cmsgPointer->cmsg_type = IPV6_HOPLIMIT;
234 cmsgPointer->cmsg_len = CMSG_LEN(sizeof(hopLimit));
235 memcpy(CMSG_DATA(cmsgPointer), &hopLimit, sizeof(hopLimit));
236
237 rval = sendmsg(mInfraIfIcmp6Socket, &msgHeader, 0);
238 if (rval < 0)
239 {
240 otLogWarnPlat("failed to send ICMPv6 message: %s", strerror(errno));
241 ExitNow(error = OT_ERROR_FAILED);
242 }
243
244 if (static_cast<size_t>(rval) != iov.iov_len)
245 {
246 otLogWarnPlat("failed to send ICMPv6 message: partially sent");
247 ExitNow(error = OT_ERROR_FAILED);
248 }
249
250 exit:
251 return error;
252 }
253
IsRunning(void) const254 bool InfraNetif::IsRunning(void) const
255 {
256 int sock;
257 struct ifreq ifReq;
258
259 OT_ASSERT(mInfraIfIndex != 0);
260
261 sock = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketBlock);
262 VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
263
264 memset(&ifReq, 0, sizeof(ifReq));
265 static_assert(sizeof(ifReq.ifr_name) >= sizeof(mInfraIfName), "mInfraIfName is not of appropriate size.");
266 strcpy(ifReq.ifr_name, mInfraIfName);
267
268 VerifyOrDie(ioctl(sock, SIOCGIFFLAGS, &ifReq) != -1, OT_EXIT_ERROR_ERRNO);
269
270 close(sock);
271
272 return (ifReq.ifr_flags & IFF_RUNNING) && HasLinkLocalAddress();
273 }
274
HasLinkLocalAddress(void) const275 bool InfraNetif::HasLinkLocalAddress(void) const
276 {
277 bool hasLla = false;
278 struct ifaddrs *ifAddrs = nullptr;
279
280 if (getifaddrs(&ifAddrs) < 0)
281 {
282 otLogCritPlat("failed to get netif addresses: %s", strerror(errno));
283 DieNow(OT_EXIT_ERROR_ERRNO);
284 }
285
286 for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next)
287 {
288 struct sockaddr_in6 *ip6Addr;
289
290 if (strncmp(addr->ifa_name, mInfraIfName, sizeof(mInfraIfName)) != 0 || addr->ifa_addr->sa_family != AF_INET6)
291 {
292 continue;
293 }
294
295 ip6Addr = reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr);
296 if (IN6_IS_ADDR_LINKLOCAL(&ip6Addr->sin6_addr))
297 {
298 hasLla = true;
299 break;
300 }
301 }
302
303 freeifaddrs(ifAddrs);
304 return hasLla;
305 }
306
Init(const char * aIfName)307 void InfraNetif::Init(const char *aIfName)
308 {
309 ssize_t rval;
310 uint32_t ifIndex = 0;
311
312 if (aIfName == nullptr || aIfName[0] == '\0')
313 {
314 otLogWarnPlat("Border Routing feature is disabled: infra/backbone interface is missing");
315 ExitNow();
316 }
317
318 VerifyOrDie(strnlen(aIfName, sizeof(mInfraIfName)) <= sizeof(mInfraIfName) - 1, OT_EXIT_INVALID_ARGUMENTS);
319 strcpy(mInfraIfName, aIfName);
320
321 // Initializes the infra interface.
322 ifIndex = if_nametoindex(aIfName);
323 if (ifIndex == 0)
324 {
325 otLogCritPlat("Failed to get the index for infra interface %s", aIfName);
326 DieNow(OT_EXIT_INVALID_ARGUMENTS);
327 }
328 mInfraIfIndex = ifIndex;
329
330 mInfraIfIcmp6Socket = CreateIcmp6Socket();
331 #ifdef __linux__
332 rval = setsockopt(mInfraIfIcmp6Socket, SOL_SOCKET, SO_BINDTODEVICE, mInfraIfName, strlen(mInfraIfName));
333 #else // __NetBSD__ || __FreeBSD__ || __APPLE__
334 rval = setsockopt(mInfraIfIcmp6Socket, IPPROTO_IPV6, IPV6_BOUND_IF, &mInfraIfIndex, sizeof(mInfraIfIndex));
335 #endif // __linux__
336 VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
337
338 mNetLinkSocket = CreateNetLinkSocket();
339
340 exit:
341 return;
342 }
343
SetUp(void)344 void InfraNetif::SetUp(void)
345 {
346 OT_ASSERT(gInstance != nullptr);
347 VerifyOrExit(mInfraIfIndex != 0);
348
349 SuccessOrDie(otBorderRoutingInit(gInstance, mInfraIfIndex, platformInfraIfIsRunning()));
350 SuccessOrDie(otBorderRoutingSetEnabled(gInstance, /* aEnabled */ true));
351 Mainloop::Manager::Get().Add(*this);
352 exit:
353 return;
354 }
355
TearDown(void)356 void InfraNetif::TearDown(void)
357 {
358 VerifyOrExit(mInfraIfIndex != 0);
359
360 Mainloop::Manager::Get().Remove(*this);
361
362 exit:
363 return;
364 }
365
Deinit(void)366 void InfraNetif::Deinit(void)
367 {
368 if (mInfraIfIcmp6Socket != -1)
369 {
370 close(mInfraIfIcmp6Socket);
371 mInfraIfIcmp6Socket = -1;
372 }
373
374 if (mNetLinkSocket != -1)
375 {
376 close(mNetLinkSocket);
377 mNetLinkSocket = -1;
378 }
379
380 mInfraIfIndex = 0;
381 }
382
Update(otSysMainloopContext & aContext)383 void InfraNetif::Update(otSysMainloopContext &aContext)
384 {
385 VerifyOrExit(mInfraIfIcmp6Socket != -1);
386 VerifyOrExit(mNetLinkSocket != -1);
387
388 FD_SET(mInfraIfIcmp6Socket, &aContext.mReadFdSet);
389 aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mInfraIfIcmp6Socket);
390
391 FD_SET(mNetLinkSocket, &aContext.mReadFdSet);
392 aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mNetLinkSocket);
393
394 exit:
395 return;
396 }
397
ReceiveNetLinkMessage(void)398 void InfraNetif::ReceiveNetLinkMessage(void)
399 {
400 const size_t kMaxNetLinkBufSize = 8192;
401 ssize_t len;
402 union
403 {
404 nlmsghdr mHeader;
405 uint8_t mBuffer[kMaxNetLinkBufSize];
406 } msgBuffer;
407
408 len = recv(mNetLinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0);
409 if (len < 0)
410 {
411 otLogCritPlat("Failed to receive netlink message: %s", strerror(errno));
412 ExitNow();
413 }
414
415 for (struct nlmsghdr *header = &msgBuffer.mHeader; NLMSG_OK(header, static_cast<size_t>(len));
416 header = NLMSG_NEXT(header, len))
417 {
418 switch (header->nlmsg_type)
419 {
420 // There are no effective netlink message types to get us notified
421 // of interface RUNNING state changes. But addresses events are
422 // usually associated with interface state changes.
423 case RTM_NEWADDR:
424 case RTM_DELADDR:
425 case RTM_NEWLINK:
426 case RTM_DELLINK:
427 SuccessOrDie(otPlatInfraIfStateChanged(gInstance, mInfraIfIndex, platformInfraIfIsRunning()));
428 break;
429 case NLMSG_ERROR:
430 {
431 struct nlmsgerr *errMsg = reinterpret_cast<struct nlmsgerr *>(NLMSG_DATA(header));
432
433 OT_UNUSED_VARIABLE(errMsg);
434 otLogWarnPlat("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error);
435 break;
436 }
437 default:
438 break;
439 }
440 }
441
442 exit:
443 return;
444 }
445
ReceiveIcmp6Message(void)446 void InfraNetif::ReceiveIcmp6Message(void)
447 {
448 otError error = OT_ERROR_NONE;
449 uint8_t buffer[1500];
450 uint16_t bufferLength;
451
452 ssize_t rval;
453 struct msghdr msg;
454 struct iovec bufp;
455 char cmsgbuf[128];
456 struct cmsghdr *cmh;
457 uint32_t ifIndex = 0;
458 int hopLimit = -1;
459
460 struct sockaddr_in6 srcAddr;
461 struct in6_addr dstAddr;
462
463 memset(&srcAddr, 0, sizeof(srcAddr));
464 memset(&dstAddr, 0, sizeof(dstAddr));
465
466 bufp.iov_base = buffer;
467 bufp.iov_len = sizeof(buffer);
468 msg.msg_iov = &bufp;
469 msg.msg_iovlen = 1;
470 msg.msg_name = &srcAddr;
471 msg.msg_namelen = sizeof(srcAddr);
472 msg.msg_control = cmsgbuf;
473 msg.msg_controllen = sizeof(cmsgbuf);
474
475 rval = recvmsg(mInfraIfIcmp6Socket, &msg, 0);
476 if (rval < 0)
477 {
478 otLogWarnPlat("Failed to receive ICMPv6 message: %s", strerror(errno));
479 ExitNow(error = OT_ERROR_DROP);
480 }
481
482 bufferLength = static_cast<uint16_t>(rval);
483
484 for (cmh = CMSG_FIRSTHDR(&msg); cmh; cmh = CMSG_NXTHDR(&msg, cmh))
485 {
486 if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO &&
487 cmh->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
488 {
489 struct in6_pktinfo pktinfo;
490
491 memcpy(&pktinfo, CMSG_DATA(cmh), sizeof pktinfo);
492 ifIndex = pktinfo.ipi6_ifindex;
493 dstAddr = pktinfo.ipi6_addr;
494 }
495 else if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_HOPLIMIT &&
496 cmh->cmsg_len == CMSG_LEN(sizeof(int)))
497 {
498 hopLimit = *(int *)CMSG_DATA(cmh);
499 }
500 }
501
502 VerifyOrExit(ifIndex == mInfraIfIndex, error = OT_ERROR_DROP);
503
504 // We currently accept only RA & RS messages for the Border Router and it requires that
505 // the hoplimit must be 255 and the source address must be a link-local address.
506 VerifyOrExit(hopLimit == 255 && IN6_IS_ADDR_LINKLOCAL(&srcAddr.sin6_addr), error = OT_ERROR_DROP);
507
508 otPlatInfraIfRecvIcmp6Nd(gInstance, ifIndex, reinterpret_cast<otIp6Address *>(&srcAddr.sin6_addr), buffer,
509 bufferLength);
510
511 exit:
512 if (error != OT_ERROR_NONE)
513 {
514 otLogDebgPlat("Failed to handle ICMPv6 message: %s", otThreadErrorToString(error));
515 }
516 }
517
Process(const otSysMainloopContext & aContext)518 void InfraNetif::Process(const otSysMainloopContext &aContext)
519 {
520 VerifyOrExit(mInfraIfIcmp6Socket != -1);
521 VerifyOrExit(mNetLinkSocket != -1);
522
523 if (FD_ISSET(mInfraIfIcmp6Socket, &aContext.mReadFdSet))
524 {
525 ReceiveIcmp6Message();
526 }
527
528 if (FD_ISSET(mNetLinkSocket, &aContext.mReadFdSet))
529 {
530 ReceiveNetLinkMessage();
531 }
532
533 exit:
534 return;
535 }
536
Get(void)537 InfraNetif &InfraNetif::Get(void)
538 {
539 static InfraNetif sInstance;
540
541 return sInstance;
542 }
543
544 } // namespace Posix
545 } // namespace ot
546 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
547