/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "common/libs/net/netlink_client.h"
#include <errno.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <sys/socket.h>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/glog/logging.h"
namespace cvd {
namespace {
// NetlinkClient implementation.
// Talks to libnetlink to apply network changes.
class NetlinkClientImpl : public NetlinkClient {
public:
NetlinkClientImpl() = default;
virtual ~NetlinkClientImpl() = default;
virtual bool Send(const NetlinkRequest& message);
// Initialize NetlinkClient instance.
// Open netlink channel and initialize interface list.
// Parameter |type| specifies which netlink target to address, eg.
// NETLINK_ROUTE.
// Returns true, if initialization was successful.
bool OpenNetlink(int type);
private:
bool CheckResponse(uint32_t seq_no);
SharedFD netlink_fd_;
sockaddr_nl address_;
};
bool NetlinkClientImpl::CheckResponse(uint32_t seq_no) {
uint32_t len;
char buf[4096];
struct iovec iov = { buf, sizeof(buf) };
struct sockaddr_nl sa;
struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
struct nlmsghdr *nh;
int result = netlink_fd_->RecvMsg(&msg, 0);
if (result < 0) {
LOG(ERROR) << "Netlink error: " << strerror(errno);
return false;
}
len = static_cast<uint32_t>(result);
LOG(INFO) << "Received netlink response (" << len << " bytes)";
for (nh = reinterpret_cast<nlmsghdr*>(buf);
NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
if (nh->nlmsg_seq != seq_no) {
// This really shouldn't happen. If we see this, it means somebody is
// issuing netlink requests using the same socket as us, and ignoring
// responses.
LOG(WARNING) << "Sequence number mismatch: "
<< nh->nlmsg_seq << " != " << seq_no;
continue;
}
// This is the end of multi-part message.
// It indicates there's nothing more netlink wants to tell us.
// It also means we failed to find the response to our request.
if (nh->nlmsg_type == NLMSG_DONE)
break;
// This is the 'nlmsgerr' package carrying response to our request.
// It carries an 'error' value (errno) along with the netlink header info
// that caused this error.
if (nh->nlmsg_type == NLMSG_ERROR) {
nlmsgerr* err = reinterpret_cast<nlmsgerr*>(nh + 1);
if (err->error < 0) {
LOG(ERROR) << "Failed to complete netlink request: "
<< "Netlink error: " << err->error
<< ", errno: " << strerror(errno);
return false;
}
return true;
}
}
LOG(ERROR) << "No response from netlink.";
return false;
}
bool NetlinkClientImpl::Send(const NetlinkRequest& message) {
struct sockaddr_nl netlink_addr;
struct iovec netlink_iov = {
message.RequestData(),
message.RequestLength()
};
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
memset(&netlink_addr, 0, sizeof(netlink_addr));
msg.msg_name = &address_;
msg.msg_namelen = sizeof(address_);
msg.msg_iov = &netlink_iov;
msg.msg_iovlen = sizeof(netlink_iov) / sizeof(iovec);
if (netlink_fd_->SendMsg(&msg, 0) < 0) {
LOG(ERROR) << "Failed to send netlink message: "
<< strerror(errno);
return false;
}
return CheckResponse(message.SeqNo());
}
bool NetlinkClientImpl::OpenNetlink(int type) {
netlink_fd_ = SharedFD::Socket(AF_NETLINK, SOCK_RAW, type);
if (!netlink_fd_->IsOpen()) return false;
address_.nl_family = AF_NETLINK;
address_.nl_groups = 0;
netlink_fd_->Bind(reinterpret_cast<sockaddr*>(&address_), sizeof(address_));
return true;
}
class NetlinkClientFactoryImpl : public NetlinkClientFactory {
public:
NetlinkClientFactoryImpl() = default;
~NetlinkClientFactoryImpl() override = default;
std::unique_ptr<NetlinkClient> New(int type) override {
auto client_raw = new NetlinkClientImpl();
// Use RVO when possible.
std::unique_ptr<NetlinkClient> client(client_raw);
if (!client_raw->OpenNetlink(type)) {
// Note: deletes client_raw.
client.reset();
}
return client;
}
};
} // namespace
NetlinkClientFactory* NetlinkClientFactory::Default() {
static NetlinkClientFactory &factory = *new NetlinkClientFactoryImpl();
return &factory;
}
} // namespace cvd