Firewall-related commands porting
Test: built, flashed, booted
system/netd/tests/runtests.sh passes
Change-Id: I0fcf6ac4e5d96cbf63d6752bee7202cdef940e82
diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp
index 5669a96..6a40ba2 100644
--- a/server/BandwidthController.cpp
+++ b/server/BandwidthController.cpp
@@ -72,9 +72,10 @@
using android::base::Join;
using android::base::StringAppendF;
using android::base::StringPrintf;
+using android::net::FirewallController;
using android::net::gCtls;
-using android::netdutils::StatusOr;
using android::netdutils::Status;
+using android::netdutils::StatusOr;
using android::netdutils::UniqueFile;
namespace {
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index 4e1887f..7629c9a 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -1058,11 +1058,7 @@
}
FirewallType firewallType = parseFirewallType(argv[2]);
- int res = gCtls->firewallCtrl.enableFirewall(firewallType);
- return sendGenericOkFail(cli, res);
- }
- if (!strcmp(argv[1], "disable")) {
- int res = gCtls->firewallCtrl.disableFirewall();
+ int res = gCtls->firewallCtrl.setFirewallType(firewallType);
return sendGenericOkFail(cli, res);
}
if (!strcmp(argv[1], "is_enabled")) {
diff --git a/server/FirewallController.cpp b/server/FirewallController.cpp
index 4e5e3e5..890ab5b 100644
--- a/server/FirewallController.cpp
+++ b/server/FirewallController.cpp
@@ -59,6 +59,9 @@
} // namespace
+namespace android {
+namespace net {
+
auto FirewallController::execIptablesRestore = ::execIptablesRestore;
const char* FirewallController::TABLE = "filter";
@@ -101,11 +104,11 @@
return res;
}
-int FirewallController::enableFirewall(FirewallType ftype) {
+int FirewallController::setFirewallType(FirewallType ftype) {
int res = 0;
if (mFirewallType != ftype) {
// flush any existing rules
- disableFirewall();
+ resetFirewall();
if (ftype == WHITELIST) {
// create default rule to drop all traffic
@@ -121,10 +124,10 @@
// Set this after calling disableFirewall(), since it defaults to WHITELIST there
mFirewallType = ftype;
}
- return res;
+ return res ? -EREMOTEIO : 0;
}
-int FirewallController::disableFirewall(void) {
+int FirewallController::resetFirewall(void) {
mFirewallType = WHITELIST;
mIfaceRules.clear();
@@ -136,7 +139,7 @@
":fw_FORWARD -\n"
"COMMIT\n";
- return execIptablesRestore(V4V6, command.c_str());
+ return (execIptablesRestore(V4V6, command.c_str()) == 0) ? 0 : -EREMOTEIO;
}
int FirewallController::enableChildChains(ChildChain chain, bool enable) {
@@ -177,12 +180,12 @@
int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
if (mFirewallType == BLACKLIST) {
// Unsupported in BLACKLIST mode
- return -1;
+ return -EINVAL;
}
if (!isIfaceName(iface)) {
errno = ENOENT;
- return -1;
+ return -ENOENT;
}
// Only delete rules if we actually added them, because otherwise our iptables-restore
@@ -205,7 +208,7 @@
StringPrintf("%s fw_OUTPUT -o %s -j RETURN", op, iface),
"COMMIT\n"
}, "\n");
- return execIptablesRestore(V4V6, command);
+ return (execIptablesRestore(V4V6, command) == 0) ? 0 : -EREMOTEIO;
}
FirewallType FirewallController::getFirewallType(ChildChain chain) {
@@ -253,7 +256,7 @@
break;
default:
ALOGW("Unknown child chain: %d", chain);
- return -1;
+ return -EINVAL;
}
if (mUseBpfOwnerMatch) {
return gCtls->trafficCtrl.changeUidOwnerRule(chain, uid, rule, firewallType);
@@ -266,7 +269,7 @@
}
StringAppendF(&command, "COMMIT\n");
- return execIptablesRestore(V4V6, command);
+ return (execIptablesRestore(V4V6, command) == 0) ? 0 : -EREMOTEIO;
}
int FirewallController::createChain(const char* chain, FirewallType type) {
@@ -393,3 +396,6 @@
return maxUid;
}
+
+} // namespace net
+} // namespace android
\ No newline at end of file
diff --git a/server/FirewallController.h b/server/FirewallController.h
index c43e94e..a7082da 100644
--- a/server/FirewallController.h
+++ b/server/FirewallController.h
@@ -23,16 +23,27 @@
#include <string>
#include <vector>
+#include "android/net/INetd.h"
+
#include "NetdConstants.h"
-enum FirewallRule { DENY, ALLOW };
+namespace android {
+namespace net {
+
+enum FirewallRule { ALLOW = INetd::FIREWALL_RULE_ALLOW, DENY = INetd::FIREWALL_RULE_DENY };
// WHITELIST means the firewall denies all by default, uids must be explicitly ALLOWed
// BLACKLIST means the firewall allows all by default, uids must be explicitly DENYed
-enum FirewallType { WHITELIST, BLACKLIST };
+enum FirewallType { WHITELIST = INetd::FIREWALL_WHITELIST, BLACKLIST = INetd::FIREWALL_BLACKLIST };
-enum ChildChain { NONE, DOZABLE, STANDBY, POWERSAVE, INVALID_CHAIN };
+enum ChildChain {
+ NONE = INetd::FIREWALL_CHAIN_NONE,
+ DOZABLE = INetd::FIREWALL_CHAIN_DOZABLE,
+ STANDBY = INetd::FIREWALL_CHAIN_STANDBY,
+ POWERSAVE = INetd::FIREWALL_CHAIN_POWERSAVE,
+ INVALID_CHAIN
+};
/*
* Simple firewall that drops all packets except those matching explicitly
@@ -48,8 +59,8 @@
int setupIptablesHooks(void);
- int enableFirewall(FirewallType);
- int disableFirewall(void);
+ int setFirewallType(FirewallType);
+ int resetFirewall(void);
int isFirewallEnabled(void);
/* Match traffic going in/out over the given iface. */
@@ -100,4 +111,7 @@
FirewallType getFirewallType(ChildChain);
};
+} // namespace net
+} // namespace android
+
#endif
diff --git a/server/FirewallControllerTest.cpp b/server/FirewallControllerTest.cpp
index 240c85d..836adb8 100644
--- a/server/FirewallControllerTest.cpp
+++ b/server/FirewallControllerTest.cpp
@@ -33,6 +33,9 @@
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+namespace android {
+namespace net {
+
class FirewallControllerTest : public IptablesBaseTest {
protected:
FirewallControllerTest() {
@@ -51,7 +54,6 @@
}
};
-
TEST_F(FirewallControllerTest, TestCreateWhitelistChain) {
std::vector<std::string> expectedRestore4 = {
"*filter",
@@ -241,16 +243,16 @@
};
std::vector<std::string> noCommands = {};
- EXPECT_EQ(0, mFw.disableFirewall());
+ EXPECT_EQ(0, mFw.resetFirewall());
expectIptablesRestoreCommands(disableCommands);
- EXPECT_EQ(0, mFw.disableFirewall());
+ EXPECT_EQ(0, mFw.resetFirewall());
expectIptablesRestoreCommands(disableCommands);
- EXPECT_EQ(0, mFw.enableFirewall(BLACKLIST));
+ EXPECT_EQ(0, mFw.setFirewallType(BLACKLIST));
expectIptablesRestoreCommands(disableCommands);
- EXPECT_EQ(0, mFw.enableFirewall(BLACKLIST));
+ EXPECT_EQ(0, mFw.setFirewallType(BLACKLIST));
expectIptablesRestoreCommands(noCommands);
std::vector<std::string> disableEnableCommands;
@@ -259,7 +261,7 @@
disableEnableCommands.insert(
disableEnableCommands.end(), enableCommands.begin(), enableCommands.end());
- EXPECT_EQ(0, mFw.enableFirewall(WHITELIST));
+ EXPECT_EQ(0, mFw.setFirewallType(WHITELIST));
expectIptablesRestoreCommands(disableEnableCommands);
std::vector<std::string> ifaceCommands = {
@@ -286,15 +288,15 @@
EXPECT_EQ(0, mFw.setInterfaceRule("rmnet_data0", DENY));
expectIptablesRestoreCommands(noCommands);
- EXPECT_EQ(0, mFw.enableFirewall(WHITELIST));
+ EXPECT_EQ(0, mFw.setFirewallType(WHITELIST));
expectIptablesRestoreCommands(noCommands);
- EXPECT_EQ(0, mFw.disableFirewall());
+ EXPECT_EQ(0, mFw.resetFirewall());
expectIptablesRestoreCommands(disableCommands);
- // TODO: calling disableFirewall and then enableFirewall(WHITELIST) does
+ // TODO: calling resetFirewall and then setFirewallType(WHITELIST) does
// nothing. This seems like a clear bug.
- EXPECT_EQ(0, mFw.enableFirewall(WHITELIST));
+ EXPECT_EQ(0, mFw.setFirewallType(WHITELIST));
expectIptablesRestoreCommands(noCommands);
}
@@ -344,3 +346,6 @@
EXPECT_NE(0, access(tempFile.c_str(), F_OK));
EXPECT_EQ(4294967294, FirewallController::discoverMaximumValidUid(tempFile));
}
+
+} // namespace net
+} // namespace android
\ No newline at end of file
diff --git a/server/InterfaceController.cpp b/server/InterfaceController.cpp
index 7006fee..fde7f4f 100644
--- a/server/InterfaceController.cpp
+++ b/server/InterfaceController.cpp
@@ -21,7 +21,6 @@
#include <sys/socket.h>
#include <functional>
-
#define LOG_TAG "InterfaceController"
#include <android-base/file.h>
#include <android-base/properties.h>
diff --git a/server/NetdHwService.h b/server/NetdHwService.h
index a814fe4..458c6fa 100644
--- a/server/NetdHwService.h
+++ b/server/NetdHwService.h
@@ -19,16 +19,16 @@
#include <android/system/net/netd/1.1/INetd.h>
-using android::hardware::Return;
-using android::hardware::hidl_string;
-using android::system::net::netd::V1_1::INetd;
-using StatusCode = android::system::net::netd::V1_1::INetd::StatusCode;
-
namespace android {
namespace net {
-class NetdHwService : public INetd {
-public:
+using android::hardware::Return;
+using android::hardware::hidl_string;
+using INetdHw = android::system::net::netd::V1_1::INetd;
+using StatusCode = android::system::net::netd::V1_1::INetd::StatusCode;
+
+class NetdHwService : INetdHw {
+ public:
// 1.0
status_t start();
Return<void> createOemNetwork(createOemNetwork_cb _hidl_cb) override;
diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp
index 09b7efc..1e993c1 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -959,6 +959,7 @@
gLog.log(entry.returns(res).withAutomaticDuration());
return statusFromErrcode(res);
}
+
binder::Status NetdNativeService::clatdStart(const std::string& ifName) {
NETD_LOCKING_RPC(NETWORK_STACK, gCtls->clatdCtrl.mutex);
auto entry = gLog.newEntry().prettyFunction(__PRETTY_FUNCTION__).arg(ifName);
@@ -1259,5 +1260,95 @@
return binder::Status::ok();
}
+namespace {
+std::string ruleToString(int32_t rule) {
+ switch (rule) {
+ case INetd::FIREWALL_RULE_DENY:
+ return "DENY";
+ case INetd::FIREWALL_RULE_ALLOW:
+ return "ALLOW";
+ default:
+ return "INVALID";
+ }
+}
+
+std::string typeToString(int32_t type) {
+ switch (type) {
+ case INetd::FIREWALL_WHITELIST:
+ return "WHITELIST";
+ case INetd::FIREWALL_BLACKLIST:
+ return "BLACKLIST";
+ default:
+ return "INVALID";
+ }
+}
+
+std::string chainToString(int32_t chain) {
+ switch (chain) {
+ case INetd::FIREWALL_CHAIN_NONE:
+ return "NONE";
+ case INetd::FIREWALL_CHAIN_DOZABLE:
+ return "DOZABLE";
+ case INetd::FIREWALL_CHAIN_STANDBY:
+ return "STANDBY";
+ case INetd::FIREWALL_CHAIN_POWERSAVE:
+ return "POWERSAVE";
+ default:
+ return "INVALID";
+ }
+}
+
+} // namespace
+
+binder::Status NetdNativeService::firewallSetFirewallType(int32_t firewallType) {
+ NETD_LOCKING_RPC(NETWORK_STACK, gCtls->firewallCtrl.lock);
+ auto entry =
+ gLog.newEntry().prettyFunction(__PRETTY_FUNCTION__).arg(typeToString(firewallType));
+ auto type = static_cast<FirewallType>(firewallType);
+
+ int res = gCtls->firewallCtrl.setFirewallType(type);
+ gLog.log(entry.returns(res).withAutomaticDuration());
+ return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::firewallSetInterfaceRule(const std::string& ifName,
+ int32_t firewallRule) {
+ NETD_LOCKING_RPC(NETWORK_STACK, gCtls->firewallCtrl.lock);
+ auto entry = gLog.newEntry()
+ .prettyFunction(__PRETTY_FUNCTION__)
+ .args(ifName, ruleToString(firewallRule));
+ auto rule = static_cast<FirewallRule>(firewallRule);
+
+ int res = gCtls->firewallCtrl.setInterfaceRule(ifName.c_str(), rule);
+ gLog.log(entry.returns(res).withAutomaticDuration());
+ return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::firewallSetUidRule(int32_t childChain, int32_t uid,
+ int32_t firewallRule) {
+ NETD_LOCKING_RPC(NETWORK_STACK, gCtls->firewallCtrl.lock);
+ auto entry = gLog.newEntry()
+ .prettyFunction(__PRETTY_FUNCTION__)
+ .args(chainToString(childChain), uid, ruleToString(firewallRule));
+ auto chain = static_cast<ChildChain>(childChain);
+ auto rule = static_cast<FirewallRule>(firewallRule);
+
+ int res = gCtls->firewallCtrl.setUidRule(chain, uid, rule);
+ gLog.log(entry.returns(res).withAutomaticDuration());
+ return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::firewallEnableChildChain(int32_t childChain, bool enable) {
+ NETD_LOCKING_RPC(NETWORK_STACK, gCtls->firewallCtrl.lock);
+ auto entry = gLog.newEntry()
+ .prettyFunction(__PRETTY_FUNCTION__)
+ .args(chainToString(childChain), enable);
+ auto chain = static_cast<ChildChain>(childChain);
+
+ int res = gCtls->firewallCtrl.enableChildChains(chain, enable);
+ gLog.log(entry.returns(res).withAutomaticDuration());
+ return statusFromErrcode(res);
+}
+
} // namespace net
} // namespace android
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index 1d5caf6..77179cd 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -40,6 +40,12 @@
binder::Status firewallReplaceUidChain(
const std::string& chainName, bool isWhitelist,
const std::vector<int32_t>& uids, bool *ret) override;
+ binder::Status firewallSetFirewallType(int32_t firewallType) override;
+ binder::Status firewallSetInterfaceRule(const std::string& ifName,
+ int32_t firewallRule) override;
+ binder::Status firewallSetUidRule(int32_t childChain, int32_t uid,
+ int32_t firewallRule) override;
+ binder::Status firewallEnableChildChain(int32_t childChain, bool enable) override;
// Bandwidth control commands.
binder::Status bandwidthEnableDataSaver(bool enable, bool *ret) override;
@@ -249,6 +255,8 @@
private:
std::vector<uid_t> intsToUids(const std::vector<int32_t>& intUids);
Permission convertPermission(int32_t permission);
+ static FirewallRule parseRule(int32_t firewallRule);
+ static ChildChain parseChildChain(int32_t childChain);
};
} // namespace net
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index 19a3e40..395d706 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -996,4 +996,66 @@
* @return true if the user can protect sockets from VPN, false otherwise.
*/
boolean networkCanProtect(int uid);
+
+ // Whitelist only allows packets from specific UID/Interface
+ const int FIREWALL_WHITELIST = 0;
+ // Blacklist blocks packets from specific UID/Interface
+ const int FIREWALL_BLACKLIST = 1;
+
+ /**
+ * Set type of firewall
+ * Type whitelist only allows packets from specific UID/Interface
+ * Type blacklist blocks packets from specific UID/Interface
+ *
+ * @param firewalltype type of firewall, either FIREWALL_WHITELIST or FIREWALL_BLACKLIST
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the the failure.
+ */
+ void firewallSetFirewallType(int firewalltype);
+
+ // Specify allow Rule which allows packets
+ const int FIREWALL_RULE_ALLOW = 1;
+ // Specify deny Rule which drops packets
+ const int FIREWALL_RULE_DENY = 2;
+
+ // No specific chain is chosen, use general firewall chain(fw_input, fw_output)
+ const int FIREWALL_CHAIN_NONE = 0;
+ // Specify DOZABLE chain(fw_dozable) which is used in dozable mode
+ const int FIREWALL_CHAIN_DOZABLE = 1;
+ // Specify STANDBY chain(fw_standby) which is used in standby mode
+ const int FIREWALL_CHAIN_STANDBY = 2;
+ // Specify POWERSAVE chain(fw_powersave) which is used in power save mode
+ const int FIREWALL_CHAIN_POWERSAVE = 3;
+
+ /**
+ * Set firewall rule for interface
+ *
+ * @param ifName the interface to allow/deny
+ * @param firewallRule either FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the the failure.
+ */
+ void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule);
+
+ /**
+ * Set firewall rule for uid
+ *
+ * @param childChain target chain
+ * @param uid uid to allow/deny
+ * @param firewallRule either FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the the failure.
+ */
+ void firewallSetUidRule(int childChain, int uid, int firewallRule);
+
+ /**
+ * Enable/Disable target firewall child chain
+ *
+ * @param childChain target chain to enable
+ * @param enable whether to enable or disable child chain.
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the the failure.
+ */
+ void firewallEnableChildChain(int childChain, boolean enable);
+
}
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 421af95..a6a9257 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -1997,4 +1997,327 @@
status = mNetd->tetherDnsList(&dnsList);
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
expectTetherDnsListEquals(dnsList, testDnsAddrs);
-}
\ No newline at end of file
+}
+
+namespace {
+
+constexpr char FIREWALL_INPUT[] = "fw_INPUT";
+constexpr char FIREWALL_OUTPUT[] = "fw_OUTPUT";
+constexpr char FIREWALL_FORWARD[] = "fw_FORWARD";
+constexpr char FIREWALL_DOZABLE[] = "fw_dozable";
+constexpr char FIREWALL_POWERSAVE[] = "fw_powersave";
+constexpr char FIREWALL_STANDBY[] = "fw_standby";
+constexpr char targetReturn[] = "RETURN";
+constexpr char targetDrop[] = "DROP";
+
+void expectFirewallWhitelistMode() {
+ static const char dropRule[] = "DROP all";
+ static const char rejectRule[] = "REJECT all";
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
+ EXPECT_TRUE(iptablesRuleExists(binary, FIREWALL_INPUT, dropRule));
+ EXPECT_TRUE(iptablesRuleExists(binary, FIREWALL_OUTPUT, rejectRule));
+ EXPECT_TRUE(iptablesRuleExists(binary, FIREWALL_FORWARD, rejectRule));
+ }
+}
+
+void expectFirewallBlacklistMode() {
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
+ EXPECT_EQ(2, iptablesRuleLineLength(binary, FIREWALL_INPUT));
+ EXPECT_EQ(2, iptablesRuleLineLength(binary, FIREWALL_OUTPUT));
+ EXPECT_EQ(2, iptablesRuleLineLength(binary, FIREWALL_FORWARD));
+ }
+}
+
+bool iptablesFirewallInterfaceFirstRuleExists(const char* binary, const char* chainName,
+ const std::string& expectedInterface,
+ const std::string& expectedRule) {
+ std::vector<std::string> rules = listIptablesRuleByTable(binary, FILTER_TABLE, chainName);
+ // Expected rule:
+ // Chain fw_INPUT (1 references)
+ // pkts bytes target prot opt in out source destination
+ // 0 0 RETURN all -- expectedInterface * 0.0.0.0/0 0.0.0.0/0
+ // 0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
+ int firstRuleIndex = 2;
+ if (rules.size() < 4) return false;
+ if (rules[firstRuleIndex].find(expectedInterface) != std::string::npos) {
+ if (rules[firstRuleIndex].find(expectedRule) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// TODO: It is a duplicate function, need to remove it
+bool iptablesFirewallInterfaceRuleExists(const char* binary, const char* chainName,
+ const std::string& expectedInterface,
+ const std::string& expectedRule) {
+ std::vector<std::string> rules = listIptablesRuleByTable(binary, FILTER_TABLE, chainName);
+ for (const auto& rule : rules) {
+ if (rule.find(expectedInterface) != std::string::npos) {
+ if (rule.find(expectedRule) != std::string::npos) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void expectFirewallInterfaceRuleAllowExists(const std::string& ifname) {
+ static const char returnRule[] = "RETURN all";
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
+ EXPECT_TRUE(iptablesFirewallInterfaceFirstRuleExists(binary, FIREWALL_INPUT, ifname,
+ returnRule));
+ EXPECT_TRUE(iptablesFirewallInterfaceFirstRuleExists(binary, FIREWALL_OUTPUT, ifname,
+ returnRule));
+ }
+}
+
+void expectFireWallInterfaceRuleAllowDoesNotExist(const std::string& ifname) {
+ static const char returnRule[] = "RETURN all";
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
+ EXPECT_FALSE(
+ iptablesFirewallInterfaceRuleExists(binary, FIREWALL_INPUT, ifname, returnRule));
+ EXPECT_FALSE(
+ iptablesFirewallInterfaceRuleExists(binary, FIREWALL_OUTPUT, ifname, returnRule));
+ }
+}
+
+bool iptablesFirewallUidFirstRuleExists(const char* binary, const char* chainName,
+ const std::string& expectedTarget,
+ const std::string& expectedRule) {
+ std::vector<std::string> rules = listIptablesRuleByTable(binary, FILTER_TABLE, chainName);
+ int firstRuleIndex = 2;
+ if (rules.size() < 4) return false;
+ if (rules[firstRuleIndex].find(expectedTarget) != std::string::npos) {
+ if (rules[firstRuleIndex].find(expectedRule) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool iptablesFirewallUidLastRuleExists(const char* binary, const char* chainName,
+ const std::string& expectedTarget,
+ const std::string& expectedRule) {
+ std::vector<std::string> rules = listIptablesRuleByTable(binary, FILTER_TABLE, chainName);
+ int lastRuleIndex = rules.size() - 1;
+ if (lastRuleIndex < 0) return false;
+ if (rules[lastRuleIndex].find(expectedTarget) != std::string::npos) {
+ if (rules[lastRuleIndex].find(expectedRule) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void expectFirewallUidFirstRuleExists(const char* chainName, int32_t uid) {
+ std::string uidRule = StringPrintf("owner UID match %u", uid);
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH})
+ EXPECT_TRUE(iptablesFirewallUidFirstRuleExists(binary, chainName, targetReturn, uidRule));
+}
+
+void expectFirewallUidFirstRuleDoesNotExist(const char* chainName, int32_t uid) {
+ std::string uidRule = StringPrintf("owner UID match %u", uid);
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH})
+ EXPECT_FALSE(iptablesFirewallUidFirstRuleExists(binary, chainName, targetReturn, uidRule));
+}
+
+void expectFirewallUidLastRuleExists(const char* chainName, int32_t uid) {
+ std::string uidRule = StringPrintf("owner UID match %u", uid);
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH})
+ EXPECT_TRUE(iptablesFirewallUidLastRuleExists(binary, chainName, targetDrop, uidRule));
+}
+
+void expectFirewallUidLastRuleDoesNotExist(const char* chainName, int32_t uid) {
+ std::string uidRule = StringPrintf("owner UID match %u", uid);
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH})
+ EXPECT_FALSE(iptablesFirewallUidLastRuleExists(binary, chainName, targetDrop, uidRule));
+}
+
+bool iptablesFirewallChildChainsLastRuleExists(const char* binary, const char* chainName) {
+ std::vector<std::string> inputRules =
+ listIptablesRuleByTable(binary, FILTER_TABLE, FIREWALL_INPUT);
+ std::vector<std::string> outputRules =
+ listIptablesRuleByTable(binary, FILTER_TABLE, FIREWALL_OUTPUT);
+ int inputLastRuleIndex = inputRules.size() - 1;
+ int outputLastRuleIndex = outputRules.size() - 1;
+
+ if (inputLastRuleIndex < 0 || outputLastRuleIndex < 0) return false;
+ if (inputRules[inputLastRuleIndex].find(chainName) != std::string::npos) {
+ if (outputRules[outputLastRuleIndex].find(chainName) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void expectFirewallChildChainsLastRuleExists(const char* chainRule) {
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH})
+ EXPECT_TRUE(iptablesFirewallChildChainsLastRuleExists(binary, chainRule));
+}
+
+void expectFirewallChildChainsLastRuleDoesNotExist(const char* chainRule) {
+ for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
+ EXPECT_FALSE(iptablesRuleExists(binary, FIREWALL_INPUT, chainRule));
+ EXPECT_FALSE(iptablesRuleExists(binary, FIREWALL_OUTPUT, chainRule));
+ }
+}
+
+} // namespace
+
+TEST_F(BinderTest, FirewallSetFirewallType) {
+ binder::Status status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallWhitelistMode();
+
+ status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallBlacklistMode();
+
+ // set firewall type blacklist twice
+ mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST);
+ status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallBlacklistMode();
+
+ // set firewall type whitelist twice
+ mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST);
+ status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallWhitelistMode();
+
+ // reset firewall type to default
+ status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallBlacklistMode();
+}
+
+TEST_F(BinderTest, FirewallSetInterfaceRule) {
+ // setinterfaceRule is not supported in BLACKLIST MODE
+ binder::Status status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ status = mNetd->firewallSetInterfaceRule(sTun.name(), INetd::FIREWALL_RULE_ALLOW);
+ EXPECT_FALSE(status.isOk()) << status.exceptionMessage();
+
+ // set WHITELIST mode first
+ status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+ status = mNetd->firewallSetInterfaceRule(sTun.name(), INetd::FIREWALL_RULE_ALLOW);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallInterfaceRuleAllowExists(sTun.name());
+
+ status = mNetd->firewallSetInterfaceRule(sTun.name(), INetd::FIREWALL_RULE_DENY);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFireWallInterfaceRuleAllowDoesNotExist(sTun.name());
+
+ // reset firewall mode to default
+ status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallBlacklistMode();
+}
+
+TEST_F(BinderTest, FirewallSetUidRule) {
+ SKIP_IF_BPF_SUPPORTED;
+
+ int32_t uid = randomUid();
+
+ // Doze allow
+ binder::Status status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_DOZABLE, uid,
+ INetd::FIREWALL_RULE_ALLOW);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidFirstRuleExists(FIREWALL_DOZABLE, uid);
+
+ // Doze deny
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_DOZABLE, uid,
+ INetd::FIREWALL_RULE_DENY);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidFirstRuleDoesNotExist(FIREWALL_DOZABLE, uid);
+
+ // Powersave allow
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_POWERSAVE, uid,
+ INetd::FIREWALL_RULE_ALLOW);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidFirstRuleExists(FIREWALL_POWERSAVE, uid);
+
+ // Powersave deny
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_POWERSAVE, uid,
+ INetd::FIREWALL_RULE_DENY);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidFirstRuleDoesNotExist(FIREWALL_POWERSAVE, uid);
+
+ // Standby deny
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_STANDBY, uid,
+ INetd::FIREWALL_RULE_DENY);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidLastRuleExists(FIREWALL_STANDBY, uid);
+
+ // Standby allow
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_STANDBY, uid,
+ INetd::FIREWALL_RULE_ALLOW);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidLastRuleDoesNotExist(FIREWALL_STANDBY, uid);
+
+ // None deny in BLACKLIST
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_NONE, uid, INetd::FIREWALL_RULE_DENY);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidLastRuleExists(FIREWALL_INPUT, uid);
+ expectFirewallUidLastRuleExists(FIREWALL_OUTPUT, uid);
+
+ // None allow in BLACKLIST
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_NONE, uid, INetd::FIREWALL_RULE_ALLOW);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidLastRuleDoesNotExist(FIREWALL_INPUT, uid);
+ expectFirewallUidLastRuleDoesNotExist(FIREWALL_OUTPUT, uid);
+
+ // set firewall type whitelist twice
+ status = mNetd->firewallSetFirewallType(INetd::FIREWALL_WHITELIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallWhitelistMode();
+
+ // None allow in WHITELIST
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_NONE, uid, INetd::FIREWALL_RULE_ALLOW);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidFirstRuleExists(FIREWALL_INPUT, uid);
+ expectFirewallUidFirstRuleExists(FIREWALL_OUTPUT, uid);
+
+ // None deny in WHITELIST
+ status = mNetd->firewallSetUidRule(INetd::FIREWALL_CHAIN_NONE, uid, INetd::FIREWALL_RULE_DENY);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallUidFirstRuleDoesNotExist(FIREWALL_INPUT, uid);
+ expectFirewallUidFirstRuleDoesNotExist(FIREWALL_OUTPUT, uid);
+
+ // reset firewall mode to default
+ status = mNetd->firewallSetFirewallType(INetd::FIREWALL_BLACKLIST);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallBlacklistMode();
+}
+
+TEST_F(BinderTest, FirewallEnableDisableChildChains) {
+ SKIP_IF_BPF_SUPPORTED;
+
+ binder::Status status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_DOZABLE, true);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallChildChainsLastRuleExists(FIREWALL_DOZABLE);
+
+ status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_STANDBY, true);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallChildChainsLastRuleExists(FIREWALL_STANDBY);
+
+ status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_POWERSAVE, true);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallChildChainsLastRuleExists(FIREWALL_POWERSAVE);
+
+ status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_DOZABLE, false);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallChildChainsLastRuleDoesNotExist(FIREWALL_DOZABLE);
+
+ status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_STANDBY, false);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallChildChainsLastRuleDoesNotExist(FIREWALL_STANDBY);
+
+ status = mNetd->firewallEnableChildChain(INetd::FIREWALL_CHAIN_POWERSAVE, false);
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ expectFirewallChildChainsLastRuleDoesNotExist(FIREWALL_POWERSAVE);
+}