blob: d4b35347d2059146cc2affab0eb9c7b63d5d6fe2 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/network/mdns/service/mdns_interface_transceiver_v6.h"
#include <arpa/inet.h>
#include <errno.h>
#include <lib/syslog/cpp/macros.h>
#include <sys/socket.h>
#include "src/connectivity/network/mdns/service/mdns_addresses.h"
namespace mdns {
MdnsInterfaceTransceiverV6::MdnsInterfaceTransceiverV6(inet::IpAddress address,
const std::string& name, uint32_t index,
Media media)
: MdnsInterfaceTransceiver(address, name, index, media) {}
MdnsInterfaceTransceiverV6::~MdnsInterfaceTransceiverV6() {}
int MdnsInterfaceTransceiverV6::SetOptionDisableMulticastLoop() {
int param = 0;
int result =
setsockopt(socket_fd().get(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &param, sizeof(param));
if (result < 0) {
if (errno == ENOPROTOOPT) {
FX_LOGS(WARNING) << "fxbug.dev/20688 IPV6_MULTICAST_LOOP not supported "
"(ENOPROTOOPT), continuing anyway";
result = 0;
} else {
FX_LOGS(ERROR) << "Failed to set socket option IPV6_MULTICAST_LOOP, " << strerror(errno);
}
}
return result;
}
int MdnsInterfaceTransceiverV6::SetOptionJoinMulticastGroup() {
ipv6_mreq param;
param.ipv6mr_multiaddr = addresses().v6_multicast().as_sockaddr_in6().sin6_addr;
param.ipv6mr_interface = index();
int result = setsockopt(socket_fd().get(), IPPROTO_IPV6, IPV6_JOIN_GROUP, &param, sizeof(param));
if (result < 0) {
if (errno == ENODEV) {
FX_LOGS(WARNING) << "fxbug.dev/21173 IPV6_JOIN_GROUP returned ENODEV, mDNS will "
"not communicate via IPV6";
} else {
FX_LOGS(ERROR) << "Failed to set socket option IPV6_JOIN_GROUP, " << strerror(errno);
}
}
return result;
}
int MdnsInterfaceTransceiverV6::SetOptionOutboundInterface() {
uint32_t index = this->index();
int result =
setsockopt(socket_fd().get(), IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index));
if (result < 0) {
if (errno == EADDRNOTAVAIL) {
// This is expected when the interface is removed as we try to use it. We still return
// result < 0, because we don't want to use this interface.
FX_LOGS(WARNING) << "Failed to set socket option IPV6_MULTICAST_IF, " << strerror(errno);
} else {
FX_LOGS(ERROR) << "Failed to set socket option IPV6_MULTICAST_IF, " << strerror(errno);
}
}
return result;
}
int MdnsInterfaceTransceiverV6::SetOptionUnicastTtl() {
int param = kTimeToLive_;
int result =
setsockopt(socket_fd().get(), IPPROTO_IPV6, IPV6_UNICAST_HOPS, &param, sizeof(param));
if (result < 0) {
if (errno == ENOPROTOOPT) {
// TODO(fxbug.dev/41357): remove the bug reference when the bug is fixed.
FX_LOGS(WARNING)
<< "fxbug.dev/41357: IPV6_UNICAST_HOPS not supported (ENOPROTOOPT), continuing anyway";
result = 0;
} else {
FX_LOGS(ERROR) << "Failed to set socket option IPV6_UNICAST_HOPS, " << strerror(errno);
}
}
return result;
}
int MdnsInterfaceTransceiverV6::SetOptionMulticastTtl() {
int param = kTimeToLive_;
int result =
setsockopt(socket_fd().get(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &param, sizeof(param));
if (result < 0) {
if (errno == ENOPROTOOPT) {
FX_LOGS(WARNING) << "IPV6_MULTICAST_HOPS not supported (ENOPROTOOPT), continuing anyway";
result = 0;
} else {
FX_LOGS(ERROR) << "Failed to set socket option IPV6_MULTICAST_HOPS, " << strerror(errno);
}
}
return result;
}
int MdnsInterfaceTransceiverV6::SetOptionFamilySpecific() {
// Set hop limit.
int param = 1;
int result = setsockopt(socket_fd().get(), IPPROTO_IPV6, IPV6_HOPLIMIT, &param, sizeof(param));
if (result < 0) {
if (errno == ENOPROTOOPT) {
// TODO(fxbug.dev/41358): remove the bug reference when the bug is fixed.
FX_LOGS(WARNING)
<< "fxbug.dev/41358: IPV6_HOPLIMIT not supported (ENOPROTOOPT), continuing anyway";
result = 0;
} else {
FX_LOGS(ERROR) << "Failed to set socket option IPV6_HOPLIMIT, " << strerror(errno);
}
return result;
}
// Receive V6 packets only.
param = 1;
result = setsockopt(socket_fd().get(), IPPROTO_IPV6, IPV6_V6ONLY, &param, sizeof(param));
if (result < 0) {
FX_LOGS(ERROR) << "Failed to set socket option IPV6_V6ONLY, " << strerror(errno);
return false;
}
return result;
}
int MdnsInterfaceTransceiverV6::Bind() {
int result =
bind(socket_fd().get(), addresses().v6_bind().as_sockaddr(), addresses().v6_bind().socklen());
if (result < 0) {
FX_LOGS(ERROR) << "Failed to bind socket to V6 address, " << strerror(errno);
}
return result;
}
int MdnsInterfaceTransceiverV6::SendTo(const void* buffer, size_t size,
const inet::SocketAddress& address) {
if (address == addresses().v4_multicast()) {
// |v4_multicast| indicates multicast, meaning V6 multicast in this case.
return sendto(socket_fd().get(), buffer, size, 0, addresses().v6_multicast().as_sockaddr(),
addresses().v6_multicast().socklen());
}
return sendto(socket_fd().get(), buffer, size, 0, address.as_sockaddr(), address.socklen());
}
} // namespace mdns