blob: df306187ee120c2a5a0803801960b69aaec7d47c [file] [log] [blame]
// 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 <string.h>
#include <errno.h>
#include <net/ethernet.h> // L2 stuff.
#include <net/if_arp.h> // ARPHRD_* definitions.
#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>
#include "portier/network_socket.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