Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34d22ce5 authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk Committed by Android (Google) Code Review
Browse files

Merge "Implement waitFor (interface) to avoid polling interface state"

parents 80c737b2 b759eca4
Loading
Loading
Loading
Loading
+27 −6
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

#include <array>
#include <optional>
#include <set>
#include <string>

namespace android::netdevice {
@@ -53,14 +54,34 @@ bool exists(std::string ifname);
std::optional<bool> isUp(std::string ifname);

/**
 * Checks, if the network interface exists and is up.
 *
 * This is a convenience function to call both exists() and isUp().
 * Interface condition to wait for.
 */
enum class WaitCondition {
    /**
     * Interface is present (but not necessarily up).
     */
    PRESENT,

    /**
     * Interface is up.
     */
    PRESENT_AND_UP,

    /**
     * Interface is down or not present (disconnected) at all.
     */
    DOWN_OR_GONE,
};

/**
 * Listens for interface changes until anticipated condition takes place.
 *
 * \param ifname Interface to check
 * \return true if the interface is up, false otherwise
 * \param ifnames List of interfaces to watch for.
 * \param cnd Awaited condition.
 * \param allOf true if all interfaces need to satisfy the condition, false if only one satistying
 *        interface should stop the wait.
 */
bool existsAndIsUp(const std::string& ifname);
void waitFor(std::set<std::string> ifnames, WaitCondition cnd, bool allOf = true);

/**
 * Brings network interface up.
+97 −10
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@
#include <linux/rtnetlink.h>
#include <net/if.h>

#include <sstream>

namespace android::netdevice {

void useSocketDomain(int domain) {
@@ -37,16 +39,6 @@ bool exists(std::string ifname) {
    return nametoindex(ifname) != 0;
}

std::optional<bool> isUp(std::string ifname) {
    auto ifr = ifreqs::fromName(ifname);
    if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return std::nullopt;
    return ifr.ifr_flags & IFF_UP;
}

bool existsAndIsUp(const std::string& ifname) {
    return exists(ifname) && isUp(ifname).value_or(false);
}

bool up(std::string ifname) {
    auto ifr = ifreqs::fromName(ifname);
    if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return false;
@@ -102,6 +94,101 @@ bool setHwAddr(const std::string& ifname, hwaddr_t hwaddr) {
    return ifreqs::send(SIOCSIFHWADDR, ifr);
}

std::optional<bool> 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<std::string>& ifnames) {
    std::stringstream ss;
    std::copy(ifnames.begin(), ifnames.end(), std::ostream_iterator<std::string>(ss, ","));
    auto str = ss.str();
    str.pop_back();
    return str;
}

void waitFor(std::set<std::string> ifnames, WaitCondition cnd, bool allOf) {
    nl::Socket sock(NETLINK_ROUTE, 0, RTMGRP_LINK);

    using StatesMap = std::map<std::string, WaitState>;
    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);
    while (true) {
        const auto msgBuf = sock.receive();
        CHECK(msgBuf.has_value()) << "Can't read Netlink socket";

        for (const auto rawMsg : *msgBuf) {
            const auto msg = nl::Message<ifinfomsg>::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK});
            if (!msg.has_value()) continue;

            const auto ifname = msg->attributes.get<std::string>(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;
            }
        }
    }
}

}  // namespace android::netdevice

bool operator==(const android::netdevice::hwaddr_t lhs, const unsigned char rhs[ETH_ALEN]) {