Skip to content

Commit 0d21175

Browse files
committed
pcn-firewall: dynamic pipeline reordering
this commit introduces support for pipeline reordering. in case some filtering pipeline blocks can early break the pipeline, those modules are injected before others. Signed-off-by: Matteo Bertrone <m.bertrone@gmail.com>
1 parent e1c7af1 commit 0d21175

3 files changed

Lines changed: 266 additions & 231 deletions

File tree

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

Lines changed: 163 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -208,164 +208,177 @@ void Chain::updateChain() {
208208
int startingIndex = index;
209209
Firewall::Program *firstProgramLoaded;
210210
std::vector<Firewall::Program *> newProgramsChain(3 + ModulesConstants::NR_MODULES + 2);
211-
std::map<uint8_t, std::vector<uint64_t>> states;
212-
std::map<struct IpAddr, std::vector<uint64_t>> ips;
213-
std::map<uint16_t, std::vector<uint64_t>> ports;
214-
std::map<int, std::vector<uint64_t>> protocols;
215-
std::vector<std::vector<uint64_t>> flags;
216-
217-
// Looping through conntrack
218-
conntrack_from_rules_to_map(states, rules_);
219-
if (!states.empty()) {
220-
// At least one rule requires a matching on conntrack, so it can be
221-
// injected.
222-
if (!parent_.isContrackActive()) {
223-
logger()->error(
224-
"[{0}] Conntrack is not active, please remember to activate it.",
225-
parent_.getName());
226-
}
227-
Firewall::ConntrackMatch *conntrack =
228-
new Firewall::ConntrackMatch(index, name, this->parent_);
229-
newProgramsChain[ModulesConstants::CONNTRACKMATCH] = conntrack;
230-
// Now the program is loaded, populate it.
231-
conntrack->updateMap(states);
232-
233-
// This check is not really needed here, it will always be the first module
234-
// to be injected
235-
if (index == startingIndex) {
236-
firstProgramLoaded = conntrack;
237-
}
238-
++index;
239-
}
240-
states.clear();
241-
// Done looping through conntrack
242-
243-
// Looping through IP source
244-
ip_from_rules_to_map(SOURCE_TYPE, ips, rules_);
245-
if (!ips.empty()) {
246-
// At least one rule requires a matching on ipsource, so inject
247-
// the module on the first available position
248-
Firewall::IpLookup *iplookup =
249-
new Firewall::IpLookup(index, name, SOURCE_TYPE, this->parent_);
250-
newProgramsChain[ModulesConstants::IPSOURCE] = iplookup;
251-
// If this is the first module, adjust parsing to forward to it.
252-
if (index == startingIndex) {
253-
firstProgramLoaded = iplookup;
211+
std::map<uint8_t, std::vector<uint64_t>> conntrack_map;
212+
std::map<struct IpAddr, std::vector<uint64_t>> ipsrc_map;
213+
std::map<struct IpAddr, std::vector<uint64_t>> ipdst_map;
214+
std::map<uint16_t, std::vector<uint64_t>> portsrc_map;
215+
std::map<uint16_t, std::vector<uint64_t>> portdst_map;
216+
std::map<int, std::vector<uint64_t>> protocol_map;
217+
std::vector<std::vector<uint64_t>> flags_map;
218+
219+
220+
// calculate bitvectors, and check if no wildcard is present.
221+
// if no wildcard is present, we can early break the pipeline.
222+
// so we put modules with _break flags_map, before the others in order
223+
// to maximize probability to early break the pipeline.
224+
bool conntrack_break = conntrackFromRulesToMap(conntrack_map, rules_);
225+
bool ipsrc_break = ipFromRulesToMap(SOURCE_TYPE, ipsrc_map, rules_);
226+
bool ipdst_break = ipFromRulesToMap(DESTINATION_TYPE, ipdst_map, rules_);
227+
bool protocol_break = transportProtoFromRulesToMap(protocol_map, rules_);
228+
bool portsrc_break = portFromRulesToMap(SOURCE_TYPE, portsrc_map, rules_);
229+
bool portdst_break = portFromRulesToMap(DESTINATION_TYPE, portdst_map, rules_);
230+
bool flags_break = flagsFromRulesToMap(flags_map, rules_);
231+
232+
logger()->debug(
233+
"Early break of pipeline conntrack:{0} ipsrc:{1} ipdst:{2} protocol:{3} "
234+
"portstc:{4} portdst:{5} flags_map:{6} ",
235+
conntrack_break, ipsrc_break, ipdst_break, protocol_break, portsrc_break,
236+
portdst_break, flags_break);
237+
238+
// first loop iteration pushes program that could early break the pipeline
239+
// second iteration, push others programs
240+
241+
bool second = false;
242+
243+
for (int j = 0; j < 2; j++) {
244+
if (j == 1)
245+
second = true;
246+
247+
std::cout << "++++Conntrack Map empty " << conntrack_map.empty() << std::endl;
248+
249+
// Looping through conntrack
250+
if (!conntrack_map.empty() && conntrack_break ^ second) {
251+
// At least one rule requires a matching on conntrack, so it can be
252+
// injected.
253+
if (!parent_.isContrackActive()) {
254+
logger()->error(
255+
"[{0}] Conntrack is not active, please remember to activate it.",
256+
parent_.getName());
257+
}
258+
Firewall::ConntrackMatch *conntrack =
259+
new Firewall::ConntrackMatch(index, name, this->parent_);
260+
newProgramsChain[ModulesConstants::CONNTRACKMATCH] = conntrack;
261+
// Now the program is loaded, populate it.
262+
conntrack->updateMap(conntrack_map);
263+
264+
// This check is not really needed here, it will always be the first module
265+
// to be injected
266+
if (index == startingIndex) {
267+
firstProgramLoaded = conntrack;
268+
}
269+
++index;
254270
}
255-
++index;
256-
257-
// Now the program is loaded, populate it.
258-
iplookup->updateMap(ips);
259-
}
260-
ips.clear();
261-
// Done looping through IP source
262-
263-
// Looping through IP destination
264-
ip_from_rules_to_map(DESTINATION_TYPE, ips, rules_);
265-
266-
if (!ips.empty()) {
267-
// At least one rule requires a matching on ipdestination, so inject
268-
// the module on the first available position
269-
Firewall::IpLookup *iplookup =
270-
new Firewall::IpLookup(index, name, DESTINATION_TYPE, this->parent_);
271-
newProgramsChain[ModulesConstants::IPDESTINATION] = iplookup;
272-
// If this is the first module, adjust parsing to forward to it.
273-
if (index == startingIndex) {
274-
firstProgramLoaded = iplookup;
271+
// Done looping through conntrack
272+
273+
// Looping through IP source
274+
if (!ipsrc_map.empty() && (ipsrc_break ^ second)) {
275+
// At least one rule requires a matching on ipsource, so inject
276+
// the module on the first available position
277+
Firewall::IpLookup *iplookup =
278+
new Firewall::IpLookup(index, name, SOURCE_TYPE, this->parent_);
279+
newProgramsChain[ModulesConstants::IPSOURCE] = iplookup;
280+
// If this is the first module, adjust parsing to forward to it.
281+
if (index == startingIndex) {
282+
firstProgramLoaded = iplookup;
283+
}
284+
++index;
285+
286+
// Now the program is loaded, populate it.
287+
iplookup->updateMap(ipsrc_map);
275288
}
276-
++index;
277-
278-
// Now the program is loaded, populate it.
279-
iplookup->updateMap(ips);
280-
}
281-
ips.clear();
282-
// Done looping through IP destination
283-
284-
// Looping through l4 protocol
285-
transportproto_from_rules_to_map(protocols, rules_);
286-
287-
if (!protocols.empty()) {
288-
// At least one rule requires a matching on
289-
// source ports, so inject the module
290-
// on the first available position
291-
Firewall::L4ProtocolLookup *protocollookup =
292-
new Firewall::L4ProtocolLookup(index, name, this->parent_);
293-
newProgramsChain[ModulesConstants::L4PROTO] = protocollookup;
294-
// If this is the first module, adjust parsing to forward to it.
295-
if (index == startingIndex) {
296-
firstProgramLoaded = protocollookup;
289+
// Done looping through IP source
290+
291+
// Looping through IP destination
292+
if (!ipdst_map.empty() && ipdst_break ^ second) {
293+
// At least one rule requires a matching on ipdestination, so inject
294+
// the module on the first available position
295+
Firewall::IpLookup *iplookup =
296+
new Firewall::IpLookup(index, name, DESTINATION_TYPE, this->parent_);
297+
newProgramsChain[ModulesConstants::IPDESTINATION] = iplookup;
298+
// If this is the first module, adjust parsing to forward to it.
299+
if (index == startingIndex) {
300+
firstProgramLoaded = iplookup;
301+
}
302+
++index;
303+
304+
// Now the program is loaded, populate it.
305+
iplookup->updateMap(ipdst_map);
297306
}
298-
++index;
299-
300-
// Now the program is loaded, populate it.
301-
protocollookup->updateMap(protocols);
302-
}
303-
protocols.clear();
304-
// Done looping through l4 protocol
305-
306-
// Looping through source port
307-
port_from_rules_to_map(SOURCE_TYPE, ports, rules_);
308-
309-
if (!ports.empty()) {
310-
// At least one rule requires a matching on source ports,
311-
// so inject the module on the first available position
312-
Firewall::L4PortLookup *portlookup =
313-
new Firewall::L4PortLookup(index, name, SOURCE_TYPE, this->parent_);
314-
newProgramsChain[ModulesConstants::PORTSOURCE] = portlookup;
315-
// If this is the first module, adjust parsing to forward to it.
316-
if (index == startingIndex) {
317-
firstProgramLoaded = portlookup;
307+
// Done looping through IP destination
308+
309+
// Looping through l4 protocol
310+
if (!protocol_map.empty() && protocol_break ^ second) {
311+
// At least one rule requires a matching on
312+
// source port__map, so inject the module
313+
// on the first available position
314+
Firewall::L4ProtocolLookup *protocollookup =
315+
new Firewall::L4ProtocolLookup(index, name, this->parent_);
316+
newProgramsChain[ModulesConstants::L4PROTO] = protocollookup;
317+
// If this is the first module, adjust parsing to forward to it.
318+
if (index == startingIndex) {
319+
firstProgramLoaded = protocollookup;
320+
}
321+
++index;
322+
323+
// Now the program is loaded, populate it.
324+
protocollookup->updateMap(protocol_map);
318325
}
319-
++index;
320-
321-
// Now the program is loaded, populate it.
322-
portlookup->updateMap(ports);
323-
}
324-
ports.clear();
325-
// Done looping through source port
326-
327-
// Looping through destination port
328-
port_from_rules_to_map(DESTINATION_TYPE, ports, rules_);
329-
330-
if (!ports.empty()) {
331-
// At least one rule requires a matching on source ports,
332-
// so inject the module on the first available position
333-
Firewall::L4PortLookup *portlookup =
334-
new Firewall::L4PortLookup(index, name, DESTINATION_TYPE, this->parent_);
335-
newProgramsChain[ModulesConstants::PORTDESTINATION] = portlookup;
336-
// If this is the first module, adjust parsing to forward to it.
337-
if (index == startingIndex) {
338-
firstProgramLoaded = portlookup;
326+
// Done looping through l4 protocol
327+
328+
// Looping through source port
329+
if (!portsrc_map.empty() && portsrc_break ^ second) {
330+
// At least one rule requires a matching on source port__map,
331+
// so inject the module on the first available position
332+
Firewall::L4PortLookup *portlookup =
333+
new Firewall::L4PortLookup(index, name, SOURCE_TYPE, this->parent_, portsrc_map);
334+
newProgramsChain[ModulesConstants::PORTSOURCE] = portlookup;
335+
// If this is the first module, adjust parsing to forward to it.
336+
if (index == startingIndex) {
337+
firstProgramLoaded = portlookup;
338+
}
339+
++index;
340+
341+
// Now the program is loaded, populate it.
342+
portlookup->updateMap(portsrc_map);
339343
}
340-
++index;
341-
342-
// Now the program is loaded, populate it.
343-
portlookup->updateMap(ports);
344-
}
345-
ports.clear();
346-
// Done looping through destination port
347-
348-
// Looping through tcp flags
349-
flags_from_rules_to_map(flags, rules_);
350-
351-
if (!flags.empty()) {
352-
// At least one rule requires a matching on flags,
353-
// so inject the module in the first available position
354-
Firewall::TcpFlagsLookup *tcpflagslookup =
355-
new Firewall::TcpFlagsLookup(index, name, this->parent_);
356-
newProgramsChain[ModulesConstants::TCPFLAGS] = tcpflagslookup;
357-
// If this is the first module, adjust parsing to forward to it.
358-
if (index == startingIndex) {
359-
firstProgramLoaded = tcpflagslookup;
344+
// Done looping through source port
345+
346+
// Looping through destination port
347+
if (!portdst_map.empty() && portdst_break ^ second) {
348+
// At least one rule requires a matching on source port__map,
349+
// so inject the module on the first available position
350+
Firewall::L4PortLookup *portlookup =
351+
new Firewall::L4PortLookup(index, name, DESTINATION_TYPE, this->parent_, portdst_map);
352+
newProgramsChain[ModulesConstants::PORTDESTINATION] = portlookup;
353+
// If this is the first module, adjust parsing to forward to it.
354+
if (index == startingIndex) {
355+
firstProgramLoaded = portlookup;
356+
}
357+
++index;
358+
359+
// Now the program is loaded, populate it.
360+
portlookup->updateMap(portdst_map);
360361
}
361-
++index;
362+
// Done looping through destination port
363+
364+
// Looping through tcp flags_map
365+
if (!flags_map.empty() && flags_break ^ second) {
366+
// At least one rule requires a matching on flags_map,
367+
// so inject the module in the first available position
368+
Firewall::TcpFlagsLookup *tcpflagslookup =
369+
new Firewall::TcpFlagsLookup(index, name, this->parent_);
370+
newProgramsChain[ModulesConstants::TCPFLAGS] = tcpflagslookup;
371+
// If this is the first module, adjust parsing to forward to it.
372+
if (index == startingIndex) {
373+
firstProgramLoaded = tcpflagslookup;
374+
}
375+
++index;
362376

363-
// Now the program is loaded, populate it.
364-
tcpflagslookup->updateMap(flags);
377+
// Now the program is loaded, populate it.
378+
tcpflagslookup->updateMap(flags_map);
379+
}
380+
// Done looping through tcp flags_map
365381
}
366-
flags.clear();
367-
368-
// Done looping through tcp flags
369382

370383
// Adding bitscan
371384
Firewall::BitScan *bitscan =

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

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -100,23 +100,24 @@ class Chain : public ChainBase {
100100
/*Like getRuleList, but without the last (fake) default rule*/
101101
std::vector<std::shared_ptr<ChainRule>> getRealRuleList();
102102

103-
static void ip_from_rules_to_map(
104-
const uint8_t &type, std::map<struct IpAddr, std::vector<uint64_t>> &ips,
105-
const std::vector<std::shared_ptr<ChainRule>> &rules);
103+
static bool ipFromRulesToMap(
104+
const uint8_t &type, std::map<struct IpAddr, std::vector<uint64_t>> &ips,
105+
const std::vector<std::shared_ptr<ChainRule>> &rules);
106106

107-
static void transportproto_from_rules_to_map(
108-
std::map<int, std::vector<uint64_t>> &protocols,
109-
const std::vector<std::shared_ptr<ChainRule>> &rules);
107+
static bool transportProtoFromRulesToMap(
108+
std::map<int, std::vector<uint64_t>> &protocols,
109+
const std::vector<std::shared_ptr<ChainRule>> &rules);
110110

111-
static void port_from_rules_to_map(
112-
const uint8_t &type, std::map<uint16_t, std::vector<uint64_t>> &ports,
113-
const std::vector<std::shared_ptr<ChainRule>> &rules);
111+
static bool portFromRulesToMap(
112+
const uint8_t &type, std::map<uint16_t, std::vector<uint64_t>> &ports,
113+
const std::vector<std::shared_ptr<ChainRule>> &rules);
114114

115-
static void flags_from_rules_to_map(
116-
std::vector<std::vector<uint64_t>> &flags,
117-
const std::vector<std::shared_ptr<ChainRule>> &rules);
115+
static bool flagsFromRulesToMap(
116+
std::vector<std::vector<uint64_t>> &flags,
117+
const std::vector<std::shared_ptr<ChainRule>> &rules);
118+
119+
static bool conntrackFromRulesToMap(
120+
std::map<uint8_t, std::vector<uint64_t>> &statusMap,
121+
const std::vector<std::shared_ptr<ChainRule>> &rules);
118122

119-
static void conntrack_from_rules_to_map(
120-
std::map<uint8_t, std::vector<uint64_t>> &statusMap,
121-
const std::vector<std::shared_ptr<ChainRule>> &rules);
122123
};

0 commit comments

Comments
 (0)