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

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

Merge "Implement waiting for IPv4 address for an interface"

parents b1300e33 2b162da5
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ static SocketParams getSocketParams(int domain) {
    return params;
}

bool send(unsigned long request, struct ifreq& ifr) {
int trySend(unsigned long request, struct ifreq& ifr) {
    const auto sp = getSocketParams(socketDomain);
    base::unique_fd sock(socket(sp.domain, sp.type, sp.protocol));
    if (!sock.ok()) {
@@ -55,7 +55,12 @@ bool send(unsigned long request, struct ifreq& ifr) {
        return false;
    }

    if (ioctl(sock.get(), request, &ifr) < 0) {
    if (ioctl(sock.get(), request, &ifr) < 0) return errno;
    return 0;
}

bool send(unsigned long request, struct ifreq& ifr) {
    if (trySend(request, ifr) != 0) {
        PLOG(ERROR) << "ioctl(" << std::hex << request << std::dec << ") failed";
        return false;
    }
+9 −0
Original line number Diff line number Diff line
@@ -27,6 +27,15 @@ namespace android::netdevice::ifreqs {
 */
extern std::atomic_int socketDomain;

/**
 * Tries to send ioctl interface request.
 *
 * \param request Request type (such as SIOCGIFFLAGS)
 * \param ifr Request data (both input and output)
 * \return error code of the call (0 for success)
 */
int trySend(unsigned long request, struct ifreq& ifr);

/**
 * Sends ioctl interface request.
 *
+14 −2
Original line number Diff line number Diff line
@@ -67,21 +67,33 @@ enum class WaitCondition {
     */
    PRESENT_AND_UP,

    /**
     * Interface is up and with IPv4 address configured.
     */
    PRESENT_AND_IPV4,

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

enum class Quantifier {
    ALL_OF,
    ANY_OF,
};

/**
 * Listens for interface changes until anticipated condition takes place.
 *
 * \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
 * \param quant Whether all interfaces need to satisfy the condition or just one satistying
 *        interface should stop the wait.
 * \return name of one interface that satisfied the condition
 */
void waitFor(std::set<std::string> ifnames, WaitCondition cnd, bool allOf = true);
std::optional<std::string> waitFor(std::set<std::string> ifnames, WaitCondition cnd,
                                   Quantifier quant = Quantifier::ALL_OF);

/**
 * Brings network interface up.
+76 −29
Original line number Diff line number Diff line
@@ -100,23 +100,36 @@ std::optional<bool> isUp(std::string ifname) {
    return ifr.ifr_flags & IFF_UP;
}

static bool hasIpv4(std::string ifname) {
    auto ifr = ifreqs::fromName(ifname);
    switch (const auto status = ifreqs::trySend(SIOCGIFADDR, ifr)) {
        case 0:
            return true;
        case EADDRNOTAVAIL:
        case ENODEV:
            return false;
        default:
            PLOG(WARNING) << "Failed checking IPv4 address";
            return false;
    }
}

struct WaitState {
    bool present;
    bool up;
    bool hasIpv4Addr;

    bool satisfied(WaitCondition cnd) const {
        switch (cnd) {
            case WaitCondition::PRESENT:
                if (present) return true;
                break;
                return present;
            case WaitCondition::PRESENT_AND_UP:
                if (present && up) return true;
                break;
                return present && up;
            case WaitCondition::PRESENT_AND_IPV4:
                return present && up && hasIpv4Addr;
            case WaitCondition::DOWN_OR_GONE:
                if (!present || !up) return true;
                break;
                return !present || !up;
        }
        return false;
    }
};

@@ -126,11 +139,22 @@ static std::string toString(WaitCondition cnd) {
            return "become present";
        case WaitCondition::PRESENT_AND_UP:
            return "come up";
        case WaitCondition::PRESENT_AND_IPV4:
            return "get IPv4 address";
        case WaitCondition::DOWN_OR_GONE:
            return "go down";
    }
}

static std::string toString(Quantifier quant) {
    switch (quant) {
        case Quantifier::ALL_OF:
            return "all of";
        case Quantifier::ANY_OF:
            return "any of";
    }
}

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, ","));
@@ -139,50 +163,73 @@ static std::string toString(const std::set<std::string>& ifnames) {
    return str;
}

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

    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 hasIpv4Addr = present && hasIpv4(ifname);
        states[ifname] = {present, up, hasIpv4Addr};
    }

    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);
    const auto isFullySatisfied = [&states, quant,
                                   mapConditionChecker]() -> std::optional<std::string> {
        if (quant == Quantifier::ALL_OF) {
            if (!std::all_of(states.begin(), states.end(), mapConditionChecker)) return {};
            return states.begin()->first;
        } else {  // Quantifier::ANY_OF
            const auto it = std::find_if(states.begin(), states.end(), mapConditionChecker);
            if (it == states.end()) return {};
            return it->first;
        }
    };

    if (isFullySatisfied()) return;
    if (const auto iface = isFullySatisfied()) return iface;

    LOG(DEBUG) << "Waiting for " << (allOf ? "" : "any of ") << toString(ifnames) << " to "
    LOG(DEBUG) << "Waiting for " << toString(quant) << " " << toString(ifnames) << " to "
               << toString(cnd);
    for (const auto rawMsg : sock) {
        const auto msg = nl::Message<ifinfomsg>::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK});
        if (!msg.has_value()) continue;
        if (const auto msg = nl::Message<ifinfomsg>::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK});
            msg.has_value()) {
            // Interface added / removed
            const auto ifname = msg->attributes.get<std::string>(IFLA_IFNAME);
            if (ifnames.count(ifname) == 0) continue;

            auto& state = states[ifname];
            state.present = (msg->header.nlmsg_type != RTM_DELLINK);
            state.up = state.present && (msg->data.ifi_flags & IFF_UP) != 0;
            if (!state.present) state.hasIpv4Addr = false;

        } else if (const auto msg =
                           nl::Message<ifaddrmsg>::parse(rawMsg, {RTM_NEWADDR, RTM_DELADDR});
                   msg.has_value()) {
            // Address added / removed
            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 (msg->header.nlmsg_type == RTM_NEWADDR) {
                states[ifname].hasIpv4Addr = true;
            } else {
                // instead of tracking which one got deleted, let's just ask
                states[ifname].hasIpv4Addr = hasIpv4(ifname);
            }
        }

        if (isFullySatisfied()) {
            LOG(DEBUG) << "Finished waiting for " << (allOf ? "" : "some of ") << toString(ifnames)
        if (const auto iface = isFullySatisfied()) {
            LOG(DEBUG) << "Finished waiting for " << toString(quant) << " " << toString(ifnames)
                       << " to " << toString(cnd);
            return;
            return iface;
        }
    }
    LOG(FATAL) << "Can't read Netlink socket";
    return {};
}

}  // namespace android::netdevice
+2 −0
Original line number Diff line number Diff line
@@ -37,8 +37,10 @@ cc_library_static {
        "protocols/generic/Unknown.cpp",
        "protocols/generic/families/Mac80211hwsim.cpp",
        "protocols/generic/families/Nl80211.cpp",
        "protocols/route/Addr.cpp",
        "protocols/route/Link.cpp",
        "protocols/route/Route.cpp",
        "protocols/route/attributes.cpp",
        "protocols/route/structs.cpp",
        "protocols/MessageDefinition.cpp",
        "protocols/NetlinkProtocol.cpp",
Loading