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