| /* |
| * 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 {}; |
| struct nlmsghdr *nh; |
| |
| msg.msg_name = &sa; |
| msg.msg_namelen = sizeof(sa); |
| msg.msg_iov = &iov; |
| msg.msg_iovlen = 1; |
| |
| 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 |