// Copyright 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "iptables.h"
#include <gtest/gtest.h>
#include "mock_iptables.h"
namespace {
#if defined(__ANDROID__)
const char kIpTablesPath[] = "/system/bin/iptables";
const char kIp6TablesPath[] = "/system/bin/ip6tables";
#else
const char kIpTablesPath[] = "/sbin/iptables";
const char kIp6TablesPath[] = "/sbin/ip6tables";
#endif // __ANDROID__
} // namespace
namespace firewalld {
using testing::_;
using testing::Return;
class IpTablesTest : public testing::Test {
public:
IpTablesTest() = default;
~IpTablesTest() override = default;
protected:
void SetMockExpectations(MockIpTables* iptables, bool success) {
EXPECT_CALL(*iptables, AddAcceptRule(_, _, _, _))
.WillRepeatedly(Return(success));
EXPECT_CALL(*iptables, DeleteAcceptRule(_, _, _, _))
.WillRepeatedly(Return(success));
}
void SetMockExpectationsPerExecutable(MockIpTables* iptables,
bool ip4_success,
bool ip6_success) {
EXPECT_CALL(*iptables, AddAcceptRule(kIpTablesPath, _, _, _))
.WillRepeatedly(Return(ip4_success));
EXPECT_CALL(*iptables, AddAcceptRule(kIp6TablesPath, _, _, _))
.WillRepeatedly(Return(ip6_success));
EXPECT_CALL(*iptables, DeleteAcceptRule(kIpTablesPath, _, _, _))
.WillRepeatedly(Return(ip4_success));
EXPECT_CALL(*iptables, DeleteAcceptRule(kIp6TablesPath, _, _, _))
.WillRepeatedly(Return(ip6_success));
}
private:
DISALLOW_COPY_AND_ASSIGN(IpTablesTest);
};
TEST_F(IpTablesTest, Port0Fails) {
MockIpTables mock_iptables;
// We should not be adding any rules for port 0.
EXPECT_CALL(mock_iptables, AddAcceptRule(_, _, _, _)).Times(0);
EXPECT_CALL(mock_iptables, DeleteAcceptRule(_, _, _, _)).Times(0);
// Try to punch hole for TCP port 0, port 0 is not a valid port.
EXPECT_FALSE(mock_iptables.PunchTcpHole(0, "iface"));
// Try to punch hole for UDP port 0, port 0 is not a valid port.
EXPECT_FALSE(mock_iptables.PunchUdpHole(0, "iface"));
}
TEST_F(IpTablesTest, ValidInterfaceName) {
MockIpTables mock_iptables;
SetMockExpectations(&mock_iptables, true /* success */);
EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "shortname"));
EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "shortname"));
EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "middle-dash"));
EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "middle-dash"));
EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "middle.dot"));
EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "middle.dot"));
}
TEST_F(IpTablesTest, InvalidInterfaceName) {
MockIpTables mock_iptables;
// We should not be adding any rules for invalid interface names.
EXPECT_CALL(mock_iptables, AddAcceptRule(_, _, _, _)).Times(0);
EXPECT_CALL(mock_iptables, DeleteAcceptRule(_, _, _, _)).Times(0);
EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "reallylonginterfacename"));
EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "with spaces"));
EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "with$ymbols"));
EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "-startdash"));
EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "enddash-"));
EXPECT_FALSE(mock_iptables.PunchTcpHole(80, ".startdot"));
EXPECT_FALSE(mock_iptables.PunchTcpHole(80, "enddot."));
EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "reallylonginterfacename"));
EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "with spaces"));
EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "with$ymbols"));
EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "-startdash"));
EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "enddash-"));
EXPECT_FALSE(mock_iptables.PunchUdpHole(53, ".startdot"));
EXPECT_FALSE(mock_iptables.PunchUdpHole(53, "enddot."));
}
TEST_F(IpTablesTest, PunchTcpHoleSucceeds) {
MockIpTables mock_iptables;
SetMockExpectations(&mock_iptables, true /* success */);
// Punch hole for TCP port 80, should succeed.
EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "iface"));
// Punch again, should still succeed.
EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "iface"));
// Plug the hole, should succeed.
EXPECT_TRUE(mock_iptables.PlugTcpHole(80, "iface"));
}
TEST_F(IpTablesTest, PlugTcpHoleSucceeds) {
MockIpTables mock_iptables;
SetMockExpectations(&mock_iptables, true /* success */);
// Punch hole for TCP port 80, should succeed.
EXPECT_TRUE(mock_iptables.PunchTcpHole(80, "iface"));
// Plug the hole, should succeed.
EXPECT_TRUE(mock_iptables.PlugTcpHole(80, "iface"));
// Plug again, should fail.
EXPECT_FALSE(mock_iptables.PlugTcpHole(80, "iface"));
}
TEST_F(IpTablesTest, PunchUdpHoleSucceeds) {
MockIpTables mock_iptables;
SetMockExpectations(&mock_iptables, true /* success */);
// Punch hole for UDP port 53, should succeed.
EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "iface"));
// Punch again, should still succeed.
EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "iface"));
// Plug the hole, should succeed.
EXPECT_TRUE(mock_iptables.PlugUdpHole(53, "iface"));
}
TEST_F(IpTablesTest, PlugUdpHoleSucceeds) {
MockIpTables mock_iptables;
SetMockExpectations(&mock_iptables, true /* success */);
// Punch hole for UDP port 53, should succeed.
EXPECT_TRUE(mock_iptables.PunchUdpHole(53, "iface"));
// Plug the hole, should succeed.
EXPECT_TRUE(mock_iptables.PlugUdpHole(53, "iface"));
// Plug again, should fail.
EXPECT_FALSE(mock_iptables.PlugUdpHole(53, "iface"));
}
TEST_F(IpTablesTest, PunchTcpHoleFails) {
MockIpTables mock_iptables;
SetMockExpectations(&mock_iptables, false /* success */);
// Punch hole for TCP port 80, should fail.
ASSERT_FALSE(mock_iptables.PunchTcpHole(80, "iface"));
}
TEST_F(IpTablesTest, PunchUdpHoleFails) {
MockIpTables mock_iptables;
SetMockExpectations(&mock_iptables, false /* success */);
// Punch hole for UDP port 53, should fail.
ASSERT_FALSE(mock_iptables.PunchUdpHole(53, "iface"));
}
TEST_F(IpTablesTest, PunchTcpHoleIpv6Fails) {
MockIpTables mock_iptables;
SetMockExpectationsPerExecutable(
&mock_iptables, true /* ip4_success */, false /* ip6_success */);
// Punch hole for TCP port 80, should fail because 'ip6tables' fails.
ASSERT_FALSE(mock_iptables.PunchTcpHole(80, "iface"));
}
TEST_F(IpTablesTest, PunchUdpHoleIpv6Fails) {
MockIpTables mock_iptables;
SetMockExpectationsPerExecutable(
&mock_iptables, true /* ip4_success */, false /* ip6_success */);
// Punch hole for UDP port 53, should fail because 'ip6tables' fails.
ASSERT_FALSE(mock_iptables.PunchUdpHole(53, "iface"));
}
TEST_F(IpTablesTest, ApplyVpnSetupAdd_Success) {
const std::vector<std::string> usernames = {"testuser0", "testuser1"};
const std::string interface = "ifc0";
const bool add = true;
MockIpTables mock_iptables;
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add))
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(usernames[0], add))
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(usernames[1], add))
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add))
.WillOnce(Return(true));
ASSERT_TRUE(
mock_iptables.ApplyVpnSetup(usernames, interface, add));
}
TEST_F(IpTablesTest, ApplyVpnSetupAdd_FailureInUsername) {
const std::vector<std::string> usernames = {"testuser0", "testuser1"};
const std::string interface = "ifc0";
const bool remove = false;
const bool add = true;
MockIpTables mock_iptables;
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables,
ApplyMarkForUserTraffic(usernames[0], add))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables,
ApplyMarkForUserTraffic(usernames[1], add))
.Times(1)
.WillOnce(Return(false));
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, remove))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables,
ApplyMarkForUserTraffic(usernames[0], remove))
.Times(1)
.WillOnce(Return(false));
EXPECT_CALL(mock_iptables,
ApplyMarkForUserTraffic(usernames[1], remove))
.Times(0);
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove))
.Times(1)
.WillOnce(Return(false));
ASSERT_FALSE(
mock_iptables.ApplyVpnSetup(usernames, interface, add));
}
TEST_F(IpTablesTest, ApplyVpnSetupAdd_FailureInMasquerade) {
const std::vector<std::string> usernames = {"testuser0", "testuser1"};
const std::string interface = "ifc0";
const bool remove = false;
const bool add = true;
MockIpTables mock_iptables;
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add))
.Times(1)
.WillOnce(Return(false));
EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, _)).Times(0);
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, remove))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove))
.Times(1)
.WillOnce(Return(true));
ASSERT_FALSE(
mock_iptables.ApplyVpnSetup(usernames, interface, add));
}
TEST_F(IpTablesTest, ApplyVpnSetupAdd_FailureInRuleForUserTraffic) {
const std::vector<std::string> usernames = {"testuser0", "testuser1"};
const std::string interface = "ifc0";
const bool remove = false;
const bool add = true;
MockIpTables mock_iptables;
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, _)).Times(0);
EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, _)).Times(0);
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add))
.Times(1)
.WillOnce(Return(false));
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove)).Times(1);
ASSERT_FALSE(mock_iptables.ApplyVpnSetup(usernames, interface, add));
}
TEST_F(IpTablesTest, ApplyVpnSetupRemove_Success) {
const std::vector<std::string> usernames = {"testuser0", "testuser1"};
const std::string interface = "ifc0";
const bool remove = false;
const bool add = true;
MockIpTables mock_iptables;
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, remove))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, remove))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add)).Times(0);
EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, add)).Times(0);
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add)).Times(0);
ASSERT_TRUE(mock_iptables.ApplyVpnSetup(usernames, interface, remove));
}
TEST_F(IpTablesTest, ApplyVpnSetupRemove_Failure) {
const std::vector<std::string> usernames = {"testuser0", "testuser1"};
const std::string interface = "ifc0";
const bool remove = false;
const bool add = true;
MockIpTables mock_iptables;
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, remove))
.Times(1)
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, remove))
.Times(2)
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(remove))
.Times(1)
.WillRepeatedly(Return(false));
EXPECT_CALL(mock_iptables, ApplyMasquerade(interface, add)).Times(0);
EXPECT_CALL(mock_iptables, ApplyMarkForUserTraffic(_, add)).Times(0);
EXPECT_CALL(mock_iptables, ApplyRuleForUserTraffic(add)).Times(0);
ASSERT_FALSE(mock_iptables.ApplyVpnSetup(usernames, interface, remove));
}
} // namespace firewalld