From 333f6e981e87583219cd30499d906c1869346de8 Mon Sep 17 00:00:00 2001 From: Boris Schrijver Date: Mon, 1 Feb 2016 15:57:26 +0100 Subject: [PATCH 1/8] Add iptables copnversion script. Source: https://raw.githubusercontent.com/sl0/conv/master/iptables_converter.py --- .../config/opt/cloud/bin/cs_iptables_save.py | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py b/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py new file mode 100644 index 000000000000..4b64c2b1da9f --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py @@ -0,0 +1,267 @@ +#!/usr/bin/python +# +# -*- coding: utf-8 -*- +# +""" +iptables_converter.py: + convert iptables commands within a script + into a correspondig iptables-save script + + default filename to read is rules, to read some other + file, append: -s filename + + output is written to stdout for maximum flexibilty + +Author: Johannes Hubertz +Date: 2015-03-17 +version: 0.9.8 +License: GNU General Public License version 3 or later + +Have Fun! +""" + +try: + from collections import UserDict +except ImportError: + from UserDict import UserDict +from optparse import OptionParser +import re +import sys + + +class ConverterError(): + """on accidential case of error show given reason""" + + def __init__(self, message): + """message to stdout to compatible testings 2.7 and 3.4""" + print (message) + sys.exit(1) + + +class Chains(UserDict): + """this is for one type of tables""" + + def __init__(self, name, tables): + """init Chains object""" + UserDict.__init__(self) + self.name = name + self.tables = tables + self.predef = tables + self.reset() # name, tables) + + def put_into_fgr(self, content): + """fill this line into this tabular""" + self.length += 1 + cha = "filter" + # act = "" + liste = content.split() + action = liste[0] + if "-t" in action: + liste.pop(0) # remove 1st: -t + fname = liste.pop(0) + legals = ["filter", "nat", "raw", "mangle"] + if fname not in legals: + msg = "Valid is one of %s, got: %s" % (legals, fname) + raise ValueError(msg) + action = liste[0] + content = "" # rebuild content from here + for elem in liste: + content = content + elem + " " + if len(liste) > 1: + chain_name = liste[1] + if "-F" in action: + self.reset() + return + if "-P" in action: + liste.pop(0) + cha = liste.pop(0) + new = liste.pop(0) + if new not in ["ACCEPT", "DROP", "REJECT"]: + msg = "Illegal policy: % s" % (new) + raise ValueError(msg) + self.poli[cha] = new + return + if "-X" in action: + predef = ['INPUT', 'FORWARD', 'OUTPUT', + 'PREROUTING', 'POSTROUTING'] + rem_chain_name = liste.pop(1) + if rem_chain_name in predef: + msg = "Cannot remove predefined chain" + raise ValueError(msg) + if rem_chain_name in self.data: + self.data[rem_chain_name] = [] # empty list + self.poli[rem_chain_name] = "-" # empty policy, no need + self.data.pop(rem_chain_name) + return + if "-N" in action: + new_chain_name = liste.pop(1) + existing = self.data.keys() + if new_chain_name in existing: + msg = "Chain %s already exists" % (new_chain_name) + raise ValueError(msg) + self.data[new_chain_name] = [] # empty list + self.poli[new_chain_name] = "-" # empty policy, no need + return + if "-I" in action: # or "-A" in action: + chain_name = liste[1] + existing = self.data.keys() + if chain_name not in existing: + msg = "invalid chain name: %s" % (chain_name) + raise ValueError(msg) + kette = self.data[chain_name] + if len(kette) > 0: + kette.insert(0, content) + else: + msg = "Empty chain %s allows append only!" % (chain_name) + raise ValueError(msg) + self.data[chain_name] = kette + return + if "-A" in action: # or "-I" in action: + chain_name = liste[1] + existing = self.data.keys() + if chain_name not in existing: + msg = "invalid chain name: %s" % (chain_name) + raise ValueError(msg) + kette = self.data[chain_name] + kette.append(content) + self.data[chain_name] = kette + return + msg = "Unknown filter command in input:", content + raise ValueError(msg) + + def reset(self): # name, tables): + """ + name is one of filter, nat, raw, mangle, + tables is a list of tables in that table-class + """ + self.poli = {} # empty dict + self.length = 0 + self.policy = "-" + for tabular in self.tables: + self.data[tabular] = [] + self.poli[tabular] = "ACCEPT" + + +class Tables(UserDict): + """ + some chaingroups in tables are predef: filter, nat, mangle, raw + """ + + def __init__(self, fname="reference-one"): + """init Tables Object is easy going""" + UserDict.__init__(self) + self.reset(fname) + + def reset(self, fname): + """all predefined Chains aka lists are setup as new here""" + filter = Chains("filter", ["INPUT", "FORWARD", "OUTPUT"]) + + mang = ["PREROUTING", "INPUT", "FORWARD", "OUTPUT", "POSTROUTING", ] + mangle = Chains("mangle", mang) + + # kernel 2.6.32 has no INPUT in NAT! + nat = Chains("nat", ["PREROUTING", "OUTPUT", "POSTROUTING"]) + + raw = Chains("raw", ["PREROUTING", "OUTPUT", ]) + + self.data["filter"] = filter + self.data["mangle"] = mangle + self.data["nat"] = nat + self.data["raw"] = raw + if len(fname) > 0: + self.linecounter = self.read_file(fname) + + def table_printout(self): + """printout nonempty tabulars in fixed sequence""" + for key in ["raw", "nat", "mangle", "filter"]: + len = self.data[key].length + if len > -1: + print("*%s" % (self.data[key].name)) + for chain in self.data[key].keys(): + poli = self.data[key].poli[chain] + print(":%s %s [0:0]" % (chain, poli)) + for chain in self.data[key].values(): + for elem in chain: + print(elem) + print("COMMIT") + + def put_into_tables(self, line): + """put line into matching Chains-object""" + liste = line.split() + liste.pop(0) # we always know, it's iptables + rest = "" + for elem in liste: # remove redirects and the like + if ">" not in elem: + rest = rest + elem + " " # string again with single blanks + action = liste.pop(0) # action is one of {N,F,A,I, etc.} + fam = "filter" + if "-t nat" in line: # nat filter group + fam = "nat" + elif "-t mangle" in line: # mangle filter group + fam = "mangle" + elif "-t raw" in line: # raw filter group + fam = "raw" + fam_dict = self.data[fam] # select the group dictionary + fam_dict.put_into_fgr(rest) # do action thers + + def read_file(self, fname): + """read file into Tables-object""" + self.linecounter = 0 + self.tblctr = 0 + try: + fil0 = open(fname, 'r') + for zeile in fil0: + line = str(zeile.strip()) + self.linecounter += 1 + if line.startswith('#'): + continue + for element in ['\$', '\(', '\)', ]: + if re.search(element, line): + m1 = "Line %d:\n%s\nplain files only, " % \ + (self.linecounter, line) + if element in ['\(', '\)', ]: + m2 = "unable to convert shell functions, abort" + else: + m2 = "unable to resolve shell variables, abort" + msg = m1 + m2 + raise ConverterError(msg) + for muster in ["^/sbin/iptables ", "^iptables "]: + if re.search(muster, line): + self.tblctr += 1 + self.put_into_tables(line) + fil0.close() + except ValueError as err: + print (fname + ": "), err + sys.exit(1) + except IOError as err: + print(fname + ": "), err.strerror + sys.exit(1) + if not fname == "reference-one": + print("# generated from: %s" % (fname)) + + +def main(): + """ + main parses options, filnames and the like + one option (-s) may be given: input-filename + if none given, it defaults to: rules + """ + usage = "usage: %prog --help | -h \n\n\t%prog: version 0.9.8" + usage = usage + "\tHave Fun!" + parser = OptionParser(usage) + parser.disable_interspersed_args() + parser.add_option("-s", "", dest="sourcefile", + help="file with iptables commands, default: rules\n") + (options, args) = parser.parse_args() + hlp = "\n\tplease use \"--help\" as argument, abort!\n" + if options.sourcefile is None: + options.sourcefile = "rules" + sourcefile = options.sourcefile + + chains = Tables(sourcefile) + chains.table_printout() + + +if __name__ == "__main__": + main() + sys.exit(0) \ No newline at end of file From e8c1a0560ef8215c0fa52ae70489a4374db81cf0 Mon Sep 17 00:00:00 2001 From: Boris Schrijver Date: Mon, 1 Feb 2016 15:58:27 +0100 Subject: [PATCH 2/8] Restore iptables at once using iptables-restore instead of calling iptables numerous times Conflicts: systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py --- .../debian/config/opt/cloud/bin/configure.py | 19 +-- .../config/opt/cloud/bin/cs/CsAddress.py | 23 ++- .../config/opt/cloud/bin/cs/CsLoadBalancer.py | 9 +- .../config/opt/cloud/bin/cs/CsNetfilter.py | 83 +++++------ .../config/opt/cloud/bin/cs_iptables_save.py | 132 ++++++------------ 5 files changed, 110 insertions(+), 156 deletions(-) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/configure.py b/systemvm/patches/debian/config/opt/cloud/bin/configure.py index 7f0df5be0df2..4143dac088c9 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/configure.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/configure.py @@ -17,27 +17,16 @@ # specific language governing permissions and limitations # under the License. import sys -import os import base64 -from merge import DataBag -from pprint import pprint -import subprocess -import logging import re -import time -import shutil -import os.path -import os from fcntl import flock, LOCK_EX, LOCK_UN -from cs.CsDatabag import CsDataBag, CsCmdLine -import cs.CsHelper +from cs.CsDatabag import CsDataBag from cs.CsNetfilter import CsNetfilters from cs.CsDhcp import CsDhcp from cs.CsRedundant import * from cs.CsFile import CsFile -from cs.CsApp import CsApache, CsDnsmasq from cs.CsMonitor import CsMonitor from cs.CsLoadBalancer import CsLoadBalancer from cs.CsConfig import CsConfig @@ -260,7 +249,7 @@ def create(self): rstr = "%s -m icmp --icmp-type %s" % (rstr, self.icmp_type) rstr = "%s %s -j %s" % (rstr, self.dport, self.action) rstr = rstr.replace(" ", " ").lstrip() - self.fw.append([self.table, self.count, rstr]) + self.fw.append([self.table, "", rstr]) def flushAllowAllEgressRules(self): logging.debug("Flush allow 'all' egress firewall rule") @@ -483,7 +472,7 @@ def configure_iptables(self, dev, obj): self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])]) self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 4500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])]) self.fw.append(["", "front", "-A INPUT -i %s -p esp -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])]) - self.fw.append(["nat", "front", "-A POSTROUTING -t nat -o %s -m mark --mark 0x525 -j ACCEPT" % dev]) + self.fw.append(["nat", "front", "-A POSTROUTING -o %s -m mark --mark 0x525 -j ACCEPT" % dev]) for net in obj['peer_guest_cidr_list'].lstrip().rstrip().split(','): self.fw.append(["mangle", "front", "-A FORWARD -s %s -d %s -j MARK --set-xmark 0x525/0xffffffff" % (obj['local_guest_cidr'], net)]) @@ -809,7 +798,7 @@ def forward_vr(self, rule): rule['internal_ip'], internal_fwports ) - fw4 = "-j SNAT --to-source %s -A POSTROUTING -s %s -d %s/32 -o %s -p %s -m %s --dport %s" % \ + fw4 = "-A POSTROUTING -j SNAT --to-source %s -s %s -d %s/32 -o %s -p %s -m %s --dport %s" % \ ( self.getGuestIp(), self.getNetworkByIp(rule['internal_ip']), diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py index 43cd6396587a..eabd9a4f82fa 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py @@ -401,8 +401,7 @@ def fw_router(self): self.fw.append(["filter", "", "-A INPUT -d 224.0.0.18/32 -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -d 225.0.0.50/32 -j ACCEPT"]) - self.fw.append(["filter", "", "-A INPUT -i %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % - self.dev]) + self.fw.append(["filter", "", "-A INPUT -i %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % self.dev]) self.fw.append(["filter", "", "-A INPUT -p icmp -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -i lo -j ACCEPT"]) @@ -436,6 +435,13 @@ def fw_router(self): def fw_vpcrouter(self): if not self.config.is_vpc(): return + self.fw.append(["mangle", "front", "-A PREROUTING " + + "-m state --state RELATED,ESTABLISHED " + + "-j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff"]) + + self.fw.append(["", "front", "-A FORWARD -j NETWORK_STATS"]) + self.fw.append(["", "front", "-A INPUT -j NETWORK_STATS"]) + self.fw.append(["", "front", "-A OUTPUT -j NETWORK_STATS"]) self.fw.append(["filter", "", "-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT"]) @@ -484,10 +490,6 @@ def fw_vpcrouter(self): (guestNetworkCidr, self.dev, self.address['public_ip'])]) if self.get_type() in ["public"]: - self.fw.append(["", "front", - "-A FORWARD -o %s -d %s -j ACL_INBOUND_%s" % ( - self.dev, self.address['network'], self.dev) - ]) self.fw.append( ["mangle", "", "-A FORWARD -j VPN_STATS_%s" % self.dev]) self.fw.append( @@ -495,11 +497,7 @@ def fw_vpcrouter(self): self.fw.append( ["mangle", "", "-A VPN_STATS_%s -i %s -m mark --mark 0x524/0xffffffff" % (self.dev, self.dev)]) self.fw.append( - ["", "front", "-A FORWARD -j NETWORK_STATS_%s" % self.dev]) - - self.fw.append(["", "front", "-A FORWARD -j NETWORK_STATS"]) - self.fw.append(["", "front", "-A INPUT -j NETWORK_STATS"]) - self.fw.append(["", "front", "-A OUTPUT -j NETWORK_STATS"]) + ["", "front", "-A FORWARD -j NETWORK_STATS_eth1"]) self.fw.append(["", "", "-A NETWORK_STATS -i eth0 -o eth2 -p tcp"]) self.fw.append(["", "", "-A NETWORK_STATS -i eth2 -o eth0 -p tcp"]) @@ -508,7 +506,8 @@ def fw_vpcrouter(self): self.fw.append(["filter", "", "-A INPUT -d 224.0.0.18/32 -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -d 225.0.0.50/32 -j ACCEPT"]) - + self.fw.append(["filter", "", "-A INPUT -i %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % self.dev]) + self.fw.append(["filter", "", "-A INPUT -i lo -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -p icmp -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -i lo -j ACCEPT"]) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsLoadBalancer.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsLoadBalancer.py index d8f39dcd24a2..8c178ca00cd7 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsLoadBalancer.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsLoadBalancer.py @@ -71,14 +71,15 @@ def _configure_firewall(self, add_rules, remove_rules, stat_rules): port = path[1] firewall.append(["filter", "", "-A INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)]) - for rules in remove_rules: + for rules in stat_rules: path = rules.split(':') ip = path[0] port = path[1] - firewall.append(["filter", "", "-D INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)]) + firewall.append(["filter", "", "-A INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)]) - for rules in stat_rules: + for rules in remove_rules: path = rules.split(':') ip = path[0] port = path[1] - firewall.append(["filter", "", "-A INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)]) + if ["filter", "", "-A INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)] in firewall: + firewall.remove(["filter", "", "-A INPUT -p tcp -m tcp -d %s --dport %s -m state --state NEW -j ACCEPT" % (ip, port)]) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py index 80b930e3fb6b..34e2b4f49eaf 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py @@ -15,10 +15,12 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +from __future__ import print_function + import CsHelper -from pprint import pprint -from CsDatabag import CsDataBag, CsCmdLine +from CsDatabag import CsCmdLine import logging +from cs_iptables_save import Tables class CsChain(object): @@ -81,6 +83,7 @@ class CsNetfilters(object): def __init__(self, load=True): self.rules = [] + self.iptablerules = [] self.table = CsTable() self.chain = CsChain() if load: @@ -91,7 +94,10 @@ def get_all_rules(self): if i.startswith('*'): # Table self.table.add(i[1:]) if i.startswith(':'): # Chain - self.chain.add(self.table.last(), i[1:].split(' ')[0]) + string = i[1:].split(' ')[0] + cmd = "iptables -t %s -N %s" % (self.table.last(), string) + self.iptablerules.append(cmd) + self.chain.add(self.table.last(), string) if i.startswith('-A'): # Rule self.chain.add_rule(i.split()[1]) rule = CsNetfilter() @@ -125,22 +131,25 @@ def has_rule(self, new_rule): def get_unseen(self): del_list = [x for x in self.rules if x.unseen()] for r in del_list: - cmd = "iptables -t %s %s" % (r.get_table(), r.to_str(True)) - logging.debug("unseen cmd: %s ", cmd) - CsHelper.execute(cmd) - # print "Delete rule %s from table %s" % (r.to_str(True), r.get_table()) + self.delete(r) logging.info("Delete rule %s from table %s", r.to_str(True), r.get_table()) def compare(self, list): """ Compare reality with what is needed """ - # PASS 1: Ensure all chains are present + for c in self.chain.get("filter"): + # Ensure all inbound/outbound chains have a default drop rule + if c.startswith("ACL_INBOUND") or c.startswith("ACL_OUTBOUND"): + list.append(["filter", "", "-A %s -j DROP" % c]) + # PASS 1: Ensure all chains are present and cleanup unused rules. for fw in list: new_rule = CsNetfilter() new_rule.parse(fw[2]) new_rule.set_table(fw[0]) - self.add_chain(new_rule) + self.has_rule(new_rule) + + self.del_standard() + self.get_unseen() - ruleSet = set() # PASS 2: Create rules for fw in list: tupledFw = tuple(fw) @@ -154,36 +163,32 @@ def compare(self, list): if isinstance(fw[1], int): new_rule.set_count(fw[1]) - rule_chain = new_rule.get_chain() - - logging.debug("Checking if the rule already exists: rule=%s table=%s chain=%s", new_rule.get_rule(), new_rule.get_table(), new_rule.get_chain()) - if self.has_rule(new_rule): - logging.debug("Exists: rule=%s table=%s", fw[2], new_rule.get_table()) - else: - # print "Add rule %s in table %s" % ( fw[2], new_rule.get_table()) - logging.info("Add: rule=%s table=%s", fw[2], new_rule.get_table()) - # front means insert instead of append - cpy = fw[2] - if fw[1] == "front": - cpy = cpy.replace('-A', '-I') - if isinstance(fw[1], int): - # if the rule is for ACLs, we want to insert them in order, right before the DROP all - if rule_chain.startswith("ACL_INBOUND") or rule_chain.startswith("ACL_OUTBOUND"): - rule_count = self.chain.get_count(rule_chain) - cpy = cpy.replace("-A %s" % new_rule.get_chain(), '-I %s %s' % (new_rule.get_chain(), rule_count)) - else: - cpy = cpy.replace("-A %s" % new_rule.get_chain(), '-I %s %s' % (new_rule.get_chain(), fw[1])) - CsHelper.execute("iptables -t %s %s" % (new_rule.get_table(), cpy)) - ruleSet.add(tupledFw) - self.chain.add_rule(rule_chain) - self.del_standard() - self.get_unseen() + logging.info("Add: rule=%s table=%s", fw[2], new_rule.get_table()) + # front means insert instead of append + cpy = fw[2] + if fw[1] == "front": + cpy = cpy.replace('-A', '-I') + if isinstance(fw[1], int): + cpy = cpy.replace("-A %s" % new_rule.get_chain(), '-I %s %s' % (new_rule.get_chain(), fw[1])) + + self.iptablerules.append("iptables -t %s %s" % (new_rule.get_table(), cpy)) + self.apply_rules() + + def apply_rules(self): + s = [] + for r in self.iptablerules: + if r not in s: + s.append(r) - def add_chain(self, rule): - """ Add the given chain if it is not already present """ - if not self.has_chain(rule.get_table(), rule.get_chain()): - CsHelper.execute("iptables -t %s -N %s" % (rule.get_table(), rule.get_chain())) - self.chain.add(rule.get_table(), rule.get_chain()) + chains = Tables(s) + chains.table_printout() + + # COMMIT all rules. + result = CsHelper.execute("iptables-restore < /tmp/rules.save") + if result: + logging.info("iptables-restore result: %s", result) + else: + logging.info("iptables-restore result: success!") def del_standard(self): """ Del rules that are there but should not be deleted diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py b/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py index 4b64c2b1da9f..f5e61ac05d8a 100644 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py @@ -19,14 +19,15 @@ Have Fun! """ +from __future__ import print_function try: from collections import UserDict except ImportError: from UserDict import UserDict -from optparse import OptionParser import re import sys +import logging class ConverterError(): @@ -97,8 +98,8 @@ def put_into_fgr(self, content): new_chain_name = liste.pop(1) existing = self.data.keys() if new_chain_name in existing: - msg = "Chain %s already exists" % (new_chain_name) - raise ValueError(msg) + logging.debug("Chain %s already exists" % new_chain_name) + return self.data[new_chain_name] = [] # empty list self.poli[new_chain_name] = "-" # empty policy, no need return @@ -106,22 +107,18 @@ def put_into_fgr(self, content): chain_name = liste[1] existing = self.data.keys() if chain_name not in existing: - msg = "invalid chain name: %s" % (chain_name) - raise ValueError(msg) + self.data[chain_name] = [] + self.poli[chain_name] = "-" kette = self.data[chain_name] - if len(kette) > 0: - kette.insert(0, content) - else: - msg = "Empty chain %s allows append only!" % (chain_name) - raise ValueError(msg) + kette.insert(0, content.replace("-I", "-A")) self.data[chain_name] = kette return if "-A" in action: # or "-I" in action: chain_name = liste[1] existing = self.data.keys() if chain_name not in existing: - msg = "invalid chain name: %s" % (chain_name) - raise ValueError(msg) + self.data[chain_name] = [] + self.poli[chain_name] = "-" kette = self.data[chain_name] kette.append(content) self.data[chain_name] = kette @@ -147,12 +144,12 @@ class Tables(UserDict): some chaingroups in tables are predef: filter, nat, mangle, raw """ - def __init__(self, fname="reference-one"): + def __init__(self, rules): """init Tables Object is easy going""" UserDict.__init__(self) - self.reset(fname) + self.reset(rules) - def reset(self, fname): + def reset(self, rules): """all predefined Chains aka lists are setup as new here""" filter = Chains("filter", ["INPUT", "FORWARD", "OUTPUT"]) @@ -168,22 +165,23 @@ def reset(self, fname): self.data["mangle"] = mangle self.data["nat"] = nat self.data["raw"] = raw - if len(fname) > 0: - self.linecounter = self.read_file(fname) + if rules is not None: + self.read_file(rules) def table_printout(self): """printout nonempty tabulars in fixed sequence""" - for key in ["raw", "nat", "mangle", "filter"]: - len = self.data[key].length - if len > -1: - print("*%s" % (self.data[key].name)) - for chain in self.data[key].keys(): - poli = self.data[key].poli[chain] - print(":%s %s [0:0]" % (chain, poli)) - for chain in self.data[key].values(): - for elem in chain: - print(elem) - print("COMMIT") + with open("/tmp/rules.save", 'w') as f: + for key in ["raw", "nat", "mangle", "filter"]: + len = self.data[key].length + if len > -1: + print("*%s" % (self.data[key].name), file=f) + for chain in self.data[key].keys(): + poli = self.data[key].poli[chain] + print(":%s %s [0:0]" % (chain, poli), file=f) + for chain in self.data[key].values(): + for elem in chain: + print(elem, file=f) + print("COMMIT", file=f) def put_into_tables(self, line): """put line into matching Chains-object""" @@ -204,64 +202,26 @@ def put_into_tables(self, line): fam_dict = self.data[fam] # select the group dictionary fam_dict.put_into_fgr(rest) # do action thers - def read_file(self, fname): + def read_file(self, rules): """read file into Tables-object""" self.linecounter = 0 self.tblctr = 0 - try: - fil0 = open(fname, 'r') - for zeile in fil0: - line = str(zeile.strip()) - self.linecounter += 1 - if line.startswith('#'): - continue - for element in ['\$', '\(', '\)', ]: - if re.search(element, line): - m1 = "Line %d:\n%s\nplain files only, " % \ - (self.linecounter, line) - if element in ['\(', '\)', ]: - m2 = "unable to convert shell functions, abort" - else: - m2 = "unable to resolve shell variables, abort" - msg = m1 + m2 - raise ConverterError(msg) - for muster in ["^/sbin/iptables ", "^iptables "]: - if re.search(muster, line): - self.tblctr += 1 - self.put_into_tables(line) - fil0.close() - except ValueError as err: - print (fname + ": "), err - sys.exit(1) - except IOError as err: - print(fname + ": "), err.strerror - sys.exit(1) - if not fname == "reference-one": - print("# generated from: %s" % (fname)) - - -def main(): - """ - main parses options, filnames and the like - one option (-s) may be given: input-filename - if none given, it defaults to: rules - """ - usage = "usage: %prog --help | -h \n\n\t%prog: version 0.9.8" - usage = usage + "\tHave Fun!" - parser = OptionParser(usage) - parser.disable_interspersed_args() - parser.add_option("-s", "", dest="sourcefile", - help="file with iptables commands, default: rules\n") - (options, args) = parser.parse_args() - hlp = "\n\tplease use \"--help\" as argument, abort!\n" - if options.sourcefile is None: - options.sourcefile = "rules" - sourcefile = options.sourcefile - - chains = Tables(sourcefile) - chains.table_printout() - - -if __name__ == "__main__": - main() - sys.exit(0) \ No newline at end of file + for zeile in rules: + line = str(zeile.strip()) + self.linecounter += 1 + if line.startswith('#'): + continue + for element in ['\$', '\(', '\)', ]: + if re.search(element, line): + m1 = "Line %d:\n%s\nplain files only, " % \ + (self.linecounter, line) + if element in ['\(', '\)', ]: + m2 = "unable to convert shell functions, abort" + else: + m2 = "unable to resolve shell variables, abort" + msg = m1 + m2 + raise ConverterError(msg) + for muster in ["^/sbin/iptables ", "^iptables "]: + if re.search(muster, line): + self.tblctr += 1 + self.put_into_tables(line) From 03e17fab69ea4f86866a3e46125b7b2380e30471 Mon Sep 17 00:00:00 2001 From: Boris Schrijver Date: Wed, 3 Feb 2016 15:30:19 +0100 Subject: [PATCH 3/8] Remove duplicate spaces, and thus duplicate rules. --- systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py index 34e2b4f49eaf..c186eecf6409 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsNetfilter.py @@ -177,6 +177,7 @@ def compare(self, list): def apply_rules(self): s = [] for r in self.iptablerules: + r.replace(' ', ' ') # Remove duplicate spaces if r not in s: s.append(r) From dcc989302db5e7167b6a45c348d9a0fa3d5cd7a0 Mon Sep 17 00:00:00 2001 From: Boris Schrijver Date: Wed, 3 Feb 2016 16:15:28 +0100 Subject: [PATCH 4/8] Wait for dnsmasq to finish restart Conflicts: systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py --- systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py index da9e6168ee0f..839c2c275f3d 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py @@ -60,7 +60,7 @@ def process(self): # We restart DNSMASQ every time the configure.py is called in order to avoid lease problems. if not self.cl.is_redundant() or self.cl.is_master(): - CsHelper.service("dnsmasq", "restart") + CsHelper.execute2("service dnsmasq restart") def configure_server(self): # self.conf.addeq("dhcp-hostsfile=%s" % DHCP_HOSTS) From 2048aa2311605659ade097c9fb2bc019d7a7a325 Mon Sep 17 00:00:00 2001 From: Boris Schrijver Date: Fri, 5 Feb 2016 21:04:06 +0100 Subject: [PATCH 5/8] Do not load previous firewall rules as we replace everyhing anyway Conflicts: systemvm/patches/debian/config/opt/cloud/bin/configure.py --- systemvm/patches/debian/config/opt/cloud/bin/configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/configure.py b/systemvm/patches/debian/config/opt/cloud/bin/configure.py index 4143dac088c9..a4a9034a4eab 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/configure.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/configure.py @@ -1000,7 +1000,7 @@ def main(argv): lb.process() logging.debug("Configuring iptables rules") - nf = CsNetfilters() + nf = CsNetfilters(False) nf.compare(config.get_fw()) logging.debug("Configuring iptables rules done ...saving rules") From 868e71d7f1d1bd3ebf50292fa9e54be64a6857e1 Mon Sep 17 00:00:00 2001 From: Boris Schrijver Date: Fri, 5 Feb 2016 21:11:59 +0100 Subject: [PATCH 6/8] Split the cidr lists so we won't hit the iptables-resture limits --- .../debian/config/opt/cloud/bin/configure.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/configure.py b/systemvm/patches/debian/config/opt/cloud/bin/configure.py index a4a9034a4eab..38335e0322b5 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/configure.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/configure.py @@ -196,7 +196,23 @@ def create(self): def process(self, direction, rule_list, base): count = base - for i in rule_list: + rule_list_splitted = [] + for rule in rule_list: + if ',' in rule['cidr']: + cidrs = rule['cidr'].split(',') + for cidr in cidrs: + new_rule = { + 'cidr': cidr, + 'last_port': rule['last_port'], + 'type': rule['type'], + 'first_port': rule['first_port'], + 'allowed': rule['allowed'] + } + rule_list_splitted.append(new_rule) + else: + rule_list_splitted.append(rule) + + for i in rule_list_splitted: r = self.AclRule(direction, self, i, self.config, count) r.create() count += 1 From 452b5db65c4db82842c82394095d0576deb548aa Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Mon, 8 May 2017 07:48:15 +0200 Subject: [PATCH 7/8] rat --- .../config/opt/cloud/bin/cs_iptables_save.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py b/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py index f5e61ac05d8a..59dd68a3f9f5 100644 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py @@ -2,6 +2,22 @@ # # -*- coding: utf-8 -*- # +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. """ iptables_converter.py: convert iptables commands within a script From 657e3952c220d27fc9ff01e127aa77d377f7d9f2 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 23 Nov 2017 14:14:04 +0530 Subject: [PATCH 8/8] update to latest iptables-converter contrib release 0.9.10 Signed-off-by: Rohit Yadav --- .../config/opt/cloud/bin/cs_iptables_save.py | 362 +++++++++++++----- 1 file changed, 256 insertions(+), 106 deletions(-) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py b/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py index 59dd68a3f9f5..54a7502018e5 100644 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs_iptables_save.py @@ -26,126 +26,153 @@ default filename to read is rules, to read some other file, append: -s filename - output is written to stdout for maximum flexibilty + default output is written to stdout, for writing + to some file, append: -d filename Author: Johannes Hubertz -Date: 2015-03-17 -version: 0.9.8 +Date: 2017-11-16 +Version: 0.9.10 License: GNU General Public License version 3 or later + Apache License Version 2.0 Have Fun! """ + from __future__ import print_function try: from collections import UserDict except ImportError: from UserDict import UserDict +from optparse import OptionParser import re +import os import sys -import logging - -class ConverterError(): - """on accidential case of error show given reason""" - def __init__(self, message): - """message to stdout to compatible testings 2.7 and 3.4""" - print (message) - sys.exit(1) +class ConverterError(Exception): + pass class Chains(UserDict): - """this is for one type of tables""" + """ chains are grouped in iptables + + :param str name: chain group name, 'filter', 'nat', ... + :param list tables: list of chains + :param bool sloppy: needs '-N'(default) or not - def __init__(self, name, tables): - """init Chains object""" + :return: object representing chain group + :rtype: Chains + :raises ConverterError: on some illegal conditions + """ + + def __init__(self, name, tables, sloppy=False): + """ init Chains object + """ UserDict.__init__(self) self.name = name self.tables = tables self.predef = tables + self.sloppy = sloppy self.reset() # name, tables) def put_into_fgr(self, content): - """fill this line into this tabular""" + """ fill this line into this tabular + + :param str content: one line of inputfile + :return: None + :raises ConverterError: on some illegal conditions + """ self.length += 1 - cha = "filter" - # act = "" - liste = content.split() - action = liste[0] + if len(content) == 0: + return + elements = content.split() + action = elements[0] if "-t" in action: - liste.pop(0) # remove 1st: -t - fname = liste.pop(0) + elements.pop(0) # remove 1st: -t + fname = elements.pop(0) legals = ["filter", "nat", "raw", "mangle"] if fname not in legals: msg = "Valid is one of %s, got: %s" % (legals, fname) - raise ValueError(msg) - action = liste[0] + raise ConverterError(msg) + action = elements[0] content = "" # rebuild content from here - for elem in liste: + for elem in elements: content = content + elem + " " - if len(liste) > 1: - chain_name = liste[1] + if len(elements) > 1: + chain_name = elements[1] if "-F" in action: self.reset() return if "-P" in action: - liste.pop(0) - cha = liste.pop(0) - new = liste.pop(0) + elements.pop(0) + cha = elements.pop(0) + new = elements.pop(0) if new not in ["ACCEPT", "DROP", "REJECT"]: msg = "Illegal policy: % s" % (new) - raise ValueError(msg) + raise ConverterError(msg) self.poli[cha] = new return if "-X" in action: predef = ['INPUT', 'FORWARD', 'OUTPUT', 'PREROUTING', 'POSTROUTING'] - rem_chain_name = liste.pop(1) + rem_chain_name = elements.pop(1) if rem_chain_name in predef: msg = "Cannot remove predefined chain" - raise ValueError(msg) + raise ConverterError(msg) if rem_chain_name in self.data: self.data[rem_chain_name] = [] # empty list self.poli[rem_chain_name] = "-" # empty policy, no need self.data.pop(rem_chain_name) return if "-N" in action: - new_chain_name = liste.pop(1) + new_chain_name = elements.pop(1) existing = self.data.keys() if new_chain_name in existing: - logging.debug("Chain %s already exists" % new_chain_name) - return + msg = "Chain %s already exists" % (new_chain_name) + raise ConverterError(msg) self.data[new_chain_name] = [] # empty list self.poli[new_chain_name] = "-" # empty policy, no need return - if "-I" in action: # or "-A" in action: - chain_name = liste[1] + if "-I" in action: + chain_name = elements[1] existing = self.data.keys() if chain_name not in existing: - self.data[chain_name] = [] - self.poli[chain_name] = "-" - kette = self.data[chain_name] - kette.insert(0, content.replace("-I", "-A")) - self.data[chain_name] = kette + msg = "invalid chain name: %s" % (chain_name) + if not self.sloppy: + raise ConverterError(msg) + else: + new_chain_name = elements[1] + self.data[new_chain_name] = [] + self.poli[new_chain_name] = '-' + stuff = self.data[chain_name] + if len(stuff) > 0: + stuff.insert(0, content) + else: + msg = "Empty chain %s allows append only!" % (chain_name) + raise ConverterError(msg) + self.data[chain_name] = stuff return - if "-A" in action: # or "-I" in action: - chain_name = liste[1] + if "-A" in action: + chain_name = elements[1] existing = self.data.keys() if chain_name not in existing: - self.data[chain_name] = [] - self.poli[chain_name] = "-" - kette = self.data[chain_name] - kette.append(content) - self.data[chain_name] = kette + msg = "invalid chain name: %s" % (chain_name) + if not self.sloppy: + raise ConverterError(msg) + else: + new_chain_name = elements[1] + self.data[new_chain_name] = [] + self.poli[new_chain_name] = '-' + stuff = self.data[chain_name] + stuff.append(content) + self.data[chain_name] = stuff return - msg = "Unknown filter command in input:", content - raise ValueError(msg) + msg = "Unknown filter command in input:" + content + raise ConverterError(msg) - def reset(self): # name, tables): - """ - name is one of filter, nat, raw, mangle, - tables is a list of tables in that table-class + def reset(self): + """ action method for iptables -F """ self.poli = {} # empty dict self.length = 0 @@ -156,58 +183,95 @@ def reset(self): # name, tables): class Tables(UserDict): - """ - some chaingroups in tables are predef: filter, nat, mangle, raw + """ some chaingroups in tables are predef: filter, nat, mangle, raw + + :param str destfile: which file or pathname is to be written + :param str sourcefile: which file or pathname is to be read + :param bool sloppy: '-N' is needed(default) or not + :param int ipversion: 4(default) or 6 + + :return: Tables after read sourcefile written into destfile + :rtype: Tables + + :raises ConverterError: on some illegal conditions """ - def __init__(self, rules): + def __init__(self, + destfile, + sourcefile="reference-one", + sloppy=False, + ipversion=4 + ): """init Tables Object is easy going""" UserDict.__init__(self) - self.reset(rules) + self.destfile = destfile + self.sourcefile = sourcefile + self.sloppy = sloppy + self.linecounter = 0 + self.tblctr = 0 + self.patterns = "" + self.reset(sourcefile, ipversion) + + def reset(self, sourcefile, ipversion): + """all predefined Chains aka lists are setup as new here + + :param str sourcefile: file to be read + :param int ipversion: 4 or 6 + + :return: None + """ + self.patterns = ['^iptables', '^/sbin/iptables', ] + if ipversion == 6: + self.patterns = ['^ip6tables', '^/sbin/ip6tables', ] - def reset(self, rules): - """all predefined Chains aka lists are setup as new here""" - filter = Chains("filter", ["INPUT", "FORWARD", "OUTPUT"]) + filt = ["INPUT", "FORWARD", "OUTPUT"] + filters = Chains("filter", filt, self.sloppy) mang = ["PREROUTING", "INPUT", "FORWARD", "OUTPUT", "POSTROUTING", ] - mangle = Chains("mangle", mang) + mangle = Chains("mangle", mang, self.sloppy) # kernel 2.6.32 has no INPUT in NAT! - nat = Chains("nat", ["PREROUTING", "OUTPUT", "POSTROUTING"]) + nats = ["PREROUTING", "OUTPUT", "POSTROUTING"] + nat = Chains("nat", nats, self.sloppy) - raw = Chains("raw", ["PREROUTING", "OUTPUT", ]) + raws = ["PREROUTING", "OUTPUT", ] + raw = Chains("raw", raws, self.sloppy) - self.data["filter"] = filter + self.data["filter"] = filters self.data["mangle"] = mangle self.data["nat"] = nat self.data["raw"] = raw - if rules is not None: - self.read_file(rules) + if len(sourcefile) > 0: + self.linecounter = self.read_file(sourcefile) def table_printout(self): """printout nonempty tabulars in fixed sequence""" - with open("/tmp/rules.save", 'w') as f: - for key in ["raw", "nat", "mangle", "filter"]: - len = self.data[key].length - if len > -1: - print("*%s" % (self.data[key].name), file=f) - for chain in self.data[key].keys(): - poli = self.data[key].poli[chain] - print(":%s %s [0:0]" % (chain, poli), file=f) - for chain in self.data[key].values(): - for elem in chain: - print(elem, file=f) - print("COMMIT", file=f) + self.destfile.write("# generated from: %s\n" % (self.sourcefile)) + for key in ["raw", "nat", "mangle", "filter"]: + count = self.data[key].length + if count > -1: + self.destfile.write("*%s\n" % (self.data[key].name)) + for chain in self.data[key].keys(): + poli = self.data[key].poli[chain] + self.destfile.write(":%s %s [0:0]\n" % (chain, poli)) + for chain in self.data[key].values(): + for elem in chain: + self.destfile.write(elem) + self.destfile.write('\n') + self.destfile.write("COMMIT\n") def put_into_tables(self, line): - """put line into matching Chains-object""" - liste = line.split() - liste.pop(0) # we always know, it's iptables + """put line into matching Chains-object + + :param line: one line of inputfile + :return: None + """ + elements = line.split() + elements.pop(0) # we always know, it's iptables rest = "" - for elem in liste: # remove redirects and the like + for elem in elements: # remove redirects and the like if ">" not in elem: rest = rest + elem + " " # string again with single blanks - action = liste.pop(0) # action is one of {N,F,A,I, etc.} fam = "filter" if "-t nat" in line: # nat filter group fam = "nat" @@ -218,26 +282,112 @@ def put_into_tables(self, line): fam_dict = self.data[fam] # select the group dictionary fam_dict.put_into_fgr(rest) # do action thers - def read_file(self, rules): - """read file into Tables-object""" + def read_file(self, sourcefile): + """ open file or error + + :param sourcefile: file or pathname of file to be read + + :return: file_descriptor + :raises ConverterError: on IOError + """ + try: + with open(sourcefile, 'r') as file_descriptor: + return self.read(file_descriptor) + except IOError as err: + raise ConverterError(str(err)) + + def read(self, file_descriptor): + """read data from file like object into Tables-object + + :param file_descriptor: filedescriptor of file to be read + + :return: None + :raises ConverterError: on some illegal conditions + """ self.linecounter = 0 self.tblctr = 0 - for zeile in rules: - line = str(zeile.strip()) - self.linecounter += 1 - if line.startswith('#'): - continue - for element in ['\$', '\(', '\)', ]: - if re.search(element, line): - m1 = "Line %d:\n%s\nplain files only, " % \ - (self.linecounter, line) - if element in ['\(', '\)', ]: - m2 = "unable to convert shell functions, abort" - else: - m2 = "unable to resolve shell variables, abort" - msg = m1 + m2 - raise ConverterError(msg) - for muster in ["^/sbin/iptables ", "^iptables "]: - if re.search(muster, line): - self.tblctr += 1 - self.put_into_tables(line) + try: + for this_line in file_descriptor: + line = str(this_line.strip()) + self.linecounter += 1 + if line.startswith('#'): + continue + for element in ['\$', '\(', '\)', ]: + if re.search(element, line): + mstart = "Line %d:\n%s\nplain files only, " % \ + (self.linecounter, line) + if element in ['\(', '\)', ]: + merr = "unable to convert shell functions, abort" + else: + merr = "unable to resolve shell variables, abort" + msg = mstart + merr + raise ConverterError(msg) + for pattern in self.patterns: + if re.search(pattern, line): + self.tblctr += 1 + self.put_into_tables(line) + except ValueError as err: + raise ConverterError(str(err)) + + +def my_options(): + """ commandline options assembly + + :returns: :class: OptionParser + """ + version = "version 0.9.10" + usage = "usage: %prog --help | -h \n\n\t%prog: " + version + usage = usage + "\tHave Fun!" + parser = OptionParser(usage) + parser.disable_interspersed_args() + parser.add_option("-d", "", dest="destfile", + type="string", + help="output filename, default: stdout\n") + parser.add_option("-s", "", dest="sourcefile", + type="string", + help="file with iptables commands, default: rules\n") + parser.add_option("--sloppy", "", dest="sloppy", + action="store_true", default=False, + help="-N name-of-userchain is inserted automatically,\n" + "by default -N is neccessary in input\n") + (options, _) = parser.parse_args() + return options + + +def main(): + """ main parses options, filnames and the like + option -s needs input-filename to be read, + if it is not given, it defaults to: rules. + option -d needs output-filename to be written, + if it is not given, it defaults to: sys.stdout + """ + + options = my_options() + + if options.sourcefile is None: + options.sourcefile = "rules" + sourcefile = options.sourcefile + + if options.destfile is not None: + destfile = open(options.destfile, 'w') + else: + destfile = sys.stdout + + ipversion = 4 + if os.path.basename(sys.argv[0]).startswith("ip6"): + ipversion = 6 + + try: + chains = Tables(destfile, + sourcefile, + options.sloppy, + ipversion=ipversion + ) + chains.table_printout() + except ConverterError as err: + print(str(err), file=sys.stderr) + return 1 + finally: + if destfile != sys.stdout: + destfile.close() + return 0