• 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  *   The file implements the ND Proxy management.
32  */
33 
34 #define OTBR_LOG_TAG "NDPROXY"
35 
36 #include "backbone_router/nd_proxy.hpp"
37 
38 #if OTBR_ENABLE_DUA_ROUTING
39 
40 #include <openthread/backbone_router_ftd.h>
41 
42 #include <assert.h>
43 #include <net/if.h>
44 #include <netinet/icmp6.h>
45 #include <netinet/ip6.h>
46 #include <sys/ioctl.h>
47 #include <unistd.h>
48 
49 #if __linux__
50 #include <linux/netfilter.h>
51 #else
52 #error "Platform not supported"
53 #endif
54 
55 #include "backbone_router/constants.hpp"
56 #include "common/code_utils.hpp"
57 #include "common/logging.hpp"
58 #include "common/types.hpp"
59 #include "utils/system_utils.hpp"
60 
61 namespace otbr {
62 namespace BackboneRouter {
63 
Enable(const Ip6Prefix & aDomainPrefix)64 void NdProxyManager::Enable(const Ip6Prefix &aDomainPrefix)
65 {
66     otbrError error = OTBR_ERROR_NONE;
67 
68     VerifyOrExit(!IsEnabled());
69 
70     assert(aDomainPrefix.IsValid());
71     mDomainPrefix = aDomainPrefix;
72 
73     SuccessOrExit(error = InitIcmp6RawSocket());
74     SuccessOrExit(error = UpdateMacAddress());
75     SuccessOrExit(error = InitNetfilterQueue());
76 
77     // Add ip6tables rule for unicast ICMPv6 messages
78     VerifyOrExit(SystemUtils::ExecuteCommand(
79                      "ip6tables -t raw -A PREROUTING -6 -d %s -p icmpv6 --icmpv6-type neighbor-solicitation -i %s -j "
80                      "NFQUEUE --queue-num 88",
81                      mDomainPrefix.ToString().c_str(), mBackboneInterfaceName.c_str()) == 0,
82                  error = OTBR_ERROR_ERRNO);
83 
84 exit:
85     if (error != OTBR_ERROR_NONE)
86     {
87         FiniNetfilterQueue();
88         FiniIcmp6RawSocket();
89     }
90 
91     otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
92 }
93 
Disable(void)94 void NdProxyManager::Disable(void)
95 {
96     otbrError error = OTBR_ERROR_NONE;
97 
98     VerifyOrExit(IsEnabled());
99 
100     FiniNetfilterQueue();
101     FiniIcmp6RawSocket();
102 
103     // Remove ip6tables rule for unicast ICMPv6 messages
104     VerifyOrExit(SystemUtils::ExecuteCommand(
105                      "ip6tables -t raw -D PREROUTING -6 -d %s -p icmpv6 --icmpv6-type neighbor-solicitation -i %s -j "
106                      "NFQUEUE --queue-num 88",
107                      mDomainPrefix.ToString().c_str(), mBackboneInterfaceName.c_str()) == 0,
108                  error = OTBR_ERROR_ERRNO);
109 
110 exit:
111     otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
112 }
113 
Init(void)114 void NdProxyManager::Init(void)
115 {
116     mBackboneIfIndex = if_nametoindex(mBackboneInterfaceName.c_str());
117     VerifyOrDie(mBackboneIfIndex > 0, "if_nametoindex failed");
118 }
119 
Update(MainloopContext & aMainloop)120 void NdProxyManager::Update(MainloopContext &aMainloop)
121 {
122     if (mIcmp6RawSock >= 0)
123     {
124         FD_SET(mIcmp6RawSock, &aMainloop.mReadFdSet);
125         aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, mIcmp6RawSock);
126     }
127 
128     if (mUnicastNsQueueSock >= 0)
129     {
130         FD_SET(mUnicastNsQueueSock, &aMainloop.mReadFdSet);
131         aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, mUnicastNsQueueSock);
132     }
133 }
134 
Process(const MainloopContext & aMainloop)135 void NdProxyManager::Process(const MainloopContext &aMainloop)
136 {
137     VerifyOrExit(IsEnabled());
138 
139     if (FD_ISSET(mIcmp6RawSock, &aMainloop.mReadFdSet))
140     {
141         ProcessMulticastNeighborSolicition();
142     }
143 
144     if (FD_ISSET(mUnicastNsQueueSock, &aMainloop.mReadFdSet))
145     {
146         ProcessUnicastNeighborSolicition();
147     }
148 exit:
149     return;
150 }
151 
ProcessMulticastNeighborSolicition()152 void NdProxyManager::ProcessMulticastNeighborSolicition()
153 {
154     struct msghdr     msghdr;
155     sockaddr_in6      sin6;
156     struct iovec      iovec;
157     ssize_t           len;
158     struct icmp6_hdr *icmp6header;
159     struct cmsghdr *  cmsghdr;
160     unsigned char     cbuf[2 * CMSG_SPACE(sizeof(struct in6_pktinfo))];
161     uint8_t           packet[kMaxICMP6PacketSize];
162     otbrError         error = OTBR_ERROR_NONE;
163     bool              found = false;
164 
165     iovec.iov_len  = kMaxICMP6PacketSize;
166     iovec.iov_base = packet;
167 
168     msghdr.msg_name       = &sin6;
169     msghdr.msg_namelen    = sizeof(sin6);
170     msghdr.msg_iov        = &iovec;
171     msghdr.msg_iovlen     = 1;
172     msghdr.msg_control    = cbuf;
173     msghdr.msg_controllen = sizeof(cbuf);
174 
175     len = recvmsg(mIcmp6RawSock, &msghdr, 0);
176 
177     VerifyOrExit(len >= static_cast<ssize_t>(sizeof(struct icmp6_hdr)), error = OTBR_ERROR_ERRNO);
178 
179     {
180         Ip6Address &src = *reinterpret_cast<Ip6Address *>(&sin6.sin6_addr);
181 
182         icmp6header = reinterpret_cast<icmp6_hdr *>(packet);
183 
184         // only process neighbor solicit
185         VerifyOrExit(icmp6header->icmp6_type == ND_NEIGHBOR_SOLICIT, error = OTBR_ERROR_PARSE);
186 
187         otbrLogDebug("NdProxyManager: Received ND-NS from %s", src.ToString().c_str());
188 
189         for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr))
190         {
191             if (cmsghdr->cmsg_level != IPPROTO_IPV6)
192             {
193                 continue;
194             }
195 
196             switch (cmsghdr->cmsg_type)
197             {
198             case IPV6_PKTINFO:
199                 if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
200                 {
201                     struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsghdr);
202                     Ip6Address &        dst     = *reinterpret_cast<Ip6Address *>(&pktinfo->ipi6_addr);
203                     uint32_t            ifindex = pktinfo->ipi6_ifindex;
204 
205                     for (const Ip6Address &ipaddr : mNdProxySet)
206                     {
207                         if (ipaddr.ToSolicitedNodeMulticastAddress() == dst)
208                         {
209                             found = true;
210                             break;
211                         }
212                     }
213 
214                     otbrLogDebug("NdProxyManager: dst=%s, ifindex=%d, proxying=%s", dst.ToString().c_str(), ifindex,
215                                  found ? "Y" : "N");
216                 }
217                 break;
218 
219             case IPV6_HOPLIMIT:
220                 if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(int)))
221                 {
222                     int hops = *(int *)CMSG_DATA(cmsghdr);
223 
224                     otbrLogDebug("NdProxyManager: hops=%d (%s)", hops, hops == 255 ? "Good" : "Bad");
225 
226                     VerifyOrExit(hops == 255);
227                 }
228                 break;
229             }
230         }
231 
232         VerifyOrExit(found, error = OTBR_ERROR_NOT_FOUND);
233 
234         {
235             struct nd_neighbor_solicit *ns     = reinterpret_cast<struct nd_neighbor_solicit *>(packet);
236             Ip6Address &                target = *reinterpret_cast<Ip6Address *>(&ns->nd_ns_target);
237 
238             otbrLogInfo("NdProxyManager: send solicited NA for multicast NS: src=%s, target=%s", src.ToString().c_str(),
239                         target.ToString().c_str());
240 
241             SendNeighborAdvertisement(target, src);
242         }
243     }
244 
245 exit:
246     otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
247 }
248 
ProcessUnicastNeighborSolicition(void)249 void NdProxyManager::ProcessUnicastNeighborSolicition(void)
250 {
251     otbrError error = OTBR_ERROR_NONE;
252     char      packet[kMaxICMP6PacketSize];
253     ssize_t   len;
254 
255     VerifyOrExit((len = recv(mUnicastNsQueueSock, packet, sizeof(packet), 0)) >= 0, error = OTBR_ERROR_ERRNO);
256     VerifyOrExit(nfq_handle_packet(mNfqHandler, packet, len) == 0, error = OTBR_ERROR_ERRNO);
257 
258     error = OTBR_ERROR_NONE;
259 
260 exit:
261     otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
262 }
263 
HandleBackboneRouterNdProxyEvent(otBackboneRouterNdProxyEvent aEvent,const otIp6Address * aDua)264 void NdProxyManager::HandleBackboneRouterNdProxyEvent(otBackboneRouterNdProxyEvent aEvent, const otIp6Address *aDua)
265 {
266     Ip6Address target;
267 
268     if (aEvent != OT_BACKBONE_ROUTER_NDPROXY_CLEARED)
269     {
270         assert(aDua != nullptr);
271         target = Ip6Address(aDua->mFields.m8);
272     }
273 
274     switch (aEvent)
275     {
276     case OT_BACKBONE_ROUTER_NDPROXY_ADDED:
277     case OT_BACKBONE_ROUTER_NDPROXY_RENEWED:
278     {
279         bool isNewInsert = mNdProxySet.insert(target).second;
280 
281         if (isNewInsert)
282         {
283             JoinSolicitedNodeMulticastGroup(target);
284         }
285 
286         SendNeighborAdvertisement(target, Ip6Address::GetLinkLocalAllNodesMulticastAddress());
287         break;
288     }
289     case OT_BACKBONE_ROUTER_NDPROXY_REMOVED:
290         mNdProxySet.erase(target);
291         LeaveSolicitedNodeMulticastGroup(target);
292         break;
293     case OT_BACKBONE_ROUTER_NDPROXY_CLEARED:
294         for (const Ip6Address &proxingTarget : mNdProxySet)
295         {
296             LeaveSolicitedNodeMulticastGroup(proxingTarget);
297         }
298         mNdProxySet.clear();
299         break;
300     }
301 }
302 
SendNeighborAdvertisement(const Ip6Address & aTarget,const Ip6Address & aDst)303 void NdProxyManager::SendNeighborAdvertisement(const Ip6Address &aTarget, const Ip6Address &aDst)
304 {
305     uint8_t                    packet[kMaxICMP6PacketSize];
306     uint16_t                   len = 0;
307     struct nd_neighbor_advert &na  = *reinterpret_cast<struct nd_neighbor_advert *>(packet);
308     struct nd_opt_hdr &        opt = *reinterpret_cast<struct nd_opt_hdr *>(packet + sizeof(struct nd_neighbor_advert));
309     bool                       isSolicited = !aDst.IsMulticast();
310     sockaddr_in6               dst;
311     otbrError                  error = OTBR_ERROR_NONE;
312     otBackboneRouterNdProxyInfo aNdProxyInfo;
313 
314     VerifyOrExit(otBackboneRouterGetNdProxyInfo(mNcp.GetInstance(), reinterpret_cast<const otIp6Address *>(&aTarget),
315                                                 &aNdProxyInfo) == OT_ERROR_NONE,
316                  error = OTBR_ERROR_OPENTHREAD);
317 
318     memset(packet, 0, sizeof(packet));
319 
320     na.nd_na_type = ND_NEIGHBOR_ADVERT;
321     na.nd_na_code = 0;
322     // set Solicited
323     na.nd_na_flags_reserved = isSolicited ? ND_NA_FLAG_SOLICITED : 0;
324     // set Router
325     na.nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
326     // set Override
327     na.nd_na_flags_reserved |= aNdProxyInfo.mTimeSinceLastTransaction <= kDuaRecentTime ? ND_NA_FLAG_OVERRIDE : 0;
328 
329     memcpy(&na.nd_na_target, aTarget.m8, sizeof(Ip6Address));
330     len += sizeof(struct nd_neighbor_advert);
331 
332     opt.nd_opt_type = ND_OPT_TARGET_LINKADDR;
333     opt.nd_opt_len  = 1;
334 
335     memcpy(reinterpret_cast<uint8_t *>(&opt) + 2, mMacAddress.m8, sizeof(mMacAddress));
336 
337     len += (opt.nd_opt_len * 8);
338 
339     aDst.CopyTo(dst);
340 
341     VerifyOrExit(sendto(mIcmp6RawSock, packet, len, 0, reinterpret_cast<const sockaddr *>(&dst), sizeof(dst)) == len,
342                  error = OTBR_ERROR_ERRNO);
343 
344 exit:
345     otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
346 }
347 
UpdateMacAddress(void)348 otbrError NdProxyManager::UpdateMacAddress(void)
349 {
350     otbrError error = OTBR_ERROR_NONE;
351 
352 #if !__APPLE__
353     struct ifreq ifr;
354 
355     memset(&ifr, 0, sizeof(ifr));
356     strncpy(ifr.ifr_name, mBackboneInterfaceName.c_str(), sizeof(ifr.ifr_name) - 1);
357 
358     VerifyOrExit(ioctl(mIcmp6RawSock, SIOCGIFHWADDR, &ifr) != -1, error = OTBR_ERROR_ERRNO);
359     memcpy(mMacAddress.m8, ifr.ifr_hwaddr.sa_data, sizeof(mMacAddress));
360 #else
361     ExitNow(error = OTBR_ERROR_NOT_IMPLEMENTED);
362 #endif
363 exit:
364     otbrLogResult(error, "NdProxyManager: UpdateMacAddress to %s", mMacAddress.ToString().c_str());
365     return error;
366 }
367 
InitIcmp6RawSocket(void)368 otbrError NdProxyManager::InitIcmp6RawSocket(void)
369 {
370     otbrError           error = OTBR_ERROR_NONE;
371     int                 on    = 1;
372     int                 hops  = 255;
373     struct icmp6_filter filter;
374 
375     mIcmp6RawSock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
376     VerifyOrExit(mIcmp6RawSock >= 0, error = OTBR_ERROR_ERRNO);
377 
378 #if __linux__
379     VerifyOrExit(setsockopt(mIcmp6RawSock, SOL_SOCKET, SO_BINDTODEVICE, mBackboneInterfaceName.c_str(),
380                             mBackboneInterfaceName.length()) == 0,
381                  error = OTBR_ERROR_ERRNO);
382 #else  // __NetBSD__ || __FreeBSD__ || __APPLE__
383     VerifyOrExit(
384         setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_BOUND_IF, mBackboneIfName.c_str(), mBackboneIfName.size()),
385         error = OTBR_ERROR_ERRNO);
386 #endif // __linux__
387 
388     VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) == 0,
389                  error = OTBR_ERROR_ERRNO);
390     VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) == 0,
391                  error = OTBR_ERROR_ERRNO);
392     VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)) == 0,
393                  error = OTBR_ERROR_ERRNO);
394     VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof(hops)) == 0,
395                  error = OTBR_ERROR_ERRNO);
396 
397     ICMP6_FILTER_SETBLOCKALL(&filter);
398     ICMP6_FILTER_SETPASS(ND_NEIGHBOR_SOLICIT, &filter);
399 
400     VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == 0,
401                  error = OTBR_ERROR_ERRNO);
402 exit:
403     if (error != OTBR_ERROR_NONE)
404     {
405         FiniIcmp6RawSocket();
406     }
407 
408     return error;
409 }
410 
FiniIcmp6RawSocket(void)411 void NdProxyManager::FiniIcmp6RawSocket(void)
412 {
413     if (mIcmp6RawSock != -1)
414     {
415         close(mIcmp6RawSock);
416         mIcmp6RawSock = -1;
417     }
418 }
419 
InitNetfilterQueue(void)420 otbrError NdProxyManager::InitNetfilterQueue(void)
421 {
422     otbrError error = OTBR_ERROR_ERRNO;
423 
424     VerifyOrExit((mNfqHandler = nfq_open()) != nullptr);
425     VerifyOrExit(nfq_unbind_pf(mNfqHandler, AF_INET6) >= 0);
426     VerifyOrExit(nfq_bind_pf(mNfqHandler, AF_INET6) >= 0);
427 
428     VerifyOrExit((mNfqQueueHandler = nfq_create_queue(mNfqHandler, 88, HandleNetfilterQueue, this)) != nullptr);
429     VerifyOrExit(nfq_set_mode(mNfqQueueHandler, NFQNL_COPY_PACKET, 0xffff) >= 0);
430     VerifyOrExit((mUnicastNsQueueSock = nfq_fd(mNfqHandler)) >= 0);
431 
432     error = OTBR_ERROR_NONE;
433 
434 exit:
435     otbrLogResult(error, "NdProxyManager: %s", __FUNCTION__);
436 
437     if (error != OTBR_ERROR_NONE)
438     {
439         FiniNetfilterQueue();
440     }
441 
442     return error;
443 }
444 
FiniNetfilterQueue(void)445 void NdProxyManager::FiniNetfilterQueue(void)
446 {
447     if (mUnicastNsQueueSock != -1)
448     {
449         close(mUnicastNsQueueSock);
450         mUnicastNsQueueSock = -1;
451     }
452 
453     if (mNfqQueueHandler != nullptr)
454     {
455         nfq_destroy_queue(mNfqQueueHandler);
456         mNfqQueueHandler = nullptr;
457     }
458 
459     if (mNfqHandler != nullptr)
460     {
461         nfq_close(mNfqHandler);
462         mNfqHandler = nullptr;
463     }
464 }
465 
HandleNetfilterQueue(struct nfq_q_handle * aNfQueueHandler,struct nfgenmsg * aNfMsg,struct nfq_data * aNfData,void * aContext)466 int NdProxyManager::HandleNetfilterQueue(struct nfq_q_handle *aNfQueueHandler,
467                                          struct nfgenmsg *    aNfMsg,
468                                          struct nfq_data *    aNfData,
469                                          void *               aContext)
470 {
471     return static_cast<NdProxyManager *>(aContext)->HandleNetfilterQueue(aNfQueueHandler, aNfMsg, aNfData);
472 }
473 
HandleNetfilterQueue(struct nfq_q_handle * aNfQueueHandler,struct nfgenmsg * aNfMsg,struct nfq_data * aNfData)474 int NdProxyManager::HandleNetfilterQueue(struct nfq_q_handle *aNfQueueHandler,
475                                          struct nfgenmsg *    aNfMsg,
476                                          struct nfq_data *    aNfData)
477 {
478     OTBR_UNUSED_VARIABLE(aNfMsg);
479 
480     struct nfqnl_msg_packet_hdr *ph;
481     unsigned char *              data;
482     uint32_t                     id      = 0;
483     int                          ret     = 0;
484     int                          len     = 0;
485     int                          verdict = NF_ACCEPT;
486 
487     Ip6Address        dst;
488     Ip6Address        src;
489     struct icmp6_hdr *icmp6header = nullptr;
490     struct ip6_hdr *  ip6header   = nullptr;
491     otbrError         error       = OTBR_ERROR_NONE;
492 
493     if ((ph = nfq_get_msg_packet_hdr(aNfData)) != nullptr)
494     {
495         id = ntohl(ph->packet_id);
496         otbrLogDebug("NdProxyManager: %s: id %d", __FUNCTION__, id);
497     }
498 
499     VerifyOrExit((len = nfq_get_payload(aNfData, &data)) > 0, error = OTBR_ERROR_PARSE);
500 
501     ip6header = reinterpret_cast<struct ip6_hdr *>(data);
502     src       = *reinterpret_cast<Ip6Address *>(&ip6header->ip6_src);
503     dst       = *reinterpret_cast<Ip6Address *>(&ip6header->ip6_dst);
504 
505     VerifyOrExit(ip6header->ip6_nxt == IPPROTO_ICMPV6);
506 
507     otbrLogDebug("NdProxyManager: Handle Neighbor Solicitation: from %s to %s", src.ToString().c_str(),
508                  dst.ToString().c_str());
509 
510     icmp6header = reinterpret_cast<struct icmp6_hdr *>(data + sizeof(struct ip6_hdr));
511     VerifyOrExit(icmp6header->icmp6_type == ND_NEIGHBOR_SOLICIT);
512 
513     VerifyOrExit(mNdProxySet.find(dst) != mNdProxySet.end(), error = OTBR_ERROR_NOT_FOUND);
514 
515     {
516         struct nd_neighbor_solicit &ns = *reinterpret_cast<struct nd_neighbor_solicit *>(data + sizeof(struct ip6_hdr));
517         Ip6Address &                target = *reinterpret_cast<Ip6Address *>(&ns.nd_ns_target);
518 
519         otbrLogDebug("NdProxyManager: %s: target: %s, hoplimit %d", __FUNCTION__, target.ToString().c_str(),
520                      ip6header->ip6_hlim);
521         VerifyOrExit(ip6header->ip6_hlim == 255, error = OTBR_ERROR_PARSE);
522         SendNeighborAdvertisement(target, src);
523         verdict = NF_DROP;
524     }
525 
526 exit:
527     ret = nfq_set_verdict(aNfQueueHandler, id, verdict, len, data);
528 
529     otbrLogResult(error, "NdProxyManager: %s (nfq_set_verdict id  %d, ret %d verdict %d)", __FUNCTION__, id, ret,
530                   verdict);
531 
532     return ret;
533 }
534 
JoinSolicitedNodeMulticastGroup(const Ip6Address & aTarget) const535 void NdProxyManager::JoinSolicitedNodeMulticastGroup(const Ip6Address &aTarget) const
536 {
537     ipv6_mreq  mreq;
538     otbrError  error                     = OTBR_ERROR_NONE;
539     Ip6Address solicitedMulticastAddress = aTarget.ToSolicitedNodeMulticastAddress();
540 
541     mreq.ipv6mr_interface = mBackboneIfIndex;
542     solicitedMulticastAddress.CopyTo(mreq.ipv6mr_multiaddr);
543 
544     VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == 0,
545                  error = OTBR_ERROR_ERRNO);
546 exit:
547     otbrLogResult(error, "NdProxyManager: JoinSolicitedNodeMulticastGroup of %s: %s", aTarget.ToString().c_str(),
548                   solicitedMulticastAddress.ToString().c_str());
549 }
550 
LeaveSolicitedNodeMulticastGroup(const Ip6Address & aTarget) const551 void NdProxyManager::LeaveSolicitedNodeMulticastGroup(const Ip6Address &aTarget) const
552 {
553     ipv6_mreq  mreq;
554     otbrError  error                     = OTBR_ERROR_NONE;
555     Ip6Address solicitedMulticastAddress = aTarget.ToSolicitedNodeMulticastAddress();
556 
557     mreq.ipv6mr_interface = mBackboneIfIndex;
558     solicitedMulticastAddress.CopyTo(mreq.ipv6mr_multiaddr);
559 
560     VerifyOrExit(setsockopt(mIcmp6RawSock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)) == 0,
561                  error = OTBR_ERROR_ERRNO);
562 exit:
563     otbrLogResult(error, "NdProxyManager: LeaveSolicitedNodeMulticastGroup of %s: %s", aTarget.ToString().c_str(),
564                   solicitedMulticastAddress.ToString().c_str());
565 }
566 
567 } // namespace BackboneRouter
568 } // namespace otbr
569 
570 #endif // OTBR_ENABLE_DUA_ROUTING
571