Merge "Use our netlink code to flush routes as well."
diff --git a/server/NetlinkCommands.cpp b/server/NetlinkCommands.cpp
index 3bee73b..1380196 100644
--- a/server/NetlinkCommands.cpp
+++ b/server/NetlinkCommands.cpp
@@ -74,7 +74,7 @@
 #endif
 #endif
 WARN_UNUSED_RESULT int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
-                                          const NetlinkDumpCallback& callback) {
+                                          const NetlinkDumpCallback *callback) {
     nlmsghdr nlmsg = {
         .nlmsg_type = action,
         .nlmsg_flags = flags,
@@ -90,7 +90,6 @@
         return sock;
     }
 
-
     int ret = 0;
 
     if (writev(sock, iov, iovlen) == -1) {
@@ -102,10 +101,8 @@
 
     if (flags & NLM_F_ACK) {
         ret = recvNetlinkAck(sock);
-    }
-
-    if ((flags & NLM_F_DUMP) && callback != nullptr) {
-        ret = processNetlinkDump(sock, callback);
+    } else if ((flags & NLM_F_DUMP) && callback != nullptr) {
+        ret = processNetlinkDump(sock, *callback);
     }
 
     close(sock);
@@ -148,5 +145,72 @@
     return 0;
 }
 
+WARN_UNUSED_RESULT int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction,
+                                     const char *what, const NetlinkDumpFilter& shouldDelete) {
+    // RTM_GETxxx is always RTM_DELxxx + 1, see <linux/rtnetlink.h>.
+    if (getAction != deleteAction + 1) {
+        ALOGE("Unknown flush type getAction=%d deleteAction=%d", getAction, deleteAction);
+        return -EINVAL;
+    }
+
+    int writeSock = openRtNetlinkSocket();
+    if (writeSock < 0) {
+        return writeSock;
+    }
+
+    NetlinkDumpCallback callback = [writeSock, deleteAction, shouldDelete, what] (nlmsghdr *nlh) {
+        if (!shouldDelete(nlh)) return;
+
+        nlh->nlmsg_type = deleteAction;
+        nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+        if (write(writeSock, nlh, nlh->nlmsg_len) == -1) {
+            ALOGE("Error writing flush request: %s", strerror(errno));
+            return;
+        }
+
+        int ret = recvNetlinkAck(writeSock);
+        // 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 ignore ENOENT.
+        if (ret != 0 && ret != -ENOENT) {
+            ALOGW("Flushing %s: %s", what, strerror(-ret));
+        }
+    };
+
+    int ret = 0;
+    for (const int family : { AF_INET, AF_INET6 }) {
+        // struct fib_rule_hdr and struct rtmsg are functionally identical.
+        rtmsg rule = {
+            .rtm_family = static_cast<uint8_t>(family),
+        };
+        iovec iov[] = {
+            { NULL,  0 },
+            { &rule, sizeof(rule) },
+        };
+        uint16_t flags = NETLINK_DUMP_FLAGS;
+
+        if ((ret = sendNetlinkRequest(getAction, flags, iov, ARRAY_SIZE(iov), &callback)) != 0) {
+            break;
+        }
+    }
+
+    close(writeSock);
+
+    return ret;
+}
+
+uint32_t getRtmU32Attribute(const nlmsghdr *nlh, int attribute) {
+    uint32_t rta_len = RTM_PAYLOAD(nlh);
+    rtmsg *msg = reinterpret_cast<rtmsg *>(NLMSG_DATA(nlh));
+    rtattr *rta = reinterpret_cast<rtattr *> RTM_RTA(msg);
+    for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
+        if (rta->rta_type == attribute) {
+            return *(static_cast<uint32_t *>(RTA_DATA(rta)));
+        }
+    }
+    return 0;
+}
+
 }  // namespace net
 }  // namespace android
diff --git a/server/NetlinkCommands.h b/server/NetlinkCommands.h
index 0b109e1..2db7c16 100644
--- a/server/NetlinkCommands.h
+++ b/server/NetlinkCommands.h
@@ -18,6 +18,8 @@
 #define NETD_SERVER_NETLINK_UTIL_H
 
 #include <functional>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 
 #include "NetdConstants.h"
 
@@ -26,9 +28,14 @@
 
 const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
 
