From 5b9648f475235e3b560f8a101da679f179668c8a Mon Sep 17 00:00:00 2001 From: dethophes Date: Mon, 25 Sep 2023 15:14:30 +0000 Subject: [PATCH 1/9] escape strings for xml --- iptables-graph | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/iptables-graph b/iptables-graph index 34bb844..e7cc702 100755 --- a/iptables-graph +++ b/iptables-graph @@ -45,30 +45,43 @@ def get_node_name(table_name, chain_name): def get_port_name(rule_index): return "rule_" + str(rule_index) + +xml_esc = { + re.compile('&') : '&' , + re.compile('<') : '<' , + re.compile('>') : '>' , + re.compile('"') : '"', +} +def escape_xml(s): + for k, v in xml_esc.items(): + s = k.sub(v, s) + return s + output="""digraph { graph [pad="0.5", nodesep="0.5", ranksep="2"]; node [shape=plain] rankdir=LR; """ + for table in all_chains: for chain in all_chains[table]: node_name = get_node_name(table, chain) tmp_body = node_name + """ [label=<""" if chain in defualt_chain_list: tmp_body +=""" - - """ + + """ else: tmp_body +=""" - """ + """ for i in range(len(all_chains[table][chain])): rule = all_chains[table][chain][i] tmp_body += """ """ + tmp_body += """>""" + escape_xml( rule["rule_body"] ) + """""" #tmp_body += """>""" tmp_body += """ From 5b4f54ee924d32f5a80cecf001a7ac5be28c60fd Mon Sep 17 00:00:00 2001 From: dethophes Date: Sat, 28 Oct 2023 11:35:24 +0000 Subject: [PATCH 2/9] add support for counters --- iptables-graph | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/iptables-graph b/iptables-graph index e7cc702..1dcbf24 100755 --- a/iptables-graph +++ b/iptables-graph @@ -13,8 +13,19 @@ defualt_chain_list = ['PREROUTING', 'FORWARD', 'INPUT', 'OUTPUT', 'POSTROUTING'] input_string = sys.stdin.read() line_list = input_string.splitlines() current_table = None +match_stats = re.compile(r'\s*\[(?P\d+):(?P\d+)\]\s*') for line in line_list: + stats = match_stats.search(line) + if stats: + line = match_stats.sub('', line) + stats = '[{}:{}] '.format(stats.group('pkts'), stats.group('bytes')) + else: + stats = '' token_list = line.split() + if not token_list : + continue + if token_list[0][0] in [ '#']: + continue if token_list[0][1:] in all_chains.keys(): current_table = token_list[0][1:] continue @@ -23,15 +34,15 @@ for line in line_list: if current_chain not in all_chains[current_table]: all_chains[current_table][current_chain] = list() - rule_body = '' + rule_body = stats target = '' if token_list[-2] == '-j' and token_list[-1] not in ['RETURN', 'ACCEPT', 'DROP']: target = token_list[-1] if target not in all_chains[current_table]: all_chains[current_table][target] = list() - rule_body = ' '.join(token_list[2:]) + rule_body += ' '.join(token_list[2:]) else: - rule_body = ' '.join(token_list[2:]) + rule_body += ' '.join(token_list[2:]) all_chains[current_table][current_chain].append({'rule_body':rule_body, 'target':target}) continue @@ -81,7 +92,7 @@ for table in all_chains: tmp_body += """ """ + tmp_body += """>""" + escape_xml( rule["rule_body"] ) + """""" #tmp_body += """>""" tmp_body += """ From 1a9ef82653f3ca82807736dccffe088c3861847c Mon Sep 17 00:00:00 2001 From: dethophes Date: Sat, 28 Oct 2023 11:38:34 +0000 Subject: [PATCH 3/9] add support to colorize rules based on port ranges. --- iptables-graph | 51 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/iptables-graph b/iptables-graph index 1dcbf24..b42916f 100755 --- a/iptables-graph +++ b/iptables-graph @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import sys +import os import re import string @@ -68,6 +69,50 @@ def escape_xml(s): s = k.sub(v, s) return s + +default_port_ranges = ''' +red=29700:29799,47000:48653,48659,48662,48663,48753,48754,48850:49151,49200:49799 +green=80 +yellow=22 +''' + +def expand_port_range(port_range): + prange = [] + for cr in port_range.split(','): + r = cr.split(':') + if len(r) == 2: + prange += range(int(r[0]), int(r[1]) + 1) + else: + prange = [ int(r[0]) ] + return prange + +def test_port_range_in_port_range(a, b): + for a1 in a: + if a1 in b: + return True + return False + +match_port = re.compile(r'-(?P[ds]ports?)\s+(?P[0-9,:]+)') +def get_rule_attributes(port_ranges, rule): + global match_port + m = match_port.search(rule["rule_body"]) + if m : + for me in port_ranges: + if test_port_range_in_port_range(me['r'], expand_port_range(m.group('port'))): + return ' bgcolor="{}"'.format(me['c']) + return '' + +def conv_port_range(r): + cfilter = [] + for cline in r.split('\n'): + d = cline.split('=', 2) + if len(d) != 2: + continue + cfilter.append({ 'r' : expand_port_range(d[1]), 'c' : d[0] }) + return cfilter + + +port_ranges = conv_port_range(os.getenv('PORT_RANGE', default=default_port_ranges)) output="""digraph { graph [pad="0.5", nodesep="0.5", ranksep="2"]; node [shape=plain] @@ -89,11 +134,7 @@ for table in all_chains: """ for i in range(len(all_chains[table][chain])): rule = all_chains[table][chain][i] - tmp_body += """ - """ - #tmp_body += """>""" + tmp_body += '\n'.format(get_port_name(i), get_rule_attributes(port_ranges, rule), escape_xml( rule["rule_body"] )) tmp_body += """
""" + chain + """
""" + table + """
""" + escape_xml( chain ) + """
""" + escape_xml( table ) + """
""" + chain + """
""" + table + """
""" + escape_xml( table ) + """
""" + rule["rule_body"] + """
end
""" + escape_xml( rule["rule_body"] ) + """
end
""" + escape_xml( table ) + """
""" + escape_xml( rule["rule_body"] ) + """
{}
end
>]; From 9f4ee70909a6019c322d843cb1cd9f28df3ca7a9 Mon Sep 17 00:00:00 2001 From: dethophes Date: Sun, 29 Oct 2023 07:50:15 +0000 Subject: [PATCH 4/9] Refactor code to mode into function to make it possible to reuse as module. centralize color selection, and change color scheme. Differentiate used and unused rules by color. --- iptables-graph | 205 ++++++++++++++++++++++++++----------------------- 1 file changed, 109 insertions(+), 96 deletions(-) diff --git a/iptables-graph b/iptables-graph index b42916f..6cc4182 100755 --- a/iptables-graph +++ b/iptables-graph @@ -11,44 +11,42 @@ all_chains = { 'raw': {'PREROUTING':list(), 'OUTPUT':list()}, defualt_chain_list = ['PREROUTING', 'FORWARD', 'INPUT', 'OUTPUT', 'POSTROUTING'] -input_string = sys.stdin.read() -line_list = input_string.splitlines() -current_table = None -match_stats = re.compile(r'\s*\[(?P\d+):(?P\d+)\]\s*') -for line in line_list: - stats = match_stats.search(line) - if stats: - line = match_stats.sub('', line) - stats = '[{}:{}] '.format(stats.group('pkts'), stats.group('bytes')) - else: - stats = '' - token_list = line.split() - if not token_list : - continue - if token_list[0][0] in [ '#']: - continue - if token_list[0][1:] in all_chains.keys(): - current_table = token_list[0][1:] - continue - if token_list[0] == '-A': - current_chain = token_list[1] - if current_chain not in all_chains[current_table]: - all_chains[current_table][current_chain] = list() - - rule_body = stats - target = '' - if token_list[-2] == '-j' and token_list[-1] not in ['RETURN', 'ACCEPT', 'DROP']: - target = token_list[-1] - if target not in all_chains[current_table]: - all_chains[current_table][target] = list() - rule_body += ' '.join(token_list[2:]) +def parse_input(input_string): + line_list = input_string.splitlines() + current_table = None + match_stats = re.compile(r'\s*\[(?P\d+):(?P\d+)\]\s*') + for line in line_list: + stats = match_stats.search(line) + if stats: + line = match_stats.sub('', line) + stats = '[{}:{}] '.format(stats.group('pkts'), stats.group('bytes')) else: - rule_body += ' '.join(token_list[2:]) - - all_chains[current_table][current_chain].append({'rule_body':rule_body, 'target':target}) - continue - else: - continue + stats = '' + token_list = line.split() + if not token_list : + continue + if token_list[0][1:] in all_chains.keys(): + current_table = token_list[0][1:] + continue + if token_list[0] == '-A': + current_chain = token_list[1] + if current_chain not in all_chains[current_table]: + all_chains[current_table][current_chain] = list() + + rule_body = stats + target = '' + if token_list[-2] == '-j' and token_list[-1] not in ['RETURN', 'ACCEPT', 'DROP']: + target = token_list[-1] + if target not in all_chains[current_table]: + all_chains[current_table][target] = list() + rule_body += ' '.join(token_list[2:]) + else: + rule_body += ' '.join(token_list[2:]) + + all_chains[current_table][current_chain].append({'rule_body':rule_body, 'target':target}) + continue + else: + continue def get_node_name(table_name, chain_name): @@ -93,13 +91,18 @@ def test_port_range_in_port_range(a, b): return False match_port = re.compile(r'-(?P[ds]ports?)\s+(?P[0-9,:]+)') +match_unused_rule = re.compile(r'\[0:0\]') def get_rule_attributes(port_ranges, rule): - global match_port + global match_port, colors m = match_port.search(rule["rule_body"]) if m : for me in port_ranges: if test_port_range_in_port_range(me['r'], expand_port_range(m.group('port'))): return ' bgcolor="{}"'.format(me['c']) + if match_unused_rule.search(rule["rule_body"]): + return ' bgcolor="{unused_rule}"'.format(**colors) + else: + return ' bgcolor="{used_rule}"'.format(**colors) return '' def conv_port_range(r): @@ -112,65 +115,75 @@ def conv_port_range(r): return cfilter -port_ranges = conv_port_range(os.getenv('PORT_RANGE', default=default_port_ranges)) -output="""digraph { +colors = { + 'bgcolor' : 'black', + 'norm_link' : 'white', + 'chain_link' : 'red', + 'unused_rule' : 'lightgrey', + 'used_rule' : 'darkgrey', + 'default_chain' : ['red', 'tomato'], + 'user_chain' : ['white', 'white'], +} + +def create_output(all_chains): + global default_port_ranges, colors + port_ranges = conv_port_range(os.getenv('PORT_RANGE', default=default_port_ranges)) + + output="""digraph {{ + bgcolor="{bgcolor}" graph [pad="0.5", nodesep="0.5", ranksep="2"]; node [shape=plain] rankdir=LR; -""" - -for table in all_chains: - for chain in all_chains[table]: - node_name = get_node_name(table, chain) - tmp_body = node_name + """ [label=<""" - if chain in defualt_chain_list: - tmp_body +=""" - - """ - else: - tmp_body +=""" - - """ - for i in range(len(all_chains[table][chain])): - rule = all_chains[table][chain][i] - tmp_body += '\n'.format(get_port_name(i), get_rule_attributes(port_ranges, rule), escape_xml( rule["rule_body"] )) - tmp_body += """ - -
""" + escape_xml( chain ) + """
""" + escape_xml( table ) + """
""" + chain + """
""" + escape_xml( table ) + """
{}
end
>]; -""" - output += tmp_body - -for table in all_chains: - for chain in all_chains[table]: - for i in range(len(all_chains[table][chain])): - rule = all_chains[table][chain][i] - if rule['target']: - source_node = get_node_name(table, chain) + ':' + get_port_name(i) - target_node = get_node_name(table, rule['target']) + ':begin' - output += source_node + """ -> """ + target_node + """; -""" - -def default_chain_link(src_table_name, src_chain_name, dst_table_name, dst_chain_name): - source_node = get_node_name(src_table_name, src_chain_name) + ':end' - target_node = get_node_name(dst_table_name, dst_chain_name) + ':begin' - return source_node + """ -> """ + target_node + """ [color=red]; -""" - -output += default_chain_link('raw', 'PREROUTING', 'mangle', 'PREROUTING') -output += default_chain_link('mangle', 'PREROUTING', 'nat', 'PREROUTING') -output += default_chain_link('nat', 'PREROUTING', 'mangle', 'INPUT') -output += default_chain_link('mangle', 'INPUT', 'filter', 'INPUT') -output += default_chain_link('filter', 'INPUT', 'raw', 'OUTPUT') -output += default_chain_link('raw', 'OUTPUT', 'mangle', 'OUTPUT') -output += default_chain_link('mangle', 'OUTPUT', 'nat', 'OUTPUT') -output += default_chain_link('nat', 'OUTPUT', 'filter', 'OUTPUT') -output += default_chain_link('filter', 'OUTPUT', 'mangle', 'POSTROUTING') -output += default_chain_link('mangle', 'POSTROUTING', 'nat', 'POSTROUTING') -output += default_chain_link('nat', 'PREROUTING', 'mangle', 'FORWARD') -output += default_chain_link('mangle', 'FORWARD', 'filter', 'FORWARD') -output += default_chain_link('filter', 'FORWARD', 'mangle', 'POSTROUTING') - -output += """ -}""" -print(output) +""".format(**colors) + for table in all_chains: + for chain in all_chains[table]: + node_name = get_node_name(table, chain) + tmp_body = node_name + """ [label=<""" + if chain in defualt_chain_list: + tmp_body +='\n'.format(colors['default_chain'][0], escape_xml( chain )) + tmp_body +='\n'.format( colors['default_chain'][1], escape_xml( table )) + else: + tmp_body +='\n'.format(colors['user_chain'][0], chain ) + tmp_body +='\n'.format(colors['user_chain'][1], escape_xml( table )) + for i in range(len(all_chains[table][chain])): + rule = all_chains[table][chain][i] + tmp_body += '\n'.format(get_port_name(i), get_rule_attributes(port_ranges, rule), escape_xml( rule["rule_body"] )) + tmp_body += ''.format(colors['user_chain'][1]) + tmp_body += '
{}
{}
{}
{}
{}
end
>];' + output += tmp_body + + for table in all_chains: + for chain in all_chains[table]: + for i in range(len(all_chains[table][chain])): + rule = all_chains[table][chain][i] + if rule['target']: + source_node = get_node_name(table, chain) + ':' + get_port_name(i) + target_node = get_node_name(table, rule['target']) + ':begin' + output += '{} -> {} [color="{}"]\n'.format(source_node, target_node, colors['norm_link']) + + def default_chain_link(src_table_name, src_chain_name, dst_table_name, dst_chain_name): + source_node = get_node_name(src_table_name, src_chain_name) + ':end' + target_node = get_node_name(dst_table_name, dst_chain_name) + ':begin' + return '{} -> {} [color="{}"]\n'.format(source_node, target_node, colors['chain_link']) + + output += default_chain_link('raw' , 'PREROUTING', 'mangle', 'PREROUTING') + output += default_chain_link('mangle', 'PREROUTING', 'nat', 'PREROUTING') + output += default_chain_link('nat' , 'PREROUTING', 'mangle', 'INPUT') + output += default_chain_link('mangle', 'INPUT', 'filter', 'INPUT') + output += default_chain_link('filter', 'INPUT', 'raw', 'OUTPUT') + output += default_chain_link('raw' , 'OUTPUT', 'mangle', 'OUTPUT') + output += default_chain_link('mangle', 'OUTPUT', 'nat', 'OUTPUT') + output += default_chain_link('nat' , 'OUTPUT', 'filter', 'OUTPUT') + output += default_chain_link('filter', 'OUTPUT', 'mangle', 'POSTROUTING') + output += default_chain_link('mangle', 'POSTROUTING', 'nat', 'POSTROUTING') + output += default_chain_link('nat' , 'PREROUTING', 'mangle', 'FORWARD') + output += default_chain_link('mangle', 'FORWARD', 'filter', 'FORWARD') + output += default_chain_link('filter', 'FORWARD', 'mangle', 'POSTROUTING') + + output += '\n}' + return output +if __name__ == '__main__': + parse_input(sys.stdin.read()) + output = create_output(all_chains) + print( output ) From a8d75f35766ed07b3552286777e60d9c2c2c9e63 Mon Sep 17 00:00:00 2001 From: dethophes Date: Sun, 29 Oct 2023 07:52:34 +0000 Subject: [PATCH 5/9] Add initial support for ebtables. also add the security table for iptables. --- iptables-graph | 53 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/iptables-graph b/iptables-graph index 6cc4182..17cc682 100755 --- a/iptables-graph +++ b/iptables-graph @@ -4,12 +4,22 @@ import os import re import string -all_chains = { 'raw': {'PREROUTING':list(), 'OUTPUT':list()}, +do_ebtables = os.getenv('USE_EBTABLES', default=False) +if not do_ebtables: + all_chains = { 'raw': {'PREROUTING':list(), 'OUTPUT':list()}, 'filter': {'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list()}, 'nat': {'PREROUTING':list(), 'OUTPUT':list(), 'POSTROUTING':list()}, - 'mangle': {'PREROUTING':list(), 'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list(), 'POSTROUTING':list()}} + 'mangle': {'PREROUTING':list(), 'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list(), 'POSTROUTING':list()}, + 'security': {'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list()}} -defualt_chain_list = ['PREROUTING', 'FORWARD', 'INPUT', 'OUTPUT', 'POSTROUTING'] +else: + all_chains = { + 'filter': {'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list()}, + 'nat': {'PREROUTING':list(), 'OUTPUT':list(), 'POSTROUTING':list()}, + 'broute': {'BROUTING':list()} + } + +defualt_chain_list = ['PREROUTING', 'FORWARD', 'INPUT', 'OUTPUT', 'POSTROUTING', 'BROUTING'] def parse_input(input_string): line_list = input_string.splitlines() @@ -167,19 +177,30 @@ def create_output(all_chains): target_node = get_node_name(dst_table_name, dst_chain_name) + ':begin' return '{} -> {} [color="{}"]\n'.format(source_node, target_node, colors['chain_link']) - output += default_chain_link('raw' , 'PREROUTING', 'mangle', 'PREROUTING') - output += default_chain_link('mangle', 'PREROUTING', 'nat', 'PREROUTING') - output += default_chain_link('nat' , 'PREROUTING', 'mangle', 'INPUT') - output += default_chain_link('mangle', 'INPUT', 'filter', 'INPUT') - output += default_chain_link('filter', 'INPUT', 'raw', 'OUTPUT') - output += default_chain_link('raw' , 'OUTPUT', 'mangle', 'OUTPUT') - output += default_chain_link('mangle', 'OUTPUT', 'nat', 'OUTPUT') - output += default_chain_link('nat' , 'OUTPUT', 'filter', 'OUTPUT') - output += default_chain_link('filter', 'OUTPUT', 'mangle', 'POSTROUTING') - output += default_chain_link('mangle', 'POSTROUTING', 'nat', 'POSTROUTING') - output += default_chain_link('nat' , 'PREROUTING', 'mangle', 'FORWARD') - output += default_chain_link('mangle', 'FORWARD', 'filter', 'FORWARD') - output += default_chain_link('filter', 'FORWARD', 'mangle', 'POSTROUTING') + if not do_ebtables: + output += default_chain_link('raw', 'PREROUTING', 'mangle', 'PREROUTING') + output += default_chain_link('mangle', 'PREROUTING', 'nat', 'PREROUTING') + output += default_chain_link('nat', 'PREROUTING', 'mangle', 'INPUT') + output += default_chain_link('mangle', 'INPUT', 'filter', 'INPUT') + output += default_chain_link('filter', 'INPUT', 'security', 'INPUT') + output += default_chain_link('security','INPUT', 'raw', 'OUTPUT') + output += default_chain_link('raw', 'OUTPUT', 'mangle', 'OUTPUT') + output += default_chain_link('mangle', 'OUTPUT', 'nat', 'OUTPUT') + output += default_chain_link('nat', 'OUTPUT', 'filter', 'OUTPUT') + output += default_chain_link('filter', 'OUTPUT', 'security', 'OUTPUT') + output += default_chain_link('security','OUTPUT', 'mangle', 'POSTROUTING') + output += default_chain_link('mangle', 'POSTROUTING', 'nat', 'POSTROUTING') + output += default_chain_link('nat', 'PREROUTING', 'mangle', 'FORWARD') + output += default_chain_link('mangle', 'FORWARD', 'filter', 'FORWARD') + output += default_chain_link('filter', 'FORWARD', 'security', 'FORWARD') + output += default_chain_link('security','FORWARD', 'mangle', 'POSTROUTING') + else: + output += default_chain_link('broute', 'BROUTING', 'nat' , 'PREROUTING') + output += default_chain_link('nat', 'PREROUTING', 'filter', 'INPUT') + output += default_chain_link('nat', 'OUTPUT', 'filter', 'OUTPUT') + output += default_chain_link('filter', 'OUTPUT', 'nat' , 'POSTROUTING') + output += default_chain_link('nat', 'POSTROUTING', 'filter', 'FORWARD') + output += default_chain_link('filter', 'FORWARD', 'broute', 'BROUTING') output += '\n}' return output From 8160c24e41a9b1e75e1f8906259ba0a94dd00e21 Mon Sep 17 00:00:00 2001 From: dethophes Date: Sun, 29 Oct 2023 08:22:07 +0000 Subject: [PATCH 6/9] update example image --- example.dot | 211 ++++++--------- example.svg | 745 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 516 insertions(+), 440 deletions(-) diff --git a/example.dot b/example.dot index eb5afca..a7ace63 100644 --- a/example.dot +++ b/example.dot @@ -1,137 +1,90 @@ digraph { + bgcolor="black" graph [pad="0.5", nodesep="0.5", ranksep="2"]; node [shape=plain] rankdir=LR; -filter_DOCKERUSER [label=< - - - - -
DOCKER-USER
filter
-j RETURN
end
>]; -filter_DOCKERISOLATIONSTAGE1 [label=< - - - - - -
DOCKER-ISOLATION-STAGE-1
filter
-i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-j RETURN
end
>]; -filter_DOCKERISOLATIONSTAGE2 [label=< - - - - - -
DOCKER-ISOLATION-STAGE-2
filter
-o docker0 -j DROP
-j RETURN
end
>]; -filter_OUTPUT [label=< - - - -
OUTPUT
filter
end
>]; -filter_FORWARD [label=< - - - - - - - - - -
FORWARD
filter
-j DOCKER-USER
-j DOCKER-ISOLATION-STAGE-1
-o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-o docker0 -j DOCKER
-i docker0 ! -o docker0 -j ACCEPT
-i docker0 -o docker0 -j ACCEPT
end
>]; -filter_INPUT [label=< - - - -
INPUT
filter
end
>]; -filter_DOCKER [label=< - - - -
DOCKER
filter
end
>]; -raw_OUTPUT [label=< - - - -
OUTPUT
raw
end
>]; raw_PREROUTING [label=< - - - -
PREROUTING
raw
end
>]; -mangle_FORWARD [label=< - - - -
FORWARD
mangle
end
>]; -mangle_INPUT [label=< - - - -
INPUT
mangle
end
>]; -mangle_POSTROUTING [label=< - - - -
POSTROUTING
mangle
end
>]; -mangle_PREROUTING [label=< - - - -
PREROUTING
mangle
end
>]; -mangle_OUTPUT [label=< - - - -
OUTPUT
mangle
end
>]; -nat_MASQUERADE [label=< - - - -
MASQUERADE
nat
end
>]; -nat_OUTPUT [label=< - - - - -
OUTPUT
nat
! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
end
>]; -nat_DOCKER [label=< - - - - -
DOCKER
nat
-i docker0 -j RETURN
end
>]; -nat_PREROUTING [label=< - - - - -
PREROUTING
nat
-m addrtype --dst-type LOCAL -j DOCKER
end
>]; -nat_POSTROUTING [label=< - - - - -
POSTROUTING
nat
-s 172.18.0.0/24 ! -o docker0 -j MASQUERADE
end
>]; -filter_DOCKERISOLATIONSTAGE1:rule_0 -> filter_DOCKERISOLATIONSTAGE2:begin; -filter_FORWARD:rule_0 -> filter_DOCKERUSER:begin; -filter_FORWARD:rule_1 -> filter_DOCKERISOLATIONSTAGE1:begin; -filter_FORWARD:rule_3 -> filter_DOCKER:begin; -nat_OUTPUT:rule_0 -> nat_DOCKER:begin; -nat_PREROUTING:rule_0 -> nat_DOCKER:begin; -nat_POSTROUTING:rule_0 -> nat_MASQUERADE:begin; -raw_PREROUTING:end -> mangle_PREROUTING:begin [color=red]; -mangle_PREROUTING:end -> nat_PREROUTING:begin [color=red]; -nat_PREROUTING:end -> mangle_INPUT:begin [color=red]; -mangle_INPUT:end -> filter_INPUT:begin [color=red]; -filter_INPUT:end -> raw_OUTPUT:begin [color=red]; -raw_OUTPUT:end -> mangle_OUTPUT:begin [color=red]; -mangle_OUTPUT:end -> nat_OUTPUT:begin [color=red]; -nat_OUTPUT:end -> filter_OUTPUT:begin [color=red]; -filter_OUTPUT:end -> mangle_POSTROUTING:begin [color=red]; -mangle_POSTROUTING:end -> nat_POSTROUTING:begin [color=red]; -nat_PREROUTING:end -> mangle_FORWARD:begin [color=red]; -mangle_FORWARD:end -> filter_FORWARD:begin [color=red]; -filter_FORWARD:end -> mangle_POSTROUTING:begin [color=red]; +PREROUTING +rawend>];raw_OUTPUT [label=< + +
OUTPUT
raw
end
>];filter_INPUT [label=< + +
INPUT
filter
end
>];filter_OUTPUT [label=< + +
OUTPUT
filter
end
>];filter_FORWARD [label=< + + + + + + + +
FORWARD
filter
-j DOCKER-USER
-j DOCKER-ISOLATION-STAGE-1
-o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-o docker0 -j DOCKER
-i docker0 ! -o docker0 -j ACCEPT
-i docker0 -o docker0 -j ACCEPT
end
>];filter_DOCKERUSER [label=< + + +
DOCKER-USER
filter
-j RETURN
end
>];filter_DOCKERISOLATIONSTAGE1 [label=< + + + +
DOCKER-ISOLATION-STAGE-1
filter
-i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-j RETURN
end
>];filter_DOCKER [label=< + +
DOCKER
filter
end
>];filter_DOCKERISOLATIONSTAGE2 [label=< + + + +
DOCKER-ISOLATION-STAGE-2
filter
-o docker0 -j DROP
-j RETURN
end
>];nat_PREROUTING [label=< + + +
PREROUTING
nat
-m addrtype --dst-type LOCAL -j DOCKER
end
>];nat_OUTPUT [label=< + + +
OUTPUT
nat
! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
end
>];nat_POSTROUTING [label=< + + +
POSTROUTING
nat
-s 172.18.0.0/24 ! -o docker0 -j MASQUERADE
end
>];nat_DOCKER [label=< + + +
DOCKER
nat
-i docker0 -j RETURN
end
>];nat_MASQUERADE [label=< + +
MASQUERADE
nat
end
>];mangle_PREROUTING [label=< + +
PREROUTING
mangle
end
>];mangle_INPUT [label=< + +
INPUT
mangle
end
>];mangle_OUTPUT [label=< + +
OUTPUT
mangle
end
>];mangle_FORWARD [label=< + +
FORWARD
mangle
end
>];mangle_POSTROUTING [label=< + +
POSTROUTING
mangle
end
>];security_INPUT [label=< + +
INPUT
security
end
>];security_OUTPUT [label=< + +
OUTPUT
security
end
>];security_FORWARD [label=< + +
FORWARD
security
end
>];filter_FORWARD:rule_0 -> filter_DOCKERUSER:begin [color="white"] +filter_FORWARD:rule_1 -> filter_DOCKERISOLATIONSTAGE1:begin [color="white"] +filter_FORWARD:rule_3 -> filter_DOCKER:begin [color="white"] +filter_DOCKERISOLATIONSTAGE1:rule_0 -> filter_DOCKERISOLATIONSTAGE2:begin [color="white"] +nat_PREROUTING:rule_0 -> nat_DOCKER:begin [color="white"] +nat_OUTPUT:rule_0 -> nat_DOCKER:begin [color="white"] +nat_POSTROUTING:rule_0 -> nat_MASQUERADE:begin [color="white"] +raw_PREROUTING:end -> mangle_PREROUTING:begin [color="red"] +mangle_PREROUTING:end -> nat_PREROUTING:begin [color="red"] +nat_PREROUTING:end -> mangle_INPUT:begin [color="red"] +mangle_INPUT:end -> filter_INPUT:begin [color="red"] +filter_INPUT:end -> security_INPUT:begin [color="red"] +security_INPUT:end -> raw_OUTPUT:begin [color="red"] +raw_OUTPUT:end -> mangle_OUTPUT:begin [color="red"] +mangle_OUTPUT:end -> nat_OUTPUT:begin [color="red"] +nat_OUTPUT:end -> filter_OUTPUT:begin [color="red"] +filter_OUTPUT:end -> security_OUTPUT:begin [color="red"] +security_OUTPUT:end -> mangle_POSTROUTING:begin [color="red"] +mangle_POSTROUTING:end -> nat_POSTROUTING:begin [color="red"] +nat_PREROUTING:end -> mangle_FORWARD:begin [color="red"] +mangle_FORWARD:end -> filter_FORWARD:begin [color="red"] +filter_FORWARD:end -> security_FORWARD:begin [color="red"] +security_FORWARD:end -> mangle_POSTROUTING:begin [color="red"] } diff --git a/example.svg b/example.svg index 14ca587..b3cba94 100644 --- a/example.svg +++ b/example.svg @@ -1,359 +1,482 @@ - - - + + %3 - - -filter_DOCKERUSER - - -DOCKER-USER - -filter - --j RETURN - -end + + + +raw_PREROUTING + + +PREROUTING + + +raw + + +end - -filter_DOCKERISOLATIONSTAGE1 - - -DOCKER-ISOLATION-STAGE-1 - -filter - --i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2 - --j RETURN - -end + + +mangle_PREROUTING + + +PREROUTING + + +mangle + + +end - -filter_DOCKERISOLATIONSTAGE2 - - -DOCKER-ISOLATION-STAGE-2 - -filter - --o docker0 -j DROP - --j RETURN - -end + + +raw_PREROUTING:end->mangle_PREROUTING:begin + + - -filter_DOCKERISOLATIONSTAGE1:rule_0->filter_DOCKERISOLATIONSTAGE2:begin - - + + +raw_OUTPUT + + +OUTPUT + + +raw + + +end - -filter_OUTPUT - - - -OUTPUT - - -filter - -end + + +mangle_OUTPUT + + +OUTPUT + + +mangle + + +end - -mangle_POSTROUTING - - - -POSTROUTING - - -mangle - -end - - -filter_OUTPUT:end->mangle_POSTROUTING:begin - - + + +raw_OUTPUT:end->mangle_OUTPUT:begin + + + + + +filter_INPUT + + +INPUT + + +filter + + +end + + + +security_INPUT + + +INPUT + + +security + + +end + + + +filter_INPUT:end->security_INPUT:begin + + + + + +filter_OUTPUT + + +OUTPUT + + +filter + + +end + + + +security_OUTPUT + + +OUTPUT + + +security + + +end + + + +filter_OUTPUT:end->security_OUTPUT:begin + + -filter_FORWARD - - - -FORWARD - - -filter - --j DOCKER-USER - --j DOCKER-ISOLATION-STAGE-1 - --o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT - --o docker0 -j DOCKER - --i docker0 ! -o docker0 -j ACCEPT - --i docker0 -o docker0 -j ACCEPT - -end + +filter_FORWARD + + +FORWARD + + +filter + + +-j DOCKER-USER + + +-j DOCKER-ISOLATION-STAGE-1 + + +-o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT + + +-o docker0 -j DOCKER + + +-i docker0 ! -o docker0 -j ACCEPT + + +-i docker0 -o docker0 -j ACCEPT + + +end + + + +filter_DOCKERUSER + + +DOCKER-USER + + +filter + + +-j RETURN + + +end -filter_FORWARD:rule_0->filter_DOCKERUSER:begin - - + +filter_FORWARD:rule_0->filter_DOCKERUSER:begin + + + + + +filter_DOCKERISOLATIONSTAGE1 + + +DOCKER-ISOLATION-STAGE-1 + + +filter + + +-i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2 + + +-j RETURN + + +end -filter_FORWARD:rule_1->filter_DOCKERISOLATIONSTAGE1:begin - - + +filter_FORWARD:rule_1->filter_DOCKERISOLATIONSTAGE1:begin + + -filter_DOCKER - - -DOCKER - -filter - -end + +filter_DOCKER + + +DOCKER + + +filter + + +end -filter_FORWARD:rule_3->filter_DOCKER:begin - - - - -filter_FORWARD:end->mangle_POSTROUTING:begin - - + +filter_FORWARD:rule_3->filter_DOCKER:begin + + + + + +security_FORWARD + + +FORWARD + + +security + + +end + + + +filter_FORWARD:end->security_FORWARD:begin + + - -filter_INPUT - - - -INPUT - - -filter - -end + + +filter_DOCKERISOLATIONSTAGE2 + + +DOCKER-ISOLATION-STAGE-2 + + +filter + + +-o docker0 -j DROP + + +-j RETURN + + +end - -raw_OUTPUT - - - -OUTPUT - - -raw - -end - - -filter_INPUT:end->raw_OUTPUT:begin - - + + +filter_DOCKERISOLATIONSTAGE1:rule_0->filter_DOCKERISOLATIONSTAGE2:begin + + - -mangle_OUTPUT - - - -OUTPUT - - -mangle - -end + + +nat_PREROUTING + + +PREROUTING + + +nat + + +-m addrtype --dst-type LOCAL -j DOCKER + + +end - -raw_OUTPUT:end->mangle_OUTPUT:begin - - + + +nat_DOCKER + + +DOCKER + + +nat + + +-i docker0 -j RETURN + + +end - -raw_PREROUTING - - - -PREROUTING - - -raw - -end + + +nat_PREROUTING:rule_0->nat_DOCKER:begin + + - -mangle_PREROUTING - - - -PREROUTING - - -mangle - -end + + +mangle_INPUT + + +INPUT + + +mangle + + +end - -raw_PREROUTING:end->mangle_PREROUTING:begin - - + + +nat_PREROUTING:end->mangle_INPUT:begin + + -mangle_FORWARD - - - -FORWARD - - -mangle - -end + +mangle_FORWARD + + +FORWARD + + +mangle + + +end - -mangle_FORWARD:end->filter_FORWARD:begin - - + + +nat_PREROUTING:end->mangle_FORWARD:begin + + - -mangle_INPUT - - - -INPUT - - -mangle - -end + + +nat_OUTPUT + + +OUTPUT + + +nat + + +! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER + + +end - -mangle_INPUT:end->filter_INPUT:begin - - + + +nat_OUTPUT:end->filter_OUTPUT:begin + + + + + +nat_OUTPUT:rule_0->nat_DOCKER:begin + + -nat_POSTROUTING - - - -POSTROUTING - - -nat - --s 172.18.0.0/24 ! -o docker0 -j MASQUERADE - -end + +nat_POSTROUTING + + +POSTROUTING + + +nat + + +-s 172.18.0.0/24 ! -o docker0 -j MASQUERADE + + +end - -mangle_POSTROUTING:end->nat_POSTROUTING:begin - - + + +nat_MASQUERADE + + +MASQUERADE + + +nat + + +end - -nat_PREROUTING - - - -PREROUTING - - -nat - --m addrtype --dst-type LOCAL -j DOCKER - -end + + +nat_POSTROUTING:rule_0->nat_MASQUERADE:begin + + -mangle_PREROUTING:end->nat_PREROUTING:begin - - + +mangle_PREROUTING:end->nat_PREROUTING:begin + + - -nat_OUTPUT - - - -OUTPUT - - -nat - -! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER - -end + + +mangle_INPUT:end->filter_INPUT:begin + + -mangle_OUTPUT:end->nat_OUTPUT:begin - - + +mangle_OUTPUT:end->nat_OUTPUT:begin + + - -nat_MASQUERADE - - -MASQUERADE - -nat - -end - - -nat_OUTPUT:end->filter_OUTPUT:begin - - - - -nat_DOCKER - - -DOCKER - -nat - --i docker0 -j RETURN - -end - - -nat_OUTPUT:rule_0->nat_DOCKER:begin - - - - -nat_PREROUTING:end->mangle_FORWARD:begin - - - - -nat_PREROUTING:end->mangle_INPUT:begin - - + + +mangle_FORWARD:end->filter_FORWARD:begin + + - -nat_PREROUTING:rule_0->nat_DOCKER:begin - - + + +mangle_POSTROUTING + + +POSTROUTING + + +mangle + + +end - -nat_POSTROUTING:rule_0->nat_MASQUERADE:begin - - + + +mangle_POSTROUTING:end->nat_POSTROUTING:begin + + + + + +security_INPUT:end->raw_OUTPUT:begin + + + + + +security_OUTPUT:end->mangle_POSTROUTING:begin + + + + + +security_FORWARD:end->mangle_POSTROUTING:begin + + From 8bb1018a9bf28b9e798cd553157094660c2c46c4 Mon Sep 17 00:00:00 2001 From: dethophes Date: Sun, 29 Oct 2023 08:23:25 +0000 Subject: [PATCH 7/9] update link to use local example img --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c58fa0..f5ad3c2 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ $ dot -Tsvg a.dot -o a.svg ``` ### Example Graph -![example.svg](https://raw.githubusercontent.com/AChingYo/iptables-graph/main/example.svg) +![example.svg](https://raw.githubusercontent.com/dethrophes/iptables-graph/main/example.svg) From 7210effc49d65641dfaceabd1457650d70085b74 Mon Sep 17 00:00:00 2001 From: dethophes Date: Sun, 29 Oct 2023 13:31:36 +0000 Subject: [PATCH 8/9] Add chain target. Show return paths for --jump and --goto. --- iptables-graph | 140 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 109 insertions(+), 31 deletions(-) diff --git a/iptables-graph b/iptables-graph index 17cc682..e23854e 100755 --- a/iptables-graph +++ b/iptables-graph @@ -4,19 +4,22 @@ import os import re import string +def default_chain_setup(): + return { 'target': None, 'rules' : list(), 'jump_to_list' : list(), 'goto_list' : list() } + do_ebtables = os.getenv('USE_EBTABLES', default=False) if not do_ebtables: - all_chains = { 'raw': {'PREROUTING':list(), 'OUTPUT':list()}, - 'filter': {'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list()}, - 'nat': {'PREROUTING':list(), 'OUTPUT':list(), 'POSTROUTING':list()}, - 'mangle': {'PREROUTING':list(), 'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list(), 'POSTROUTING':list()}, - 'security': {'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list()}} + all_chains = { 'raw': {'PREROUTING':default_chain_setup(), 'OUTPUT':default_chain_setup()}, + 'filter': {'INPUT':default_chain_setup(), 'OUTPUT':default_chain_setup(), 'FORWARD':default_chain_setup()}, + 'nat': {'PREROUTING':default_chain_setup(), 'OUTPUT':default_chain_setup(), 'POSTROUTING':default_chain_setup()}, + 'mangle': {'PREROUTING':default_chain_setup(), 'INPUT':default_chain_setup(), 'OUTPUT':default_chain_setup(), 'FORWARD':default_chain_setup(), 'POSTROUTING':default_chain_setup()}, + 'security': {'INPUT':default_chain_setup(), 'OUTPUT':default_chain_setup(), 'FORWARD':default_chain_setup()}} else: all_chains = { - 'filter': {'INPUT':list(), 'OUTPUT':list(), 'FORWARD':list()}, - 'nat': {'PREROUTING':list(), 'OUTPUT':list(), 'POSTROUTING':list()}, - 'broute': {'BROUTING':list()} + 'filter': {'INPUT':default_chain_setup(), 'OUTPUT':default_chain_setup(), 'FORWARD':default_chain_setup()}, + 'nat': {'PREROUTING':default_chain_setup(), 'OUTPUT':default_chain_setup(), 'POSTROUTING':default_chain_setup()}, + 'broute': {'BROUTING':default_chain_setup()} } defualt_chain_list = ['PREROUTING', 'FORWARD', 'INPUT', 'OUTPUT', 'POSTROUTING', 'BROUTING'] @@ -35,28 +38,39 @@ def parse_input(input_string): token_list = line.split() if not token_list : continue - if token_list[0][1:] in all_chains.keys(): - current_table = token_list[0][1:] + if token_list[0][0] == '*': + if token_list[0][1:] in all_chains: + current_table = token_list[0][1:] + continue + elif token_list[0][0] == ':': + current_chain = token_list[0][1:] + if current_chain not in all_chains[current_table]: + all_chains[current_table][current_chain] = default_chain_setup() + all_chains[current_table][current_chain]['target'] = token_list[1] continue if token_list[0] == '-A': current_chain = token_list[1] if current_chain not in all_chains[current_table]: - all_chains[current_table][current_chain] = list() + all_chains[current_table][current_chain] = default_chain_setup() rule_body = stats - target = '' - if token_list[-2] == '-j' and token_list[-1] not in ['RETURN', 'ACCEPT', 'DROP']: - target = token_list[-1] - if target not in all_chains[current_table]: - all_chains[current_table][target] = list() - rule_body += ' '.join(token_list[2:]) - else: - rule_body += ' '.join(token_list[2:]) + target = None + for i, token in enumerate(token_list): + if token in [ '-j', '--jump', '-g', '--goto' ] and token_list[i + 1] : + target = { + 'chain' : token_list[i + 1], + 'type' : token + } + if target['chain'] in ['RETURN', 'ACCEPT', 'DROP', 'DNAT', 'SNAT', 'NFLOG']: + target['type'] = 'SPECIAL' + elif target['chain'] not in all_chains[current_table]: + all_chains[current_table][target['chain']] = default_chain_setup() - all_chains[current_table][current_chain].append({'rule_body':rule_body, 'target':target}) - continue - else: - continue + rule_body += ' '.join(token_list[2:]) + + all_chains[current_table][current_chain]['rules'].append({'rule_body':rule_body, 'target':target}) + + continue def get_node_name(table_name, chain_name): @@ -80,8 +94,9 @@ def escape_xml(s): default_port_ranges = ''' red=29700:29799,47000:48653,48659,48662,48663,48753,48754,48850:49151,49200:49799 -green=80 +green=80,443,8080,8000,4443 yellow=22 +pink=0:1024 ''' def expand_port_range(port_range): @@ -128,6 +143,9 @@ def conv_port_range(r): colors = { 'bgcolor' : 'black', 'norm_link' : 'white', + 'return_link' : 'green', + 'goto_return_link' : 'blue', + 'jump_return_link' : 'purple', 'chain_link' : 'red', 'unused_rule' : 'lightgrey', 'used_rule' : 'darkgrey', @@ -135,6 +153,39 @@ colors = { 'user_chain' : ['white', 'white'], } +from pprint import pprint + +cached_links = list() +def filter_duplicate_links(link): + global cached_links + if link in cached_links: + return '' + + cached_links.append(link) + return link + +def create_goto_backref(chains, chain, target_node, color): + global colors + output = '' + for source_node in chains[chain]['jump_to_list']: + output += filter_duplicate_links('{} -> {} [color="{}"]\n'.format(target_node, source_node, colors[color])) + for cchain in chains[chain]['goto_list']: + output += create_goto_backref(chains, cchain, target_node, color) + return output + + + +def test_chain_return(chains, chain, recurse): + """ Test if the last rule of the destination chain is ACCEPT or DROP """ + if chains[chain]['rules']: + last_rule = chains[chain]['rules'][-1] + if last_rule['target']: + if last_rule['target']['type'] in ['SPECIAL'] and last_rule['target']['chain'] in ['ACCEPT', 'DROP']: + return False + if recurse: + return test_chain_return(chains, last_rule['target']['chain'], recurse) + return True + def create_output(all_chains): global default_port_ranges, colors port_ranges = conv_port_range(os.getenv('PORT_RANGE', default=default_port_ranges)) @@ -151,13 +202,15 @@ def create_output(all_chains): node_name = get_node_name(table, chain) tmp_body = node_name + """ [label=<""" if chain in defualt_chain_list: - tmp_body +='\n'.format(colors['default_chain'][0], escape_xml( chain )) + tmp_body +='\n'.format(colors['default_chain'][0], escape_xml( '{}[{}]'.format(chain, all_chains[table][chain]['target']) )) tmp_body +='\n'.format( colors['default_chain'][1], escape_xml( table )) else: - tmp_body +='\n'.format(colors['user_chain'][0], chain ) + tmp_body +='\n'.format(colors['user_chain'][0], escape_xml( '{}[{}]'.format(chain, all_chains[table][chain]['target']) ) ) tmp_body +='\n'.format(colors['user_chain'][1], escape_xml( table )) - for i in range(len(all_chains[table][chain])): - rule = all_chains[table][chain][i] + + + + for i, rule in enumerate(all_chains[table][chain]['rules']): tmp_body += '\n'.format(get_port_name(i), get_rule_attributes(port_ranges, rule), escape_xml( rule["rule_body"] )) tmp_body += ''.format(colors['user_chain'][1]) tmp_body += '
{}
{}
{}
{}
{}
{}
{}
end
>];' @@ -165,13 +218,38 @@ def create_output(all_chains): for table in all_chains: for chain in all_chains[table]: - for i in range(len(all_chains[table][chain])): - rule = all_chains[table][chain][i] + for i, rule in enumerate(all_chains[table][chain]['rules']): if rule['target']: source_node = get_node_name(table, chain) + ':' + get_port_name(i) - target_node = get_node_name(table, rule['target']) + ':begin' + if rule['target']['type'] in ['-j' , '--jump']: + all_chains[table][rule['target']['chain']]['jump_to_list'].append(source_node) + elif rule['target']['type'] in ['-g' , '--goto']: + if chain not in all_chains[table][chain]['goto_list']: + all_chains[table][rule['target']['chain']]['goto_list'].append(chain) + + for table in all_chains: + for chain in all_chains[table]: + for i, rule in enumerate(all_chains[table][chain]['rules']): + if rule['target']: + source_node = get_node_name(table, chain) + ':' + get_port_name(i) + + if rule['target']['type'] in ['SPECIAL']: + if rule['target']['chain'] in ['RETURN']: + output += create_goto_backref(all_chains[table], chain, source_node, 'return_link') + continue + target_node = get_node_name(table, rule['target']['chain']) + ':begin' output += '{} -> {} [color="{}"]\n'.format(source_node, target_node, colors['norm_link']) + # + # Create return links. + # + if test_chain_return(all_chains[table], rule['target']['chain'], True): + target_node = get_node_name(table, rule['target']['chain']) + ':end' + if rule['target']['type'] in ['-j' , '--jump']: + output += '{} -> {} [color="{}"]\n'.format(target_node, source_node, colors['jump_return_link']) + elif rule['target']['type'] in ['-g' , '--goto']: + output += create_goto_backref(all_chains[table], chain, target_node, 'goto_return_link') + def default_chain_link(src_table_name, src_chain_name, dst_table_name, dst_chain_name): source_node = get_node_name(src_table_name, src_chain_name) + ':end' target_node = get_node_name(dst_table_name, dst_chain_name) + ':begin' From 96c0c273a9f7b84d1f98801b3f0710830dcc7284 Mon Sep 17 00:00:00 2001 From: dethophes Date: Sun, 29 Oct 2023 20:04:04 +0000 Subject: [PATCH 9/9] fix typo in range calculation. --- iptables-graph | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iptables-graph b/iptables-graph index e23854e..d0bcdfc 100755 --- a/iptables-graph +++ b/iptables-graph @@ -106,7 +106,7 @@ def expand_port_range(port_range): if len(r) == 2: prange += range(int(r[0]), int(r[1]) + 1) else: - prange = [ int(r[0]) ] + prange += [ int(r[0]) ] return prange def test_port_range_in_port_range(a, b):