merged 2 lines
[ferm.git] / test / canonical.pl
blob33d24464bae61bef7246c7bd68b6825b9d7fde16
1 #!/usr/bin/perl -w
2 # $Id$
4 # Canonicalize ferm output. You can use this script to check whether
5 # the output from two ferm versions are functionally identical. This
6 # is used in the compatibility tests.
8 # Author: Max Kellermann (max@duempel.org)
10 use strict;
12 sub shell_unescape {
13 my $token = shift;
14 $token =~ s/\\'/'/g;
15 $token =~ s/^(["'])(.*)\1/$2/s;
16 return $token;
19 my $data;
21 while (<>) {
22 next if /^\s*(?:#.*)?$/s;
24 # workaround: not supported in ipchains
25 next
26 if /cannot set the policy for non-built in chains, exiting|Cannot create new chains if using ipfwadm|Ipfwadm allows only accept, masq, deny and reject targets/;
28 # execute backticks
29 s/`(.*?)`/`$1`/egs;
31 if (s/^(ip6?)tables //) {
32 my $item;
34 $item->{domain} = $1;
36 # get table
37 my $table;
38 s/-t (\w+)/$table = $1; ''/eg;
39 $table = 'filter'
40 unless defined $table;
42 # get command and chain
43 my ($command, $chain);
45 if (s/-P (\w+) (\w+)//g) {
46 if ($2 eq 'ACCEPT') {
47 delete $data->{iptables}{$table}{$1}{policy}
48 if exists $data->{iptables}{$table}
49 and exists $data->{iptables}{$table}{$1};
50 } else {
51 $data->{iptables}{$table}{$1}{policy} = $2;
53 next;
56 s/-([ALFZNXE])(?: ([-\w]+))?/($command, $chain) = ($1, $2); ''/eg;
58 next if $command eq 'F' or $command eq 'X';
60 if ($command eq 'N') {
61 if (defined $chain) {
62 push @{$data->{iptables}{$table}{$chain}{rules}}, $command;
63 } else {
64 push @{$data->{iptables}{$table}{rules}}, $command;
66 next;
69 die 'no chain specified'
70 unless defined $chain;
72 # module list
73 my %modules;
74 s/--match/-m/g;
75 s/-m (\w+)/$modules{$1} = 1; ''/eg;
76 $item->{modules} = [ grep { not /^(?:tcp|udp|icmp)$/ } keys %modules ];
78 # short to long
79 s/-j\b/--jump/g;
80 s/-g\b/--goto/g;
81 s/-i\b/--in-interface/g;
82 s/-o\b/--out-interface/g;
83 s/-p\b/--protocol/g;
84 s/-d\b/--destination/g;
85 s/-s\b/--source/g;
86 s/-f\b/--fragment/g;
88 # evaluate options with name collisions
89 s/--set\s+(\w+)\s+([\w,]+)/$item->{ipset_set} = [$1, $2]; ''/eg
90 if exists $modules{set};
92 # evaluate options with zero, one, two parameters
93 s/(?:(!)\s*)?--(syn|clamp-mss-to-pmtu|set|rcheck|log-tcp-sequence|log-tcp-options|log-ip-options|continue|save-mark|restore-mark|save|restore|fragment|ecn-tcp-cwr|ecn-tcp-ece|physdev-is-(?:in|out|bridged)|strict|next|frag(res|first|more|last)|nodst|random|ssrr|lsrr|no-srr|rr|ts|ra|any-opt|ecn-tcp-remove|ahres|soft|rt-0-res|rt-0-not-strict|ashort|new|rttl|rsource|rdest|utc|localtz)(?:\s|$)/$item->{$2} = $1; ''/eg;
94 s/--(tcp-flags|chunk-types|add-set|del-set)\s+(?:(\!)\s+)?(\S+)\s+(\S+)/$item->{$1} = [ $2, $3, $4 ]; ''/eg;
95 s/(?:(!)\s*)?--(iplimit-above|src-range|dst-range|connlimit-above|connbytes|tos)\s+(\S+)/$item->{$2} = [ $1, $2 ]; ''/eg;
96 s/--(\w[-\w]*)\s+(!)?\s*(".*?"|'.*?'|\S+)/$item->{$1} = (defined $2 ? "$2\t" : "") . shell_unescape($3); ''/eg;
98 # after we parsed everything we know, nothing must be left
99 die "unparsed rest from line $.: $_"
100 if /\S/;
102 # add this item
103 push @{$data->{iptables}{$table}{$chain}{rules}}, $item;
104 } elsif (s/^ipchains //) {
105 my $item;
107 # get command and chain
108 my ($command, $chain);
110 if (s/-P (\w+) (\w+)//g) {
111 $data->{ipchains}{$1}{policy} = $2;
112 next;
115 s/-([AFZNX])(?: (\w+))?/($command, $chain) = ($1, $2); ''/eg;
117 if ($command eq 'F' or $command eq 'N' or $command eq 'X') {
118 if (defined $chain) {
119 delete $data->{ipchains}{$chain}{rules};
120 } else {
121 delete $data->{ipchains};
123 next;
126 die 'no chain specified'
127 unless defined $chain;
129 # short to long
130 s/-j\b/--jump/g;
131 s/-i\b/--interface/g;
132 s/-d\b/--destination/g;
133 s/-s\b/--source/g;
134 s/-l\b/--log/g;
135 s/-p\b/--protocol/g;
136 s/-y\b/--syn/g;
138 # evaluate options with zero, one parameter
139 s/(!\s*)?--(log|syn)\b/$item->{$2} = $1; ''/eg;
140 s/--(jump|protocol|interface|destination|source|protocol|
141 dport|destination-port|sport|source-port
142 )\s+(".*?"|(?:!\s*)?\S+)/$item->{$1} = $2; ''/egx;
144 # after we parsed everything we know, nothing must be left
145 die "unparsed rest from line $.: $_"
146 if /\S/;
148 # add this item
149 push @{$data->{ipchains}{$chain}{rules}}, $item;
150 } elsif (s/^ipfwadm //) {
151 my $item;
153 # get chain
154 s/-([IOF])//
155 or die "No chain in line $.";
157 my $chain = $1;
159 # handle command
160 if (s/-p (\w+)//) {
161 $data->{ipfwadm}{$chain}{policy} = $1;
162 next;
165 $item->{policy} = $1
166 if s/-a (\w+)//;
168 # evaluate options
169 s/-([m])/$item->{$1} = 1; ''/eg;
170 s/-([PVW])\s+((?:!\s*)?\S+)/$item->{$1} = $2; ''/egx;
172 # after we parsed everything we know, nothing must be left
173 die "unparsed rest from line $.: $_"
174 if /\S/;
176 # add this item
177 push @{$data->{ipfwadm}{$chain}{rules}}, $item;
178 } else {
179 die "syntax error line $.";
183 use Data::Dumper;
184 $Data::Dumper::Sortkeys = 1;
185 print Dumper($data);