diff options
| author | Lorenzo Colitti <lorenzo@google.com> | 2017-02-14 17:24:28 +0900 |
|---|---|---|
| committer | Lorenzo Colitti <lorenzo@google.com> | 2017-02-16 17:12:16 +0900 |
| commit | f3e299a7c2a0136a84b58652b69e60a22bb0e708 (patch) | |
| tree | 096e714d6d30e34ff85c8a3cafcebd876b55ef12 /server/RouteController.cpp | |
| parent | 4142d02b27793c9effb18ceb4c028e3ccaff7ebf (diff) | |
Use our netlink code to flush routes as well.
Most of the CL is refactoring the rule flush code to be more
generic and move it and various callback definitions to
NetlinkCommands. After that, flushing routes is very simple.
Test: bullhead builds, boots
Test: netd_{unit,integration}_test pass
Bug: 34873832
Change-Id: I0613d525f043d0a8b234a89982281b909011c7e5
Diffstat (limited to 'server/RouteController.cpp')
| -rw-r--r-- | server/RouteController.cpp | 166 |
1 files changed, 42 insertions, 124 deletions
diff --git a/server/RouteController.cpp b/server/RouteController.cpp index 87b0043c..e1a0e8d6 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -92,14 +92,8 @@ struct fib_rule_uid_range { __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 bool MODIFY_NON_UID_BASED_RULES = true; 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 @@ WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint8_t 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 @@ WARN_UNUSED_RESULT int modifyIpRoute(uint16_t action, uint32_t table, const char 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 @@ WARN_UNUSED_RESULT int modifyTetheredNetwork(uint16_t action, const char* inputI 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,6 +871,44 @@ WARN_UNUSED_RESULT int modifyRoute(uint16_t action, const char* interface, const return 0; } +WARN_UNUSED_RESULT int clearTetheringRules(const char* inputInterface) { + int ret = 0; + while (ret == 0) { + ret = modifyIpRule(RTM_DELRULE, RULE_PRIORITY_TETHERING, 0, MARK_UNSET, MARK_UNSET, + inputInterface, OIF_NONE, INVALID_UID, INVALID_UID); + } + + if (ret == -ENOENT) { + return 0; + } else { + return ret; + } +} + +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); @@ -952,63 +916,17 @@ WARN_UNUSED_RESULT int flushRoutes(const char* interface) { 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; - } - } + 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) { + if (ret == 0) { interfaceToTable.erase(interface); } return ret; } -WARN_UNUSED_RESULT int clearTetheringRules(const char* inputInterface) { - int ret = 0; - while (ret == 0) { - ret = modifyIpRule(RTM_DELRULE, RULE_PRIORITY_TETHERING, 0, MARK_UNSET, MARK_UNSET, - inputInterface, OIF_NONE, INVALID_UID, INVALID_UID); - } - - if (ret == -ENOENT) { - return 0; - } else { - return ret; - } -} - } // namespace int RouteController::Init(unsigned localNetId) { |