+const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
+const uint16_t NETLINK_CREATE_REQUEST_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_CREATE | NLM_F_EXCL;
+const uint16_t NETLINK_DUMP_FLAGS = NLM_F_REQUEST | NLM_F_DUMP;
+
 // Generic code for processing netlink dumps.
 const int kNetlinkDumpBufferSize = 8192;
 typedef std::function<void(nlmsghdr *)> NetlinkDumpCallback;
+typedef std::function<bool(nlmsghdr *)> NetlinkDumpFilter;
 
 // Opens an RTNetlink socket and connects it to the kernel.
 WARN_UNUSED_RESULT int openRtNetlinkSocket();
@@ -40,7 +47,6 @@
 // Sends a netlink request and possibly expects an ACK. The first element of iov should be null and
 // will be set to the netlink message headerheader. The subsequent elements are the contents of the
 // request.
-WARN_UNUSED_RESULT int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen);
 
 // Disable optimizations in ASan build.
 // ASan reports an out-of-bounds 32-bit(!) access in the first loop of the
@@ -51,11 +57,21 @@
 #endif
 #endif
 WARN_UNUSED_RESULT int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
-                                          const NetlinkDumpCallback& callback);
+                                          const NetlinkDumpCallback *callback);
 
 // Processes a netlink dump, passing every message to the specified |callback|.
 WARN_UNUSED_RESULT int processNetlinkDump(int sock, const NetlinkDumpCallback& callback);
 
+// Flushes netlink objects that take an rtmsg structure (FIB rules, routes...). |getAction| and
+// |deleteAction| specify the netlink message types, e.g., RTM_GETRULE and RTM_DELRULE.
+// |shouldDelete| specifies whether a given object should be deleted or not. |what| is a
+// human-readable name for the objects being flushed, e.g. "rules".
+WARN_UNUSED_RESULT int rtNetlinkFlush(uint16_t getAction, uint16_t deleteAction,
+                                      const char *what, const NetlinkDumpFilter& shouldDelete);
+
+// Returns the value of the specific __u32 attribute, or 0 if the attribute was not present.
+uint32_t getRtmU32Attribute(const nlmsghdr *nlh, int attribute);
+
 }  // namespace net
 }  // namespace android
 
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 87b0043..e1a0e8d 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -92,14 +92,8 @@
         __u32           end;
 };
 
-const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
-const uint16_t NETLINK_CREATE_REQUEST_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_CREATE | NLM_F_EXCL;
-const uint16_t NETLINK_DUMP_FLAGS = NLM_F_REQUEST | NLM_F_DUMP;
-
 const uint8_t AF_FAMILIES[] = {AF_INET, AF_INET6};
 
-const char* const IP_VERSIONS[] = {"-4", "-6"};
-
 const uid_t UID_ROOT = 0;
 const char* const IIF_LOOPBACK = "lo";
 const char* const IIF_NONE = NULL;
@@ -111,8 +105,6 @@
 const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
 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) {
@@ -311,7 +303,7 @@
     uint16_t flags = (action == RTM_NEWRULE) ? NETLINK_CREATE_REQUEST_FLAGS : NETLINK_REQUEST_FLAGS;
     for (size_t i = 0; i < ARRAY_SIZE(AF_FAMILIES); ++i) {
         rule.family = AF_FAMILIES[i];
-        if (int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov))) {
+        if (int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr)) {
             if (!(action == RTM_DELRULE && ret == -ENOENT && priority == RULE_PRIORITY_TETHERING)) {
                 // Don't log when deleting a tethering rule that's not there. This matches the
                 // behaviour of clearTetheringRules, which ignores ENOENT in this case.
@@ -423,7 +415,7 @@
 
     uint16_t flags = (action == RTM_NEWROUTE) ? NETLINK_CREATE_REQUEST_FLAGS :
                                                 NETLINK_REQUEST_FLAGS;
-    int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov));
+    int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr);
     if (ret) {
         ALOGE("Error %s route %s -> %s %s to table %u: %s",
               actionName(action), destination, nexthop, interface, table, strerror(-ret));
@@ -842,72 +834,6 @@
                         inputInterface, OIF_NONE, INVALID_UID, INVALID_UID);
 }
 
