Implement waiting for IPv4 address for an interface
Bug: 239577572
Test: build, flash, check Internet
Change-Id: I339d6ae595fac13690a90d98ef82a6659819406c
diff --git a/automotive/can/1.0/default/libnetdevice/ifreqs.cpp b/automotive/can/1.0/default/libnetdevice/ifreqs.cpp
index 8df6434..8471173 100644
--- a/automotive/can/1.0/default/libnetdevice/ifreqs.cpp
+++ b/automotive/can/1.0/default/libnetdevice/ifreqs.cpp
@@ -47,7 +47,7 @@
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 @@
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;
}
diff --git a/automotive/can/1.0/default/libnetdevice/ifreqs.h b/automotive/can/1.0/default/libnetdevice/ifreqs.h
index 74e5877..d8d6fe0 100644
--- a/automotive/can/1.0/default/libnetdevice/ifreqs.h
+++ b/automotive/can/1.0/default/libnetdevice/ifreqs.h
@@ -28,6 +28,15 @@
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.
*
* \param request Request type (such as SIOCGIFFLAGS)
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
index 70cb688..657f9b2 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
@@ -68,20 +68,32 @@
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.
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
index 4c5b309..17f1cbc 100644
--- a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -100,23 +100,36 @@
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 @@
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 @@
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;
- 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;
- const bool present = (msg->header.nlmsg_type != RTM_DELLINK);
- const bool up = present && (msg->data.ifi_flags & IFF_UP) != 0;
- states[ifname] = {present, up};
+ } 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;
- if (isFullySatisfied()) {
- LOG(DEBUG) << "Finished waiting for " << (allOf ? "" : "some of ") << toString(ifnames)
+ 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 (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