• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2020, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the infrastructure interface for posix.
32  */
33 
34 #include "platform-posix.h"
35 
36 #if OPENTHREAD_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