-uint32_t getRulePriority(const nlmsghdr *nlh) {
-    uint32_t rta_len = RTM_PAYLOAD(nlh);
-    rtmsg *msg = reinterpret_cast<rtmsg *>(NLMSG_DATA(nlh));
-    rtattr *rta = reinterpret_cast<rtattr *> RTM_RTA(msg);
-    for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
-        if (rta->rta_type == FRA_PRIORITY) {
-            return *(static_cast<uint32_t *>(RTA_DATA(rta)));
-        }
-    }
-    return 0;
-}
-
-// Returns 0 on success or negative errno on failure.
-WARN_UNUSED_RESULT int flushRules() {
-    int readSock = openRtNetlinkSocket();
-    if (readSock < 0) {
-        return readSock;
-    }
-
-    int writeSock = openRtNetlinkSocket();
-    if (writeSock < 0) {
-        close(readSock);
-        return writeSock;
-    }
-
-    NetlinkDumpCallback callback = [writeSock] (nlmsghdr *nlh) {
-        // Don't touch rules at priority 0 because by default they are used for local input.
-        if (getRulePriority(nlh) == 0) return;
-
-        nlh->nlmsg_type = RTM_DELRULE;
-        nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
-        if (write(writeSock, nlh, nlh->nlmsg_len) == -1) {
-            ALOGE("Error writing flush request: %s", strerror(errno));
-            return;
-        }
-
-        int ret = recvNetlinkAck(writeSock);
-        if (ret != 0 && ret != -ENOENT) {
-            ALOGW("Flushing rules: %s", strerror(-ret));
-        }
-    };
-
-    int ret = 0;
-    for (const int family : { AF_INET, AF_INET6 }) {
-        // struct fib_rule_hdr and struct rtmsg are functionally identical.
-        rtmsg rule = {
-            .rtm_family = static_cast<uint8_t>(family),
-        };
-        iovec iov[] = {
-            { NULL,  0 },
-            { &rule, sizeof(rule) },
-        };
-        uint16_t action = RTM_GETRULE;
-        uint16_t flags = NETLINK_DUMP_FLAGS;
-
-        if ((ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), callback)) != 0) {
-            break;
-        }
-    }
-
-    close(readSock);
-    close(writeSock);
-
-    return ret;
-}
-
 // Adds or removes an IPv4 or IPv6 route to the specified table and, if it's a directly-connected
 // route, to the main table as well.
 // Returns 0 on success or negative errno on failure.
@@ -945,56 +871,6 @@
     return 0;
 }
 
-// Returns 0 on success or negative errno on failure.
-WARN_UNUSED_RESULT int flushRoutes(const char* interface) {
-    uint32_t table = getRouteTableForInterface(interface);
-    if (table == RT_TABLE_UNSPEC) {
-        return -ESRCH;
-    }
-
-    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,
-            IP_VERSIONS[i],
-            "route",
-            "flush",
-            "table",
-            tableString,
-        };
-
-        // 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;
-        }
-    }
-
-    // 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;
-}
-
 WARN_UNUSED_RESULT int clearTetheringRules(const char* inputInterface) {
     int ret = 0;
     while (ret == 0) {
@@ -1009,6 +885,48 @@
     }
 }
 
+uint32_t getRulePriority(const nlmsghdr *nlh) {
+    return getRtmU32Attribute(nlh, FRA_PRIORITY);
+}
+
+uint32_t getRouteTable(const nlmsghdr *nlh) {
+    return getRtmU32Attribute(nlh, RTA_TABLE);
+}
+
+WARN_UNUSED_RESULT int flushRules() {
+    NetlinkDumpFilter shouldDelete = [] (nlmsghdr *nlh) {
+        // Don't touch rules at priority 0 because by default they are used for local input.
+        return getRulePriority(nlh) != 0;
+    };
+    return rtNetlinkFlush(RTM_GETRULE, RTM_DELRULE, "rules", shouldDelete);
+}
+
+WARN_UNUSED_RESULT int flushRoutes(uint32_t table) {
+    NetlinkDumpFilter shouldDelete = [table] (nlmsghdr *nlh) {
+        return getRouteTable(nlh) == table;
+    };
+
+    return rtNetlinkFlush(RTM_GETROUTE, RTM_DELROUTE, "routes", shouldDelete);
+}
+
+// Returns 0 on success or negative errno on failure.
+WARN_UNUSED_RESULT int flushRoutes(const char* interface) {
+    uint32_t table = getRouteTableForInterface(interface);
+    if (table == RT_TABLE_UNSPEC) {
+        return -ESRCH;
+    }
+
+    int ret = flushRoutes(table);
+
+    // If we failed to flush routes, the caller may elect to keep this interface around, so keep
+    // track of its name.
+    if (ret == 0) {
+        interfaceToTable.erase(interface);
+    }
+
+    return ret;
+}
+
 }  // namespace
 
 int RouteController::Init(unsigned localNetId) {