Skip to content

Commit f71203a

Browse files
committed
pcn-firewall: add HORUS optimization
HORUS - Homogeneous RUleset analySis Horus optimization allows to a) offload a group of contiguous rules matching on same field b) match the group of offloaded rules with complexity O(1) - single hashmap lookup c) dynamically adapting to different groups of rules, matching each combination of ipsrc/dst, portsrc/dst, tcpflags d) dynamically check when the optimization is possible according to current ruleset. It means check orthogonality of rules before the offloaded group, respect to the group itself. each pkt received by the program, is looked-up vs the HORUS HASHMAP. hit: -DROP action: drop the packet; -ACCEPT action: goto CTLABELING and CTTABLEUPDATE without going through pipeline miss: -GOTO all pipeline steps Signed-off-by: Matteo Bertrone <m.bertrone@gmail.com>
1 parent 32c87a7 commit f71203a

26 files changed

Lines changed: 860 additions & 41 deletions

src/services/pcn-firewall/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ load_file_as_variable(pcn-firewall datapaths/Firewall_L4PortLookup_dp.c firewall
4343
load_file_as_variable(pcn-firewall datapaths/Firewall_L4ProtocolLookup_dp.c firewall_code_l4protolookup)
4444
load_file_as_variable(pcn-firewall datapaths/Firewall_Parser_dp.c firewall_code_parser)
4545
load_file_as_variable(pcn-firewall datapaths/Firewall_TcpFlagsLookup_dp.c firewall_code_tcpflagslookup)
46+
load_file_as_variable(pcn-firewall datapaths/Firewall_Horus_dp.c firewall_code_horus)
4647

4748
# load datamodel in a variable
4849
load_file_as_variable(pcn-firewall

src/services/pcn-firewall/src/Chain.cpp

Lines changed: 131 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,18 @@ ChainResetCountersOutputJsonObject Chain::resetCounters() {
138138
ChainResetCountersOutputJsonObject result;
139139
try {
140140
std::vector<Firewall::Program *> *programs;
141+
142+
bool * horus_runtime_enabled_;
143+
bool * horus_swap_;
144+
141145
if (name == ChainNameEnum::INGRESS) {
142146
programs = &parent_.ingress_programs;
147+
horus_runtime_enabled_ = &parent_.horus_runtime_enabled_ingress_;
148+
horus_swap_ = &parent_.horus_swap_ingress_;
143149
} else if (name == ChainNameEnum::EGRESS) {
144-
programs = &parent_.egress_programs;
150+
programs = &parent_.egress_programs;
151+
horus_runtime_enabled_ = &parent_.horus_runtime_enabled_egress_;
152+
horus_swap_ = &parent_.horus_swap_egress_;
145153
} else {
146154
return result;
147155
}
@@ -153,12 +161,25 @@ ChainResetCountersOutputJsonObject Chain::resetCounters() {
153161
auto actionProgram = dynamic_cast<Firewall::ActionLookup *>(
154162
programs->at(ModulesConstants::ACTION));
155163

164+
Firewall::Horus * horusProgram;
165+
166+
if (*horus_runtime_enabled_) {
167+
if (!(*horus_swap_)) {
168+
horusProgram = dynamic_cast<Firewall::Horus *>(programs->at(ModulesConstants::HORUS_INGRESS));
169+
} else {
170+
horusProgram = dynamic_cast<Firewall::Horus *>(programs->at(ModulesConstants::HORUS_INGRESS_SWAP));
171+
}
172+
}
173+
156174
for (auto cr : rules_) {
157175
actionProgram->flushCounters(cr->getId());
176+
if (*horus_runtime_enabled_){
177+
horusProgram->flushCounters(cr->getId());
178+
}
158179
}
159180

160181
dynamic_cast<Firewall::DefaultAction *>(
161-
programs->at(ModulesConstants::DEFAULTACTION))->flushCounters(name);
182+
programs->at(ModulesConstants::DEFAULTACTION))->flushCounters(name);
162183

163184
counters_.clear();
164185

@@ -189,10 +210,17 @@ uint32_t Chain::getNrRules() {
189210

190211
void Chain::updateChain() {
191212
std::vector<Firewall::Program *> *programs;
213+
bool * horus_runtime_enabled_;
214+
bool * horus_swap_;
215+
192216
if (name == ChainNameEnum::INGRESS) {
193217
programs = &parent_.ingress_programs;
218+
horus_runtime_enabled_ = &parent_.horus_runtime_enabled_ingress_;
219+
horus_swap_ = &parent_.horus_swap_ingress_;
194220
} else if (name == ChainNameEnum::EGRESS) {
195221
programs = &parent_.egress_programs;
222+
horus_runtime_enabled_ = &parent_.horus_runtime_enabled_egress_;
223+
horus_swap_ = &parent_.horus_swap_egress_;
196224
} else {
197225
return;
198226
}
@@ -203,11 +231,11 @@ void Chain::updateChain() {
203231
// std::lock_guard<std::mutex> lkBpf(parent_.bpfInjectMutex);
204232
auto start = std::chrono::high_resolution_clock::now();
205233

206-
int index = 3 + (chainNumber * ModulesConstants::NR_MODULES);
234+
int index = ModulesConstants::NR_INITIAL_MODULES + (chainNumber * ModulesConstants::NR_MODULES);
207235

208236
int startingIndex = index;
209237
Firewall::Program *firstProgramLoaded;
210-
std::vector<Firewall::Program *> newProgramsChain(3 + ModulesConstants::NR_MODULES + 2);
238+
std::vector<Firewall::Program *> newProgramsChain(ModulesConstants::NR_INITIAL_MODULES + ModulesConstants::NR_MODULES + 1);
211239
std::map<uint8_t, std::vector<uint64_t>> conntrack_map;
212240
std::map<struct IpAddr, std::vector<uint64_t>> ipsrc_map;
213241
std::map<struct IpAddr, std::vector<uint64_t>> ipdst_map;
@@ -216,6 +244,105 @@ void Chain::updateChain() {
216244
std::map<int, std::vector<uint64_t>> protocol_map;
217245
std::vector<std::vector<uint64_t>> flags_map;
218246

247+
std::map<struct HorusRule, struct HorusValue> horus;
248+
249+
250+
/*
251+
* HORUS - Homogeneous RUleset analySis
252+
*
253+
* Horus optimization allows to
254+
* a) offload a group of contiguous rules matching on same field
255+
* b) match the group of offloaded rules with complexity O(1) - single hashmap lookup
256+
* c) dynamically adapting to different groups of rules, matching each
257+
* combination of ipsrc/dst, portsrc/dst, tcpflags
258+
* d) dynamically check when the optimization is possible according to current
259+
* ruleset. It means check orthogonality
260+
* of rules before the offloaded group, respect to the group itself.
261+
*
262+
* each pkt received by the program, is looked-up vs the HORUS HASHMAP.
263+
* hit ->
264+
* -DROP action: drop the packet;
265+
* -ACCEPT action: goto CTLABELING and CTTABLEUPDATE without going through pipeline
266+
* miss ->
267+
* -GOTO all pipeline steps
268+
*/
269+
270+
*horus_runtime_enabled_ = false;
271+
272+
// Apply Horus optimization only if it is enabled
273+
if (parent_.horus_enabled) {
274+
// if len Chain >= MIN_RULES_HORUS_OPTIMIZATION
275+
if (getRuleList().size() >= HorusConst::MIN_RULE_SIZE_FOR_HORUS) {
276+
// calculate horus ruleset
277+
horusFromRulesToMap(horus, getRuleList());
278+
279+
// if horus.size() >= MIN_RULES_HORUS_OPTIMIZATION
280+
if (horus.size() >= HorusConst::MIN_RULE_SIZE_FOR_HORUS) {
281+
logger()->info("Horus Optimization ENABLED for this rule-set");
282+
283+
*horus_runtime_enabled_ = true;
284+
285+
// SWAP indexes
286+
*horus_swap_ = !(*horus_swap_);
287+
288+
uint8_t horus_index_new = -1;
289+
uint8_t horus_index_old = -1;
290+
291+
// Apply Horus optimization
292+
293+
// Calculate current new/old indexes
294+
if (*horus_swap_) {
295+
horus_index_new = ModulesConstants::HORUS_INGRESS_SWAP;
296+
horus_index_old = ModulesConstants::HORUS_INGRESS;
297+
} else {
298+
horus_index_old = ModulesConstants::HORUS_INGRESS_SWAP;
299+
horus_index_new = ModulesConstants::HORUS_INGRESS;
300+
}
301+
302+
// Compile and inject program
303+
304+
std::vector<Firewall::Program *> *prog;
305+
306+
if (name == ChainNameEnum::INGRESS) {
307+
prog = &parent_.ingress_programs;
308+
} else if (name == ChainNameEnum::EGRESS) {
309+
prog = &parent_.egress_programs;
310+
} else {
311+
throw std::runtime_error("No ingress/egress chain");
312+
}
313+
314+
Firewall::Horus * horusptr =
315+
new Firewall::Horus(horus_index_new, parent_, name, horus);
316+
prog->at(horus_index_new) = horusptr;
317+
318+
auto horusProgram = dynamic_cast<Firewall::Horus *>(
319+
programs->at(horus_index_new));
320+
321+
horusProgram->updateMap(horus);
322+
323+
auto parserIngress = dynamic_cast<Firewall::Parser *>(programs->at(ModulesConstants::PARSER));
324+
parserIngress->reload();
325+
326+
// Delete old Horus, if present
327+
328+
if (programs->at(horus_index_old) != nullptr) {
329+
delete programs->at(horus_index_old);
330+
}
331+
programs->at(horus_index_old) = nullptr;
332+
}
333+
}
334+
}
335+
if (*horus_runtime_enabled_ == false) {
336+
auto parserIngress = dynamic_cast<Firewall::Parser *>(programs->at(ModulesConstants::PARSER));
337+
parserIngress->reload();
338+
339+
// Delete old Horus, if present
340+
delete programs->at(ModulesConstants::HORUS_INGRESS);
341+
delete programs->at(ModulesConstants::HORUS_INGRESS_SWAP);
342+
programs->at(ModulesConstants::HORUS_INGRESS) = nullptr;
343+
programs->at(ModulesConstants::HORUS_INGRESS_SWAP) = nullptr;
344+
}
345+
219346

220347
// calculate bitvectors, and check if no wildcard is present.
221348
// if no wildcard is present, we can early break the pipeline.

src/services/pcn-firewall/src/Chain.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,11 @@ class Chain : public ChainBase {
120120
std::map<uint8_t, std::vector<uint64_t>> &statusMap,
121121
const std::vector<std::shared_ptr<ChainRule>> &rules);
122122

123+
static void horusFromRulesToMap(
124+
std::map<struct HorusRule, struct HorusValue> &horus,
125+
const std::vector<std::shared_ptr<ChainRule>> &rules);
126+
127+
static bool fromRuleToHorusKeyValue(std::shared_ptr<ChainRule> rule,
128+
struct HorusRule &key,
129+
struct HorusValue &value);
123130
};

src/services/pcn-firewall/src/ChainStats.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,18 @@ void ChainStats::fetchCounters(const Chain &parent, const uint32_t &id,
9797
pkts = 0;
9898
bytes = 0;
9999

100+
bool * horus_runtime_enabled_;
101+
bool * horus_swap_;
102+
100103
std::vector<Firewall::Program *> *programs;
101104
if (parent.name == ChainNameEnum::INGRESS) {
102105
programs = &parent.parent_.ingress_programs;
106+
horus_runtime_enabled_ = &parent.parent_.horus_runtime_enabled_ingress_;
107+
horus_swap_ = &parent.parent_.horus_swap_ingress_;
103108
} else if (parent.name == ChainNameEnum::EGRESS) {
104109
programs = &parent.parent_.egress_programs;
110+
horus_runtime_enabled_ = &parent.parent_.horus_runtime_enabled_egress_;
111+
horus_swap_ = &parent.parent_.horus_swap_egress_;
105112
} else {
106113
return;
107114
}
@@ -116,6 +123,24 @@ void ChainStats::fetchCounters(const Chain &parent, const uint32_t &id,
116123
bytes = actionProgram->getBytesCount(id);
117124
pkts = actionProgram->getPktsCount(id);
118125
actionProgram->flushCounters(id);
126+
127+
if (*horus_runtime_enabled_) {
128+
if (!(*horus_swap_)) {
129+
auto horusProgram = dynamic_cast<Firewall::Horus *>(
130+
programs->at(ModulesConstants::HORUS_INGRESS));
131+
132+
bytes += horusProgram->getBytesCount(id);
133+
pkts += horusProgram->getPktsCount(id);
134+
horusProgram->flushCounters(id);
135+
} else {
136+
auto horusProgram = dynamic_cast<Firewall::Horus *>(
137+
programs->at(ModulesConstants::HORUS_INGRESS_SWAP));
138+
139+
bytes += horusProgram->getBytesCount(id);
140+
pkts += horusProgram->getPktsCount(id);
141+
horusProgram->flushCounters(id);
142+
}
143+
}
119144
}
120145

121146
std::shared_ptr<ChainStats> ChainStats::getDefaultActionCounters(

src/services/pcn-firewall/src/Firewall.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,23 @@ Firewall::Firewall(const std::string name, const FirewallJsonObject &conf)
3030
addChain(ChainNameEnum::EGRESS, chain);
3131
logger()->debug("Ingress and Egress chain added");
3232

33-
ingress_programs.resize(3 + ModulesConstants::NR_MODULES + 2, nullptr);
34-
egress_programs.resize(3 + ModulesConstants::NR_MODULES + 2, nullptr);
33+
ingress_programs.resize(ModulesConstants::NR_INITIAL_MODULES + ModulesConstants::NR_MODULES, nullptr);
34+
egress_programs.resize(ModulesConstants::NR_INITIAL_MODULES + ModulesConstants::NR_MODULES, nullptr);
3535

3636
/* Initialize ingress programs */
3737
ingress_programs[ModulesConstants::PARSER] =
38-
new Firewall::Parser(0, ChainNameEnum::INGRESS, *this);
38+
new Firewall::Parser(ModulesConstants::PARSER, ChainNameEnum::INGRESS, *this);
3939
ingress_programs[ModulesConstants::CONNTRACKLABEL] =
40-
new Firewall::ConntrackLabel(1, ChainNameEnum::INGRESS, *this);
40+
new Firewall::ConntrackLabel(ModulesConstants::CONNTRACKLABEL, ChainNameEnum::INGRESS, *this);
4141
ingress_programs[ModulesConstants::CHAINFORWARDER] =
42-
new Firewall::ChainForwarder(2, ChainNameEnum::INGRESS, *this);
42+
new Firewall::ChainForwarder(ModulesConstants::CHAINFORWARDER, ChainNameEnum::INGRESS, *this);
4343

4444
egress_programs[ModulesConstants::PARSER] =
45-
new Firewall::Parser(0, ChainNameEnum::EGRESS, *this);
45+
new Firewall::Parser(ModulesConstants::PARSER, ChainNameEnum::EGRESS, *this);
4646
egress_programs[ModulesConstants::CONNTRACKLABEL] =
47-
new Firewall::ConntrackLabel(1, ChainNameEnum::EGRESS, *this);
47+
new Firewall::ConntrackLabel(ModulesConstants::CONNTRACKLABEL, ChainNameEnum::EGRESS, *this);
4848
egress_programs[ModulesConstants::CHAINFORWARDER] =
49-
new Firewall::ChainForwarder(2, ChainNameEnum::EGRESS, *this);
49+
new Firewall::ChainForwarder(ModulesConstants::CHAINFORWARDER, ChainNameEnum::EGRESS, *this);
5050

5151
/*
5252
* 3 modules in the beginning
@@ -59,19 +59,19 @@ Firewall::Firewall(const std::string name, const FirewallJsonObject &conf)
5959
*/
6060

6161
ingress_programs[ModulesConstants::DEFAULTACTION] =
62-
new Firewall::DefaultAction(3 + ModulesConstants::NR_MODULES * 2,
62+
new Firewall::DefaultAction(ModulesConstants::DEFAULTACTION,
6363
ChainNameEnum::INGRESS,*this);
6464

6565
ingress_programs[ModulesConstants::CONNTRACKTABLEUPDATE] =
66-
new Firewall::ConntrackTableUpdate(3 + ModulesConstants::NR_MODULES * 2 + 1,
66+
new Firewall::ConntrackTableUpdate(ModulesConstants::CONNTRACKTABLEUPDATE,
6767
ChainNameEnum::INGRESS, *this);
6868

6969
egress_programs[ModulesConstants::DEFAULTACTION] =
70-
new Firewall::DefaultAction(3 + ModulesConstants::NR_MODULES * 2,
70+
new Firewall::DefaultAction(ModulesConstants::DEFAULTACTION,
7171
ChainNameEnum::EGRESS, *this);
7272

7373
egress_programs[ModulesConstants::CONNTRACKTABLEUPDATE] =
74-
new Firewall::ConntrackTableUpdate(3 + ModulesConstants::NR_MODULES * 2 + 1,
74+
new Firewall::ConntrackTableUpdate(ModulesConstants::CONNTRACKTABLEUPDATE,
7575
ChainNameEnum::EGRESS, *this);
7676
}
7777

@@ -126,7 +126,7 @@ FirewallAcceptEstablishedEnum Firewall::getAcceptEstablished() {
126126
void Firewall::setAcceptEstablished(
127127
const FirewallAcceptEstablishedEnum &value) {
128128
if (conntrackMode == ConntrackModes::DISABLED) {
129-
throw new std::runtime_error("Please enable conntrack first.");
129+
throw std::runtime_error("Please enable conntrack first.");
130130
}
131131

132132
if (value == FirewallAcceptEstablishedEnum::ON &&

src/services/pcn-firewall/src/Firewall.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,31 @@ class Firewall : public FirewallBase {
175175
std::string getCode();
176176
};
177177

178-
class ChainForwarder : public Program {
178+
class Horus : public Program {
179+
public:
180+
enum HorusType { HORUS_INGRESS, HORUS_EGRESS };
181+
182+
Horus(const int &index, Firewall &outer, const ChainNameEnum &direction,
183+
const std::map<struct HorusRule, struct HorusValue> &horus);
184+
~Horus();
185+
186+
std::string getCode();
187+
std::string defaultActionString(ChainNameEnum chain); // Overrides
188+
189+
uint64_t getPktsCount(int rule_number);
190+
uint64_t getBytesCount(int rule_number);
191+
192+
void flushCounters(int rule_number);
193+
void updateTableValue(struct HorusRule horus_key,
194+
struct HorusValue horus_value);
195+
void updateMap(const std::map<struct HorusRule, struct HorusValue> &horus);
196+
197+
private:
198+
HorusType type_;
199+
std::map<struct HorusRule, struct HorusValue> horus_;
200+
};
201+
202+
class ChainForwarder : public Program {
179203
public:
180204
ChainForwarder(const int &index, const ChainNameEnum &direction,
181205
Firewall &outer);
@@ -318,6 +342,16 @@ class Firewall : public FirewallBase {
318342
std::vector<Firewall::Program *> ingress_programs;
319343
std::vector<Firewall::Program *> egress_programs;
320344

345+
// HORUS optimization enabled by current rule set
346+
bool horus_runtime_enabled_ingress_ = false;
347+
bool horus_runtime_enabled_egress_ = false;
348+
349+
bool horus_enabled = true;
350+
351+
// are we on swap or regular horus program index
352+
bool horus_swap_ingress_ = false;
353+
bool horus_swap_egress_ = false;
354+
321355
/*==========================
322356
*METHODS DECLARATION
323357
*==========================*/

0 commit comments

Comments
 (0)