You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
4.8 KiB
169 lines
4.8 KiB
/*
|
|
* 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 "android-base/logging.h"
|
|
|
|
namespace cuttlefish {
|
|
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 cuttlefish
|