Support getting fwmark for a network
NetworkStack will use the tcp info queried from kernel to
diagnose internet health. The diagnosis should only focus on
specific network, e.g. default network. NetworkStack needs a way
to filter the target network. The only identifier is the fwmark
value for each socket. The fwmark calculation may be modified
in native and may not sync with NetworkStack. Thus, NetworkStack
will need a way to get the netId mask and the network fwmark to
know the network information contained in the fwmark. Expose this
function to ensure the fwmark implementation is aligned wih netd.
Bug: 130325409
Test: cd system/netd; atest
Change-Id: I52fba39e041490016224beffb273693e64ce4338
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 080e22d..7d3d296 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -43,6 +43,7 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android/multinetwork.h>
#include <binder/IPCThreadState.h>
#include <bpf/BpfMap.h>
#include <bpf/BpfUtils.h>
@@ -56,6 +57,7 @@
#include "InterfaceController.h"
#include "NetdClient.h"
#include "NetdConstants.h"
+#include "NetworkController.h"
#include "TestUnsolService.h"
#include "XfrmController.h"
#include "android/net/INetd.h"
@@ -91,6 +93,7 @@
using android::net::INetd;
using android::net::InterfaceConfigurationParcel;
using android::net::InterfaceController;
+using android::net::MarkMaskParcel;
using android::net::TetherStatsParcel;
using android::net::TunInterface;
using android::net::UidRangeParcel;
@@ -3246,3 +3249,59 @@
ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk());
expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, sTun, sTun2);
}
+
+namespace {
+
+int32_t createIpv6SocketAndCheckMark(int type, const in6_addr& dstAddr) {
+ const sockaddr_in6 dst6 = {
+ .sin6_family = AF_INET6,
+ .sin6_port = 1234,
+ .sin6_addr = dstAddr,
+ };
+ // create non-blocking socket.
+ int sockFd = socket(AF_INET6, type | SOCK_NONBLOCK, 0);
+ EXPECT_NE(-1, sockFd);
+ EXPECT_EQ((type == SOCK_STREAM) ? -1 : 0, connect(sockFd, (sockaddr*)&dst6, sizeof(dst6)));
+
+ // Get socket fwmark.
+ Fwmark fwmark;
+ socklen_t fwmarkLen = sizeof(fwmark.intValue);
+ EXPECT_EQ(0, getsockopt(sockFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen));
+ EXPECT_EQ(0, close(sockFd));
+ return fwmark.intValue;
+}
+
+} // namespace
+
+TEST_F(BinderTest, GetFwmarkForNetwork) {
+ in6_addr v6Addr = {
+ {// 2001:db8:cafe::8888
+ .u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88}}};
+ // Add test physical network 1 and set as default network.
+ EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID1, INetd::PERMISSION_NONE).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
+ EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID1, sTun.name(), "2001:db8::/32", "").isOk());
+ EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk());
+ // Add test physical network 2
+ EXPECT_TRUE(mNetd->networkCreatePhysical(TEST_NETID2, INetd::PERMISSION_NONE).isOk());
+ EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID2, sTun2.name()).isOk());
+
+ // Get fwmark for network 1.
+ MarkMaskParcel maskMarkNet1;
+ ASSERT_TRUE(mNetd->getFwmarkForNetwork(TEST_NETID1, &maskMarkNet1).isOk());
+
+ uint32_t fwmarkTcp = createIpv6SocketAndCheckMark(SOCK_STREAM, v6Addr);
+ uint32_t fwmarkUdp = createIpv6SocketAndCheckMark(SOCK_DGRAM, v6Addr);
+ EXPECT_EQ(maskMarkNet1.mark, static_cast<int>(fwmarkTcp & maskMarkNet1.mask));
+ EXPECT_EQ(maskMarkNet1.mark, static_cast<int>(fwmarkUdp & maskMarkNet1.mask));
+
+ // Get fwmark for network 2.
+ MarkMaskParcel maskMarkNet2;
+ ASSERT_TRUE(mNetd->getFwmarkForNetwork(TEST_NETID2, &maskMarkNet2).isOk());
+ EXPECT_NE(maskMarkNet2.mark, static_cast<int>(fwmarkTcp & maskMarkNet2.mask));
+ EXPECT_NE(maskMarkNet2.mark, static_cast<int>(fwmarkUdp & maskMarkNet2.mask));
+
+ // Remove test physical network.
+ EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID2).isOk());
+ EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
+}