1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <gtest/gtest.h>
18 #include <private/android_filesystem_config.h>
19
20 #define BPF_MAP_MAKE_VISIBLE_FOR_TESTING
21 #include "DnsBpfHelper.h"
22
23 using namespace android::bpf; // NOLINT(google-build-using-namespace): exempted
24
25 namespace android {
26 namespace net {
27
28 constexpr int TEST_MAP_SIZE = 2;
29
30 #define ASSERT_VALID(x) ASSERT_TRUE((x).isValid())
31
32 class DnsBpfHelperTest : public ::testing::Test {
33 protected:
34 DnsBpfHelper mDnsBpfHelper;
35 BpfMap<uint32_t, uint32_t> mFakeConfigurationMap;
36 BpfMap<uint32_t, UidOwnerValue> mFakeUidOwnerMap;
37 BpfMap<uint32_t, bool> mFakeDataSaverEnabledMap;
38
SetUp()39 void SetUp() {
40 mFakeConfigurationMap.resetMap(BPF_MAP_TYPE_ARRAY, CONFIGURATION_MAP_SIZE);
41 ASSERT_VALID(mFakeConfigurationMap);
42
43 mFakeUidOwnerMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE);
44 ASSERT_VALID(mFakeUidOwnerMap);
45
46 mFakeDataSaverEnabledMap.resetMap(BPF_MAP_TYPE_ARRAY, DATA_SAVER_ENABLED_MAP_SIZE);
47 ASSERT_VALID(mFakeDataSaverEnabledMap);
48
49 mDnsBpfHelper.mConfigurationMap = mFakeConfigurationMap;
50 ASSERT_VALID(mDnsBpfHelper.mConfigurationMap);
51 mDnsBpfHelper.mUidOwnerMap = mFakeUidOwnerMap;
52 ASSERT_VALID(mDnsBpfHelper.mUidOwnerMap);
53 mDnsBpfHelper.mDataSaverEnabledMap = mFakeDataSaverEnabledMap;
54 ASSERT_VALID(mDnsBpfHelper.mDataSaverEnabledMap);
55 }
56
ResetAllMaps()57 void ResetAllMaps() {
58 mDnsBpfHelper.mConfigurationMap.reset();
59 mDnsBpfHelper.mUidOwnerMap.reset();
60 mDnsBpfHelper.mDataSaverEnabledMap.reset();
61 }
62 };
63
TEST_F(DnsBpfHelperTest,IsUidNetworkingBlocked)64 TEST_F(DnsBpfHelperTest, IsUidNetworkingBlocked) {
65 struct TestConfig {
66 const uid_t uid;
67 const uint32_t enabledRules;
68 const uint32_t uidRules;
69 const int expectedResult;
70 std::string toString() const {
71 return fmt::format(
72 "uid: {}, enabledRules: {}, uidRules: {}, expectedResult: {}",
73 uid, enabledRules, uidRules, expectedResult);
74 }
75 } testConfigs[] = {
76 // clang-format off
77 // No rule enabled:
78 // uid, enabledRules, uidRules, expectedResult
79 {AID_APP_START, NO_MATCH, NO_MATCH, false},
80
81 // An allowlist rule:
82 {AID_APP_START, NO_MATCH, DOZABLE_MATCH, false},
83 {AID_APP_START, DOZABLE_MATCH, NO_MATCH, true},
84 {AID_APP_START, DOZABLE_MATCH, DOZABLE_MATCH, false},
85 // A denylist rule
86 {AID_APP_START, NO_MATCH, STANDBY_MATCH, false},
87 {AID_APP_START, STANDBY_MATCH, NO_MATCH, false},
88 {AID_APP_START, STANDBY_MATCH, STANDBY_MATCH, true},
89
90 // Multiple rules enabled:
91 // Match only part of the enabled allowlist rules.
92 {AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH, true},
93 {AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, POWERSAVE_MATCH, true},
94 // Match all of the enabled allowlist rules.
95 {AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH|POWERSAVE_MATCH, false},
96 // Match allowlist.
97 {AID_APP_START, DOZABLE_MATCH|STANDBY_MATCH, DOZABLE_MATCH, false},
98 // Match no rule.
99 {AID_APP_START, DOZABLE_MATCH|STANDBY_MATCH, NO_MATCH, true},
100 {AID_APP_START, DOZABLE_MATCH|POWERSAVE_MATCH, NO_MATCH, true},
101
102 // System UID: always unblocked.
103 {AID_SYSTEM, NO_MATCH, NO_MATCH, false},
104 {AID_SYSTEM, NO_MATCH, DOZABLE_MATCH, false},
105 {AID_SYSTEM, DOZABLE_MATCH, NO_MATCH, false},
106 {AID_SYSTEM, DOZABLE_MATCH, DOZABLE_MATCH, false},
107 {AID_SYSTEM, NO_MATCH, STANDBY_MATCH, false},
108 {AID_SYSTEM, STANDBY_MATCH, NO_MATCH, false},
109 {AID_SYSTEM, STANDBY_MATCH, STANDBY_MATCH, false},
110 {AID_SYSTEM, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH, false},
111 {AID_SYSTEM, DOZABLE_MATCH|POWERSAVE_MATCH, POWERSAVE_MATCH, false},
112 {AID_SYSTEM, DOZABLE_MATCH|POWERSAVE_MATCH, DOZABLE_MATCH|POWERSAVE_MATCH, false},
113 {AID_SYSTEM, DOZABLE_MATCH|STANDBY_MATCH, DOZABLE_MATCH, false},
114 {AID_SYSTEM, DOZABLE_MATCH|STANDBY_MATCH, NO_MATCH, false},
115 {AID_SYSTEM, DOZABLE_MATCH|POWERSAVE_MATCH, NO_MATCH, false},
116 // clang-format on
117 };
118
119 for (const auto& config : testConfigs) {
120 SCOPED_TRACE(config.toString());
121
122 // Setup maps.
123 EXPECT_RESULT_OK(mFakeConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY,
124 config.enabledRules, BPF_EXIST));
125 EXPECT_RESULT_OK(mFakeUidOwnerMap.writeValue(config.uid, {.iif = 0, .rule = config.uidRules},
126 BPF_ANY));
127
128 // Verify the function.
129 auto result = mDnsBpfHelper.isUidNetworkingBlocked(config.uid, /*metered=*/false);
130 EXPECT_TRUE(result.ok());
131 EXPECT_EQ(config.expectedResult, result.value());
132 }
133 }
134
TEST_F(DnsBpfHelperTest,IsUidNetworkingBlocked_uninitialized)135 TEST_F(DnsBpfHelperTest, IsUidNetworkingBlocked_uninitialized) {
136 ResetAllMaps();
137
138 auto result = mDnsBpfHelper.isUidNetworkingBlocked(AID_APP_START, /*metered=*/false);
139 EXPECT_FALSE(result.ok());
140 EXPECT_EQ(EUNATCH, result.error().code());
141
142 result = mDnsBpfHelper.isUidNetworkingBlocked(AID_SYSTEM, /*metered=*/false);
143 EXPECT_TRUE(result.ok());
144 EXPECT_FALSE(result.value());
145 }
146
147 // Verify DataSaver on metered network.
TEST_F(DnsBpfHelperTest,IsUidNetworkingBlocked_metered)148 TEST_F(DnsBpfHelperTest, IsUidNetworkingBlocked_metered) {
149 struct TestConfig {
150 const uint32_t enabledRules; // Settings in configuration map.
151 const bool dataSaverEnabled; // Settings in data saver enabled map.
152 const uint32_t uidRules; // Settings in uid owner map.
153 const int blocked; // Whether the UID is expected to be networking blocked or not.
154 std::string toString() const {
155 return fmt::format(
156 ", enabledRules: {}, dataSaverEnabled: {}, uidRules: {}, expect blocked: {}",
157 enabledRules, dataSaverEnabled, uidRules, blocked);
158 }
159 } testConfigs[]{
160 // clang-format off
161 // enabledRules, dataSaverEnabled, uidRules, blocked
162 {NO_MATCH, false, NO_MATCH, false},
163 {NO_MATCH, false, PENALTY_BOX_USER_MATCH, true},
164 {NO_MATCH, false, PENALTY_BOX_ADMIN_MATCH, true},
165 {NO_MATCH, false, PENALTY_BOX_USER_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
166 {NO_MATCH, false, HAPPY_BOX_MATCH, false},
167 {NO_MATCH, false, PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
168 {NO_MATCH, false, PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
169 {NO_MATCH, true, NO_MATCH, true},
170 {NO_MATCH, true, PENALTY_BOX_USER_MATCH, true},
171 {NO_MATCH, true, PENALTY_BOX_ADMIN_MATCH, true},
172 {NO_MATCH, true, PENALTY_BOX_USER_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
173 {NO_MATCH, true, HAPPY_BOX_MATCH, false},
174 {NO_MATCH, true, PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
175 {NO_MATCH, true, PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
176 {STANDBY_MATCH, false, STANDBY_MATCH, true},
177 {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_USER_MATCH, true},
178 {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
179 {STANDBY_MATCH, false, STANDBY_MATCH|HAPPY_BOX_MATCH, true},
180 {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
181 {STANDBY_MATCH, false, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
182 {STANDBY_MATCH, true, STANDBY_MATCH, true},
183 {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_USER_MATCH, true},
184 {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH, true},
185 {STANDBY_MATCH, true, STANDBY_MATCH|HAPPY_BOX_MATCH, true},
186 {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_USER_MATCH|HAPPY_BOX_MATCH, true},
187 {STANDBY_MATCH, true, STANDBY_MATCH|PENALTY_BOX_ADMIN_MATCH|HAPPY_BOX_MATCH, true},
188 // clang-format on
189 };
190
191 for (const auto& config : testConfigs) {
192 SCOPED_TRACE(config.toString());
193
194 // Setup maps.
195 EXPECT_RESULT_OK(mFakeConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY,
196 config.enabledRules, BPF_EXIST));
197 EXPECT_RESULT_OK(mFakeDataSaverEnabledMap.writeValue(DATA_SAVER_ENABLED_KEY,
198 config.dataSaverEnabled, BPF_EXIST));
199 EXPECT_RESULT_OK(mFakeUidOwnerMap.writeValue(AID_APP_START, {.iif = 0, .rule = config.uidRules},
200 BPF_ANY));
201
202 // Verify the function.
203 auto result = mDnsBpfHelper.isUidNetworkingBlocked(AID_APP_START, /*metered=*/true);
204 EXPECT_RESULT_OK(result);
205 EXPECT_EQ(config.blocked, result.value());
206 }
207 }
208
209 } // namespace net
210 } // namespace android
211