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