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