| // Copyright 2018 The Chromium OS 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 "portier/network_socket.h" | |
| #include <errno.h> | |
| #include <net/ethernet.h> // L2 stuff. | |
| #include <net/if_arp.h> // ARPHRD_* definitions. | |
| #include <string.h> | |
| #include <sys/ioctl.h> | |
| #include <base/logging.h> | |
| #include <base/posix/safe_strerror.h> | |
| #include <base/strings/stringprintf.h> | |
| #include <shill/net/byte_string.h> | |
| namespace portier { | |
| using std::string; | |
| using base::safe_strerror; | |
| using shill::ByteString; | |
| using Code = Status::Code; | |
| using State = NetworkSocket::State; | |
| namespace { | |
| // Used for ioctl() calls. | |
| constexpr int kTrue = true; | |
| constexpr int kFalse = false; | |
| } // namespace | |
| string NetworkSocket::GetStateName(State state) { | |
| switch (state) { | |
| case State::UNINITIALIZED: | |
| return "Unitialized"; | |
| case State::READY: | |
| return "Ready"; | |
| case State::CLOSED: | |
| return "Closed"; | |
| } | |
| return base::StringPrintf("Unknown (%d)", static_cast<int>(state)); | |
| } | |
| NetworkSocket::NetworkSocket(const string& if_name) | |
| : name_(if_name), index_(-1), state_(State::UNINITIALIZED) {} | |
| // State getters. | |
| // private. | |
| bool NetworkSocket::IsUnitialized() const { | |
| return state() == State::UNINITIALIZED; | |
| } | |
| bool NetworkSocket::IsReady() const { | |
| return state() == State::READY; | |
| } | |
| bool NetworkSocket::IsClosed() const { | |
| return state() == State::CLOSED; | |
| } | |
| Status NetworkSocket::Close() { | |
| if (IsUnitialized()) { | |
| return Status(Code::BAD_INTERNAL_STATE, | |
| "Cannot close an uninitialized socket"); | |
| } | |
| if (IsClosed()) { | |
| return Status(); | |
| } | |
| CloseFd(); | |
| state_ = State::CLOSED; | |
| return Status(); | |
| } | |
| void NetworkSocket::CloseFd() { | |
| fd_.reset(); | |
| } | |
| // Interface information getters. | |
| Status NetworkSocket::GetLinkLayerAddress(LLAddress* ll_address) const { | |
| DCHECK(ll_address != nullptr) | |
| << "Output parameter `ll_address' cannot be null"; | |
| if (!IsReady()) { | |
| return Status(Code::BAD_INTERNAL_STATE) << "Socket is not ready"; | |
| } | |
| struct ifreq ifreq; | |
| PrepareIfRequestStruct(&ifreq); | |
| if (ioctl(fd(), SIOCGIFHWADDR, &ifreq) < 0) { | |
| const int saved_errno = errno; | |
| return Status(Code::UNEXPECTED_FAILURE) | |
| << "Failed to get interface hardware address: ioctl(): " | |
| << safe_strerror(saved_errno); | |
| } | |
| // TODO(sigquit): figure out how to validate the device type | |
| // (ifreq.ifr_hwaddr.sa_family) is supported by the current framework. | |
| // A list of possible values can be found in <linux/if_arp.h>, using the | |
| // ARPHRD_* list. Good luck! | |
| // For now, going to just assume the only valid option is ARPHRD_ETHER. | |
| if (ARPHRD_ETHER != ifreq.ifr_hwaddr.sa_family) { | |
| return Status(Code::UNSUPPORTED_TYPE) | |
| << "Hardware type is not supported: " | |
| << base::StringPrintf("Got ARPHRD id %d", | |
| ifreq.ifr_hwaddr.sa_family); | |
| } | |
| *ll_address = LLAddress(LLAddress::Type::kEui48, | |
| ByteString(ifreq.ifr_hwaddr.sa_data, ETHER_ADDR_LEN)); | |
| return Status(); | |
| } | |
| Status NetworkSocket::GetLinkMTU(uint32_t* mtu) const { | |
| DCHECK(mtu != nullptr) << "Output parameter `mtu' cannot be null"; | |
| if (!IsReady()) { | |
| return Status(Code::BAD_INTERNAL_STATE) << "Socket is not ready"; | |
| } | |
| struct ifreq ifreq; | |
| PrepareIfRequestStruct(&ifreq); | |
| if (ioctl(fd(), SIOCGIFMTU, &ifreq) < 0) { | |
| const int saved_errno = errno; | |
| return Status(Code::UNEXPECTED_FAILURE) | |
| << "Failed to get interface MTU: ioctl(): " | |
| << safe_strerror(saved_errno); | |
| } | |
| *mtu = static_cast<uint32_t>(ifreq.ifr_mtu); | |
| return Status(); | |
| } | |
| Status NetworkSocket::GetLoopbackFlag(bool* loopback_flag) const { | |
| DCHECK(loopback_flag != nullptr) | |
| << "Output parameter `loopback_flag' cannot be null"; | |
| if (!IsReady()) { | |
| return Status(Code::BAD_INTERNAL_STATE) << "Socket is not ready"; | |
| } | |
| int16_t flags = 0; | |
| PORTIER_RETURN_ON_FAILURE(GetInterfaceFlags(&flags)) | |
| << "Failed to get loopback flag"; | |
| *loopback_flag = ((flags & IFF_LOOPBACK) != 0); | |
| return Status(); | |
| } | |
| Status NetworkSocket::SetNonBlockingMode(bool enabled) { | |
| if (!IsReady()) { | |
| return Status(Code::BAD_INTERNAL_STATE) << "Socket is not ready"; | |
| } | |
| if (ioctl(fd(), FIONBIO, enabled ? &kTrue : &kFalse) < 0) { | |
| const int saved_errno = errno; | |
| return Status(Code::UNEXPECTED_FAILURE) | |
| << "Failed to set non-blocking mode enabled " | |
| << (enabled ? "true" : "false") | |
| << ": setsockopt(): " << safe_strerror(saved_errno); | |
| } | |
| return Status(); | |
| } | |
| // private. | |
| void NetworkSocket::PrepareIfRequestStruct(struct ifreq* ifreq) const { | |
| DCHECK(ifreq != nullptr); | |
| memset(ifreq, 0, sizeof(struct ifreq)); | |
| strncpy(ifreq->ifr_name, name().c_str(), IFNAMSIZ - 1); | |
| } | |
| // private. | |
| Status NetworkSocket::SetInterfaceFlags(int16_t flags) { | |
| if (!IsReady()) { | |
| return Status(Code::BAD_INTERNAL_STATE) << "Socket is not ready"; | |
| } | |
| struct ifreq ifreq; | |
| PrepareIfRequestStruct(&ifreq); | |
| ifreq.ifr_flags = flags; | |
| if (ioctl(fd(), SIOCSIFFLAGS, &ifreq) < 0) { | |
| const int saved_errno = errno; | |
| return Status(Code::UNEXPECTED_FAILURE) | |
| << "Failed to set interface flags: ioctl(): " | |
| << safe_strerror(saved_errno); | |
| } | |
| return Status(); | |
| } | |
| // private. | |
| Status NetworkSocket::GetInterfaceFlags(int16_t* flags) const { | |
| DCHECK(flags != nullptr) << "Output parameter `flags' cannot be null"; | |
| if (!IsReady()) { | |
| return Status(Code::BAD_INTERNAL_STATE) << "Socket is not ready"; | |
| } | |
| struct ifreq ifreq; | |
| PrepareIfRequestStruct(&ifreq); | |
| if (ioctl(fd(), SIOCGIFFLAGS, &ifreq) < 0) { | |
| const int saved_errno = errno; | |
| return Status(Code::UNEXPECTED_FAILURE) | |
| << "Failed to get interface flags: ioctl(): " | |
| << safe_strerror(saved_errno); | |
| } | |
| *flags = ifreq.ifr_flags; | |
| return Status(); | |
| } | |
| } // namespace portier |