| /* |
| * Copyright (c) 2024, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "mdns_socket.hpp" |
| |
| #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE |
| |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <net/if.h> |
| #include <netinet/in.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "ip6_utils.hpp" |
| #include "platform-posix.h" |
| #include "common/code_utils.hpp" |
| |
| extern "C" otError otPlatMdnsSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex) |
| { |
| return ot::Posix::MdnsSocket::Get().SetListeningEnabled(aInstance, aEnable, aInfraIfIndex); |
| } |
| |
| extern "C" void otPlatMdnsSendMulticast(otInstance *aInstance, otMessage *aMessage, uint32_t aInfraIfIndex) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| return ot::Posix::MdnsSocket::Get().SendMulticast(aMessage, aInfraIfIndex); |
| } |
| |
| extern "C" void otPlatMdnsSendUnicast(otInstance *aInstance, otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| return ot::Posix::MdnsSocket::Get().SendUnicast(aMessage, aAddress); |
| } |
| |
| namespace ot { |
| namespace Posix { |
| |
| using namespace ot::Posix::Ip6Utils; |
| |
| const char MdnsSocket::kLogModuleName[] = "MdnsSocket"; |
| |
| MdnsSocket &MdnsSocket::Get(void) |
| { |
| static MdnsSocket sInstance; |
| |
| return sInstance; |
| } |
| |
| void MdnsSocket::Init(void) |
| { |
| mEnabled = false; |
| mInfraIfIndex = 0; |
| mFd6 = -1; |
| mFd4 = -1; |
| mPendingIp6Tx = 0; |
| mPendingIp4Tx = 0; |
| |
| // mDNS multicast IPv6 address "ff02::fb" |
| memset(&mMulticastIp6Address, 0, sizeof(otIp6Address)); |
| mMulticastIp6Address.mFields.m8[0] = 0xff; |
| mMulticastIp6Address.mFields.m8[1] = 0x02; |
| mMulticastIp6Address.mFields.m8[15] = 0xfb; |
| |
| // mDNS multicast IPv4 address "224.0.0.251" |
| memset(&mMulticastIp4Address, 0, sizeof(otIp4Address)); |
| mMulticastIp4Address.mFields.m8[0] = 224; |
| mMulticastIp4Address.mFields.m8[3] = 251; |
| |
| memset(&mTxQueue, 0, sizeof(mTxQueue)); |
| } |
| |
| void MdnsSocket::SetUp(void) |
| { |
| otMessageQueueInit(&mTxQueue); |
| Mainloop::Manager::Get().Add(*this); |
| } |
| |
| void MdnsSocket::TearDown(void) |
| { |
| Mainloop::Manager::Get().Remove(*this); |
| |
| if (mEnabled) |
| { |
| ClearTxQueue(); |
| mEnabled = false; |
| } |
| } |
| |
| void MdnsSocket::Deinit(void) |
| { |
| CloseIp4Socket(); |
| CloseIp6Socket(); |
| } |
| |
| void MdnsSocket::Update(otSysMainloopContext &aContext) |
| { |
| VerifyOrExit(mEnabled); |
| |
| FD_SET(mFd6, &aContext.mReadFdSet); |
| FD_SET(mFd4, &aContext.mReadFdSet); |
| |
| if (mPendingIp6Tx > 0) |
| { |
| FD_SET(mFd6, &aContext.mWriteFdSet); |
| } |
| |
| if (mPendingIp4Tx > 0) |
| { |
| FD_SET(mFd4, &aContext.mWriteFdSet); |
| } |
| |
| if (aContext.mMaxFd < mFd6) |
| { |
| aContext.mMaxFd = mFd6; |
| } |
| |
| if (aContext.mMaxFd < mFd4) |
| { |
| aContext.mMaxFd = mFd4; |
| } |
| |
| exit: |
| return; |
| } |
| |
| void MdnsSocket::Process(const otSysMainloopContext &aContext) |
| { |
| VerifyOrExit(mEnabled); |
| |
| if (FD_ISSET(mFd6, &aContext.mWriteFdSet)) |
| { |
| SendQueuedMessages(kIp6Msg); |
| } |
| |
| if (FD_ISSET(mFd4, &aContext.mWriteFdSet)) |
| { |
| SendQueuedMessages(kIp4Msg); |
| } |
| |
| if (FD_ISSET(mFd6, &aContext.mReadFdSet)) |
| { |
| ReceiveMessage(kIp6Msg); |
| } |
| |
| if (FD_ISSET(mFd4, &aContext.mReadFdSet)) |
| { |
| ReceiveMessage(kIp4Msg); |
| } |
| |
| exit: |
| return; |
| } |
| |
| otError MdnsSocket::SetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aEnable != mEnabled); |
| mInstance = aInstance; |
| |
| if (aEnable) |
| { |
| error = Enable(aInfraIfIndex); |
| } |
| else |
| { |
| Disable(aInfraIfIndex); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError MdnsSocket::Enable(uint32_t aInfraIfIndex) |
| { |
| otError error; |
| |
| SuccessOrExit(error = OpenIp4Socket(aInfraIfIndex)); |
| SuccessOrExit(error = JoinOrLeaveIp4MulticastGroup(/* aJoin */ true, aInfraIfIndex)); |
| |
| SuccessOrExit(error = OpenIp6Socket(aInfraIfIndex)); |
| SuccessOrExit(error = JoinOrLeaveIp6MulticastGroup(/* aJoin */ true, aInfraIfIndex)); |
| |
| mEnabled = true; |
| mInfraIfIndex = aInfraIfIndex; |
| |
| LogInfo("Enabled"); |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| CloseIp4Socket(); |
| CloseIp6Socket(); |
| } |
| |
| return error; |
| } |
| |
| void MdnsSocket::Disable(uint32_t aInfraIfIndex) |
| { |
| ClearTxQueue(); |
| |
| IgnoreError(JoinOrLeaveIp4MulticastGroup(/* aJoin */ false, aInfraIfIndex)); |
| IgnoreError(JoinOrLeaveIp6MulticastGroup(/* aJoin */ false, aInfraIfIndex)); |
| CloseIp4Socket(); |
| CloseIp6Socket(); |
| |
| mEnabled = false; |
| |
| LogInfo("Disabled"); |
| } |
| |
| void MdnsSocket::SendMulticast(otMessage *aMessage, uint32_t aInfraIfIndex) |
| { |
| Metadata metadata; |
| uint16_t length; |
| |
| VerifyOrExit(mEnabled); |
| VerifyOrExit(aInfraIfIndex == mInfraIfIndex); |
| |
| length = otMessageGetLength(aMessage); |
| |
| if (length > kMaxMessageLength) |
| { |
| LogWarn("Multicast msg length %u is longer than max %u", length, kMaxMessageLength); |
| ExitNow(); |
| } |
| |
| metadata.mIp6Address = mMulticastIp6Address; |
| metadata.mIp6Port = kMdnsPort; |
| metadata.mIp4Address = mMulticastIp4Address; |
| metadata.mIp4Port = kMdnsPort; |
| |
| SuccessOrExit(otMessageAppend(aMessage, &metadata, sizeof(Metadata))); |
| |
| mPendingIp4Tx++; |
| mPendingIp6Tx++; |
| |
| otMessageQueueEnqueue(&mTxQueue, aMessage); |
| aMessage = NULL; |
| |
| exit: |
| if (aMessage != NULL) |
| { |
| otMessageFree(aMessage); |
| } |
| } |
| |
| void MdnsSocket::SendUnicast(otMessage *aMessage, const otPlatMdnsAddressInfo *aAddress) |
| { |
| bool isIp4 = false; |
| Metadata metadata; |
| uint16_t length; |
| |
| VerifyOrExit(mEnabled); |
| VerifyOrExit(aAddress->mInfraIfIndex == mInfraIfIndex); |
| |
| length = otMessageGetLength(aMessage); |
| |
| if (length > kMaxMessageLength) |
| { |
| LogWarn("Unicast msg length %u is longer than max %u", length, kMaxMessageLength); |
| ExitNow(); |
| } |
| |
| memset(&metadata, 0, sizeof(Metadata)); |
| |
| if (otIp4FromIp4MappedIp6Address(&aAddress->mAddress, &metadata.mIp4Address) == OT_ERROR_NONE) |
| { |
| isIp4 = true; |
| metadata.mIp4Port = aAddress->mPort; |
| metadata.mIp6Port = 0; |
| } |
| else |
| { |
| metadata.mIp6Address = aAddress->mAddress; |
| metadata.mIp4Port = 0; |
| metadata.mIp6Port = aAddress->mPort; |
| } |
| |
| SuccessOrExit(otMessageAppend(aMessage, &metadata, sizeof(Metadata))); |
| |
| if (isIp4) |
| { |
| mPendingIp4Tx++; |
| } |
| else |
| { |
| mPendingIp6Tx++; |
| } |
| |
| otMessageQueueEnqueue(&mTxQueue, aMessage); |
| aMessage = NULL; |
| |
| exit: |
| if (aMessage != NULL) |
| { |
| otMessageFree(aMessage); |
| } |
| } |
| |
| void MdnsSocket::ClearTxQueue(void) |
| { |
| otMessage *message; |
| |
| while ((message = otMessageQueueGetHead(&mTxQueue)) != NULL) |
| { |
| otMessageQueueDequeue(&mTxQueue, message); |
| otMessageFree(message); |
| } |
| |
| mPendingIp4Tx = 0; |
| mPendingIp6Tx = 0; |
| } |
| |
| void MdnsSocket::SendQueuedMessages(MsgType aMsgType) |
| { |
| switch (aMsgType) |
| { |
| case kIp6Msg: |
| VerifyOrExit(mPendingIp6Tx > 0); |
| break; |
| case kIp4Msg: |
| VerifyOrExit(mPendingIp4Tx > 0); |
| break; |
| } |
| |
| for (otMessage *message = otMessageQueueGetHead(&mTxQueue); message != NULL; |
| message = otMessageQueueGetNext(&mTxQueue, message)) |
| { |
| bool isTxPending = false; |
| uint16_t length; |
| uint16_t offset; |
| int bytesSent; |
| Metadata metadata; |
| uint8_t buffer[kMaxMessageLength]; |
| struct sockaddr_in6 addr6; |
| struct sockaddr_in addr; |
| |
| length = otMessageGetLength(message); |
| |
| offset = length - sizeof(Metadata); |
| length -= sizeof(Metadata); |
| |
| otMessageRead(message, offset, &metadata, sizeof(Metadata)); |
| |
| switch (aMsgType) |
| { |
| case kIp6Msg: |
| isTxPending = (metadata.mIp6Port != 0); |
| break; |
| case kIp4Msg: |
| isTxPending = (metadata.mIp4Port != 0); |
| break; |
| } |
| |
| if (!isTxPending) |
| { |
| continue; |
| } |
| |
| otMessageRead(message, 0, buffer, length); |
| |
| switch (aMsgType) |
| { |
| case kIp6Msg: |
| memset(&addr6, 0, sizeof(addr6)); |
| addr6.sin6_family = AF_INET6; |
| addr6.sin6_port = htons(metadata.mIp6Port); |
| CopyIp6AddressTo(metadata.mIp6Address, &addr6.sin6_addr); |
| bytesSent = sendto(mFd6, buffer, length, 0, reinterpret_cast<struct sockaddr *>(&addr6), sizeof(addr6)); |
| VerifyOrExit(bytesSent == length); |
| metadata.mIp6Port = 0; |
| mPendingIp6Tx--; |
| break; |
| |
| case kIp4Msg: |
| memset(&addr, 0, sizeof(addr)); |
| addr.sin_family = AF_INET; |
| addr.sin_port = htons(metadata.mIp4Port); |
| memcpy(&addr.sin_addr.s_addr, &metadata.mIp4Address, sizeof(otIp4Address)); |
| bytesSent = sendto(mFd4, buffer, length, 0, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)); |
| VerifyOrExit(bytesSent == length); |
| metadata.mIp4Port = 0; |
| mPendingIp4Tx--; |
| break; |
| } |
| |
| if (metadata.CanFreeMessage()) |
| { |
| otMessageQueueDequeue(&mTxQueue, message); |
| otMessageFree(message); |
| } |
| else |
| { |
| otMessageWrite(message, offset, &metadata, sizeof(Metadata)); |
| } |
| } |
| |
| exit: |
| return; |
| } |
| |
| void MdnsSocket::ReceiveMessage(MsgType aMsgType) |
| { |
| otMessage *message = nullptr; |
| uint8_t buffer[kMaxMessageLength]; |
| otPlatMdnsAddressInfo addrInfo; |
| uint16_t length = 0; |
| struct sockaddr_in6 sockaddr6; |
| struct sockaddr_in sockaddr; |
| socklen_t len = sizeof(sockaddr6); |
| ssize_t rval; |
| |
| memset(&addrInfo, 0, sizeof(addrInfo)); |
| |
| switch (aMsgType) |
| { |
| case kIp6Msg: |
| len = sizeof(sockaddr6); |
| memset(&sockaddr6, 0, sizeof(sockaddr6)); |
| rval = recvfrom(mFd6, reinterpret_cast<char *>(&buffer), sizeof(buffer), 0, |
| reinterpret_cast<struct sockaddr *>(&sockaddr6), &len); |
| VerifyOrExit(rval >= 0, LogCrit("recvfrom() for IPv6 socket failed, errno: %s", strerror(errno))); |
| length = static_cast<uint16_t>(rval); |
| ReadIp6AddressFrom(&sockaddr6.sin6_addr, addrInfo.mAddress); |
| break; |
| |
| case kIp4Msg: |
| len = sizeof(sockaddr); |
| memset(&sockaddr, 0, sizeof(sockaddr)); |
| rval = recvfrom(mFd4, reinterpret_cast<char *>(&buffer), sizeof(buffer), 0, |
| reinterpret_cast<struct sockaddr *>(&sockaddr), &len); |
| VerifyOrExit(rval >= 0, LogCrit("recvfrom() for IPv4 socket failed, errno: %s", strerror(errno))); |
| length = static_cast<uint16_t>(rval); |
| otIp4ToIp4MappedIp6Address((otIp4Address *)(&sockaddr.sin_addr.s_addr), &addrInfo.mAddress); |
| break; |
| } |
| |
| VerifyOrExit(length > 0); |
| |
| message = otIp6NewMessage(mInstance, nullptr); |
| VerifyOrExit(message != nullptr); |
| SuccessOrExit(otMessageAppend(message, buffer, length)); |
| |
| addrInfo.mPort = kMdnsPort; |
| addrInfo.mInfraIfIndex = mInfraIfIndex; |
| |
| otPlatMdnsHandleReceive(mInstance, message, /* aInUnicast */ false, &addrInfo); |
| message = nullptr; |
| |
| exit: |
| if (message != nullptr) |
| { |
| otMessageFree(message); |
| } |
| } |
| |
| //--------------------------------------------------------------------------------------------------------------------- |
| // Socket helpers |
| |
| otError MdnsSocket::OpenIp4Socket(uint32_t aInfraIfIndex) |
| { |
| otError error = OT_ERROR_FAILED; |
| struct sockaddr_in addr; |
| int fd; |
| |
| fd = socket(AF_INET, SOCK_DGRAM, 0); |
| VerifyOrExit(fd >= 0, LogCrit("Failed to create IPv4 socket")); |
| |
| #ifdef __linux__ |
| { |
| char nameBuffer[IF_NAMESIZE]; |
| const char *ifname; |
| |
| ifname = if_indextoname(aInfraIfIndex, nameBuffer); |
| VerifyOrExit(ifname != NULL, LogCrit("if_indextoname() failed")); |
| |
| error = SetSocketOptionValue(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname), "SO_BINDTODEVICE"); |
| SuccessOrExit(error); |
| } |
| #else |
| { |
| int ifindex = static_cast<int>(aInfraIfIndex); |
| |
| SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IP, IP_BOUND_IF, ifindex, "IP_BOUND_IF")); |
| } |
| #endif |
| |
| SuccessOrExit(error = SetSocketOption<uint8_t>(fd, IPPROTO_IP, IP_MULTICAST_TTL, 255, "IP_MULTICAST_TTL")); |
| SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IP, IP_TTL, 255, "IP_TTL")); |
| SuccessOrExit(error = SetSocketOption<uint8_t>(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 1, "IP_MULTICAST_LOOP")); |
| SuccessOrExit(error = SetReuseAddrPortOptions(fd)); |
| |
| { |
| struct ip_mreqn mreqn; |
| |
| memset(&mreqn, 0, sizeof(mreqn)); |
| mreqn.imr_multiaddr.s_addr = inet_addr("224.0.0.251"); |
| mreqn.imr_ifindex = aInfraIfIndex; |
| |
| SuccessOrExit( |
| error = SetSocketOptionValue(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn), "IP_MULTICAST_IF")); |
| } |
| |
| memset(&addr, 0, sizeof(addr)); |
| addr.sin_family = AF_INET; |
| addr.sin_addr.s_addr = htonl(INADDR_ANY); |
| addr.sin_port = htons(kMdnsPort); |
| |
| if (bind(fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) < 0) |
| { |
| LogCrit("bind() to mDNS port for IPv4 socket failed, errno: %s", strerror(errno)); |
| error = OT_ERROR_FAILED; |
| ExitNow(); |
| } |
| |
| mFd4 = fd; |
| |
| LogInfo("Successfully opened IPv4 socket"); |
| |
| exit: |
| return error; |
| } |
| |
| otError MdnsSocket::JoinOrLeaveIp4MulticastGroup(bool aJoin, uint32_t aInfraIfIndex) |
| { |
| struct ip_mreqn mreqn; |
| |
| memset(&mreqn, 0, sizeof(mreqn)); |
| memcpy(&mreqn.imr_multiaddr.s_addr, &mMulticastIp4Address, sizeof(otIp4Address)); |
| mreqn.imr_ifindex = aInfraIfIndex; |
| |
| if (aJoin) |
| { |
| // Suggested workaround for netif not dropping |
| // a previous multicast membership. |
| setsockopt(mFd4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)); |
| } |
| |
| return SetSocketOption(mFd4, IPPROTO_IP, aJoin ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, mreqn, |
| "IP_ADD/DROP_MEMBERSHIP"); |
| } |
| |
| void MdnsSocket::CloseIp4Socket(void) |
| { |
| if (mFd4 >= 0) |
| { |
| close(mFd4); |
| mFd4 = -1; |
| } |
| } |
| |
| otError MdnsSocket::OpenIp6Socket(uint32_t aInfraIfIndex) |
| { |
| otError error = OT_ERROR_FAILED; |
| struct sockaddr_in6 addr6; |
| int fd; |
| int ifindex = static_cast<int>(aInfraIfIndex); |
| |
| fd = socket(AF_INET6, SOCK_DGRAM, 0); |
| VerifyOrExit(fd >= 0, LogCrit("Failed to create IPv4 socket")); |
| |
| #ifdef __linux__ |
| { |
| char nameBuffer[IF_NAMESIZE]; |
| const char *ifname; |
| |
| ifname = if_indextoname(aInfraIfIndex, nameBuffer); |
| VerifyOrExit(ifname != NULL, LogCrit("if_indextoname() failed")); |
| |
| error = SetSocketOptionValue(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname), "SO_BINDTODEVICE"); |
| SuccessOrExit(error); |
| } |
| #else |
| { |
| SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_BOUND_IF, ifindex, "IPV6_BOUND_IF")); |
| } |
| #endif |
| |
| SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255, "IPV6_MULTICAST_HOPS")); |
| SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255, "IPV6_UNICAST_HOPS")); |
| SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_V6ONLY, 1, "IPV6_V6ONLY")); |
| SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, ifindex, "IPV6_MULTICAST_IF")); |
| SuccessOrExit(error = SetSocketOption<int>(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 1, "IPV6_MULTICAST_LOOP")); |
| SuccessOrExit(error = SetReuseAddrPortOptions(fd)); |
| |
| memset(&addr6, 0, sizeof(addr6)); |
| addr6.sin6_family = AF_INET6; |
| addr6.sin6_port = htons(kMdnsPort); |
| |
| if (bind(fd, reinterpret_cast<struct sockaddr *>(&addr6), sizeof(addr6)) < 0) |
| { |
| LogCrit("bind() to mDNS port for IPv6 socket failed, errno: %s", strerror(errno)); |
| error = OT_ERROR_FAILED; |
| ExitNow(); |
| } |
| |
| mFd6 = fd; |
| |
| LogInfo("Successfully opened IPv6 socket"); |
| |
| exit: |
| return error; |
| } |
| |
| #ifndef IPV6_ADD_MEMBERSHIP |
| #ifdef IPV6_JOIN_GROUP |
| #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP |
| #endif |
| #endif |
| |
| #ifndef IPV6_DROP_MEMBERSHIP |
| #ifdef IPV6_LEAVE_GROUP |
| #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP |
| #endif |
| #endif |
| |
| otError MdnsSocket::JoinOrLeaveIp6MulticastGroup(bool aJoin, uint32_t aInfraIfIndex) |
| { |
| struct ipv6_mreq mreq6; |
| |
| memset(&mreq6, 0, sizeof(mreq6)); |
| Ip6Utils::CopyIp6AddressTo(mMulticastIp6Address, &mreq6.ipv6mr_multiaddr); |
| |
| mreq6.ipv6mr_interface = static_cast<int>(aInfraIfIndex); |
| |
| if (aJoin) |
| { |
| // Suggested workaround for netif not dropping |
| // a previous multicast membership. |
| setsockopt(mFd6, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)); |
| } |
| |
| return SetSocketOptionValue(mFd6, IPPROTO_IPV6, aJoin ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, |
| sizeof(mreq6), "IP6_ADD/DROP_MEMBERSHIP"); |
| } |
| |
| void MdnsSocket::CloseIp6Socket(void) |
| { |
| if (mFd6 >= 0) |
| { |
| close(mFd6); |
| mFd6 = -1; |
| } |
| } |
| |
| otError MdnsSocket::SetReuseAddrPortOptions(int aFd) |
| { |
| otError error; |
| |
| SuccessOrExit(error = SetSocketOption<int>(aFd, SOL_SOCKET, SO_REUSEADDR, 1, "SO_REUSEADDR")); |
| SuccessOrExit(error = SetSocketOption<int>(aFd, SOL_SOCKET, SO_REUSEPORT, 1, "SO_REUSEPORT")); |
| |
| exit: |
| return error; |
| } |
| |
| otError MdnsSocket::SetSocketOptionValue(int aFd, |
| int aLevel, |
| int aOption, |
| const void *aValue, |
| uint32_t aValueLength, |
| const char *aOptionName) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (setsockopt(aFd, aLevel, aOption, aValue, aValueLength) != 0) |
| { |
| error = OT_ERROR_FAILED; |
| LogCrit("Failed to setsockopt(%s) - errno: %s", aOptionName, strerror(errno)); |
| } |
| |
| return error; |
| } |
| |
| } // namespace Posix |
| } // namespace ot |
| |
| #endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE |