3 # Save, create or delete a rule in a chain
5 require './firewall-lib.pl';
7 &error_setup($text{'save_err'});
8 @tables = &get_iptables_save();
9 $table = $tables[$in{'table'}];
10 &can_edit_table($table->{'name'}) || &error($text{'etable'});
12 $rule = { 'chain' => $in{'chain'} };
15 $rule = $table->{'rules'}->[$in{'idx'}];
16 &can_jump($rule) || &error($text{'ejump'});
19 # Go back to the editing page
20 &redirect("edit_rule.cgi?new=1&clone=$in{'idx'}&".
21 "table=".&urlize($in{'table'})."&".
22 "chain=".&urlize($rule->{'chain'}));
25 &lock_file($iptables_save_file);
27 # Just delete this rule
28 splice(@{$table->{'rules'}}, $in{'idx'}, 1);
31 # Validate and store inputs
32 if ($config{'comment_mod'}) {
34 $rule->{'comment'} = [ "", $in{'cmt'} ];
35 push(@mods, "comment");
38 delete($rule->{'comment'});
42 $rule->{'cmt'} = $in{'cmt'};
43 delete($rule->{'comment'});
44 @mods = grep { $_ ne "comment" } @mods;
46 if ($in{'jump'} eq '*') {
47 $in{'other'} =~ /^\S+$/ || &error($text{'save_echain'});
48 $rule->{'j'} = [ "", $in{'other'} ];
51 $rule->{'j'} = [ "", $in{'jump'} ];
56 &can_jump($rule) || &error($text{'save_ecanjump'});
57 if (defined($in{'rwithtype'})) {
58 if ($rule->{'j'}->[1] eq 'REJECT' && !$in{'rwithdef'}) {
59 $rule->{'reject-with'} = [ "", $in{'rwithtype'} ];
62 delete($rule->{'reject-with'});
66 # Parse redirect or masquerade input
67 if ($table->{'name'} eq 'nat') {
68 if ($rule->{'j'}->[1] eq 'REDIRECT' && !$in{'rtodef'}) {
69 $in{'rtofrom'} =~ /^\d+$/ ||
70 &error($text{'save_ertoports'});
71 $in{'rtoto'} =~ /^\d*$/ ||
72 &error($text{'save_ertoports'});
73 $rule->{'to-ports'} = [ "", $in{'rtoto'} eq '' ?
74 $in{'rtofrom'} : $in{'rtofrom'}."-".$in{'rtoto'} ];
76 elsif ($rule->{'j'}->[1] eq 'MASQUERADE' && !$in{'mtodef'}) {
77 $in{'mtofrom'} =~ /^\d+$/ ||
78 &error($text{'save_emtoports'});
79 $in{'mtoto'} =~ /^\d*$/ ||
80 &error($text{'save_emtoports'});
81 $rule->{'to-ports'} = [ "", $in{'mtoto'} eq '' ?
82 $in{'mtofrom'} : $in{'mtofrom'}."-".$in{'mtoto'} ];
85 delete($rule->{'to-ports'});
88 if ($table->{'name'} eq 'nat' && $rule->{'chain'} ne 'POSTROUTING') {
89 if ($rule->{'j'}->[1] eq 'DNAT' && !$in{'dnatdef'}) {
90 &check_ipaddress($in{'dipfrom'}) ||
91 &error($text{'save_edipfrom'});
92 !$in{'dipto'} || &check_ipaddress($in{'dipto'}) ||
93 &error($text{'save_edipto'});
94 local $v = $in{'dipfrom'};
95 $v .= "-".$in{'dipto'} if ($in{'dipto'});
96 if ($in{'dpfrom'} ne '') {
97 $in{'dpfrom'} =~ /^\d+$/ ||
98 &error($text{'save_edpfrom'});
99 $in{'dpto'} =~ /^\d*$/ ||
100 &error($text{'save_edpto'});
101 if ($in{'dpto'} eq '') {
102 $v .= ":".$in{'dpfrom'};
105 $v .= ":".$in{'dpfrom'}."-".$in{'dpto'};
108 $rule->{'to-destination'} = [ "", $v ];
111 delete($rule->{'to-destination'});
114 if ($table->{'name'} eq 'nat' && $rule->{'chain'} ne 'PREROUTING' &&
115 $rule->{'chain'} ne 'OUTPUT') {
116 if ($rule->{'j'}->[1] eq 'SNAT' && !$in{'snatdef'}) {
117 &check_ipaddress($in{'sipfrom'}) ||
118 &error($text{'save_esipfrom'});
119 !$in{'sipto'} || &check_ipaddress($in{'sipto'}) ||
120 &error($text{'save_esipto'});
121 local $v = $in{'sipfrom'};
122 $v .= "-".$in{'sipto'} if ($in{'sipto'});
123 if ($in{'spfrom'} ne '') {
124 $in{'spfrom'} =~ /^\d+$/ ||
125 &error($text{'save_espfrom'});
126 $in{'spto'} =~ /^\d*$/ ||
127 &error($text{'save_espto'});
128 if ($in{'spto'} eq '') {
129 $v .= ":".$in{'spfrom'};
132 $v .= ":".$in{'spfrom'}."-".$in{'spto'};
135 $rule->{'to-source'} = [ "", $v ];
138 delete($rule->{'to-source'});
141 if (&parse_mode("source", $rule, "s")) {
142 &check_ipmask($in{'source'}) || &error($text{'save_esource'});
143 $rule->{'s'}->[1] = $in{'source'};
145 if (&parse_mode("dest", $rule, "d")) {
146 &check_ipmask($in{'dest'}) || &error($text{'save_edest'});
147 $rule->{'d'}->[1] = $in{'dest'};
149 if (&parse_mode("in", $rule, "i")) {
150 $in{'in'} ne '' || $in{'in_other'} =~ /^\S+$/ ||
151 &error($text{'save_ein'});
152 $rule->{'i'}->[1] = $in{'in'} eq '' || $in{'in'} eq 'other' ?
153 $in{'in_other'} : $in{'in'};
155 if (&parse_mode("out", $rule, "o")) {
156 $in{'out'} ne '' || $in{'out_other'} =~ /^\S+$/ ||
157 &error($text{'save_eout'});
158 $rule->{'o'}->[1] = $in{'out'} eq '' || $in{'out'} eq 'other' ?
159 $in{'out_other'} : $in{'out'};
161 if ($in{'frag'} == 0) { delete($rule->{'f'}); }
162 elsif ($in{'frag'} == 1) { $rule->{'f'} = [ "" ]; }
163 else { $rule->{'f'} = [ "!" ]; }
164 if (&parse_mode("proto", $rule, "p")) {
165 $in{'proto'} || $in{'proto_other'} =~ /^\d+$/ ||
166 &error($text{'save_eproto'});
167 $rule->{'p'}->[1] = $in{'proto'} || $in{'proto_other'};
168 if (!$rule->{'p'}->[0]) {
169 $proto = $in{'proto'};
170 push(@mods, $in{'proto'})
171 if ($proto eq 'tcp' || $proto eq 'udp' ||
172 $proto eq 'icmp' && $in{'icmptype_mode'});
176 if (&parse_mode("sport", $rule, "sport")) {
177 $proto eq "tcp" || $proto eq "udp" || $proto eq "sctp" ||
178 &error($text{'save_etcpudp'});
179 if ($in{"sport_type"} == 0) {
180 $in{"sport"} =~ /^\S+$/ ||
181 &error($text{'save_esport'});
182 if ($in{"sport"} =~ /,/) {
183 $rule->{'sports'}->[1] = $in{"sport"};
184 $rule->{'sports'}->[0] = $rule->{'sport'}->[0];
185 push(@mods, "multiport");
186 delete($rule->{'sport'});
189 $rule->{'sport'}->[1] = $in{"sport"};
190 delete($rule->{'sports'});
194 $in{"sport_from"} =~ /^\d*$/ ||
195 &error($text{'save_esportfrom'});
196 $in{"sport_to"} =~ /^\d*$/ ||
197 &error($text{'save_esportto'});
198 $rule->{'sport'}->[1] = $in{"sport_from"}.":".
200 $rule->{'sport'}->[1] eq ":" &&
201 &error($text{'save_esportrange'});
202 delete($rule->{'sports'});
206 delete($rule->{'sports'});
208 if (&parse_mode("dport", $rule, "dport")) {
209 $proto eq "tcp" || $proto eq "udp" || $proto eq "sctp" ||
210 &error($text{'save_etcpudp'});
211 if ($in{"dport_type"} == 0) {
212 $in{"dport"} =~ /^\S+$/ ||
213 &error($text{'save_edport'});
214 if ($in{"dport"} =~ /,/) {
215 $rule->{'dports'}->[1] = $in{"dport"};
216 $rule->{'dports'}->[0] = $rule->{'dport'}->[0];
217 push(@mods, "multiport");
218 delete($rule->{'dport'});
221 $rule->{'dport'}->[1] = $in{"dport"};
222 delete($rule->{'dports'});
226 $in{"dport_from"} =~ /^\d*$/ ||
227 &error($text{'save_edportfrom'});
228 $in{"dport_to"} =~ /^\d*$/ ||
229 &error($text{'save_edportto'});
230 $rule->{'dport'}->[1] = $in{"dport_from"}.":".
232 $rule->{'dport'}->[1] eq ":" &&
233 &error($text{'save_edportrange'});
234 delete($rule->{'dports'});
238 delete($rule->{'dports'});
240 if (&parse_mode("ports", $rule, "ports")) {
241 $proto eq "tcp" || $proto eq "udp" || $proto eq "sctp" ||
242 &error($text{'save_etcpudp'});
243 $in{"ports"} =~ /^\S+$/ || &error($text{'save_eports'});
244 $rule->{'ports'}->[1] = $in{'ports'};
245 push(@mods, "multiport");
247 if (&parse_mode("tcpflags", $rule, "tcp-flags")) {
248 $proto eq "tcp" || &error($text{'save_etcp1'});
249 local $tcp0 = join(",", split(/\0/, $in{"tcpflags0"}));
250 local $tcp1 = join(",", split(/\0/, $in{"tcpflags1"}));
251 #$tcp0 && $tcp1 || &error($text{'save_etcpflags'});
252 $tcp0 || &error($text{'save_etcpflags2'});
253 $rule->{'tcp-flags'}->[1] = $tcp0;
254 $rule->{'tcp-flags'}->[2] = $tcp1 || "NONE";
256 if (&parse_mode("tcpoption", $rule, "tcp-option")) {
257 $proto eq "tcp" || &error($text{'save_etcp2'});
258 $in{"tcpoption"} =~ /^\d+$/ ||
259 &error($text{'save_etcpoption'});
260 $rule->{'tcp-option'}->[1] = $in{"tcpoption"};
262 if (&parse_mode("icmptype", $rule, "icmp-type")) {
263 $proto eq "icmp" || &error($text{'save_eicmp'});
264 $rule->{'icmp-type'}->[1] = $in{'icmptype'};
266 if (&parse_mode("macsource", $rule, "mac-source")) {
267 $in{"macsource"} =~ /^([0-9a-z]{2}:){5}[[0-9a-z]{2}$/i ||
268 &error($text{'save_emac'});
269 $rule->{'mac-source'}->[1] = $in{'macsource'};
272 if (&parse_mode("limit", $rule, "limit")) {
273 $in{'limit0'} =~ /^\d+$/ || &error($text{'save_elimit'});
274 $rule->{'limit'}->[1] = $in{'limit0'}."/".$in{'limit1'};
275 push(@mods, "limit");
277 if (&parse_mode("limitburst", $rule, "limit-burst")) {
278 $in{'limitburst'} =~ /^\d+$/ ||
279 &error($text{'save_elimitburst'});
280 $rule->{'limit-burst'}->[1] = $in{'limitburst'};
281 push(@mods, "limit");
284 if ($rule->{'chain'} eq 'OUTPUT') {
285 if (&parse_mode("uidowner", $rule, "uid-owner")) {
286 defined(getpwnam($in{"uidowner"})) ||
287 &error($text{'save_euidowner'});
288 $rule->{'uid-owner'}->[1] = $in{"uidowner"};
289 push(@mods, "owner");
291 if (&parse_mode("gidowner", $rule, "gid-owner")) {
292 defined(getgrnam($in{"gidowner"})) ||
293 &error($text{'save_egidowner'});
294 $rule->{'gid-owner'}->[1] = $in{"gidowner"};
295 push(@mods, "owner");
297 if (&parse_mode("pidowner", $rule, "pid-owner")) {
298 $in{"pidowner"} =~ /^\d+$/ ||
299 &error($text{'save_epidowner'});
300 $rule->{'pid-owner'}->[1] = $in{"pidowner"};
301 push(@mods, "owner");
303 if (&parse_mode("sidowner", $rule, "sid-owner")) {
304 $in{"sidowner"} =~ /^\d+$/ ||
305 &error($text{'save_esidowner'});
306 $rule->{'sid-owner'}->[1] = $in{"sidowner"};
307 push(@mods, "owner");
311 # Save connection states and TOS
312 if (&parse_mode("state", $rule, "state")) {
313 @states = split(/\0/, $in{'state'});
314 @states || &error($text{'save_estates'});
315 $rule->{'state'}->[1] = join(",", @states);
316 push(@mods, "state");
318 if (&parse_mode("tos", $rule, "tos")) {
319 $rule->{'tos'}->[1] = $in{'tos'};
323 # Parse physical input and output interfaces
324 if (&parse_mode("physdevin", $rule, "physdev-in")) {
325 $in{'physdevin'} ne '' || $in{'physdevin_other'} =~ /^\S+$/ ||
326 &error($text{'save_ephysdevin'});
327 $rule->{'physdev-in'}->[1] =
328 $in{'physdevin'} eq '' || $in{'physdevin'} eq 'other' ?
329 $in{'physdevin_other'} : $in{'physdevin'};
330 push(@mods, "physdev");
332 if (&parse_mode("physdevout", $rule, "physdev-out")) {
333 $in{'physdevout'} ne '' || $in{'physdevout_other'} =~ /^\S+$/ ||
334 &error($text{'save_ephysdevout'});
335 $rule->{'physdev-out'}->[1] =
336 $in{'physdevout'} eq '' || $in{'physdevout'} eq 'other' ?
337 $in{'physdevout_other'} : $in{'physdevout'};
338 push(@mods, "physdev");
341 # Parse physdev match modes
342 if (&parse_mode("physdevisin", $rule, "physdev-is-in")) {
343 push(@mods, "physdev");
345 if (&parse_mode("physdevisout", $rule, "physdev-is-out")) {
346 push(@mods, "physdev");
348 if (&parse_mode("physdevisbridged", $rule, "physdev-is-bridged")) {
349 push(@mods, "physdev");
352 # Add custom paramters and modules
353 $rule->{'args'} = $in{'args'};
354 push(@mods, split(/\s+/, $in{'mods'}));
358 $rule->{'m'} = [ map { [ "", $_ ] } &unique(@mods) ];
361 delete($rule->{'m'});
363 delete($rule->{'j'}) if (!$in{'jump'});
365 if ($in{'before'} ne '') {
366 splice(@{$table->{'rules'}}, $in{'before'}, 0, $rule);
368 elsif ($in{'after'} ne '') {
369 splice(@{$table->{'rules'}}, $in{'after'}+1, 0, $rule);
372 push(@{$table->{'rules'}}, $rule);
377 # Write out the new save file
378 &run_before_command();
380 &run_after_command();
382 &unlock_file($iptables_save_file);
383 &webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "modify",
384 "rule", undef, { 'chain' => $rule->{'chain'},
385 'table' => $table->{'name'} });
386 &redirect("index.cgi?table=$in{'table'}");
388 # parse_mode(name, &rule, option)
391 if ($in{"$_[0]_mode"} == 0) {
392 delete($_[1]->{$_[2]});
395 elsif ($in{"$_[0]_mode"} == 1) {
396 $_[1]->{$_[2]} = [ "" ];
400 $_[1]->{$_[2]} = [ "!" ];
407 return &to_ipaddress($_[0]) ||
408 $_[0] =~ /^([0-9\.]+)\/([0-9\.]+)$/ &&
409 &to_ipaddress("$1") &&
410 (&check_ipaddress("$2") || ($2 =~ /^\d+$/ && $2 <= 32));