/* * Copyright (C) 2019 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 #include "common.h" #include "ifreqs.h" #include #include #include #include #include #include #include namespace android::netdevice { void useSocketDomain(int domain) { ifreqs::socketDomain = domain; } bool exists(std::string ifname) { return nametoindex(ifname) != 0; } bool up(std::string ifname) { auto ifr = ifreqs::fromName(ifname); if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return false; ifr.ifr_flags |= IFF_UP; return ifreqs::send(SIOCSIFFLAGS, ifr); } bool down(std::string ifname) { auto ifr = ifreqs::fromName(ifname); if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return false; ifr.ifr_flags &= ~IFF_UP; return ifreqs::send(SIOCSIFFLAGS, ifr); } bool add(std::string dev, std::string type) { nl::MessageFactory req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); req.add(IFLA_IFNAME, dev); { auto linkinfo = req.addNested(IFLA_LINKINFO); req.add(IFLA_INFO_KIND, type); } nl::Socket sock(NETLINK_ROUTE); return sock.send(req) && sock.receiveAck(req); } bool del(std::string dev) { nl::MessageFactory req(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK); req.add(IFLA_IFNAME, dev); nl::Socket sock(NETLINK_ROUTE); return sock.send(req) && sock.receiveAck(req); } std::optional getHwAddr(const std::string& ifname) { auto ifr = ifreqs::fromName(ifname); if (!ifreqs::send(SIOCGIFHWADDR, ifr)) return std::nullopt; hwaddr_t hwaddr; memcpy(hwaddr.data(), ifr.ifr_hwaddr.sa_data, hwaddr.size()); return hwaddr; } bool setHwAddr(const std::string& ifname, hwaddr_t hwaddr) { auto ifr = ifreqs::fromName(ifname); // fetch sa_family if (!ifreqs::send(SIOCGIFHWADDR, ifr)) return false; memcpy(ifr.ifr_hwaddr.sa_data, hwaddr.data(), hwaddr.size()); return ifreqs::send(SIOCSIFHWADDR, ifr); } std::optional isUp(std::string ifname) { auto ifr = ifreqs::fromName(ifname); if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return std::nullopt; return ifr.ifr_flags & IFF_UP; } struct WaitState { bool present; bool up; bool satisfied(WaitCondition cnd) const { switch (cnd) { case WaitCondition::PRESENT: if (present) return true; break; case WaitCondition::PRESENT_AND_UP: if (present && up) return true; break; case WaitCondition::DOWN_OR_GONE: if (!present || !up) return true; break; } return false; } }; static std::string toString(WaitCondition cnd) { switch (cnd) { case WaitCondition::PRESENT: return "become present"; case WaitCondition::PRESENT_AND_UP: return "come up"; case WaitCondition::DOWN_OR_GONE: return "go down"; } } static std::string toString(const std::set& ifnames) { std::stringstream ss; std::copy(ifnames.begin(), ifnames.end(), std::ostream_iterator(ss, ",")); auto str = ss.str(); str.pop_back(); return str; } void waitFor(std::set ifnames, WaitCondition cnd, bool allOf) { nl::Socket sock(NETLINK_ROUTE, 0, RTMGRP_LINK); using StatesMap = std::map; StatesMap states = {}; for (const auto ifname : ifnames) { const auto present = exists(ifname); const auto up = present && isUp(ifname).value_or(false); states[ifname] = {present, up}; } const auto mapConditionChecker = [cnd](const StatesMap::iterator::value_type& it) { return it.second.satisfied(cnd); }; const auto isFullySatisfied = [&states, allOf, mapConditionChecker]() { if (allOf) { return std::all_of(states.begin(), states.end(), mapConditionChecker); } else { return std::any_of(states.begin(), states.end(), mapConditionChecker); } }; if (isFullySatisfied()) return; LOG(DEBUG) << "Waiting for " << (allOf ? "" : "any of ") << toString(ifnames) << " to " << toString(cnd); for (const auto rawMsg : sock) { const auto msg = nl::Message::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK}); if (!msg.has_value()) continue; const auto ifname = msg->attributes.get(IFLA_IFNAME); if (ifnames.count(ifname) == 0) continue; const bool present = (msg->header.nlmsg_type != RTM_DELLINK); const bool up = present && (msg->data.ifi_flags & IFF_UP) != 0; states[ifname] = {present, up}; if (isFullySatisfied()) { LOG(DEBUG) << "Finished waiting for " << (allOf ? "" : "some of ") << toString(ifnames) << " to " << toString(cnd); return; } } LOG(FATAL) << "Can't read Netlink socket"; } } // namespace android::netdevice bool operator==(const android::netdevice::hwaddr_t lhs, const unsigned char rhs[ETH_ALEN]) { static_assert(lhs.size() == ETH_ALEN); return 0 == memcmp(lhs.data(), rhs, lhs.size()); }