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