1 /*
2
3 Firewall Builder
4
5 Copyright (C) 2007 NetCitadel, LLC
6
7 Author: Vadim Kurland vadim@vk.crocodile.org
8
9 $Id$
10
11 This program is free software which we release under the GNU General Public
12 License. You may redistribute and/or modify this program under the terms
13 of that license as published by the Free Software Foundation; either
14 version 2 of the License, or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 To get a copy of the GNU General Public License, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
24 */
25
26 #include "config.h"
27
28 #include "PolicyCompiler_nxosacl.h"
29 #include "NamedObjectsAndGroupsSupport.h"
30
31 #include "fwbuilder/AddressTable.h"
32 #include "fwbuilder/FWObjectDatabase.h"
33 #include "fwbuilder/ICMPService.h"
34 #include "fwbuilder/IPService.h"
35 #include "fwbuilder/Interface.h"
36 #include "fwbuilder/Library.h"
37 #include "fwbuilder/Management.h"
38 #include "fwbuilder/Network.h"
39 #include "fwbuilder/ObjectMirror.h"
40 #include "fwbuilder/Policy.h"
41 #include "fwbuilder/Resources.h"
42 #include "fwbuilder/RuleElement.h"
43 #include "fwbuilder/TCPService.h"
44 #include "fwbuilder/UDPService.h"
45
46 #include <assert.h>
47
48 using namespace libfwbuilder;
49 using namespace fwcompiler;
50 using namespace std;
51
myPlatformName()52 string PolicyCompiler_nxosacl::myPlatformName() { return "nxosacl"; }
53
PolicyCompiler_nxosacl(FWObjectDatabase * _db,Firewall * fw,bool ipv6_policy,OSConfigurator * _oscnf)54 PolicyCompiler_nxosacl::PolicyCompiler_nxosacl(FWObjectDatabase *_db,
55 Firewall *fw,
56 bool ipv6_policy,
57 OSConfigurator *_oscnf) :
58 PolicyCompiler_cisco(_db, fw, ipv6_policy, _oscnf)
59 {
60 resetinbound = false;
61 fragguard = false;
62 comment_symbol = "!";
63 }
64
prolog()65 int PolicyCompiler_nxosacl::prolog()
66 {
67 string version = fw->getStr("version");
68 string platform = fw->getStr("platform");
69 string host_os = fw->getStr("host_OS");
70
71 if (platform!="nxosacl")
72 abort("Unsupported platform " + platform );
73
74 fw->getOptionsObject()->setBool("generate_out_acl", true);
75
76 fw->getOptionsObject()->setBool(
77 "use_acl_remarks",
78 fw->getOptionsObject()->getBool("nxosacl_use_acl_remarks"));
79
80 // object_groups = new Group();
81 // persistent_objects->add( object_groups );
82
83 setAllNetworkZonesToNone();
84
85 return PolicyCompiler::prolog();
86 }
87
findDynamicInterface(PolicyRule * rule,RuleElement * rel)88 bool PolicyCompiler_nxosacl::checkForDynamicInterface::findDynamicInterface(
89 PolicyRule *rule, RuleElement *rel)
90 {
91 string vers=compiler->fw->getStr("version");
92 for (list<FWObject*>::iterator i1=rel->begin(); i1!=rel->end(); ++i1)
93 {
94 FWObject *o = *i1;
95 FWObject *obj = NULL;
96 if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
97 Interface *iface=Interface::cast(obj);
98 if (iface!=NULL && iface->isDyn())
99 compiler->abort(
100 rule,
101 "Dynamic interface can not be used in the NXOS ACL rules.");
102 }
103
104 return true;
105 }
106
processNext()107 bool PolicyCompiler_nxosacl::checkForDynamicInterface::processNext()
108 {
109 PolicyRule *rule = getNext(); if (rule==NULL) return false;
110
111 findDynamicInterface(rule,rule->getSrc());
112 findDynamicInterface(rule,rule->getDst());
113
114 tmp_queue.push_back(rule);
115 return true;
116 }
117
118 /*
119 * Copy all references from rule element re1 to rule element re2.
120 */
duplicateRuleElement(RuleElement * re1,RuleElement * re2)121 void PolicyCompiler_nxosacl::mirrorRule::duplicateRuleElement(
122 RuleElement *re1, RuleElement *re2)
123 {
124 re2->clearChildren();
125 for (list<FWObject*>::iterator i1=re1->begin(); i1!=re1->end(); ++i1)
126 {
127 FWObject *obj = FWReference::getObject(*i1);
128 re2->addRef(obj);
129 }
130 }
131
processNext()132 bool PolicyCompiler_nxosacl::mirrorRule::processNext()
133 {
134 //PolicyCompiler_nxosacl *nxosacl_comp=dynamic_cast<PolicyCompiler_nxosacl*>(compiler);
135 PolicyRule *rule = getNext(); if (rule==NULL) return false;
136 if (rule->getOptionsObject()->getBool("nxosacl_add_mirror_rule"))
137 {
138 PolicyRule *r= compiler->dbcopy->createPolicyRule();
139 compiler->temp_ruleset->add(r);
140 r->duplicate(rule);
141
142 r->setAction(rule->getAction());
143
144 switch (rule->getDirection())
145 {
146 case PolicyRule::Inbound: r->setDirection(PolicyRule::Outbound); break;
147 case PolicyRule::Outbound: r->setDirection(PolicyRule::Inbound); break;
148 default: r->setDirection(PolicyRule::Both); break;
149 }
150
151 RuleElementSrc *osrc = rule->getSrc();
152 RuleElementDst *odst = rule->getDst();
153 RuleElementSrv *osrv = rule->getSrv();
154 RuleElementItf *oitf = rule->getItf();
155
156 RuleElementSrc *nsrc = r->getSrc();
157 RuleElementDst *ndst = r->getDst();
158 RuleElementSrv *nsrv = r->getSrv();
159 RuleElementItf *nitf = r->getItf();
160
161 duplicateRuleElement(osrc, ndst);
162 duplicateRuleElement(odst, nsrc);
163 duplicateRuleElement(oitf, nitf);
164
165 if (!osrv->isAny())
166 {
167 ObjectMirror mirror;
168 nsrv->clearChildren();
169 for (list<FWObject*>::iterator i1=osrv->begin(); i1!=osrv->end(); ++i1)
170 {
171 Service *nobj = mirror.getMirroredService(
172 Service::cast(FWReference::getObject(*i1)));
173 if (nobj->getParent() == NULL)
174 compiler->persistent_objects->add(nobj, false);
175 nsrv->addRef(nobj);
176 }
177 }
178
179 tmp_queue.push_back(r);
180 }
181 tmp_queue.push_back(rule);
182 return true;
183 }
184
processNext()185 bool PolicyCompiler_nxosacl::SpecialServices::processNext()
186 {
187 //PolicyCompiler_nxosacl *nxosacl_comp=dynamic_cast<PolicyCompiler_nxosacl*>(compiler);
188 PolicyRule *rule=getNext(); if (rule==NULL) return false;
189 Service *s = compiler->getFirstSrv(rule);
190
191 if (IPService::cast(s)!=NULL)
192 {
193 if (s->getBool("rr") ||
194 s->getBool("ssrr") ||
195 s->getBool("ts") )
196 compiler->abort(
197 rule,
198 "NXOS ACL does not support checking for IP options in ACLs.");
199 }
200 if (TCPService::cast(s)!=NULL && TCPService::cast(s)->inspectFlags())
201 {
202 string version = compiler->fw->getStr("version");
203 if (XMLTools::version_compare(version, "12.4")<0)
204 compiler->abort(rule, "TCP flags match requires NXOS v12.4 or later.");
205 }
206
207 tmp_queue.push_back(rule);
208 return true;
209 }
210
211 /*
212 * This rule processor is used to separate TCP service objects that
213 * match tcp flags when generated config uses object-group clause
214 */
processNext()215 bool PolicyCompiler_nxosacl::splitTCPServiceWithFlags::processNext()
216 {
217 PolicyRule *rule=getNext(); if (rule==NULL) return false;
218 RuleElementSrv *srv = rule->getSrv();
219
220 if (srv->size() > 1)
221 {
222 std::list<FWObject*> cl;
223 for (list<FWObject*>::iterator i1=srv->begin(); i1!=srv->end(); ++i1)
224 {
225 FWObject *o = *i1;
226 FWObject *obj = NULL;
227 if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
228 Service *s=Service::cast(obj);
229 assert(s!=NULL);
230
231 TCPService *tcp_srv = TCPService::cast(s);
232 if (tcp_srv && (tcp_srv->inspectFlags() || tcp_srv->getEstablished()))
233 cl.push_back(s);
234 }
235
236 while (!cl.empty())
237 {
238 PolicyRule *r = compiler->dbcopy->createPolicyRule();
239 compiler->temp_ruleset->add(r);
240 r->duplicate(rule);
241
242 RuleElementSrv *nsrv = r->getSrv();
243 nsrv->clearChildren();
244 nsrv->addRef( cl.front() );
245 tmp_queue.push_back(r);
246
247 srv->removeRef( cl.front() );
248 cl.pop_front();
249 }
250 if (srv->size()>0) tmp_queue.push_back(rule);
251
252 } else
253 tmp_queue.push_back(rule);
254
255 return true;
256 }
257
258
259
compile()260 void PolicyCompiler_nxosacl::compile()
261 {
262 string banner = " Compiling ruleset " + getSourceRuleSet()->getName();
263 if (ipv6) banner += ", IPv6";
264 info(banner);
265
266 string version = fw->getStr("version");
267 bool supports_object_groups = XMLTools::version_compare(version, "12.4")>=0 &&
268 fw->getOptionsObject()->getBool("nxosacl_use_object_groups") && ! ipv6;
269
270 string vers = fw->getStr("version");
271 string platform = fw->getStr("platform");
272
273 Compiler::compile();
274
275 if ( fw->getOptionsObject()->getBool ("check_shading") &&
276 ! inSingleRuleCompileMode())
277 {
278 add( new Begin("Detecting rule shadowing" ) );
279 add( new printTotalNumberOfRules());
280
281 add( new ItfNegation("process negation in Itf" ) );
282 add( new InterfacePolicyRules(
283 "process interface policy rules and store interface ids"));
284
285 add( new recursiveGroupsInSrc("check for recursive groups in SRC"));
286 add( new recursiveGroupsInDst("check for recursive groups in DST"));
287 add( new recursiveGroupsInSrv("check for recursive groups in SRV"));
288
289 add( new ExpandGroups("expand groups"));
290 add( new dropRuleWithEmptyRE(
291 "drop rules with empty rule elements"));
292 add( new eliminateDuplicatesInSRC("eliminate duplicates in SRC"));
293 add( new eliminateDuplicatesInDST("eliminate duplicates in DST"));
294 add( new eliminateDuplicatesInSRV("eliminate duplicates in SRV"));
295 add( new ExpandMultipleAddressesInSrc(
296 "expand objects with multiple addresses in SRC" ) );
297 add( new ExpandMultipleAddressesInDst(
298 "expand objects with multiple addresses in DST" ) );
299 add( new dropRuleWithEmptyRE(
300 "drop rules with empty rule elements"));
301
302 add( new mirrorRule("Add mirrored rules"));
303
304 add( new ConvertToAtomic("convert to atomic rules" ) );
305
306 add( new checkForObjectsWithErrors(
307 "check if we have objects with errors in rule elements"));
308
309 add( new DetectShadowing("Detect shadowing" ) );
310 add( new simplePrintProgress() );
311
312 runRuleProcessors();
313 deleteRuleProcessors();
314 }
315
316
317 add( new Begin (" Start processing rules" ) );
318 add( new printTotalNumberOfRules ( ) );
319
320 add( new singleRuleFilter());
321
322 add( new recursiveGroupsInSrc( "check for recursive groups in SRC" ) );
323 add( new recursiveGroupsInDst( "check for recursive groups in DST" ) );
324 add( new recursiveGroupsInSrv( "check for recursive groups in SRV" ) );
325
326 add( new emptyGroupsInSrc( "check for empty groups in SRC" ) );
327 add( new emptyGroupsInDst( "check for empty groups in DST" ) );
328 add( new emptyGroupsInSrv( "check for empty groups in SRV" ) );
329
330 add( new ExpandGroups ("expand groups" ) );
331 add( new dropRuleWithEmptyRE(
332 "drop rules with empty rule elements"));
333 add( new eliminateDuplicatesInSRC( "eliminate duplicates in SRC" ) );
334 add( new eliminateDuplicatesInDST( "eliminate duplicates in DST" ) );
335 add( new eliminateDuplicatesInSRV( "eliminate duplicates in SRV" ) );
336
337 add( new processMultiAddressObjectsInSrc(
338 "process MultiAddress objects in Src") );
339 add( new processMultiAddressObjectsInDst(
340 "process MultiAddress objects in Dst") );
341
342 add( new expandGroupsInItf("expand groups in Interface" ));
343 add( new replaceClusterInterfaceInItf(
344 "replace cluster interfaces with member interfaces in the Interface rule element"));
345
346 add( new ItfNegation( "process negation in Itf" ) );
347 add( new InterfacePolicyRules(
348 "process interface policy rules and store interface ids") );
349
350 add( new groupServicesByProtocol ("split rules with different protocols" ) );
351
352 add( new ExpandMultipleAddressesInSrc(
353 "expand objects with multiple addresses in SRC" ) );
354 add( new MACFiltering ("check for MAC address filtering" ) );
355 // add( new splitByNetworkZonesForSrc ("split rule if objects in Src belong to different network zones " ) );
356 // add( new replaceFWinDSTPolicy ("replace fw with its interface in DST in global policy rules") );
357
358 add( new ExpandMultipleAddressesInDst(
359 "expand objects with multiple addresses in DST" ) );
360 add( new MACFiltering(
361 "check for MAC address filtering" ) );
362 add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
363
364 // add( new splitByNetworkZonesForDst ("split rule if objects in Dst belong to different network zones " ) );
365
366 if (ipv6)
367 add( new DropIPv4Rules("drop ipv4 rules"));
368 else
369 add( new DropIPv6Rules("drop ipv6 rules"));
370 add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
371
372 add( new checkForUnnumbered("check for unnumbered interfaces"));
373
374 if ( ! supports_object_groups)
375 add( new addressRanges("process address ranges"));
376
377 add( new mirrorRule("Add mirrored rules"));
378
379 add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
380
381 add( new setInterfaceAndDirectionBySrc(
382 "Set interface and direction for rules with interface 'all' using SRC"));
383 add( new setInterfaceAndDirectionByDst(
384 "Set interface and direction for rules with interface 'all' using DST"));
385 add( new setInterfaceAndDirectionIfInterfaceSet(
386 "Set direction for rules with interface not 'all'"));
387
388 add( new specialCaseWithDynInterface(
389 "check for a special cases with dynamic interface" ) );
390
391 // first arg is true because we use "ip access-list" for NXOS.
392 add( new pickACL( true, "assign ACLs" ) );
393
394 add( new SpecialServices( "check for special services" ) );
395 add( new CheckForUnsupportedUserService("check for user service") );
396
397 add( new checkForZeroAddr( "check for zero addresses" ) );
398 add( new checkForDynamicInterface("check for dynamic interfaces" ) );
399
400 /* remove redundant objects only after all splits has been
401 * done, right before object groups are created
402 */
403 add( new removeRedundantAddressesFromSrc(
404 "remove redundant addresses from Src") );
405 add( new removeRedundantAddressesFromDst(
406 "remove redundant addresses from Dst") );
407
408 add( new checkForObjectsWithErrors(
409 "check if we have objects with errors in rule elements"));
410
411 if (supports_object_groups)
412 {
413 // "object-group service" does not seem to support
414 // matching of tcp flags and "established". Need to
415 // separate objects using these into separate rules to avoid
416 // object-group
417
418 add( new splitTCPServiceWithFlags(
419 "separate TCP service with tcp flags"));
420
421 add( new CreateObjectGroupsForSrc("create object groups for Src",
422 named_objects_manager));
423 add( new CreateObjectGroupsForDst("create object groups for Dst",
424 named_objects_manager));
425 add( new CreateObjectGroupsForSrv("create object groups for Srv",
426 named_objects_manager));
427 } else
428 {
429 add( new ConvertToAtomic ("convert to atomic rules" ) );
430 }
431
432 add( new simplePrintProgress());
433 add( new createNewCompilerPass("Creating object groups and ACLs"));
434
435 // This processor prints each ACL separately in one block.
436 // It adds comments inside to denote original rules.
437 //
438 add( new PrintCompleteACLs("Print ACLs"));
439 add( new simplePrintProgress());
440
441 runRuleProcessors();
442
443 }
444
printAccessGroupCmd(ciscoACL * acl,bool neg)445 string PolicyCompiler_nxosacl::printAccessGroupCmd(ciscoACL *acl, bool neg)
446 {
447 ostringstream str;
448
449 string addr_family_prefix = "ip";
450 if (ipv6) addr_family_prefix = "ipv6";
451
452 if (getSourceRuleSet()->isTop())
453 {
454 string dir;
455 if (acl->direction()=="in" || acl->direction()=="Inbound") dir="in";
456 if (acl->direction()=="out" || acl->direction()=="Outbound") dir="out";
457
458 str << "interface " << acl->getInterface()->getName() << endl;
459 if (neg) str << " no";
460 str << " " << addr_family_prefix << " ";
461 str << getAccessGroupCommandForAddressFamily(ipv6);
462 str << " " << acl->workName() << " " << dir << endl;
463 str << "exit" << endl;
464 }
465 return str.str();
466 }
467
epilog()468 void PolicyCompiler_nxosacl::epilog()
469 {
470 output << endl;
471
472 for (map<string,ciscoACL*>::iterator i=acls.begin(); i!=acls.end(); ++i)
473 {
474 ciscoACL *acl=(*i).second;
475 if (acl->size()!=0) output << printAccessGroupCmd(acl, false);
476 }
477 output << endl;
478
479 if ( fw->getOptionsObject()->getBool("nxosacl_regroup_commands") )
480 {
481 info(" Regrouping commands");
482 regroup();
483 }
484 }
485
getAccessGroupCommandForAddressFamily(bool ipv6)486 string PolicyCompiler_nxosacl::getAccessGroupCommandForAddressFamily(bool ipv6)
487 {
488 if (ipv6) return "traffic-filter";
489 return "access-group";
490 }
491
printClearCommands()492 string PolicyCompiler_nxosacl::printClearCommands()
493 {
494 ostringstream output;
495
496 string version = fw->getStr("version");
497 string platform = fw->getStr("platform");
498
499 string xml_element = "clear_ip_acl";
500 if (ipv6) xml_element = "clear_ipv6_acl";
501
502 string clearACLCmd = Resources::platform_res[platform]->getResourceStr(
503 string("/FWBuilderResources/Target/options/") +
504 "version_" + version + "/nxosacl_commands/" + xml_element);
505
506 assert( !clearACLCmd.empty());
507
508 // No need to output "clear" commands in single rule compile mode
509 if ( fw->getOptionsObject()->getBool("nxosacl_acl_basic") ||
510 fw->getOptionsObject()->getBool("nxosacl_acl_substitution"))
511 {
512 for (map<string,ciscoACL*>::iterator i=acls.begin(); i!=acls.end(); ++i)
513 {
514 ciscoACL *acl = (*i).second;
515 output << clearACLCmd << " " << acl->workName() << endl;
516 }
517 }
518
519 return output.str();
520 }
521
522