blob: 8e7b2ce3f4d7b3ee6c3f98e52d7aff54410da518 [file] [log] [blame]
Jason Jeremy Iman54c046f2020-06-23 23:12:00 +09001// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "patchpanel/mock_firewall.h"
6
7#include <algorithm>
8
9namespace patchpanel {
10
11int MockFirewall::SetRunInMinijailFailCriterion(
12 const std::vector<std::string>& keywords, bool repeat, bool omit_failure) {
13 match_criteria_.push_back(Criterion{keywords, repeat, omit_failure, 0});
14 return match_criteria_.size() - 1;
15}
16
17int MockFirewall::GetRunInMinijailCriterionMatchCount(int id) {
18 if (id < 0 || id >= static_cast<int>(match_criteria_.size()))
19 return -1;
20 return match_criteria_[id].match_count;
21}
22
23bool MockFirewall::MatchAndUpdate(const std::vector<std::string>& argv) {
24 // Make a copy of the container so that we can delete elements while
25 // iterating.
26 auto criteria = match_criteria_;
27 for (auto& criterion : criteria) {
28 bool match = true;
29 // Empty criterion is a catch all -- fail on any RunInMinijail.
30 for (const std::string& keyword : criterion.keywords) {
31 match =
32 match && (std::find(argv.begin(), argv.end(), keyword) != argv.end());
33 }
34 if (match) {
35 criterion.match_count++;
36 if (!criterion.repeat) {
37 match_criteria_.erase(std::remove(match_criteria_.begin(),
38 match_criteria_.end(), criterion),
39 match_criteria_.end());
40 }
41 // If not negating, treat as fail criterion,
42 // otherwise ignore (return false).
43 return !criterion.omit_failure;
44 }
45 }
46 return false;
47}
48
49std::vector<std::string> MockFirewall::GetAllCommands() {
50 std::vector<std::string> commands;
51 commands.reserve(commands_.size());
52 for (const auto& argv : commands_) {
53 std::string joined_argv = "";
54 std::string separator = "";
55 for (const auto& arg : argv) {
56 joined_argv += separator + arg;
57 separator = " ";
58 }
59 commands.push_back(joined_argv);
60 }
61 return commands;
62}
63
64int MockFirewall::RunInMinijail(const std::vector<std::string>& argv) {
65 commands_.push_back(argv);
66 return MatchAndUpdate(argv) ? 1 : 0;
67}
68
69// Returns the inverse of a given command. For an insert or append returns a
70// command to delete that rule, for a deletion returns a command to insert it at
71// the start. Assumes that the inverse of -D is always -I and that -I|--insert
72// is used without index arguments. This holds as of 2019-11-20 but if you start
73// using them you'll need to update this method.
74std::vector<std::string> MockFirewall::GetInverseCommand(
75 const std::vector<std::string>& argv) {
76 std::vector<std::string> inverse;
77 if (argv.size() == 0) {
78 return inverse;
79 }
80
81 bool isIpTablesCommand = argv[0] == kIpTablesPath;
82 for (const auto& arg : argv) {
83 if (isIpTablesCommand && (arg == "-I" || arg == "-A" || arg == "--insert" ||
84 arg == "--append")) {
85 inverse.push_back("-D");
86 } else if (isIpTablesCommand && arg == "-D") {
87 inverse.push_back("-I");
88 } else {
89 inverse.push_back(arg);
90 }
91 }
92 return inverse;
93}
94
95// This check does not enforce ordering. It only checks
96// that for each command that adds a rule/mark with
97// ip/iptables, there is a later match command that
98// deletes that same rule/mark.
99bool MockFirewall::CheckCommandsUndone() {
100 return CountActiveCommands() == 0;
101}
102
103// For each command, if it's an insert or an append it checks if there's a
104// corresponding delete later on, then it returns a count of all rules without
105// deletes. Will skip any rule that's not an append or insert e.g. delete
106// without an insert first will return 0 not -1.
107int MockFirewall::CountActiveCommands() {
108 int count = 0;
109 for (std::vector<std::vector<std::string>>::iterator it = commands_.begin();
110 it != commands_.end(); it++) {
111 // For each command that adds a rule, check that its inverse
112 // exists later in the log of commands.
113 if ((std::find(it->begin(), it->end(), "-A") != it->end()) ||
114 (std::find(it->begin(), it->end(), "--append") != it->end()) ||
115 (std::find(it->begin(), it->end(), "-I") != it->end()) ||
116 (std::find(it->begin(), it->end(), "--insert") != it->end())) {
117 bool found = false;
118 std::vector<std::string> inverse = GetInverseCommand(*it);
119 // If GetInverseCommand returns the same command, then it was
120 // not an ip/iptables command that added/removed a rule/mark.
121 if (std::equal(it->begin(), it->end(), inverse.begin()))
122 continue;
123 for (auto it_next = it + 1; it_next != commands_.end(); it_next++) {
124 if (*it_next == inverse) {
125 found = true;
126 break;
127 }
128 }
129 if (!found)
130 count++;
131 }
132 }
133 return count;
134}
135
136void MockFirewall::ResetStoredCommands() {
137 commands_.clear();
138}
139
140} // namespace patchpanel