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