blob: 026029f2d879c7e18f6d91e354a2c85563ceead3 [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 <sys/socket.h>
#include "src/connectivity/network/mdns/service/mdns_addresses.h"
#include "src/lib/syslog/cpp/logger.h"
namespace mdns {
MdnsInterfaceTransceiverV6::MdnsInterfaceTransceiverV6(inet::IpAddress address,
const std::string& name, uint32_t index)
: MdnsInterfaceTransceiver(address, name, index) {}
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) << "NET-291 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) << "NET-2180 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 == EOPNOTSUPP) {
FX_LOGS(WARNING) << "NET-1901 IPV6_MULTICAST_IF not supported "
"(EOPNOTSUPP), continuing anyway";
result = 0;
} 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(fxb/41357): remove the bug reference when the bug is fixed.
FX_LOGS(WARNING)
<< "fxb/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(fxb/41358): remove the bug reference when the bug is fixed.
FX_LOGS(WARNING) << "fxb/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()) {
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