Skip to content

Instantly share code, notes, and snippets.

@craigphicks
Last active April 7, 2020 10:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save craigphicks/b596ae415ac8325f6b331826cd2b2d64 to your computer and use it in GitHub Desktop.
Save craigphicks/b596ae415ac8325f6b331826cd2b2d64 to your computer and use it in GitHub Desktop.
Convert `iptables -S` output to a depth-first tree listing
#!/usr/bin/env python3
'''
Convert `iptables -S` output to a depth-first tree listing
Reads from standard input
Craig P Hicks 2019
MIT License
'''
import sys
tbls=[(None,''),('ACCEPT',''),('DROP',''),('REJECT',''),('RETURN',''),('LOG',''),('QUEUE','')]
rules=[]
tbl_rule_pairs=[]
tbl_rule_pairs_be=[]
def tbl_index(name):
global tbls
for i in range(0,len(tbls)):
if tbls[i][0]==name:
return i
def parse_input():
global tbls
global rules
global tbl_rule_pairs
global tbl_rule_pairs_be
lines=sys.stdin.readlines()
for line in lines:
line=line.strip()
toks=line.split()
if len(toks)>=2:
if toks[0]=='-N':
tbls.append((toks[1],line,0,0))
elif toks[0]=='-P':
tbl_parent_idx=0
tbl_child_idx=len(tbls)
tbl_rule_pairs.append((tbl_parent_idx,len(rules)))
rules.append((0,tbl_child_idx,line))
tbls.append((toks[1],line,0,0))
elif toks[0]=='-A':
tbl_parent_idx=tbl_index(toks[1])
assert '-j' in toks
jidx=toks.index('-j')
tbl_child_idx=tbl_index(toks[jidx+1])
tbl_rule_pairs.append((tbl_parent_idx,len(rules)))
rules.append((tbl_parent_idx,tbl_child_idx,line))
tbl_rule_pairs_be=[None]*len(tbls)
tbl_rule_pairs.sort()
tblidx=-1
pair_b=-1
for trpidx in range(0,len(tbl_rule_pairs)):
if tbl_rule_pairs[trpidx][0]!=tblidx:
if tblidx!=-1:
tbl_rule_pairs_be[tblidx]=(pair_b,trpidx)
tblidx=tbl_rule_pairs[trpidx][0]
pair_b=trpidx
if tblidx!=-1:
tbl_rule_pairs_be[tblidx]=(pair_b,len(tbl_rule_pairs))
def trav_df(indent_level,tblidx):
global tbls
global rules
global tbl_rule_pairs
global tbl_rule_pairs_be
#print(tbls[tblidx][1])
trp_be=tbl_rule_pairs_be[tblidx]
if not trp_be:
return
for trp in tbl_rule_pairs[trp_be[0]:trp_be[1]]:
assert trp[0]==tblidx
print((' '*indent_level*2)+rules[trp[1]][2])
trav_df(indent_level+1, rules[trp[1]][1])
if __name__ == '__main__':
parse_input()
trav_df(0,0)
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT ACCEPT
-N forwarding_lan_rule
-N forwarding_rule
-N forwarding_wan_rule
-N input_lan_rule
-N input_rule
-N input_wan_rule
-N output_lan_rule
-N output_rule
-N output_wan_rule
-N reject
-N syn_flood
-N zone_lan_dest_ACCEPT
-N zone_lan_forward
-N zone_lan_input
-N zone_lan_output
-N zone_lan_src_ACCEPT
-N zone_wan_dest_ACCEPT
-N zone_wan_dest_REJECT
-N zone_wan_forward
-N zone_wan_input
-N zone_wan_output
-N zone_wan_src_REJECT
-A INPUT -i lo -m comment --comment "!fw3" -j ACCEPT
-A INPUT -m comment --comment "!fw3: Custom input rule chain" -j input_rule
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "!fw3" -j ACCEPT
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m comment --comment "!fw3" -j syn_flood
-A INPUT -i br-lan -m comment --comment "!fw3" -j zone_lan_input
-A FORWARD -m comment --comment "!fw3: Custom forwarding rule chain" -j forwarding_rule
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "!fw3" -j ACCEPT
-A FORWARD -i br-lan -m comment --comment "!fw3" -j zone_lan_forward
-A FORWARD -m comment --comment "!fw3" -j reject
-A OUTPUT -o lo -m comment --comment "!fw3" -j ACCEPT
-A OUTPUT -m comment --comment "!fw3: Custom output rule chain" -j output_rule
-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "!fw3" -j ACCEPT
-A OUTPUT -o br-lan -m comment --comment "!fw3" -j zone_lan_output
-A reject -p tcp -m comment --comment "!fw3" -j REJECT --reject-with tcp-reset
-A reject -m comment --comment "!fw3" -j REJECT --reject-with icmp-port-unreachable
-A syn_flood -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m limit --limit 25/sec --limit-burst 50 -m comment --comment "!fw3" -j RETURN
-A syn_flood -m comment --comment "!fw3" -j DROP
-A zone_lan_dest_ACCEPT -o br-lan -m comment --comment "!fw3" -j ACCEPT
-A zone_lan_forward -m comment --comment "!fw3: Custom lan forwarding rule chain" -j forwarding_lan_rule
-A zone_lan_forward -m comment --comment "!fw3: Zone lan to wan forwarding policy" -j zone_wan_dest_ACCEPT
-A zone_lan_forward -m conntrack --ctstate DNAT -m comment --comment "!fw3: Accept port forwards" -j ACCEPT
-A zone_lan_forward -m comment --comment "!fw3" -j zone_lan_dest_ACCEPT
-A zone_lan_input -m comment --comment "!fw3: Custom lan input rule chain" -j input_lan_rule
-A zone_lan_input -m conntrack --ctstate DNAT -m comment --comment "!fw3: Accept port redirections" -j ACCEPT
-A zone_lan_input -m comment --comment "!fw3" -j zone_lan_src_ACCEPT
-A zone_lan_output -m comment --comment "!fw3: Custom lan output rule chain" -j output_lan_rule
-A zone_lan_output -m comment --comment "!fw3" -j zone_lan_dest_ACCEPT
-A zone_lan_src_ACCEPT -i br-lan -m conntrack --ctstate NEW,UNTRACKED -m comment --comment "!fw3" -j ACCEPT
-A zone_wan_forward -m comment --comment "!fw3: Custom wan forwarding rule chain" -j forwarding_wan_rule
-A zone_wan_forward -p esp -m comment --comment "!fw3: Allow-IPSec-ESP" -j zone_lan_dest_ACCEPT
-A zone_wan_forward -p udp -m udp --dport 500 -m comment --comment "!fw3: Allow-ISAKMP" -j zone_lan_dest_ACCEPT
-A zone_wan_forward -m conntrack --ctstate DNAT -m comment --comment "!fw3: Accept port forwards" -j ACCEPT
-A zone_wan_forward -m comment --comment "!fw3" -j zone_wan_dest_REJECT
-A zone_wan_input -m comment --comment "!fw3: Custom wan input rule chain" -j input_wan_rule
-A zone_wan_input -p udp -m udp --dport 68 -m comment --comment "!fw3: Allow-DHCP-Renew" -j ACCEPT
-A zone_wan_input -p icmp -m icmp --icmp-type 8 -m comment --comment "!fw3: Allow-Ping" -j ACCEPT
-A zone_wan_input -p igmp -m comment --comment "!fw3: Allow-IGMP" -j ACCEPT
-A zone_wan_input -m conntrack --ctstate DNAT -m comment --comment "!fw3: Accept port redirections" -j ACCEPT
-A zone_wan_input -m comment --comment "!fw3" -j zone_wan_src_REJECT
-A zone_wan_output -m comment --comment "!fw3: Custom wan output rule chain" -j output_wan_rule
-A zone_wan_output -m comment --comment "!fw3" -j zone_wan_dest_ACCEPT
-P INPUT DROP
-A INPUT -i lo -m comment --comment "!fw3" -j ACCEPT
-A INPUT -m comment --comment "!fw3: Custom input rule chain" -j input_rule
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "!fw3" -j ACCEPT
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m comment --comment "!fw3" -j syn_flood
-A syn_flood -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m limit --limit 25/sec --limit-burst 50 -m comment --comment "!fw3" -j RETURN
-A syn_flood -m comment --comment "!fw3" -j DROP
-A INPUT -i br-lan -m comment --comment "!fw3" -j zone_lan_input
-A zone_lan_input -m comment --comment "!fw3: Custom lan input rule chain" -j input_lan_rule
-A zone_lan_input -m conntrack --ctstate DNAT -m comment --comment "!fw3: Accept port redirections" -j ACCEPT
-A zone_lan_input -m comment --comment "!fw3" -j zone_lan_src_ACCEPT
-A zone_lan_src_ACCEPT -i br-lan -m conntrack --ctstate NEW,UNTRACKED -m comment --comment "!fw3" -j ACCEPT
-P FORWARD DROP
-A FORWARD -m comment --comment "!fw3: Custom forwarding rule chain" -j forwarding_rule
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "!fw3" -j ACCEPT
-A FORWARD -i br-lan -m comment --comment "!fw3" -j zone_lan_forward
-A zone_lan_forward -m comment --comment "!fw3: Custom lan forwarding rule chain" -j forwarding_lan_rule
-A zone_lan_forward -m comment --comment "!fw3: Zone lan to wan forwarding policy" -j zone_wan_dest_ACCEPT
-A zone_lan_forward -m conntrack --ctstate DNAT -m comment --comment "!fw3: Accept port forwards" -j ACCEPT
-A zone_lan_forward -m comment --comment "!fw3" -j zone_lan_dest_ACCEPT
-A zone_lan_dest_ACCEPT -o br-lan -m comment --comment "!fw3" -j ACCEPT
-A FORWARD -m comment --comment "!fw3" -j reject
-A reject -p tcp -m comment --comment "!fw3" -j REJECT --reject-with tcp-reset
-A reject -m comment --comment "!fw3" -j REJECT --reject-with icmp-port-unreachable
-P OUTPUT ACCEPT
-A OUTPUT -o lo -m comment --comment "!fw3" -j ACCEPT
-A OUTPUT -m comment --comment "!fw3: Custom output rule chain" -j output_rule
-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "!fw3" -j ACCEPT
-A OUTPUT -o br-lan -m comment --comment "!fw3" -j zone_lan_output
-A zone_lan_output -m comment --comment "!fw3: Custom lan output rule chain" -j output_lan_rule
-A zone_lan_output -m comment --comment "!fw3" -j zone_lan_dest_ACCEPT
-A zone_lan_dest_ACCEPT -o br-lan -m comment --comment "!fw3" -j ACCEPT
@craigphicks
Copy link
Author

craigphicks commented Apr 12, 2019

This program "iptree.py" is designed to read output from "iptables -S". It first parses that into a call-tree, and then traverses the tree in depth first order. Not-connected rules and empty tables are omitted. IMHO, it is easier to understand in tree form.

The sample-input.txt file is from the default(*) iptables rules for a OpenWRT 18.02.6. The result after processing with iptree.py is shown in sample-output.txt. Note that the device (a raspberry pi) I am testing has no "WAN" port hence the WAN rules are all disconnected.

(* except that policy for INPUT was changed from ACCEPT to DROP)

@cipri-tom
Copy link

This is such a life-saver ! Thank you!

I think iptables -S should have a --human-readable flag and show it like this. Thank you so much !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment