1 /*
2  * Copyright 2016 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  * BandwidthControllerTest.cpp - unit tests for BandwidthController.cpp
17  */
18 
19 #include <string>
20 #include <vector>
21 
22 #include <inttypes.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 
28 #include <gtest/gtest.h>
29 
30 #include <android-base/strings.h>
31 #include <android-base/stringprintf.h>
32 
33 #include <netdutils/MockSyscalls.h>
34 #include "BandwidthController.h"
35 #include "Fwmark.h"
36 #include "IptablesBaseTest.h"
37 #include "bpf/BpfUtils.h"
38 #include "netdbpf/bpf_shared.h"
39 #include "tun_interface.h"
40 
41 using ::testing::_;
42 using ::testing::ByMove;
43 using ::testing::Invoke;
44 using ::testing::Return;
45 using ::testing::StrictMock;
46 
47 using android::base::Join;
48 using android::base::StringPrintf;
49 using android::net::TunInterface;
50 using android::netdutils::UniqueFile;
51 using android::netdutils::status::ok;
52 
53 const std::string ACCOUNT_RULES_WITHOUT_BPF =
54         "*filter\n"
55         "-A bw_INPUT -j bw_global_alert\n"
56         "-A bw_INPUT -p esp -j RETURN\n"
57         "-A bw_INPUT -m mark --mark 0x100000/0x100000 -j RETURN\n"
58         "-A bw_INPUT -m owner --socket-exists\n"
59         "-A bw_INPUT -j MARK --or-mark 0x100000\n"
60         "-A bw_OUTPUT -j bw_global_alert\n"
61         "-A bw_OUTPUT -o ipsec+ -j RETURN\n"
62         "-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN\n"
63         "-A bw_OUTPUT -m owner --uid-owner clat -j RETURN\n"
64         "-A bw_OUTPUT -m owner --socket-exists\n"
65         "-A bw_costly_shared -j bw_penalty_box\n"
66         "\n"
67         "-A bw_penalty_box -j bw_happy_box\n"
68         "-A bw_happy_box -j bw_data_saver\n"
69         "-A bw_data_saver -j RETURN\n"
70         "-I bw_happy_box -m owner --uid-owner 0-9999 -j RETURN\n"
71         "COMMIT\n"
72         "*raw\n"
73         "-A bw_raw_PREROUTING -i ipsec+ -j RETURN\n"
74         "-A bw_raw_PREROUTING -m policy --pol ipsec --dir in -j RETURN\n"
75         "-A bw_raw_PREROUTING -m owner --socket-exists\n"
76         "COMMIT\n"
77         "*mangle\n"
78         "-A bw_mangle_POSTROUTING -o ipsec+ -j RETURN\n"
79         "-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN\n"
80         "-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x100000\n"
81         "-A bw_mangle_POSTROUTING -m owner --uid-owner clat -j RETURN\n"
82         "-A bw_mangle_POSTROUTING -m owner --socket-exists\n"
83         "COMMIT\n";
84 
85 const std::string ACCOUNT_RULES_WITH_BPF =
86         "*filter\n"
87         "-A bw_INPUT -j bw_global_alert\n"
88         "-A bw_INPUT -p esp -j RETURN\n"
89         "-A bw_INPUT -m mark --mark 0x100000/0x100000 -j RETURN\n"
90         "\n"
91         "-A bw_INPUT -j MARK --or-mark 0x100000\n"
92         "-A bw_OUTPUT -j bw_global_alert\n"
93         "\n"
94         "\n"
95         "\n"
96         "\n"
97         "-A bw_costly_shared -j bw_penalty_box\n" +
98         StringPrintf("-I bw_penalty_box -m bpf --object-pinned %s -j REJECT\n",
99                      XT_BPF_DENYLIST_PROG_PATH) +
100         "-A bw_penalty_box -j bw_happy_box\n"
101         "-A bw_happy_box -j bw_data_saver\n"
102         "-A bw_data_saver -j RETURN\n" +
103         StringPrintf("-I bw_happy_box -m bpf --object-pinned %s -j RETURN\n",
104                      XT_BPF_ALLOWLIST_PROG_PATH) +
105         "COMMIT\n"
106         "*raw\n"
107         "-A bw_raw_PREROUTING -i ipsec+ -j RETURN\n"
108         "-A bw_raw_PREROUTING -m policy --pol ipsec --dir in -j RETURN\n" +
109         StringPrintf("-A bw_raw_PREROUTING -m bpf --object-pinned %s\n", XT_BPF_INGRESS_PROG_PATH) +
110         "COMMIT\n"
111         "*mangle\n"
112         "-A bw_mangle_POSTROUTING -o ipsec+ -j RETURN\n"
113         "-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN\n"
114         "-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x100000\n"
115         "-A bw_mangle_POSTROUTING -m owner --uid-owner clat -j RETURN\n" +
116         StringPrintf("-A bw_mangle_POSTROUTING -m bpf --object-pinned %s\n",
117                      XT_BPF_EGRESS_PROG_PATH) +
118         "COMMIT\n";
119 
120 class BandwidthControllerTest : public IptablesBaseTest {
121 protected:
BandwidthControllerTest()122     BandwidthControllerTest() {
123         BandwidthController::iptablesRestoreFunction = fakeExecIptablesRestoreWithOutput;
124     }
125     BandwidthController mBw;
126     TunInterface mTun;
127 
SetUp()128     void SetUp() {
129         ASSERT_EQ(0, mTun.init());
130     }
131 
TearDown()132     void TearDown() {
133         mTun.destroy();
134     }
135 
expectSetupCommands(const std::string & expectedClean,const std::string & expectedAccounting)136     void expectSetupCommands(const std::string& expectedClean,
137                              const std::string& expectedAccounting) {
138         std::string expectedList =
139             "*filter\n"
140             "-S\n"
141             "COMMIT\n";
142 
143         std::string expectedFlush =
144                 "*filter\n"
145                 ":bw_INPUT -\n"
146                 ":bw_OUTPUT -\n"
147                 ":bw_FORWARD -\n"
148                 ":bw_happy_box -\n"
149                 ":bw_penalty_box -\n"
150                 ":bw_data_saver -\n"
151                 ":bw_costly_shared -\n"
152                 ":bw_global_alert -\n"
153                 "COMMIT\n"
154                 "*raw\n"
155                 ":bw_raw_PREROUTING -\n"
156                 "COMMIT\n"
157                 "*mangle\n"
158                 ":bw_mangle_POSTROUTING -\n"
159                 "COMMIT\n";
160 
161         ExpectedIptablesCommands expected = {{ V4, expectedList }};
162         if (expectedClean.size()) {
163             expected.push_back({ V4V6, expectedClean });
164         }
165         expected.push_back({ V4V6, expectedFlush });
166         if (expectedAccounting.size()) {
167             expected.push_back({ V4V6, expectedAccounting });
168         }
169 
170         expectIptablesRestoreCommands(expected);
171     }
172 
173     using IptOp = BandwidthController::IptOp;
174 
runIptablesAlertCmd(IptOp a,const char * b,int64_t c)175     int runIptablesAlertCmd(IptOp a, const char* b, int64_t c) {
176         return mBw.runIptablesAlertCmd(a, b, c);
177     }
178 
setCostlyAlert(const std::string & a,int64_t b,int64_t * c)179     int setCostlyAlert(const std::string& a, int64_t b, int64_t* c) {
180         return mBw.setCostlyAlert(a, b, c);
181     }
182 
removeCostlyAlert(const std::string & a,int64_t * b)183     int removeCostlyAlert(const std::string& a, int64_t* b) { return mBw.removeCostlyAlert(a, b); }
184 
expectUpdateQuota(uint64_t quota)185     void expectUpdateQuota(uint64_t quota) {
186         uintptr_t dummy;
187         FILE* dummyFile = reinterpret_cast<FILE*>(&dummy);
188 
189         EXPECT_CALL(mSyscalls, fopen(_, _)).WillOnce(Return(ByMove(UniqueFile(dummyFile))));
190         EXPECT_CALL(mSyscalls, vfprintf(dummyFile, _, _))
191             .WillOnce(Invoke([quota](FILE*, const std::string&, va_list ap) {
192                 EXPECT_EQ(quota, va_arg(ap, uint64_t));
193                 return 0;
194             }));
195         EXPECT_CALL(mSyscalls, fclose(dummyFile)).WillOnce(Return(ok));
196     }
197 
checkBandwithControl(bool useBpf)198     void checkBandwithControl(bool useBpf) {
199         // Pretend no bw_costly_shared_<iface> rules already exist...
200         addIptablesRestoreOutput(
201                 "-P OUTPUT ACCEPT\n"
202                 "-N bw_costly_shared\n"
203                 "-N unrelated\n");
204 
205         // ... so none are flushed or deleted.
206         std::string expectedClean = "";
207 
208         std::string expectedAccounting =
209                 useBpf ? ACCOUNT_RULES_WITH_BPF : ACCOUNT_RULES_WITHOUT_BPF;
210         mBw.setBpfEnabled(useBpf);
211         mBw.enableBandwidthControl();
212         expectSetupCommands(expectedClean, expectedAccounting);
213     }
214 
215     StrictMock<android::netdutils::ScopedMockSyscalls> mSyscalls;
216 };
217 
TEST_F(BandwidthControllerTest,TestSetupIptablesHooks)218 TEST_F(BandwidthControllerTest, TestSetupIptablesHooks) {
219     // Pretend some bw_costly_shared_<iface> rules already exist...
220     addIptablesRestoreOutput(
221         "-P OUTPUT ACCEPT\n"
222         "-N bw_costly_rmnet_data0\n"
223         "-N bw_costly_shared\n"
224         "-N unrelated\n"
225         "-N bw_costly_rmnet_data7\n");
226 
227     // ... and expect that they be flushed and deleted.
228     std::string expectedCleanCmds =
229         "*filter\n"
230         ":bw_costly_rmnet_data0 -\n"
231         "-X bw_costly_rmnet_data0\n"
232         ":bw_costly_rmnet_data7 -\n"
233         "-X bw_costly_rmnet_data7\n"
234         "COMMIT\n";
235 
236     mBw.setupIptablesHooks();
237     expectSetupCommands(expectedCleanCmds, "");
238 }
239 
TEST_F(BandwidthControllerTest,TestCheckUidBillingMask)240 TEST_F(BandwidthControllerTest, TestCheckUidBillingMask) {
241     uint32_t uidBillingMask = Fwmark::getUidBillingMask();
242 
243     // If mask is non-zero, and mask & mask-1 is equal to 0, then the mask is a power of two.
244     bool isPowerOfTwo = uidBillingMask && (uidBillingMask & (uidBillingMask - 1)) == 0;
245 
246     // Must be exactly a power of two
247     EXPECT_TRUE(isPowerOfTwo);
248 }
249 
TEST_F(BandwidthControllerTest,TestEnableBandwidthControlWithBpf)250 TEST_F(BandwidthControllerTest, TestEnableBandwidthControlWithBpf) {
251     checkBandwithControl(true);
252 }
253 
TEST_F(BandwidthControllerTest,TestEnableBandwidthControlWithoutBpf)254 TEST_F(BandwidthControllerTest, TestEnableBandwidthControlWithoutBpf) {
255     checkBandwithControl(false);
256 }
257 
TEST_F(BandwidthControllerTest,TestDisableBandwidthControl)258 TEST_F(BandwidthControllerTest, TestDisableBandwidthControl) {
259     // Pretend some bw_costly_shared_<iface> rules already exist...
260     addIptablesRestoreOutput(
261         "-P OUTPUT ACCEPT\n"
262         "-N bw_costly_rmnet_data0\n"
263         "-N bw_costly_shared\n"
264         "-N unrelated\n"
265         "-N bw_costly_rmnet_data7\n");
266 
267     // ... and expect that they be flushed.
268     std::string expectedCleanCmds =
269         "*filter\n"
270         ":bw_costly_rmnet_data0 -\n"
271         ":bw_costly_rmnet_data7 -\n"
272         "COMMIT\n";
273 
274     mBw.disableBandwidthControl();
275     expectSetupCommands(expectedCleanCmds, "");
276 }
277 
TEST_F(BandwidthControllerTest,TestEnableDataSaver)278 TEST_F(BandwidthControllerTest, TestEnableDataSaver) {
279     mBw.enableDataSaver(true);
280     std::string expected4 =
281             "*filter\n"
282             ":bw_data_saver -\n"
283             "-A bw_data_saver -j REJECT\n"
284             "COMMIT\n";
285     std::string expected6 =
286             "*filter\n"
287             ":bw_data_saver -\n"
288             "-A bw_data_saver -p icmpv6 --icmpv6-type packet-too-big -j RETURN\n"
289             "-A bw_data_saver -p icmpv6 --icmpv6-type router-solicitation -j RETURN\n"
290             "-A bw_data_saver -p icmpv6 --icmpv6-type router-advertisement -j RETURN\n"
291             "-A bw_data_saver -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN\n"
292             "-A bw_data_saver -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN\n"
293             "-A bw_data_saver -p icmpv6 --icmpv6-type redirect -j RETURN\n"
294             "-A bw_data_saver -j REJECT\n"
295             "COMMIT\n";
296     expectIptablesRestoreCommands({
297         {V4, expected4},
298         {V6, expected6},
299     });
300 
301     mBw.enableDataSaver(false);
302     std::string expected = {
303             "*filter\n"
304             ":bw_data_saver -\n"
305             "-A bw_data_saver -j RETURN\n"
306             "COMMIT\n"};
307     expectIptablesRestoreCommands({
308         {V4, expected},
309         {V6, expected},
310     });
311 }
312 
makeInterfaceQuotaCommands(const std::string & iface,int ruleIndex,int64_t quota)313 const std::vector<std::string> makeInterfaceQuotaCommands(const std::string& iface, int ruleIndex,
314                                                           int64_t quota) {
315     const std::string chain = "bw_costly_" + iface;
316     const char* c_chain = chain.c_str();
317     const char* c_iface = iface.c_str();
318     std::vector<std::string> cmds = {
319             "*filter",
320             StringPrintf(":%s -", c_chain),
321             StringPrintf("-A %s -j bw_penalty_box", c_chain),
322             StringPrintf("-I bw_INPUT %d -i %s -j %s", ruleIndex, c_iface, c_chain),
323             StringPrintf("-I bw_OUTPUT %d -o %s -j %s", ruleIndex, c_iface, c_chain),
324             StringPrintf("-A bw_FORWARD -i %s -j %s", c_iface, c_chain),
325             StringPrintf("-A bw_FORWARD -o %s -j %s", c_iface, c_chain),
326             StringPrintf("-A %s -m quota2 ! --quota %" PRIu64 " --name %s -j REJECT", c_chain,
327                          quota, c_iface),
328             "COMMIT\n",
329     };
330     return {Join(cmds, "\n")};
331 }
332 
removeInterfaceQuotaCommands(const std::string & iface)333 const std::vector<std::string> removeInterfaceQuotaCommands(const std::string& iface) {
334     const std::string chain = "bw_costly_" + iface;
335     const char* c_chain = chain.c_str();
336     const char* c_iface = iface.c_str();
337     std::vector<std::string> cmds = {
338             "*filter",
339             StringPrintf("-D bw_INPUT -i %s -j %s", c_iface, c_chain),
340             StringPrintf("-D bw_OUTPUT -o %s -j %s", c_iface, c_chain),
341             StringPrintf("-D bw_FORWARD -i %s -j %s", c_iface, c_chain),
342             StringPrintf("-D bw_FORWARD -o %s -j %s", c_iface, c_chain),
343             StringPrintf("-F %s", c_chain),
344             StringPrintf("-X %s", c_chain),
345             "COMMIT\n",
346     };
347     return {Join(cmds, "\n")};
348 }
349 
TEST_F(BandwidthControllerTest,TestSetInterfaceQuota)350 TEST_F(BandwidthControllerTest, TestSetInterfaceQuota) {
351     constexpr uint64_t kOldQuota = 123456;
352     const std::string iface = mTun.name();
353     std::vector<std::string> expected = makeInterfaceQuotaCommands(iface, 1, kOldQuota);
354 
355     EXPECT_EQ(0, mBw.setInterfaceQuota(iface, kOldQuota));
356     expectIptablesRestoreCommands(expected);
357 
358     constexpr uint64_t kNewQuota = kOldQuota + 1;
359     expected = {};
360     expectUpdateQuota(kNewQuota);
361     EXPECT_EQ(0, mBw.setInterfaceQuota(iface, kNewQuota));
362     expectIptablesRestoreCommands(expected);
363 
364     expected = removeInterfaceQuotaCommands(iface);
365     EXPECT_EQ(0, mBw.removeInterfaceQuota(iface));
366     expectIptablesRestoreCommands(expected);
367 }
368 
makeInterfaceSharedQuotaCommands(const std::string & iface,int ruleIndex,int64_t quota,bool insertQuota)369 const std::vector<std::string> makeInterfaceSharedQuotaCommands(const std::string& iface,
370                                                                 int ruleIndex, int64_t quota,
371                                                                 bool insertQuota) {
372     const std::string chain = "bw_costly_shared";
373     const char* c_chain = chain.c_str();
374     const char* c_iface = iface.c_str();
375     std::vector<std::string> cmds = {
376             "*filter",
377             StringPrintf("-I bw_INPUT %d -i %s -j %s", ruleIndex, c_iface, c_chain),
378             StringPrintf("-I bw_OUTPUT %d -o %s -j %s", ruleIndex, c_iface, c_chain),
379             StringPrintf("-A bw_FORWARD -i %s -j %s", c_iface, c_chain),
380             StringPrintf("-A bw_FORWARD -o %s -j %s", c_iface, c_chain),
381     };
382     if (insertQuota) {
383         cmds.push_back(StringPrintf("-I %s -m quota2 ! --quota %" PRIu64 " --name shared -j REJECT",
384                                     c_chain, quota));
385     }
386     cmds.push_back("COMMIT\n");
387     return {Join(cmds, "\n")};
388 }
389 
removeInterfaceSharedQuotaCommands(const std::string & iface,int64_t quota,bool deleteQuota)390 const std::vector<std::string> removeInterfaceSharedQuotaCommands(const std::string& iface,
391                                                                   int64_t quota, bool deleteQuota) {
392     const std::string chain = "bw_costly_shared";
393     const char* c_chain = chain.c_str();
394     const char* c_iface = iface.c_str();
395     std::vector<std::string> cmds = {
396             "*filter",
397             StringPrintf("-D bw_INPUT -i %s -j %s", c_iface, c_chain),
398             StringPrintf("-D bw_OUTPUT -o %s -j %s", c_iface, c_chain),
399             StringPrintf("-D bw_FORWARD -i %s -j %s", c_iface, c_chain),
400             StringPrintf("-D bw_FORWARD -o %s -j %s", c_iface, c_chain),
401     };
402     if (deleteQuota) {
403         cmds.push_back(StringPrintf("-D %s -m quota2 ! --quota %" PRIu64 " --name shared -j REJECT",
404                                     c_chain, quota));
405     }
406     cmds.push_back("COMMIT\n");
407     return {Join(cmds, "\n")};
408 }
409 
TEST_F(BandwidthControllerTest,TestSetInterfaceSharedQuotaDuplicate)410 TEST_F(BandwidthControllerTest, TestSetInterfaceSharedQuotaDuplicate) {
411     constexpr uint64_t kQuota = 123456;
412     const std::string iface = mTun.name();
413     std::vector<std::string> expected = makeInterfaceSharedQuotaCommands(iface, 1, 123456, true);
414     EXPECT_EQ(0, mBw.setInterfaceSharedQuota(iface, kQuota));
415     expectIptablesRestoreCommands(expected);
416 
417     expected = {};
418     EXPECT_EQ(0, mBw.setInterfaceSharedQuota(iface, kQuota));
419     expectIptablesRestoreCommands(expected);
420 
421     expected = removeInterfaceSharedQuotaCommands(iface, kQuota, true);
422     EXPECT_EQ(0, mBw.removeInterfaceSharedQuota(iface));
423     expectIptablesRestoreCommands(expected);
424 }
425 
TEST_F(BandwidthControllerTest,TestSetInterfaceSharedQuotaUpdate)426 TEST_F(BandwidthControllerTest, TestSetInterfaceSharedQuotaUpdate) {
427     constexpr uint64_t kOldQuota = 123456;
428     const std::string iface = mTun.name();
429     std::vector<std::string> expected = makeInterfaceSharedQuotaCommands(iface, 1, kOldQuota, true);
430     EXPECT_EQ(0, mBw.setInterfaceSharedQuota(iface, kOldQuota));
431     expectIptablesRestoreCommands(expected);
432 
433     constexpr uint64_t kNewQuota = kOldQuota + 1;
434     expected = {};
435     expectUpdateQuota(kNewQuota);
436     EXPECT_EQ(0, mBw.setInterfaceSharedQuota(iface, kNewQuota));
437     expectIptablesRestoreCommands(expected);
438 
439     expected = removeInterfaceSharedQuotaCommands(iface, kNewQuota, true);
440     EXPECT_EQ(0, mBw.removeInterfaceSharedQuota(iface));
441     expectIptablesRestoreCommands(expected);
442 }
443 
TEST_F(BandwidthControllerTest,TestSetInterfaceSharedQuotaTwoInterfaces)444 TEST_F(BandwidthControllerTest, TestSetInterfaceSharedQuotaTwoInterfaces) {
445     constexpr uint64_t kQuota = 123456;
446     const std::vector<std::string> ifaces{
447         {"a" + mTun.name()},
448         {"b" + mTun.name()},
449     };
450 
451     for (const auto& iface : ifaces) {
452         // Quota rule is only added when the total number of
453         // interfaces transitions from 0 -> 1.
454         bool first = (iface == ifaces[0]);
455         auto expected = makeInterfaceSharedQuotaCommands(iface, 1, kQuota, first);
456         EXPECT_EQ(0, mBw.setInterfaceSharedQuota(iface, kQuota));
457         expectIptablesRestoreCommands(expected);
458     }
459 
460     for (const auto& iface : ifaces) {
461         // Quota rule is only removed when the total number of
462         // interfaces transitions from 1 -> 0.
463         bool last = (iface == ifaces[1]);
464         auto expected = removeInterfaceSharedQuotaCommands(iface, kQuota, last);
465         EXPECT_EQ(0, mBw.removeInterfaceSharedQuota(iface));
466         expectIptablesRestoreCommands(expected);
467     }
468 }
469 
TEST_F(BandwidthControllerTest,IptablesAlertCmd)470 TEST_F(BandwidthControllerTest, IptablesAlertCmd) {
471     std::vector<std::string> expected = {
472             "*filter\n"
473             "-I bw_global_alert -m quota2 ! --quota 123456 --name MyWonderfulAlert\n"
474             "COMMIT\n"};
475     EXPECT_EQ(0, runIptablesAlertCmd(IptOp::IptOpInsert, "MyWonderfulAlert", 123456));
476     expectIptablesRestoreCommands(expected);
477 
478     expected = {
479             "*filter\n"
480             "-D bw_global_alert -m quota2 ! --quota 123456 --name MyWonderfulAlert\n"
481             "COMMIT\n"};
482     EXPECT_EQ(0, runIptablesAlertCmd(IptOp::IptOpDelete, "MyWonderfulAlert", 123456));
483     expectIptablesRestoreCommands(expected);
484 }
485 
TEST_F(BandwidthControllerTest,CostlyAlert)486 TEST_F(BandwidthControllerTest, CostlyAlert) {
487     const int64_t kQuota = 123456;
488     int64_t alertBytes = 0;
489 
490     std::vector<std::string> expected = {
491         "*filter\n"
492         "-A bw_costly_shared -m quota2 ! --quota 123456 --name sharedAlert\n"
493         "COMMIT\n"
494     };
495     EXPECT_EQ(0, setCostlyAlert("shared", kQuota, &alertBytes));
496     EXPECT_EQ(kQuota, alertBytes);
497     expectIptablesRestoreCommands(expected);
498 
499     expected = {};
500     expectUpdateQuota(kQuota);
501     EXPECT_EQ(0, setCostlyAlert("shared", kQuota + 1, &alertBytes));
502     EXPECT_EQ(kQuota + 1, alertBytes);
503     expectIptablesRestoreCommands(expected);
504 
505     expected = {
506         "*filter\n"
507         "-D bw_costly_shared -m quota2 ! --quota 123457 --name sharedAlert\n"
508         "COMMIT\n"
509     };
510     EXPECT_EQ(0, removeCostlyAlert("shared", &alertBytes));
511     EXPECT_EQ(0, alertBytes);
512     expectIptablesRestoreCommands(expected);
513 }
514 
TEST_F(BandwidthControllerTest,ManipulateSpecialApps)515 TEST_F(BandwidthControllerTest, ManipulateSpecialApps) {
516     std::vector<const char *> appUids = { "1000", "1001", "10012" };
517 
518     std::vector<std::string> expected = {
519             "*filter\n"
520             "-I bw_happy_box -m owner --uid-owner 1000 -j RETURN\n"
521             "-I bw_happy_box -m owner --uid-owner 1001 -j RETURN\n"
522             "-I bw_happy_box -m owner --uid-owner 10012 -j RETURN\n"
523             "COMMIT\n"};
524     EXPECT_EQ(0, mBw.addNiceApps(appUids.size(), const_cast<char**>(&appUids[0])));
525     expectIptablesRestoreCommands(expected);
526 
527     expected = {
528             "*filter\n"
529             "-D bw_penalty_box -m owner --uid-owner 1000 -j REJECT\n"
530             "-D bw_penalty_box -m owner --uid-owner 1001 -j REJECT\n"
531             "-D bw_penalty_box -m owner --uid-owner 10012 -j REJECT\n"
532             "COMMIT\n"};
533     EXPECT_EQ(0, mBw.removeNaughtyApps(appUids.size(), const_cast<char**>(&appUids[0])));
534     expectIptablesRestoreCommands(expected);
535 }
536