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