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) {