Make destroying networks more robust.
1. Retry route flushes if they fail.
2. Make destroyNetwork ignore (but return) errors.
Bug: 16944962
Change-Id: I26301613437d7cc373ff64955fd44d716e9982b9
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index fca70a6..ca888df 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -92,6 +92,8 @@
const int RT_TABLES_FLAGS = O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
const mode_t RT_TABLES_MODE = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // mode 0644, rw-r--r--
+const unsigned ROUTE_FLUSH_ATTEMPTS = 2;
+
// Avoids "non-constant-expression cannot be narrowed from type 'unsigned int' to 'unsigned short'"
// warnings when using RTA_LENGTH(x) inside static initializers (even when x is already uint16_t).
constexpr uint16_t U16_RTA_LENGTH(uint16_t x) {
@@ -828,6 +830,7 @@
char tableString[UINT32_STRLEN];
snprintf(tableString, sizeof(tableString), "%u", table);
+ int ret = 0;
for (size_t i = 0; i < ARRAY_SIZE(IP_VERSIONS); ++i) {
const char* argv[] = {
IP_PATH,
@@ -837,14 +840,34 @@
"table",
tableString,
};
- if (android_fork_execvp(ARRAY_SIZE(argv), const_cast<char**>(argv), NULL, false, false)) {
- ALOGE("failed to flush routes");
- return -EREMOTEIO;
+
+ // A flush works by dumping routes and deleting each route as it's returned, and it can
+ // fail if something else deletes the route between the dump and the delete. This can
+ // happen, for example, if an interface goes down while we're trying to flush its routes.
+ // So try multiple times and only return an error if the last attempt fails.
+ //
+ // TODO: replace this with our own netlink code.
+ unsigned attempts = 0;
+ int err;
+ do {
+ err = android_fork_execvp(ARRAY_SIZE(argv), const_cast<char**>(argv),
+ NULL, false, false);
+ ++attempts;
+ } while (err != 0 && attempts < ROUTE_FLUSH_ATTEMPTS);
+ if (err) {
+ ALOGE("failed to flush %s routes in table %s after %d attempts",
+ IP_VERSIONS[i], tableString, attempts);
+ ret = -EREMOTEIO;
}
}
- interfaceToTable.erase(interface);
- return 0;
+ // If we failed to flush routes, the caller may elect to keep this interface around, so keep
+ // track of its name.
+ if (!ret) {
+ interfaceToTable.erase(interface);
+ }
+
+ return ret;
}
} // namespace