blob: e9a43cf7d535fba3a5267f5f45aacf87ba28de80 [file] [log] [blame]
// Copyright 2015 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 <algorithm>
#include "mock_iptables.h"
namespace firewalld {
MockIpTables::~MockIpTables() {
PlugAllHoles();
}
int MockIpTables::SetRunInMinijailFailCriterion(
const std::vector<std::string>& keywords, bool repeat,
bool omit_failure) {
match_criteria_.push_back(Criterion{keywords, repeat, omit_failure, 0});
return match_criteria_.size() - 1;
}
int MockIpTables::GetRunInMinijailCriterionMatchCount(int id) {
if (id < 0 || id >= (int) match_criteria_.size())
return -1;
return match_criteria_[id].match_count;
}
bool MockIpTables::MatchAndUpdate(const std::vector<std::string>& argv) {
for (auto& criterion : match_criteria_) {
bool match = true;
// Empty criterion is a catch all -- fail on any RunInMinijail.
for (const std::string& keyword : criterion.keywords) {
match = match &&
(std::find(argv.begin(), argv.end(), keyword) != argv.end());
}
if (match) {
criterion.match_count++;
if (!criterion.repeat) {
match_criteria_.erase(std::remove(match_criteria_.begin(),
match_criteria_.end(),
criterion),
match_criteria_.end());
}
// If not negating, treat as fail criterion,
// otherwise ignore (return false).
return !criterion.omit_failure;
}
}
return false;
}
int MockIpTables::RunInMinijail(const std::vector<std::string>& argv) {
if (MatchAndUpdate(argv))
return 1;
commands_.push_back(argv);
return 0;
}
std::vector<std::string> MockIpTables::GetInverseCommand(
const std::vector<std::string>& argv) {
std::vector<std::string> inverse;
bool isIpTablesCommand = (argv.size() > 0) && argv[0] == kIpTablesPath;
bool isIpCommand = (argv.size() > 0) && argv[0] == kIpPath;
for (const auto& arg : argv) {
if (arg == "-A" && isIpTablesCommand)
inverse.push_back("-D");
else if (arg == "-D" && isIpTablesCommand)
inverse.push_back("-A");
else if (arg == "add" && isIpCommand)
inverse.push_back("delete");
else if (arg == "delete" && isIpCommand)
inverse.push_back("add");
else
inverse.push_back(arg);
}
return inverse;
}
// This check does not enforce ordering. It only checks
// that for each command that adds a rule/mark with
// ip/iptables, there is a later match command that
// deletes that same rule/mark.
bool MockIpTables::CheckCommandsUndone() {
for (std::vector<std::vector<std::string>>::iterator it = commands_.begin();
it != commands_.end(); it++) {
// For each command that adds a rule, check that its inverse
// exists later in the log of commands.
if ((std::find(it->begin(), it->end(), "-A") != it->end()) ||
(std::find(it->begin(), it->end(), "add") != it->end())) {
bool found = false;
std::vector<std::string> inverse = GetInverseCommand(*it);
// If GetInverseCommand returns the same command, then it was
// not an ip/iptables command that added/removed a rule/mark.
if (std::equal(it->begin(), it->end(), inverse.begin()))
continue;
for (auto it_next = it + 1; it_next != commands_.end(); it_next++) {
if (*it_next == inverse) {
found = true;
break;
}
}
if (!found)
return false;
}
}
return true;
}
} // namespace firewalld