summaryrefslogtreecommitdiff
path: root/server/RouteController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'server/RouteController.cpp')
-rw-r--r--server/RouteController.cpp153
1 files changed, 74 insertions, 79 deletions
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index a50dab32..87b0043c 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -27,9 +27,10 @@
#include <map>
+#include "DummyNetwork.h"
#include "Fwmark.h"
+#include "NetlinkCommands.h"
#include "UidRanges.h"
-#include "DummyNetwork.h"
#include "android-base/file.h"
#define LOG_TAG "Netd"
@@ -41,6 +42,9 @@
using android::base::WriteStringToFile;
using android::net::UidRange;
+namespace android {
+namespace net {
+
namespace {
// BEGIN CONSTANTS --------------------------------------------------------------------------------
@@ -90,8 +94,7 @@ struct fib_rule_uid_range {
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 sockaddr_nl NETLINK_ADDRESS = {AF_NETLINK, 0, 0, 0};
+const uint16_t NETLINK_DUMP_FLAGS = NLM_F_REQUEST | NLM_F_DUMP;
const uint8_t AF_FAMILIES[] = {AF_INET, AF_INET6};
@@ -196,70 +199,6 @@ void updateTableNamesFile() {
}
}
-// Sends a netlink request and possibly expects an ack.
-// |iov| is an array of struct iovec that contains the netlink message payload.
-// The netlink header is generated by this function based on |action| and |flags|.
-// Returns -errno if there was an error or if the kernel reported an error.
-
-// Disable optimizations in ASan build.
-// ASan reports an out-of-bounds 32-bit(!) access in the first loop of the
-// function (over iov[]).
-#ifdef __clang__
-#if __has_feature(address_sanitizer)
-__attribute__((optnone))
-#endif
-#endif
-WARN_UNUSED_RESULT int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen) {
- nlmsghdr nlmsg = {
- .nlmsg_type = action,
- .nlmsg_flags = flags,
- };
- iov[0].iov_base = &nlmsg;
- iov[0].iov_len = sizeof(nlmsg);
- for (int i = 0; i < iovlen; ++i) {
- nlmsg.nlmsg_len += iov[i].iov_len;
- }
-
- int sock = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
- if (sock == -1) {
- return -errno;
- }
-
- int ret = 0;
- struct {
- nlmsghdr msg;
- nlmsgerr err;
- } response;
-
-
- if (connect(sock, reinterpret_cast<const sockaddr*>(&NETLINK_ADDRESS),
- sizeof(NETLINK_ADDRESS)) == -1 || writev(sock, iov, iovlen) == -1) {
- ret = -errno;
- ALOGE("netlink socket connect/writev failed (%s)", strerror(-ret));
- close(sock);
- return ret;
- }
-
- if (flags & NLM_F_ACK) {
- ret = recv(sock, &response, sizeof(response), 0);
- if (ret == sizeof(response)) {
- ret = response.err.error; // Netlink errors are negative errno.
- } else if (ret == -1) {
- ret = -errno;
- ALOGE("netlink recv failed (%s)", strerror(-ret));
- } else {
- ALOGE("bad netlink response message size (%d != %zu)", ret, sizeof(response));
- ret = -EBADMSG;
- }
- }
-
- if (sock != -1) {
- close(sock);
- }
-
- return ret;
-}
-
// Returns 0 on success or negative errno on failure.
int padInterfaceName(const char* input, char* name, size_t* length, uint16_t* padding) {
if (!input) {
@@ -373,8 +312,12 @@ WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint8_t
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))) {
- ALOGE("Error %s %s rule: %s", actionName(action), familyName(rule.family),
- strerror(-ret));
+ 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.
+ ALOGE("Error %s %s rule: %s", actionName(action), familyName(rule.family),
+ strerror(-ret));
+ }
return ret;
}
}
@@ -899,21 +842,70 @@ 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() {
- for (size_t i = 0; i < ARRAY_SIZE(IP_VERSIONS); ++i) {
- const char* argv[] = {
- IP_PATH,
- IP_VERSIONS[i],
- "rule",
- "flush",
+ 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) },
};
- if (android_fork_execvp(ARRAY_SIZE(argv), const_cast<char**>(argv), NULL, false, false)) {
- ALOGE("failed to flush rules");
- return -EREMOTEIO;
+ uint16_t action = RTM_GETRULE;
+ uint16_t flags = NETLINK_DUMP_FLAGS;
+
+ if ((ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), callback)) != 0) {
+ break;
}
}
- return 0;
+
+ 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
@@ -1164,3 +1156,6 @@ int RouteController::removeVirtualNetworkFallthrough(unsigned vpnNetId,
Permission permission) {
return modifyVpnFallthroughRule(RTM_DELRULE, vpnNetId, physicalInterface, permission);
}
+
+} // namespace net
+} // namespace android