Use netlink code to flush rules.
This removes two calls to /sbin/ip on netd startup, which saves
about 70ms. In the future we will be able to use this to flush
routes as well, which will provide similar time savings on every
network destroy operation.
Bug: 34873832
Test: bullhead builds, boots
Test: rules flushed correctly when netd is killed
Change-Id: I4875ac7fec1a92dc5fa2cb68f8fab2a903348c20
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index a0fd7d5..532a43f 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -94,6 +94,7 @@
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};
@@ -837,23 +838,73 @@
inputInterface, OIF_NONE, INVALID_UID, INVALID_UID);
}
-// Returns 0 on success or negative errno on failure.
-WARN_UNUSED_RESULT int flushRules() {
- for (size_t i = 0; i < ARRAY_SIZE(IP_VERSIONS); ++i) {
- const char* argv[] = {
- IP_PATH,
- IP_VERSIONS[i],
- "rule",
- "flush",
- };
- if (android_fork_execvp(ARRAY_SIZE(argv), const_cast<char**>(argv), NULL, false, false)) {
- ALOGE("failed to flush rules");
- return -EREMOTEIO;
+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) {
+ if (nlh == nullptr) return;
+ // 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.