1 /*
2
3 Firewall Builder
4
5 Copyright (C) 2002 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 "NATCompiler_pf.h"
29
30 #include "fwcompiler/OSConfigurator.h"
31
32 #include "fwbuilder/AddressRange.h"
33 #include "fwbuilder/AddressTable.h"
34 #include "fwbuilder/Cluster.h"
35 #include "fwbuilder/FailoverClusterGroup.h"
36 #include "fwbuilder/Firewall.h"
37 #include "fwbuilder/Host.h"
38 #include "fwbuilder/ICMPService.h"
39 #include "fwbuilder/IPService.h"
40 #include "fwbuilder/IPv4.h"
41 #include "fwbuilder/Interface.h"
42 #include "fwbuilder/Library.h"
43 #include "fwbuilder/NAT.h"
44 #include "fwbuilder/Network.h"
45 #include "fwbuilder/RuleElement.h"
46 #include "fwbuilder/TCPService.h"
47 #include "fwbuilder/UDPService.h"
48
49 #include <iostream>
50 #include <iomanip>
51
52 #include <assert.h>
53
54 using namespace libfwbuilder;
55 using namespace fwcompiler;
56 using namespace std;
57
myPlatformName()58 string NATCompiler_pf::myPlatformName() { return "pf"; }
59
60
prolog()61 int NATCompiler_pf::prolog()
62 {
63 int n=NATCompiler::prolog();
64
65 if ( n>0 )
66 {
67 list<FWObject*> l2=fw->getByType(Interface::TYPENAME);
68 for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
69 {
70 Interface *iface=dynamic_cast<Interface*>(*i);
71 assert(iface);
72
73 if ( iface->isDyn())
74 {
75 iface->setBool("use_var_address",true);
76
77 /* dynamic interface should not have IPv4 child object(s). We issue a
78 * warning if it does in a policy compiler, there is no need to repeat
79 * it here
80 */
81 list<FWObject*> l3=iface->getByType(IPv4::TYPENAME);
82 for (list<FWObject*>::iterator j=l3.begin(); j!=l3.end(); ++j)
83 iface->remove(*j);
84 }
85 }
86 }
87
88 /* pseudo-host with ip address 127.0.0.1 We'll use it for redirection
89 * NAT rules
90 */
91 //FWObject *grp;
92 loopback_address = dbcopy->createIPv4();
93 loopback_address->setName("__loopback_address__");
94 loopback_address->setId(FWObjectDatabase::generateUniqueId()); // "__loopback_address_id__");
95
96 IPv4::cast(loopback_address)->setAddress(InetAddr::getLoopbackAddr());
97
98 persistent_objects->add(loopback_address,false);
99
100 if (tables)
101 {
102 tables->init(dbcopy);
103 if (!getSourceRuleSet()->isTop())
104 tables->setRuleSetName(getRuleSetName());
105 }
106
107 return n;
108 }
109
debugPrintRule(libfwbuilder::Rule * r)110 string NATCompiler_pf::debugPrintRule(libfwbuilder::Rule *r)
111 {
112 NATRule *rule = NATRule::cast(r);
113 RuleElementItfOutb *itf_re = rule->getItfOutb();
114 FWObject *rule_iface = FWObjectReference::getObject(itf_re->front());
115 // FWObject *rule_iface = dbcopy->findInIndex(rule->getInterfaceId());
116
117 return NATCompiler::debugPrintRule(rule) +
118 " " + string( (rule_iface!=NULL)?rule_iface->getName():"") +
119 " (type=" + rule->getRuleTypeAsString() + ")";
120 }
121
_expand_addr(Rule * rule,FWObject * s,bool expand_cluster_interfaces_fully)122 void NATCompiler_pf::_expand_addr(Rule *rule,
123 FWObject *s,
124 bool expand_cluster_interfaces_fully)
125 {
126 if (RuleElementTSrc::isA(s))
127 {
128 // do not replace interfaces with their ip addresses in TSrc
129 // to be able to generate "nat ... -> (em0)" command later
130 list<FWObject*> interfaces_in_re;
131 for (FWObject::iterator i1=s->begin(); i1!=s->end(); ++i1)
132 {
133 FWObject *o = FWReference::getObject(*i1);
134 assert(o);
135 if (Interface::isA(o))
136 interfaces_in_re.push_back(o);
137 }
138 if (interfaces_in_re.size() > 1)
139 {
140 for (list<FWObject*>::iterator i=interfaces_in_re.begin();
141 i!=interfaces_in_re.end(); ++i) s->removeRef(*i);
142
143 NATCompiler::_expand_addr(
144 rule, s, expand_cluster_interfaces_fully);
145
146 for (list<FWObject*>::iterator i=interfaces_in_re.begin();
147 i!=interfaces_in_re.end(); ++i) s->addRef(*i);
148
149 }
150 } else
151 NATCompiler::_expand_addr(
152 rule, s, expand_cluster_interfaces_fully);
153 }
154
processNext()155 bool NATCompiler_pf::NATRuleType::processNext()
156 {
157 NATRule *rule=getNext(); if (rule==NULL) return false;
158 tmp_queue.push_back(rule);
159
160 if (rule->getRuleType()!=NATRule::Unknown) return true;
161
162 RuleElementTSrc *tsrcre = rule->getTSrc();
163 RuleElementTDst *tdstre = rule->getTDst();
164 RuleElementTSrv *tsrvre = rule->getTSrv();
165
166 Service *osrv=compiler->getFirstOSrv(rule);
167
168 Address *tsrc = compiler->getFirstTSrc(rule);
169 Address *tdst = compiler->getFirstTDst(rule);
170 Service *tsrv=compiler->getFirstTSrv(rule);
171
172 if (rule->getAction() == NATRule::Branch)
173 {
174 rule->setRuleType(NATRule::NATBranch);
175 if (!tsrcre->isAny() || !tdstre->isAny() || !tsrvre->isAny())
176 {
177 tsrcre->clearChildren();
178 tsrcre->setAnyElement();
179
180 tdstre->clearChildren();
181 tdstre->setAnyElement();
182
183 tsrvre->clearChildren();
184 tsrvre->setAnyElement();
185
186 compiler->warning(
187 rule,
188 "Translated Src, Dst and Srv are ignored in the NAT "
189 "rule with action 'Branch'");
190 }
191 return true;
192 }
193
194 if (tsrc->isAny() && tdst->isAny() &&
195 (tsrv->isAny() || (tsrv->getId() == osrv->getId()))
196 )
197 {
198 rule->setRuleType(NATRule::NONAT);
199 return true;
200 }
201
202 bool osrv_defines_src_port = false;
203 Q_UNUSED(osrv_defines_src_port);
204 bool osrv_defines_dst_port = false;
205 Q_UNUSED(osrv_defines_dst_port);
206 bool tsrv_translates_src_port = false;
207 bool tsrv_translates_dst_port = false;
208
209 if (TCPUDPService::cast(osrv))
210 {
211 TCPUDPService *tu_osrv = TCPUDPService::cast(osrv);
212
213 osrv_defines_src_port = \
214 (tu_osrv->getSrcRangeStart() != 0 && tu_osrv->getDstRangeStart() == 0);
215 osrv_defines_dst_port = \
216 (tu_osrv->getSrcRangeStart() == 0 && tu_osrv->getDstRangeStart() != 0);
217 }
218
219 if (TCPUDPService::cast(tsrv))
220 {
221 TCPUDPService *tu_tsrv = TCPUDPService::cast(tsrv);
222
223 tsrv_translates_src_port = \
224 (tu_tsrv->getSrcRangeStart() != 0 && tu_tsrv->getDstRangeStart() == 0);
225 tsrv_translates_dst_port = \
226 (tu_tsrv->getSrcRangeStart() == 0 && tu_tsrv->getDstRangeStart() != 0);
227 }
228
229
230 if (
231 (! tsrc->isAny() && tdst->isAny()) ||
232 (tsrc->isAny() && tdst->isAny() && tsrv_translates_src_port)
233 )
234 {
235 rule->setRuleType(NATRule::SNAT);
236 return true;
237 }
238
239 if (
240 (tsrc->isAny() && ! tdst->isAny()) ||
241 (tsrc->isAny() && tdst->isAny() && tsrv_translates_dst_port)
242 )
243 {
244 /* this is load balancing rule if there are multiple objects in TDst */
245 if ( tdstre->size()>1 ) rule->setRuleType(NATRule::LB);
246 else
247 {
248
249 if ( compiler->complexMatch(tdst,compiler->fw) ) rule->setRuleType(NATRule::Redirect);
250 else rule->setRuleType(NATRule::DNAT);
251
252 // if ( tdst->getId()==compiler->fw->getId() ) rule->setRuleType(NATRule::Redirect);
253 // else rule->setRuleType(NATRule::DNAT);
254 }
255 return true;
256 }
257
258 if (
259 ( ! tsrc->isAny() && ! tdst->isAny() ) ||
260 ( ! tsrc->isAny() && tsrv_translates_dst_port) ||
261 ( ! tdst->isAny() && tsrv_translates_src_port)
262 )
263 {
264 rule->setRuleType(NATRule::SDNAT);
265 return true;
266 }
267
268 compiler->abort(rule, "Unsupported translation.");
269
270 return false;
271 }
272
273 /*
274 * This processor should be called after classifyNATRule. Should call
275 * classifyNATRule after this processor again.
276 *
277 * This algorithm is very much specific to iptables. Platforms where
278 * this simple algorithm for SDNAT rules is not appropriate, should
279 * either implement equivalent of this processor using different
280 * algorithm, or should catch SDNAT rules and abort in their own
281 * verifyNATRule processor.
282 */
processNext()283 bool NATCompiler_pf::splitSDNATRule::processNext()
284 {
285 NATRule *rule=getNext(); if (rule==NULL) return false;
286
287 if ( rule->getRuleType()==NATRule::SDNAT)
288 {
289 RuleElementODst *odst;
290 RuleElementOSrv *osrv;
291 RuleElementTSrc *tsrc;
292 RuleElementTDst *tdst;
293
294 /* first rule translates destination and may translate service (depends
295 * on the original rule) */
296 NATRule *r = compiler->dbcopy->createNATRule();
297 r->duplicate(rule);
298 compiler->temp_ruleset->add(r);
299 r->setRuleType(NATRule::Unknown);
300
301 tsrc=r->getTSrc();
302 tsrc->clearChildren();
303 tsrc->setAnyElement();
304
305 tmp_queue.push_back(r);
306
307 /* the second rule translates source and uses translated object in
308 * ODst. Since the service could have been translated by the first
309 * rule, we use TSrv in OSrv */
310 r = compiler->dbcopy->createNATRule();
311 r->duplicate(rule);
312 compiler->temp_ruleset->add(r);
313 r->setRuleType(NATRule::Unknown);
314
315 odst=r->getODst();
316 odst->clearChildren();
317 for (FWObject::iterator i=rule->getTDst()->begin(); i!=rule->getTDst()->end(); i++)
318 {
319 FWObject *o = FWReference::getObject(*i);
320 odst->addRef(o);
321 }
322
323 if ( ! rule->getTSrv()->isAny())
324 {
325 /*
326 * See "pf flow diagram" at http://homepage.mac.com/quension/pf/flow.png
327 * rdr happens first, then nat. This means nat sees packet with
328 * translated destination address and port.
329 *
330 * If the first rule in the pair translated service and
331 * changed destination port, we need to match it in the
332 * second rule to only trsnslate source in the packets
333 * that have been processed by the first rule. However
334 * this only applies to the case when destination port has
335 * been translated because the first rule uses DNAT which
336 * can only translate dest. port. So, if TSrv has zero
337 * dest. port range but non-zero source port range, we
338 * should not match it here because in this case no
339 * dest. port translation occurs. If TSrv translates both
340 * source and destination ports, we create new TCP(UDP)
341 * service object with only dest. port part and use it to
342 * match.
343 */
344 Service *tsrv = compiler->getFirstTSrv(rule);
345 TCPUDPService *tu_tsrv = TCPUDPService::cast(tsrv);
346 if (tu_tsrv && tu_tsrv->getDstRangeStart() != 0)
347 {
348 TCPUDPService *match_service = NULL;
349 if (tu_tsrv->getSrcRangeStart() == 0)
350 {
351 // no source port tranlsation
352 match_service = tu_tsrv;
353 } else
354 {
355 // both source and dest port translation occurs
356 match_service = TCPUDPService::cast(
357 compiler->dbcopy->create(tsrv->getTypeName()));
358 match_service->setName(tsrv->getName() + "_dport");
359 compiler->persistent_objects->add(match_service);
360 match_service->setDstRangeStart(tu_tsrv->getDstRangeStart());
361 match_service->setDstRangeEnd(tu_tsrv->getDstRangeEnd());
362 }
363 osrv = r->getOSrv();
364 osrv->clearChildren();
365 osrv->addRef(match_service);
366 }
367 }
368
369 tdst=r->getTDst();
370 tdst->clearChildren();
371 tdst->setAnyElement();
372
373 tmp_queue.push_back(r);
374 }
375 else
376 tmp_queue.push_back(rule);
377
378 return true;
379 }
380
381
processNext()382 bool NATCompiler_pf::VerifyRules::processNext()
383 {
384 NATRule *rule=getNext(); if (rule==NULL) return false;
385
386 string version = compiler->fw->getStr("version");
387
388 RuleElementOSrc *osrc=rule->getOSrc(); assert(osrc);
389 RuleElementODst *odst=rule->getODst(); assert(odst);
390 RuleElementOSrv *osrv=rule->getOSrv(); assert(osrv);
391
392 RuleElementTSrc *tsrc=rule->getTSrc(); assert(tsrc);
393 RuleElementTDst *tdst=rule->getTDst(); assert(tdst);
394 RuleElementTSrv *tsrv=rule->getTSrv(); assert(tsrv);
395
396 /*
397 * because of the change in the nat and rdr rules syntax in
398 * 4.7, I can no longer implement no-nat rules correctly for
399 * this version. They dropped the "no" keyword and their
400 * examples suggest using "pass" to implement exclusions for
401 * the nat rules. I need no-nat rule to just not translate but
402 * not make a decision whether the packet should be passed or
403 * dropped. In the new PF model, translation rules are just
404 * options on the matching policy rules and they do not offer
405 * any keyword or option to not translate.
406 */
407 if (rule->getRuleType()==NATRule::NONAT &&
408 XMLTools::version_compare(version, "4.7")>=0)
409 {
410 compiler->abort(
411 rule,
412 "No translation rules are not supported for PF 4.7, "
413 "use negation to implement exclusions");
414 return true;
415 }
416
417 if (osrv->getNeg())
418 {
419 compiler->abort(
420 rule,
421 "Negation in original service is not supported.");
422 return true;
423 }
424
425 /* bug #1276083: "Destination NAT rules". this restriction is not
426 * true at least as of OpenBSD 3.5
427 *
428 if (rule->getRuleType()==NATRule::DNAT && osrv->isAny())
429 compiler->abort("Service must be specified for destination translation rule. Rule "+rule->getLabel());
430 */
431
432 if (rule->getRuleType()==NATRule::DNAT && osrv->isAny() && !tsrv->isAny())
433 {
434 compiler->abort(
435 rule,
436 "Can not translate 'any' into a specific service.");
437 return true;
438 }
439
440 if (tsrc->getNeg())
441 {
442 compiler->abort(
443 rule,
444 "Can not use negation in translated source.");
445 return true;
446 }
447
448 if (tdst->getNeg())
449 {
450 compiler->abort(
451 rule,
452 "Can not use negation in translated destination.");
453 return true;
454 }
455
456 if (tsrv->getNeg())
457 {
458 compiler->abort(
459 rule,
460 "Can not use negation in translated service.");
461 return true;
462 }
463
464 if (tsrv->size()!=1)
465 {
466 compiler->abort(
467 rule,
468 "Translated service should be 'Original' or should contain single object.");
469 return true;
470 }
471
472 FWObject *o=tsrv->front();
473 if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
474
475 if ( Group::cast(o)!=NULL)
476 {
477 compiler->abort(
478 rule,
479 "Can not use group in translated service.");
480 return true;
481 }
482
483 #if 0
484 if (rule->getRuleType()==NATRule::SNAT )
485 {
486 Address* o1=compiler->getFirstTSrc(rule);
487 if ( Network::cast(o1)!=NULL || AddressRange::cast(o1)!=NULL )
488 compiler->abort("Can not use network or address range object in translated source. Rule "+rule->getLabel());
489 }
490 #endif
491
492 if (rule->getRuleType()==NATRule::SNAT )
493 {
494 if (tsrc->isAny())
495 {
496 compiler->abort(rule,
497 "Source translation rule needs an address in "
498 "Translated Source.");
499 return true;
500 }
501
502 FWObject *o = FWReference::getObject(tsrc->front());
503 if (Interface::isA(o) && Interface::cast(o)->isUnnumbered())
504 {
505 compiler->abort(rule,
506 "Can not use unnumbered interface in "
507 "Translated Source of a Source translation rule.");
508 return true;
509 }
510
511 }
512
513 if (rule->getRuleType()==NATRule::DNAT || rule->getRuleType()==NATRule::Redirect )
514 {
515 if (tdst->isAny())
516 {
517 compiler->abort(
518 rule,
519 "Destination translation rule needs an address in "
520 "Translated Destination.");
521 return true;
522 }
523
524 if ( tdst->size()!=1)
525 {
526 compiler->abort(
527 rule,
528 "There should be no more than one object in translated destination");
529 return true;
530 }
531
532 Address* o1=compiler->getFirstTDst(rule);
533 if ( Network::cast(o1)!=NULL || AddressRange::cast(o1)!=NULL )
534 {
535 compiler->abort(
536 rule,
537 "Can not use network or address range object in translated destination.");
538 return true;
539 }
540
541 }
542
543
544 if (rule->getRuleType()==NATRule::SNetnat && !tsrc->isAny() )
545 {
546 Network *a1=Network::cast(compiler->getFirstOSrc(rule));
547 Network *a2=Network::cast(compiler->getFirstTSrc(rule));
548 if ( a1==NULL || a2==NULL ||
549 a1->getNetmaskPtr()->getLength()!=a2->getNetmaskPtr()->getLength() )
550 {
551 compiler->abort(
552 rule,
553 "Original and translated source should both be networks of the same size.");
554 return true;
555 }
556
557 }
558
559 if (rule->getRuleType()==NATRule::DNetnat && !tsrc->isAny() )
560 {
561 Network *a1=Network::cast(compiler->getFirstODst(rule));
562 Network *a2=Network::cast(compiler->getFirstTDst(rule));
563 if ( a1==NULL || a2==NULL ||
564 a1->getNetmaskPtr()->getLength()!=a2->getNetmaskPtr()->getLength() )
565 {
566 compiler->abort(
567 rule,
568 "Original and translated destination should both be networks of the same size.");
569 return true;
570 }
571
572 }
573
574 if (rule->getRuleType()==NATRule::NATBranch )
575 {
576 RuleSet *branch = rule->getBranch();
577 if (branch == NULL)
578 {
579 compiler->abort(
580 rule,
581 "Action 'Branch' needs NAT rule set to point to");
582 return true;
583 } else
584 {
585 if (!NAT::isA(branch))
586 {
587 compiler->abort(
588 rule,
589 "Action 'Branch' must point to a NAT rule set "
590 "(points to " + branch->getTypeName() + ")");
591 return true;
592 }
593
594 }
595 }
596
597 tmp_queue.push_back(rule);
598 return true;
599 }
600
processNext()601 bool NATCompiler_pf::splitOnOSrv::processNext()
602 {
603 NATRule *rule=getNext(); if (rule==NULL) return false;
604
605 RuleElementOSrv *osrv=rule->getOSrv(); assert(osrv);
606 if (osrv->size()!=1)
607 {
608 for(list<FWObject*>::iterator i=osrv->begin(); i!=osrv->end(); ++i)
609 {
610 FWObject *o= *i;
611 // if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
612 if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
613 Service *s=Service::cast( o );
614 assert(s);
615
616 NATRule *r= compiler->dbcopy->createNATRule();
617 compiler->temp_ruleset->add(r);
618 r->duplicate(rule);
619 RuleElementOSrv *nosrv=r->getOSrv();
620 nosrv->clearChildren();
621
622 nosrv->addRef( s );
623
624 tmp_queue.push_back( r );
625 }
626 } else
627 tmp_queue.push_back(rule);
628
629 return true;
630 }
631
processNext()632 bool NATCompiler_pf::fillTranslatedSrv::processNext()
633 {
634 NATRule *rule=getNext(); if (rule==NULL) return false;
635 tmp_queue.push_back(rule);
636
637 Service *osrv_o=compiler->getFirstOSrv(rule);
638 Service *tsrv_o=compiler->getFirstTSrv(rule);
639
640 if ( ! osrv_o->isAny() && tsrv_o->isAny() )
641 {
642 RuleElementTSrv *tsrv=rule->getTSrv();
643 tsrv->addRef(osrv_o);
644 }
645 return true;
646 }
647
processNext()648 bool NATCompiler_pf::addVirtualAddress::processNext()
649 {
650 NATRule *rule=getNext(); if (rule==NULL) return false;
651 tmp_queue.push_back(rule);
652
653 Address *a=NULL;
654 if (rule->getRuleType()==NATRule::SNAT) a=compiler->getFirstTSrc(rule);
655 else
656 if (rule->getRuleType()==NATRule::DNAT) a=compiler->getFirstODst(rule);
657 else return true;
658 assert(a!=NULL);
659 const InetAddr *a_addr = a->getAddressPtr();
660
661 if ( ! a->isAny() && a->getId()!=compiler->getFwId() && a_addr)
662 {
663 list<FWObject*> l2=compiler->fw->getByType(Interface::TYPENAME);
664 for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
665 {
666 Interface *iface=dynamic_cast<Interface*>(*i);
667 assert(iface);
668 const InetAddr *iface_addr = iface->getAddressPtr();
669 if (iface_addr && *a_addr == *iface_addr )
670 return true;
671 }
672 compiler->osconfigurator->addVirtualAddressForNAT( a );
673 }
674
675 return true;
676 }
677
678
processNext()679 bool NATCompiler_pf::splitForTSrc::processNext()
680 {
681 NATRule *rule=getNext(); if (rule==NULL) return false;
682 RuleElementTSrc *tsrc=rule->getTSrc(); assert(tsrc);
683
684 map<int,list<FWObject*> > interfaceGroups;
685
686 for(list<FWObject*>::iterator i=tsrc->begin(); i!=tsrc->end(); ++i)
687 {
688 FWObject *o= *i;
689 if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
690 Interface *iface = compiler->findInterfaceFor(Address::cast(o),
691 compiler->fw);
692 if (iface!=NULL)
693 interfaceGroups[iface->getId()].push_back(o);
694 }
695
696 if (interfaceGroups.size()<=1) tmp_queue.push_back(rule);
697 else
698 {
699 map<int,list<FWObject*> >::iterator i;
700 for (i=interfaceGroups.begin(); i!=interfaceGroups.end(); i++)
701 {
702 list<FWObject*> &objSubset = (*i).second;
703
704 RuleElementTSrc *ntsrc = NULL;
705 NATRule *r = compiler->dbcopy->createNATRule();
706 r->duplicate(rule);
707 compiler->temp_ruleset->add(r);
708
709 ntsrc=r->getTSrc();
710 ntsrc->clearChildren();
711 ntsrc->setAnyElement();
712 for (FWObject::iterator j=objSubset.begin(); j!=objSubset.end(); j++)
713 {
714 ntsrc->addRef(*j);
715 }
716 tmp_queue.push_back(r);
717 }
718 }
719 return true;
720 }
721
722
assignInterfaceToNATRule(NATRule * rule,Address * addr)723 bool NATCompiler_pf::assignInterfaceToNATRule(NATRule *rule, Address *addr)
724 {
725 RuleElementItfOutb *itf_re = rule->getItfOutb();
726 assert(itf_re!=NULL);
727
728 if (Interface::isA(addr) || IPv4::isA(addr))
729 {
730 FWObject *p = addr;
731 while ( p && ! Interface::isA(p) ) p = p->getParent();
732 Interface *intf = Interface::cast(p);
733
734 if (intf && intf->isFailoverInterface())
735 {
736 FailoverClusterGroup *fg = FailoverClusterGroup::cast(
737 intf->getFirstByType(FailoverClusterGroup::TYPENAME));
738 if (fg)
739 intf = fg->getInterfaceForMemberFirewall(fw);
740 }
741
742 if (intf && intf->isChildOf(fw))
743 {
744 if ( ! itf_re->hasRef(intf)) itf_re->addRef(intf);
745 return true;
746 }
747 }
748 return false;
749 }
750
processNext()751 bool NATCompiler_pf::AssignInterface::processNext()
752 {
753 NATCompiler_pf *pf_comp = dynamic_cast<NATCompiler_pf*>(compiler);
754 NATRule *rule = getNext(); if (rule==NULL) return false;
755
756 if (rule->getStr(".iface") == "nil")
757 {
758 tmp_queue.push_back(rule);
759 return true;
760 }
761
762 RuleElementItfOutb *itf_re = rule->getItfOutb();
763 assert(itf_re!=NULL);
764
765 if ( ! itf_re->isAny())
766 {
767 tmp_queue.push_back(rule);
768 return true;
769 }
770
771 switch ( rule->getRuleType() )
772 {
773 case NATRule::SNAT:
774 {
775 RuleElementTSrc *tsrc_re = rule->getTSrc();
776 bool have_interface = false;
777 for (FWObject::iterator i1=tsrc_re->begin(); i1!=tsrc_re->end(); ++i1)
778 {
779 Address *addr = Address::cast(FWObjectReference::getObject(*i1));
780 have_interface |= pf_comp->assignInterfaceToNATRule(rule, addr);
781 }
782
783 if (have_interface)
784 {
785 tmp_queue.push_back(rule);
786 return true;
787 }
788
789 /* if we appear here, then TSrc is not an interface or address of
790 * an interface. Generate NAT rule without "on iface" clause
791 */
792 // rule->setInterfaceStr("");
793 itf_re->clearChildren();
794 itf_re->setAnyElement();
795 }
796 break;
797
798 case NATRule::DNAT:
799 {
800 RuleElementODst *odst_re = rule->getODst();
801 bool have_interface = false;
802 for (FWObject::iterator i1=odst_re->begin(); i1!=odst_re->end(); ++i1)
803 {
804 Address *addr = Address::cast(FWObjectReference::getObject(*i1));
805 have_interface |= pf_comp->assignInterfaceToNATRule(rule, addr);
806 }
807
808 if (have_interface)
809 {
810 tmp_queue.push_back(rule);
811 return true;
812 }
813
814 /* if we appear here, then ODst is not an interface or address of an
815 * interface. If this is so, just do not specify interface for rdr
816 * rule.
817 */
818 itf_re->clearChildren();
819 itf_re->setAnyElement();
820 }
821 break;
822
823 default: break;
824 }
825
826 tmp_queue.push_back(rule);
827 return true;
828 }
829
830
831 /*
832 * I assume that there is always only one object in ODst, TSrc and TDst
833 * rule elements. This should have been assured by inspector VerifyRules
834 */
processNext()835 bool NATCompiler_pf::ReplaceFirewallObjectsODst::processNext()
836 {
837 NATRule *rule=getNext(); if (rule==NULL) return false;
838 tmp_queue.push_back(rule);
839
840 list<FWObject*> cl;
841 RuleElementODst *rel;
842 Address *obj=NULL;
843
844 rel = rule->getODst(); assert(rel);
845 obj =compiler->getFirstODst(rule); assert(obj);
846
847 if (obj->getId()==compiler->getFwId() )
848 {
849 list<FWObject*> l2 = compiler->fw->getByTypeDeep(Interface::TYPENAME);
850 for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
851 {
852 Interface *interface_ = Interface::cast(*i);
853 /*
854 * update 03/20/03:
855 *
856 * generally we assume that if firewall object is used in the rule,
857 * then any or all its interface will be used. This means that if
858 * firewall is in ODst we should really use all of its interfaces, not
859 * only external ones.
860 */
861 if (! interface_->isLoopback() ) cl.push_back(interface_);
862
863 }
864 if ( ! cl.empty() )
865 {
866 rel->clearChildren();
867 for (FWObject::iterator i1=cl.begin(); i1!=cl.end(); ++i1)
868 {
869 rel->addRef( *i1 );
870 }
871 }
872 /*
873 * update for ticket 1397 If firewall object is in ODst, do not assign
874 * the rule to any interface. I use attribute ".iface" to signal
875 * AssignInterface that it should not do anything.
876 */
877 rule->setStr(".iface", "nil");
878 }
879
880 return true;
881 }
882
883 /*
884 * I assume that there is always only one object in ODst, TSrc and TDst
885 * rule elements. This should have been assured by inspector VerifyRules
886 */
processNext()887 bool NATCompiler_pf::ReplaceFirewallObjectsTSrc::processNext()
888 {
889 NATRule *rule=getNext(); if (rule==NULL) return false;
890 tmp_queue.push_back(rule);
891
892 list<FWObject*> cl;
893 RuleElementTSrc *rel;
894 Address *obj=NULL;
895
896 switch (rule->getRuleType())
897 {
898 case NATRule::Masq: return true;
899 default:
900 rel=rule->getTSrc(); assert(rel);
901 obj=compiler->getFirstTSrc(rule); assert(obj);
902
903 if (obj->getId()==compiler->getFwId() )
904 {
905 Address *odst=compiler->getFirstODst(rule);
906
907 rel->clearChildren();
908
909 Interface *iface=compiler->findInterfaceFor(odst,compiler->fw);
910
911 if (!odst->isAny() && !rule->getODst()->getNeg() && iface!=NULL)
912 rel->addRef(iface);
913 else // else use all interfaces except loopback and unnumbered ones
914 {
915 list<FWObject*> l2=compiler->fw->getByType(Interface::TYPENAME);
916 for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
917 {
918 Interface *iface=Interface::cast(*i);
919 if (! iface->isLoopback() &&
920 ! iface->isUnnumbered() &&
921 ! iface->isBridgePort())
922 rel->addRef( *i );
923 }
924 for (FWObject::iterator i1=cl.begin(); i1!=cl.end(); ++i1)
925 rel->addRef( *i1 );
926
927 /* it is an error if rule element is empty at this point. this could have
928 * happened if all external interfaces are unnumbered */
929 if (rel->size()==0)
930 {
931 QString err(
932 "Could not find suitable interface for the NAT rule %1. "
933 "Perhaps all interfaces are unnumbered?");
934 compiler->abort(
935 rule,
936 err.arg(rule->getLabel().c_str()).toStdString());
937 }
938 }
939 }
940 }
941 return true;
942 }
943
944 /*
945 * I assume that there is always only one object in ODst, TSrc and TDst
946 * rule elements. This should have been assured by inspector VerifyRules
947 */
processNext()948 bool NATCompiler_pf::ReplaceObjectsTDst::processNext()
949 {
950 NATRule *rule=getNext(); if (rule==NULL) return false;
951 NATCompiler_pf *pf_comp=dynamic_cast<NATCompiler_pf*>(compiler);
952
953 tmp_queue.push_back(rule);
954
955 if (rule->getRuleType()==NATRule::Redirect)
956 {
957 Service *tsrv=compiler->getFirstTSrv(rule);
958 RuleElementTDst *rel=rule->getTDst(); assert(rel);
959 Address *otdst=compiler->getFirstTDst(rule);
960 Interface *loopback=NULL;
961 FWObject *loopback_address=NULL;
962
963 /* if firewall is used in TDst in redirection rule, replace it with
964 * its loopback interface
965 */
966 if (otdst->getId()==compiler->fw->getId())
967 {
968 std::list<FWObject*> l2=compiler->fw->getByType(Interface::TYPENAME);
969 for (std::list<FWObject*>::iterator i=l2.begin();
970 i!=l2.end(); ++i)
971 {
972 Interface *iface = dynamic_cast<Interface*>(*i);
973 assert(iface);
974 if (iface->isLoopback())
975 {
976 loopback = iface;
977 loopback_address = loopback->getFirstByType(IPv4::TYPENAME);
978 }
979 }
980
981 if (loopback_address==NULL)
982 {
983 compiler->abort(rule,
984 "Can not configure redirection for the NAT rule "
985 "because loopback interface is missing.");
986 }
987
988 rel->clearChildren();
989 rel->addRef( loopback_address );
990
991 pf_comp->redirect_rules.push_back(
992 redirectRuleInfo( rule->getLabel(), otdst,
993 loopback_address, tsrv ) );
994 }
995 }
996 return true;
997 }
998
999
processNext()1000 bool NATCompiler_pf::swapAddressTableObjectsInRE::processNext()
1001 {
1002 NATCompiler_pf *pf_comp=dynamic_cast<NATCompiler_pf*>(compiler);
1003 Rule *rule=prev_processor->getNextRule(); if (rule==NULL) return false;
1004
1005 RuleElement *re=RuleElement::cast( rule->getFirstByType(re_type) );
1006
1007 list<MultiAddress*> cl;
1008 for (FWObject::iterator i=re->begin(); i!=re->end(); i++)
1009 {
1010 FWObject *o= *i;
1011 if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
1012 /*
1013 * All addressTable objects will be run-time here because we
1014 * switch them in preprocessor. The difference is: if address
1015 * table was originally run-time, at this point it will have
1016 * no children, however if it was compile-time originally, it
1017 * will have children objects. That is how we distinguish
1018 * them in this rule processor. Here we only deal with
1019 * AddressTable objects that originally used to be
1020 * compile-time because we need to create tables for them.
1021 */
1022 if (AddressTable::cast(o)!=NULL &&
1023 AddressTable::cast(o)->isRunTime() &&
1024 o->size() > 0)
1025 cl.push_back(MultiAddress::cast(o));
1026 }
1027
1028 if (!cl.empty())
1029 {
1030 for (list<MultiAddress*>::iterator i=cl.begin(); i!=cl.end(); i++)
1031 {
1032 MultiAddress *atbl = *i;
1033
1034 // Need to make sure the ID of the MultiAddressRunTime
1035 // object created here is stable and is always the same
1036 // for the same MultiAddress object. In particular this
1037 // ensures that we reuse tables between policy and NAT rules
1038 string mart_id_str = FWObjectDatabase::getStringId(atbl->getId()) +
1039 "_runtime";
1040 int mart_id = FWObjectDatabase::registerStringId(mart_id_str);
1041
1042 MultiAddressRunTime *mart =
1043 MultiAddressRunTime::cast(compiler->dbcopy->findInIndex(mart_id));
1044 if (mart==NULL)
1045 {
1046 mart = new MultiAddressRunTime(atbl);
1047
1048 // need to ensure stable ID for the runtime object, so
1049 // that when the same object is replaced in different
1050 // rulesets by different compiler passes, chosen
1051 // runtime object has the same ID and is identified as
1052 // the same by the compiler.
1053
1054 mart->setId( mart_id );
1055 compiler->dbcopy->addToIndex(mart);
1056 compiler->persistent_objects->add(mart);
1057
1058 // register this object as a table
1059 string tblname = atbl->getName();
1060 string tblID = tblname + "_addressTableObject";
1061 pf_comp->tables->registerTable(tblname,tblID,atbl);
1062 }
1063
1064 re->removeRef(atbl);
1065 re->addRef(mart);
1066 }
1067 tmp_queue.push_back(rule);
1068 return true;
1069 }
1070
1071 tmp_queue.push_back(rule);
1072 return true;
1073 }
1074
1075
processNext()1076 bool NATCompiler_pf::processMultiAddressObjectsInRE::processNext()
1077 {
1078 NATCompiler_pf *pf_comp=dynamic_cast<NATCompiler_pf*>(compiler);
1079 NATRule *rule=getNext(); if (rule==NULL) return false;
1080
1081 RuleElement *re=RuleElement::cast( rule->getFirstByType(re_type) );
1082 bool neg = re->getNeg();
1083
1084 list<FWObject*> cl;
1085
1086 try
1087 {
1088 for (FWObject::iterator i=re->begin(); i!=re->end(); i++)
1089 {
1090 FWObject *o= *i;
1091 if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
1092
1093 MultiAddressRunTime *atrt = MultiAddressRunTime::cast(o);
1094 if (atrt!=NULL && atrt->getSubstitutionTypeName()==AddressTable::TYPENAME)
1095 {
1096 if (re->size()>1 && neg)
1097 {
1098 string err = "AddressTable object can not be used with "
1099 "negation in combination with other objects "
1100 "in the same rule element.";
1101 compiler->abort(rule, err);
1102 }
1103 o->setBool("pf_table",true);
1104 string tblname = o->getName();
1105 string tblID = tblname + "_addressTableObject";
1106 pf_comp->tables->registerTable(tblname,tblID,o);
1107 cl.push_back(o);
1108 }
1109 }
1110 } catch(FWException &ex) // TableFactory::registerTable throws exception
1111 {
1112 string err;
1113 err = "Can not process MultiAddress object in rule " +
1114 rule->getLabel() + ". Error: " + ex.toString();
1115 compiler->abort(rule, err);
1116 }
1117
1118 if (!cl.empty())
1119 {
1120 RuleElement *nre;
1121
1122 for (FWObject::iterator i=cl.begin(); i!=cl.end(); i++)
1123 {
1124 NATRule *r= compiler->dbcopy->createNATRule();
1125 compiler->temp_ruleset->add(r);
1126 r->duplicate(rule);
1127 nre=RuleElement::cast( r->getFirstByType(re_type) );
1128 nre->clearChildren();
1129 nre->addRef( *i );
1130 tmp_queue.push_back(r);
1131 }
1132
1133 for (FWObject::iterator i=cl.begin(); i!=cl.end(); i++)
1134 re->removeRef( *i );
1135
1136 if (!re->isAny())
1137 tmp_queue.push_back(rule);
1138
1139 return true;
1140 }
1141
1142 tmp_queue.push_back(rule);
1143 return true;
1144 }
1145
1146
1147
findDynamicInterfaces(RuleElement * re,Rule * rule)1148 void NATCompiler_pf::checkForDynamicInterfacesOfOtherObjects::findDynamicInterfaces(RuleElement *re,
1149 Rule *rule)
1150 {
1151 if (re->isAny()) return;
1152 list<FWObject*> cl;
1153 for (list<FWObject*>::iterator i1=re->begin(); i1!=re->end(); ++i1)
1154 {
1155 FWObject *o = *i1;
1156 FWObject *obj = o;
1157 if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
1158 Interface *ifs = Interface::cast(obj);
1159
1160 if (ifs && Cluster::isA(ifs->getParent()))
1161 {
1162 FailoverClusterGroup *failover_group =
1163 FailoverClusterGroup::cast(
1164 ifs->getFirstByType(FailoverClusterGroup::TYPENAME));
1165 if (failover_group)
1166 {
1167 for (FWObjectTypedChildIterator it =
1168 failover_group->findByType(FWObjectReference::TYPENAME);
1169 it != it.end(); ++it)
1170 {
1171 Interface *member_iface = Interface::cast(FWObjectReference::getObject(*it));
1172 assert(member_iface);
1173 if (member_iface->isChildOf(compiler->fw))
1174 {
1175 ifs = member_iface;
1176 break;
1177 }
1178 }
1179 }
1180 }
1181
1182 if (ifs && ifs->isDyn() && ! ifs->isChildOf(compiler->fw))
1183 {
1184 QString err(
1185 "Can not build rule using dynamic interface '%1' "
1186 "of the object '%2' because its address is unknown.");
1187 compiler->abort(
1188 rule, err
1189 .arg(ifs->getName().c_str())
1190 .arg(ifs->getParent()->getName().c_str()).toStdString());
1191 }
1192 }
1193 }
1194
1195
processNext()1196 bool NATCompiler_pf::checkForDynamicInterfacesOfOtherObjects::processNext()
1197 {
1198 NATRule *rule=getNext(); if (rule==NULL) return false;
1199
1200 findDynamicInterfaces( rule->getOSrc() , rule );
1201 findDynamicInterfaces( rule->getODst() , rule );
1202 findDynamicInterfaces( rule->getTSrc() , rule );
1203 findDynamicInterfaces( rule->getTDst() , rule );
1204
1205 tmp_queue.push_back(rule);
1206 return true;
1207 }
1208
processNext()1209 bool NATCompiler_pf::createTables::processNext()
1210 {
1211 NATCompiler_pf *pf_comp=dynamic_cast<NATCompiler_pf*>(compiler);
1212 NATRule *rule=getNext(); if (rule==NULL) return false;
1213
1214 RuleElementOSrc *osrc=rule->getOSrc();
1215 RuleElementODst *odst=rule->getODst();
1216
1217 if (osrc->size()!=1) pf_comp->tables->createTablesForRE(osrc,rule);
1218 if (odst->size()!=1) pf_comp->tables->createTablesForRE(odst,rule);
1219
1220 #if 0
1221 RuleElementTSrc *tsrc=rule->getTSrc();
1222 RuleElementTDst *tdst=rule->getTDst();
1223
1224 if (tsrc->size()!=1) pf_comp->tables->createTablesForRE(tsrc,rule);
1225 if (tdst->size()!=1) pf_comp->tables->createTablesForRE(tdst,rule);
1226 #endif
1227
1228 tmp_queue.push_back(rule);
1229 return true;
1230 }
1231
1232
compile()1233 void NATCompiler_pf::compile()
1234 {
1235 bool manage_virtual_addr=fwopt->getBool("manage_virtual_addr");
1236
1237 string banner = " Compiling NAT rules for " + fw->getName();
1238 if (!getRuleSetName().empty()) banner += " ruleset " + getRuleSetName();
1239 if (ipv6) banner += ", IPv6";
1240 info(banner);
1241
1242 Compiler::compile();
1243
1244 add( new Begin());
1245 add( new printTotalNumberOfRules() );
1246
1247 add( new singleRuleFilter());
1248
1249 add(new expandGroupsInItfOutb("expand groups in Interface"));
1250 add(new replaceClusterInterfaceInItfOutb(
1251 "replace cluster interfaces with member interfaces in "
1252 "the Interface rule element"));
1253 add(new singleObjectNegationItfOutb(
1254 "process single object negation in inbound Itf"));
1255 add(new ItfOutbNegation("process negation in Itf"));
1256
1257 add( new recursiveGroupsInOSrc("check for recursive groups in OSRC") );
1258 add( new recursiveGroupsInODst("check for recursive groups in ODST") );
1259 add( new recursiveGroupsInOSrv("check for recursive groups in OSRV") );
1260
1261 add( new recursiveGroupsInTSrc("check for recursive groups in TSRC") );
1262 add( new recursiveGroupsInTDst("check for recursive groups in TDST") );
1263 add( new recursiveGroupsInTSrv("check for recursive groups in TSRV") );
1264
1265 add( new emptyGroupsInOSrc( "check for empty groups in OSRC" ) );
1266 add( new emptyGroupsInODst( "check for empty groups in ODST" ) );
1267 add( new emptyGroupsInOSrv( "check for empty groups in OSRV" ) );
1268
1269 add( new emptyGroupsInTSrc( "check for empty groups in TSRC" ) );
1270 add( new emptyGroupsInTDst( "check for empty groups in TDST" ) );
1271 add( new emptyGroupsInTSrv( "check for empty groups in TSRV" ) );
1272
1273 if (fw->getOptionsObject()->getBool("preserve_group_names"))
1274 {
1275 add(new RegisterGroupsAndTablesInOSrc(
1276 "register object groups and tables in OSrc"));
1277 add(new RegisterGroupsAndTablesInODst(
1278 "register object groups and tables in ODst"));
1279 }
1280
1281 add( new ExpandGroups( "expand groups" ) );
1282 add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
1283 add( new eliminateDuplicatesInOSRC( "eliminate duplicates in OSRC") );
1284 add( new eliminateDuplicatesInODST( "eliminate duplicates in ODST") );
1285 add( new eliminateDuplicatesInOSRV( "eliminate duplicates in OSRV") );
1286
1287 add( new swapMultiAddressObjectsInOSrc(
1288 " swap MultiAddress -> MultiAddressRunTime in OSrc") );
1289 add( new swapMultiAddressObjectsInODst(
1290 " swap MultiAddress -> MultiAddressRunTime in ODst") );
1291 add( new swapMultiAddressObjectsInTSrc(
1292 " swap MultiAddress -> MultiAddressRunTime in TSrc") );
1293 add( new swapMultiAddressObjectsInTDst(
1294 " swap MultiAddress -> MultiAddressRunTime in TDst") );
1295
1296 add( new swapAddressTableObjectsInOSrc(
1297 "AddressTable -> MultiAddressRunTime in OSrc") );
1298 add( new swapAddressTableObjectsInODst(
1299 "AddressTable -> MultiAddressRunTime in ODst") );
1300 add( new swapAddressTableObjectsInTSrc(
1301 "AddressTable -> MultiAddressRunTime in TSrc") );
1302 add( new swapAddressTableObjectsInTDst(
1303 "AddressTable -> MultiAddressRunTime in TDst") );
1304
1305 add( new processMultiAddressObjectsInOSrc(
1306 "process MultiAddress objects in OSrc") );
1307 add( new processMultiAddressObjectsInODst(
1308 "process MultiAddress objects in ODst") );
1309 add( new processMultiAddressObjectsInTSrc(
1310 "process MultiAddress objects in TSrc") );
1311 add( new processMultiAddressObjectsInTDst(
1312 "process MultiAddress objects in TDst") );
1313
1314 add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
1315
1316 add( new splitOnOSrv( "split rule on original service" ) );
1317 add( new fillTranslatedSrv( "fill translated service" ) );
1318
1319 //add( new doOSrcNegation( "process negation in OSrc" ) );
1320 //add( new doODstNegation( "process negation in ODst" ) );
1321 //add( new doOSrvNegation( "process negation in OSrv" ) );
1322
1323 add( new NATRuleType( "determine NAT rule types" ) );
1324 add( new splitSDNATRule("split SDNAT rules" ) );
1325 add( new NATRuleType( "determine NAT rule types" ) );
1326 add( new VerifyRules( "verify NAT rules" ) );
1327
1328 add( new ReplaceFirewallObjectsODst(
1329 "replace references to the firewall in ODst" ) );
1330 add( new ReplaceFirewallObjectsTSrc(
1331 "replace references to the firewall in TSrc" ) );
1332
1333 add( new ReplaceObjectsTDst( "replace objects in TDst" ) );
1334
1335 add( new ExpandMultipleAddresses( "expand multiple addresses" ) );
1336
1337 // we might get empty RE after expanding multiple addresses,
1338 // for example when unnumbered interface is used in TSRC. Note
1339 // that VerifyRules should not allow this, but we may still
1340 // get here in the test mode. Calling dropRuleWithEmptyRE works
1341 // as a fail-safe and prevents crash.
1342 add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
1343
1344 if ( manage_virtual_addr )
1345 add( new addVirtualAddress("add virtual addresses for NAT rules"));
1346
1347 add( new checkForUnnumbered("check for unnumbered interfaces" ) );
1348 add( new checkForDynamicInterfacesOfOtherObjects(
1349 "check for dynamic interfaces of other hosts and firewalls"));
1350 add( new ExpandAddressRanges( "expand address range objects" ) );
1351
1352 add( new splitForTSrc(
1353 "split if addresses in TSrc belong to different networks" ));
1354
1355 add( new AssignInterface( "assign rules to interfaces" ) );
1356
1357 add( new checkForObjectsWithErrors(
1358 "check if we have objects with errors in rule elements"));
1359
1360 add( new createTables("create tables"));
1361 // add( new PrintTables( "print tables" ) );
1362
1363 add( new PrintRule("generate pf code") );
1364 add( new simplePrintProgress() );
1365
1366 runRuleProcessors();
1367
1368 }
1369
1370
epilog()1371 void NATCompiler_pf::epilog()
1372 {
1373 }
1374
~NATCompiler_pf()1375 NATCompiler_pf::~NATCompiler_pf()
1376 {
1377 //if (tables) tables->detach();
1378 }
1379
1380