2 * Copyright (c) 2020, The OpenThread Authors.
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.
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.
31 * The file implements the ND Proxy management.
34 #include "backbone_router/nd_proxy.hpp"
36 #include <openthread/backbone_router_ftd.h>
40 #include <netinet/icmp6.h>
41 #include <netinet/ip6.h>
42 #include <sys/ioctl.h>
46 #include <linux/netfilter.h>
48 #error "Platform not supported"
51 #include "agent/instance_params.hpp"
52 #include "backbone_router/constants.hpp"
53 #include "common/code_utils.hpp"
54 #include "common/logging.hpp"
55 #include "common/types.hpp"
56 #include "utils/system_utils.hpp"
59 namespace BackboneRouter {
61 void NdProxyManager::Enable(const Ip6Prefix &aDomainPrefix)
63 otbrError error = OTBR_ERROR_NONE;
65 VerifyOrExit(!IsEnabled());
67 assert(aDomainPrefix.IsValid());
68 mDomainPrefix = aDomainPrefix;
70 SuccessOrExit(error = InitIcmp6RawSocket());
71 SuccessOrExit(error = UpdateMacAddress());
72 SuccessOrExit(error = InitNetfilterQueue());
74 // Add ip6tables rule for unicast ICMPv6 messages
75 VerifyOrExit(SystemUtils::ExecuteCommand(
76 "ip6tables -t raw -A PREROUTING -6 -d %s -p icmpv6 --icmpv6-type neighbor-solicitation -i %s -j "
77 "NFQUEUE --queue-num 88",
78 mDomainPrefix.ToString().c_str(), InstanceParams::Get().GetBackboneIfName()) == 0,
79 error = OTBR_ERROR_ERRNO);
82 if (error != OTBR_ERROR_NONE)
88 otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
91 void NdProxyManager::Disable(void)
93 otbrError error = OTBR_ERROR_NONE;
95 VerifyOrExit(IsEnabled());
100 // Remove ip6tables rule for unicast ICMPv6 messages
101 VerifyOrExit(SystemUtils::ExecuteCommand(
102 "ip6tables -t raw -D PREROUTING -6 -d %s -p icmpv6 --icmpv6-type neighbor-solicitation -i %s -j "
103 "NFQUEUE --queue-num 88",
104 mDomainPrefix.ToString().c_str(), InstanceParams::Get().GetBackboneIfName()) == 0,
105 error = OTBR_ERROR_ERRNO);
108 otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
111 void NdProxyManager::Init(void)
113 mBackboneIfIndex = if_nametoindex(InstanceParams::Get().GetBackboneIfName());
114 VerifyOrDie(mBackboneIfIndex > 0, "if_nametoindex failed");
117 void NdProxyManager::UpdateFdSet(fd_set & aReadFdSet,
118 fd_set & aWriteFdSet,
119 fd_set & aErrorFdSet,
121 timeval &aTimeout) const
123 OTBR_UNUSED_VARIABLE(aWriteFdSet);
124 OTBR_UNUSED_VARIABLE(aErrorFdSet);
125 OTBR_UNUSED_VARIABLE(aTimeout);
127 if (mIcmp6RawSock >= 0)
129 FD_SET(mIcmp6RawSock, &aReadFdSet);
130 aMaxFd = std::max(aMaxFd, mIcmp6RawSock);
133 if (mUnicastNsQueueSock >= 0)
135 FD_SET(mUnicastNsQueueSock, &aReadFdSet);
136 aMaxFd = std::max(aMaxFd, mUnicastNsQueueSock);
140 void NdProxyManager::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet, const fd_set &aErrorFdSet)
142 OTBR_UNUSED_VARIABLE(aWriteFdSet);
143 OTBR_UNUSED_VARIABLE(aErrorFdSet);
145 VerifyOrExit(IsEnabled());
147 if (FD_ISSET(mIcmp6RawSock, &aReadFdSet))
149 ProcessMulticastNeighborSolicition();
152 if (FD_ISSET(mUnicastNsQueueSock, &aReadFdSet))
154 ProcessUnicastNeighborSolicition();
160 void NdProxyManager::ProcessMulticastNeighborSolicition()
162 struct msghdr msghdr;
166 struct icmp6_hdr *icmp6header;
167 struct cmsghdr * cmsghdr;
168 unsigned char cbuf[2 * CMSG_SPACE(sizeof(struct in6_pktinfo))];
169 uint8_t packet[kMaxICMP6PacketSize];
170 otbrError error = OTBR_ERROR_NONE;
173 iovec.iov_len = kMaxICMP6PacketSize;
174 iovec.iov_base = packet;
176 msghdr.msg_name = &sin6;
177 msghdr.msg_namelen = sizeof(sin6);
178 msghdr.msg_iov = &iovec;
179 msghdr.msg_iovlen = 1;
180 msghdr.msg_control = cbuf;
181 msghdr.msg_controllen = sizeof(cbuf);
183 len = recvmsg(mIcmp6RawSock, &msghdr, 0);
185 VerifyOrExit(len >= static_cast<ssize_t>(sizeof(struct icmp6_hdr)), error = OTBR_ERROR_ERRNO);
188 Ip6Address &src = *reinterpret_cast<Ip6Address *>(&sin6.sin6_addr);
190 icmp6header = reinterpret_cast<icmp6_hdr *>(packet);
192 // only process neighbor solicit
193 VerifyOrExit(icmp6header->icmp6_type == ND_NEIGHBOR_SOLICIT, error = OTBR_ERROR_PARSE);
195 otbrLog(OTBR_LOG_DEBUG, "NdProxyManager: Received ND-NS from %s", src.ToString().c_str());
197 for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr))
199 if (cmsghdr->cmsg_level != IPPROTO_IPV6)
204 switch (cmsghdr->cmsg_type)
207 if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
209 struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsghdr);
210 Ip6Address & dst = *reinterpret_cast<Ip6Address *>(&pktinfo->ipi6_addr);
211 uint32_t ifindex = pktinfo->ipi6_ifindex;
213 for (const Ip6Address &ipaddr : mNdProxySet)
215 if (ipaddr.ToSolicitedNodeMulticastAddress() == dst)
222 otbrLog(OTBR_LOG_DEBUG, "NdProxyManager: dst=%s, ifindex=%d, proxying=%s", dst.ToString().c_str(),
223 ifindex, found ? "Y" : "N");
228 if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(int)))
230 int hops = *(int *)CMSG_DATA(cmsghdr);
232 otbrLog(OTBR_LOG_DEBUG, "NdProxyManager: hops=%d (%s)", hops, hops == 255 ? "Good" : "Bad");
234 VerifyOrExit(hops == 255);
240 VerifyOrExit(found, error = OTBR_ERROR_NOT_FOUND);
243 struct nd_neighbor_solicit *ns = reinterpret_cast<struct nd_neighbor_solicit *>(packet);
244 Ip6Address & target = *reinterpret_cast<Ip6Address *>(&ns->nd_ns_target);
246 otbrLog(OTBR_LOG_INFO, "NdProxyManager: send solicited NA for multicast NS: src=%s, target=%s",
247 src.ToString().c_str(), target.ToString().c_str());
249 SendNeighborAdvertisement(target, src);
254 otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
257 void NdProxyManager::ProcessUnicastNeighborSolicition(void)
259 otbrError error = OTBR_ERROR_NONE;
260 char packet[kMaxICMP6PacketSize];
263 VerifyOrExit((len = recv(mUnicastNsQueueSock, packet, sizeof(packet), 0)) >= 0, error = OTBR_ERROR_ERRNO);
264 VerifyOrExit(nfq_handle_packet(mNfqHandler, packet, len) == 0, error = OTBR_ERROR_ERRNO);
266 error = OTBR_ERROR_NONE;
269 otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
272 void NdProxyManager::HandleBackboneRouterNdProxyEvent(otBackboneRouterNdProxyEvent aEvent, const otIp6Address *aDua)
276 if (aEvent != OT_BACKBONE_ROUTER_NDPROXY_CLEARED)
278 assert(aDua != nullptr);
279 target = Ip6Address(aDua->mFields.m8);
284 case OT_BACKBONE_ROUTER_NDPROXY_ADDED:
285 case OT_BACKBONE_ROUTER_NDPROXY_RENEWED:
287 bool isNewInsert = mNdProxySet.insert(target).second;
291 JoinSolicitedNodeMulticastGroup(target);
294 SendNeighborAdvertisement(target, Ip6Address::GetLinkLocalAllNodesMulticastAddress());
297 case OT_BACKBONE_ROUTER_NDPROXY_REMOVED:
298 mNdProxySet.erase(target);
299 LeaveSolicitedNodeMulticastGroup(target);
301 case OT_BACKBONE_ROUTER_NDPROXY_CLEARED:
302 for (const Ip6Address &proxingTarget : mNdProxySet)
304 LeaveSolicitedNodeMulticastGroup(proxingTarget);
311 void NdProxyManager::SendNeighborAdvertisement(const Ip6Address &aTarget, const Ip6Address &aDst)
313 uint8_t packet[kMaxICMP6PacketSize];
315 struct nd_neighbor_advert &na = *reinterpret_cast<struct nd_neighbor_advert *>(packet);
316 struct nd_opt_hdr & opt = *reinterpret_cast<struct nd_opt_hdr *>(packet + sizeof(struct nd_neighbor_advert));
317 bool isSolicited = !aDst.IsMulticast();
319 otbrError error = OTBR_ERROR_NONE;
320 otBackboneRouterNdProxyInfo aNdProxyInfo;
322 VerifyOrExit(otBackboneRouterGetNdProxyInfo(mNcp.GetInstance(), reinterpret_cast<const otIp6Address *>(&aTarget),
323 &aNdProxyInfo) == OT_ERROR_NONE,
324 error = OTBR_ERROR_OPENTHREAD);
326 memset(packet, 0, sizeof(packet));
328 na.nd_na_type = ND_NEIGHBOR_ADVERT;
331 na.nd_na_flags_reserved = isSolicited ? ND_NA_FLAG_SOLICITED : 0;
333 na.nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
335 na.nd_na_flags_reserved |= aNdProxyInfo.mTimeSinceLastTransaction <= kDuaRecentTime ? ND_NA_FLAG_OVERRIDE : 0;
337 memcpy(&na.nd_na_target, aTarget.m8, sizeof(Ip6Address));
338 len += sizeof(struct nd_neighbor_advert);
340 opt.nd_opt_type = ND_OPT_TARGET_LINKADDR;
343 memcpy(reinterpret_cast<uint8_t *>(&opt) + 2, mMacAddress.m8, sizeof(mMacAddress));
345 len += (opt.nd_opt_len * 8);
349 VerifyOrExit(sendto(mIcmp6RawSock, packet, len, 0, reinterpret_cast<const sockaddr *>(&dst), sizeof(dst)) == len,
350 error = OTBR_ERROR_ERRNO);
353 otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
356 otbrError NdProxyManager::UpdateMacAddress(void)
358 otbrError error = OTBR_ERROR_NONE;
363 memset(&ifr, 0, sizeof(ifr));
364 strncpy(ifr.ifr_name, InstanceParams::Get().GetBackboneIfName(), sizeof(ifr.ifr_name) - 1);
366 VerifyOrExit(ioctl(mIcmp6RawSock, SIOCGIFHWADDR, &ifr) != -1, error = OTBR_ERROR_ERRNO);
367 memcpy(mMacAddress.m8, ifr.ifr_hwaddr.sa_data, sizeof(mMacAddress));
369 ExitNow(error = OTBR_ERROR_NOT_IMPLEMENTED);
372 otbrLogResult(error, "NdProxyManager: UpdateMacAddress to %s", mMacAddress.ToString().c_str());
376 otbrError NdProxyManager::InitIcmp6RawSocket(void)
378 otbrError error = OTBR_ERROR_NONE;
381 struct icmp6_filter filter;
383 mIcmp6RawSock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
384 VerifyOrExit(mIcmp6RawSock >= 0, error = OTBR_ERROR_ERRNO);
387 VerifyOrExit(setsockopt(mIcmp6RawSock, SOL_SOCKET, SO_BINDTODEVICE, InstanceParams::Get().GetBackboneIfName(),
388 strlen(InstanceParams::Get().GetBackboneIfName())) == 0,
389 error = OTBR_ERROR_ERRNO);
390 #else // __NetBSD__ || __FreeBSD__ || __APPLE__
391 VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IP, IP_BOUND_IF, mBackboneIfName.c_str(), mBackboneIfName.size()),
392 error = OTBR_ERROR_ERRNO);
395 VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) == 0,
396 error = OTBR_ERROR_ERRNO);
397 VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) == 0,
398 error = OTBR_ERROR_ERRNO);
399 VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)) == 0,
400 error = OTBR_ERROR_ERRNO);
401 VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof(hops)) == 0,
402 error = OTBR_ERROR_ERRNO);
404 ICMP6_FILTER_SETBLOCKALL(&filter);
405 ICMP6_FILTER_SETPASS(ND_NEIGHBOR_SOLICIT, &filter);
407 VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == 0,
408 error = OTBR_ERROR_ERRNO);
410 if (error != OTBR_ERROR_NONE)
412 FiniIcmp6RawSocket();
418 void NdProxyManager::FiniIcmp6RawSocket(void)
420 if (mIcmp6RawSock != -1)
422 close(mIcmp6RawSock);
427 otbrError NdProxyManager::InitNetfilterQueue(void)
429 otbrError error = OTBR_ERROR_ERRNO;
431 VerifyOrExit((mNfqHandler = nfq_open()) != nullptr);
432 VerifyOrExit(nfq_unbind_pf(mNfqHandler, AF_INET6) >= 0);
433 VerifyOrExit(nfq_bind_pf(mNfqHandler, AF_INET6) >= 0);
435 VerifyOrExit((mNfqQueueHandler = nfq_create_queue(mNfqHandler, 88, HandleNetfilterQueue, this)) != nullptr);
436 VerifyOrExit(nfq_set_mode(mNfqQueueHandler, NFQNL_COPY_PACKET, 0xffff) >= 0);
437 VerifyOrExit((mUnicastNsQueueSock = nfq_fd(mNfqHandler)) >= 0);
439 error = OTBR_ERROR_NONE;
442 otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
444 if (error != OTBR_ERROR_NONE)
446 FiniNetfilterQueue();
452 void NdProxyManager::FiniNetfilterQueue(void)
454 if (mUnicastNsQueueSock != -1)
456 close(mUnicastNsQueueSock);
457 mUnicastNsQueueSock = -1;
460 if (mNfqQueueHandler != nullptr)
462 nfq_destroy_queue(mNfqQueueHandler);
463 mNfqQueueHandler = nullptr;
466 if (mNfqHandler != nullptr)
468 nfq_close(mNfqHandler);
469 mNfqHandler = nullptr;
473 int NdProxyManager::HandleNetfilterQueue(struct nfq_q_handle *aNfQueueHandler,
474 struct nfgenmsg * aNfMsg,
475 struct nfq_data * aNfData,
478 return static_cast<NdProxyManager *>(aContext)->HandleNetfilterQueue(aNfQueueHandler, aNfMsg, aNfData);
481 int NdProxyManager::HandleNetfilterQueue(struct nfq_q_handle *aNfQueueHandler,
482 struct nfgenmsg * aNfMsg,
483 struct nfq_data * aNfData)
485 OTBR_UNUSED_VARIABLE(aNfMsg);
487 struct nfqnl_msg_packet_hdr *ph;
488 unsigned char * data;
492 int verdict = NF_ACCEPT;
496 struct icmp6_hdr *icmp6header = nullptr;
497 struct ip6_hdr * ip6header = nullptr;
498 otbrError error = OTBR_ERROR_NONE;
500 if ((ph = nfq_get_msg_packet_hdr(aNfData)) != nullptr)
502 id = ntohl(ph->packet_id);
503 otbrLog(OTBR_LOG_DEBUG, "NdProxyManager: %s: id %d", __FUNCTION__, id);
506 VerifyOrExit((len = nfq_get_payload(aNfData, &data)) > 0, error = OTBR_ERROR_PARSE);
508 ip6header = reinterpret_cast<struct ip6_hdr *>(data);
509 src = *reinterpret_cast<Ip6Address *>(&ip6header->ip6_src);
510 dst = *reinterpret_cast<Ip6Address *>(&ip6header->ip6_dst);
512 VerifyOrExit(ip6header->ip6_nxt == IPPROTO_ICMPV6);
514 otbrLog(OTBR_LOG_DEBUG, "NdProxyManager: Handle Neighbor Solicitation: from %s to %s", src.ToString().c_str(),
515 dst.ToString().c_str());
517 icmp6header = reinterpret_cast<struct icmp6_hdr *>(data + sizeof(struct ip6_hdr));
518 VerifyOrExit(icmp6header->icmp6_type == ND_NEIGHBOR_SOLICIT);
520 VerifyOrExit(mNdProxySet.find(dst) != mNdProxySet.end(), error = OTBR_ERROR_NOT_FOUND);
523 struct nd_neighbor_solicit &ns = *reinterpret_cast<struct nd_neighbor_solicit *>(data + sizeof(struct ip6_hdr));
524 Ip6Address & target = *reinterpret_cast<Ip6Address *>(&ns.nd_ns_target);
526 otbrLog(OTBR_LOG_DEBUG, "NdProxyManager: %s: target: %s, hoplimit %d", __FUNCTION__, target.ToString().c_str(),
527 ip6header->ip6_hlim);
528 VerifyOrExit(ip6header->ip6_hlim == 255, error = OTBR_ERROR_PARSE);
529 SendNeighborAdvertisement(target, src);
534 ret = nfq_set_verdict(aNfQueueHandler, id, verdict, len, data);
536 otbrLogResult(error, "NdProxyManager: %s (nfq_set_verdict id %d, ret %d verdict %d)", __FUNCTION__, id, ret,
542 void NdProxyManager::JoinSolicitedNodeMulticastGroup(const Ip6Address &aTarget) const
545 otbrError error = OTBR_ERROR_NONE;
546 Ip6Address solicitedMulticastAddress = aTarget.ToSolicitedNodeMulticastAddress();
548 mreq.ipv6mr_interface = mBackboneIfIndex;
549 solicitedMulticastAddress.CopyTo(mreq.ipv6mr_multiaddr);
551 VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == 0,
552 error = OTBR_ERROR_ERRNO);
554 otbrLogResult(error, "NdProxyManager: JoinSolicitedNodeMulticastGroup of %s: %s", aTarget.ToString().c_str(),
555 solicitedMulticastAddress.ToString().c_str());
558 void NdProxyManager::LeaveSolicitedNodeMulticastGroup(const Ip6Address &aTarget) const
561 otbrError error = OTBR_ERROR_NONE;
562 Ip6Address solicitedMulticastAddress = aTarget.ToSolicitedNodeMulticastAddress();
564 mreq.ipv6mr_interface = mBackboneIfIndex;
565 solicitedMulticastAddress.CopyTo(mreq.ipv6mr_multiaddr);
567 VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)) == 0,
568 error = OTBR_ERROR_ERRNO);
570 otbrLogResult(error, "NdProxyManager: LeaveSolicitedNodeMulticastGroup of %s: %s", aTarget.ToString().c_str(),
571 solicitedMulticastAddress.ToString().c_str());
574 } // namespace BackboneRouter