1#!/usr/local/bin/perl 2# edit_rule.cgi 3# Display the details of one firewall rule, or allow the adding of a new one 4 5require './firewall-lib.pl'; 6&ReadParse(); 7if (&get_ipvx_version() == 6) { 8 require './firewall6-lib.pl'; 9 } 10else { 11 require './firewall4-lib.pl'; 12 } 13@tables = &get_iptables_save(); 14$table = $tables[$in{'table'}]; 15&can_edit_table($table->{'name'}) || &error($text{'etable'}); 16if ($in{'clone'} ne '') { 17 &ui_print_header($text{"index_title_v${ipvx}"}, $text{'edit_title3'}, ""); 18 %clone = %{$table->{'rules'}->[$in{'clone'}]}; 19 $rule = \%clone; 20 } 21elsif ($in{'new'}) { 22 &ui_print_header($text{"index_title_v${ipvx}"}, $text{'edit_title1'}, ""); 23 $rule = { 'chain' => $in{'chain'}, 24 'j' => &can_jump('DROP') ? 'DROP' : "" }; 25 } 26else { 27 &ui_print_header($text{"index_title_v${ipvx}"}, $text{'edit_title2'}, ""); 28 $rule = $table->{'rules'}->[$in{'idx'}]; 29 &can_jump($rule) || &error($text{'ejump'}); 30 } 31 32print &ui_form_start("save_rule${ipvx}.cgi", "post"); 33print &ui_hidden("version", ${ipvx_arg}); 34foreach $f ('table', 'idx', 'new', 'chain', 'before', 'after') { 35 print &ui_hidden($f, $in{$f}); 36 } 37 38# Display action section 39print &ui_table_start($text{'edit_header1'}, "width=100%", 2); 40 41print &ui_table_row($text{'edit_chain'}, 42 $text{"index_chain_".lc($rule->{'chain'})} || 43 &text('index_chain', "<tt>$rule->{'chain'}</tt>")); 44 45# Rule comment 46if ($config{'comment_mod'} || $rule->{'comment'}) { 47 # Get comment from --comment option 48 $cmt = $rule->{'comment'}->[1]; 49 } 50else { 51 # Get comment from # at end of line 52 $cmt = $rule->{'cmt'}; 53 } 54print &ui_table_row($text{'edit_cmt'}, 55 &ui_textbox("cmt", $cmt, 50)); 56 57# Action to take or chain to jump to 58if ($table->{'name'} eq 'nat') { 59 @jumps = ( undef, 'ACCEPT', 'DROP' ); 60 if ($rule->{'chain'} eq 'POSTROUTING') { 61 push(@jumps, 'MASQUERADE', 'SNAT'); 62 } 63 elsif ($rule->{'chain'} eq 'PREROUTING' || 64 $rule->{'chain'} eq 'OUTPUT') { 65 push(@jumps, 'REDIRECT', 'DNAT'); 66 } 67 else { 68 push(@jumps, 'MASQUERADE', 'SNAT', 'REDIRECT', 'DNAT'); 69 } 70 } 71else { 72 @jumps = ( undef, 'ACCEPT', 'DROP', 'REJECT', 'QUEUE', 'RETURN', 'LOG' ); 73 } 74@grid = ( ); 75$found = 0; 76foreach $j (grep { &can_jump($_) } @jumps) { 77 push(@grid, &ui_oneradio("jump", $j, $text{"index_jump_".lc($j)}, 78 $rule->{'j'}->[1] eq $j)); 79 $found++ if ($rule->{'j'}->[1] eq $j); 80 } 81push(@grid, &ui_oneradio("jump", "*", $text{'edit_jump_other'}, !$found)); 82push(@grid, &ui_textbox("other", $found ? "" : $rule->{'j'}->[1], 12)); 83print &ui_table_row($text{'edit_jump'}, 84 &ui_grid_table(\@grid, 6, undef)); 85 86if (&indexof('REJECT', @jumps) >= 0 && &can_jump("REJECT")) { 87 # Show input for REJECT icmp type 88 if ($rule->{'j'}->[1] eq 'REJECT') { 89 $rwith = $rule->{'reject-with'}->[1]; 90 } 91 print &ui_table_row($text{'edit_rwith'}, 92 &ui_radio("rwithdef", $rwith eq "" ? 1 : 0, 93 [ [ 1, $text{'default'} ], 94 [ 0, &text('edit_rwithtype', 95 &icmptype_input("rwithtype", $rwith, \@ipvx_rtypes)) ], 96 ])); 97 } 98 99if (($table->{'name'} eq 'nat' && $rule->{'chain'} ne 'POSTROUTING') && 100 &can_jump("REDIRECT")) { 101 # Show inputs for redirect host and port 102 if ($rule->{'j'}->[1] eq 'REDIRECT') { 103 ($rtofrom, $rtoto) = split(/\-/, $rule->{'to-ports'}->[1]); 104 } 105 print &ui_table_row($text{'edit_rtoports'}, 106 &ui_radio("rtodef", $rtofrom eq "" ? 1 : 0, 107 [ [ 1, $text{'default'} ], 108 [ 0, &text('edit_prange', 109 &ui_textbox("rtofrom", $rtofrom, 6), 110 &ui_textbox("rtoto", $rtoto, 6)) ] ])); 111 } 112 113if (($table->{'name'} eq 'nat' && $rule->{'chain'} ne 'PREROUTING' && 114 $rule->{'chain'} ne 'OUTPUT') && 115 &can_jump("MASQUERADE")) { 116 # Show inputs for masquerading ports 117 if ($rule->{'j'}->[1] eq 'MASQUERADE') { 118 ($mtofrom, $mtoto) = split(/\-/, $rule->{'to-ports'}->[1]); 119 } 120 print &ui_table_row($text{'edit_mtoports'}, 121 &ui_radio("mtodef", $mtofrom eq "" ? 1 : 0, 122 [ [ 1, $text{'edit_any'} ], 123 [ 0, &text('edit_prange', 124 &ui_textbox("mtofrom", $mtofrom, 6), 125 &ui_textbox("mtoto", $mtoto, 6)) ] ])); 126 } 127 128if (($table->{'name'} eq 'nat' && $rule->{'chain'} ne 'POSTROUTING') && 129 &can_jump("DNAT")) { 130 if ($rule->{'j'}->[1] eq 'DNAT') { 131 if ($rule->{'to-destination'}->[1] =~ 132 /$ipvx_todestpattern/) { 133 $dipfrom = $1; 134 $dipto = $3; 135 $dpfrom = $5; 136 $dpto = $7; 137 } 138 elsif ($rule->{'to-destination'}->[1] =~ /^(:(\d+)(\-(\d+))?)?$/) { 139 $dipfrom = ""; 140 $dipto = ""; 141 $dpfrom = $2; 142 $dpto = $4; 143 } 144 } 145 print &ui_table_row($text{'edit_dnat'}, 146 &ui_radio("dnatdef", $dipfrom eq "" ? 1 : 0, 147 [ [ 1, $text{'default'} ], 148 [ 0, &text('edit_dnatip', 149 &ui_textbox("dipfrom", $dipfrom, 15), 150 &ui_textbox("dipto", $dipto, 15))." ". 151 &text('edit_prange', 152 &ui_textbox("dpfrom", $dpfrom, 6), 153 &ui_textbox("dpto", $dpto, 6)) ] ])); 154 } 155 156if (($table->{'name'} eq 'nat' && $rule->{'chain'} ne 'PREROUTING' && 157 $rule->{'chain'} ne 'OUTPUT') && 158 &can_jump("SNAT")) { 159 if ($rule->{'j'}->[1] eq 'SNAT') { 160 if ($rule->{'to-source'}->[1] =~ 161 /^([0-9\.]+)?(\-([0-9\.]+))?(:(\d+)(\-(\d+))?)?$/) { 162 $sipfrom = $1; 163 $sipto = $3; 164 $spfrom = $5; 165 $spto = $7; 166 } 167 } 168 print &ui_table_row($text{'edit_snat'}, 169 &ui_radio("snatdef", $sipfrom eq "" ? 1 : 0, 170 [ [ 1, $text{'default'} ], 171 [ 0, &text('edit_dnatip', 172 &ui_textbox("sipfrom", $sipfrom, 15), 173 &ui_textbox("sipto", $sipto, 15))." ". 174 &text('edit_prange', 175 &ui_textbox("spfrom", $spfrom, 6), 176 &ui_textbox("spto", $spto, 6)) ] ])); 177 } 178 179print &ui_table_end(); 180 181# Display conditions section 182print "$text{'edit_desc'}<br>\n"; 183print &ui_table_start($text{'edit_header2'}, "width=100%", 2); 184 185# Packet source 186print &ui_table_row($text{'edit_source'}, 187 &ui_grid_table([ 188 &print_mode("source", $rule->{'s'}), 189 &ui_textarea("source", join(" ", split(/,/, $rule->{'s'}->[1])), 190 4, 60), 191 ], 2)); 192 193# Packet destination 194print &ui_table_row($text{'edit_dest'}, 195 &ui_grid_table([ 196 &print_mode("dest", $rule->{'d'}), 197 &ui_textarea("dest", join(" ", split(/,/, $rule->{'d'}->[1])), 198 4, 60), 199 ], 2)); 200 201# Incoming interface 202print &ui_table_row($text{'edit_in'}, 203 &print_mode("in", $rule->{'i'})." ". 204 &interface_choice("in", $rule->{'i'}->[1])); 205 206# Outgoing interface 207print &ui_table_row($text{'edit_out'}, 208 &print_mode("out", $rule->{'o'})." ". 209 &interface_choice("out", $rule->{'o'}->[1])); 210 211# Packet fragmentation 212$f = !$rule->{'f'} ? 0 : $rule->{'f'}->[0] eq "!" ? 2 : 1; 213print &ui_table_row($text{'edit_frag'}, 214 &ui_radio("frag", $f, [ [ 0, $text{'edit_ignore'} ], 215 [ 1, $text{'edit_fragis'} ], 216 [ 2, $text{'edit_fragnot'} ] ])); 217 218# IP protocol 219print &ui_table_row($text{'edit_proto'}, 220 &print_mode("proto", $rule->{'p'})." ". 221 &protocol_input("proto", $rule->{'p'}->[1])); 222 223print &ui_table_hr(); 224 225# Source port 226print &ui_table_row($text{'edit_sport'}, 227 &print_mode("sport", $rule->{'sports'} || $rule->{'sport'})." ". 228 &port_input("sport", $rule->{'sports'}->[1] || $rule->{'sport'}->[1])); 229 230# Destination port 231print &ui_table_row($text{'edit_dport'}, 232 &print_mode("dport", $rule->{'dports'} || $rule->{'dport'})." ". 233 &port_input("dport", $rule->{'dports'}->[1] || $rule->{'dport'}->[1])); 234 235# Source and destination ports 236print &ui_table_row($text{'edit_ports'}, 237 &print_mode("ports", $rule->{'ports'})." ". 238 &ui_textbox("ports", $rule->{'ports'}->[1], 30)); 239 240# TCP flags 241print &ui_table_row($text{'edit_tcpflags'}, 242 "<table><tr><td>".&print_mode("tcpflags", $rule->{'tcp-flags'}). 243 "</td> <td>".&text('edit_flags', 244 &tcpflag_input("tcpflags0", $rule->{'tcp-flags'}->[1]), 245 &tcpflag_input("tcpflags1", $rule->{'tcp-flags'}->[2])). 246 "</td></tr></table>"); 247 248# TCP options 249print &ui_table_row($text{'edit_tcpoption'}, 250 &print_mode("tcpoption", $rule->{'tcp-option'})." ". 251 &ui_textbox("tcpoption", $rule->{'tcp-option'}->[1], 6)); 252 253print &ui_table_hr(); 254 255# ICMP packet type 256print &ui_table_row($text{'edit_icmptype'}, 257 &print_mode("icmptype", $rule->{"icmp${ipvx_icmp}-type"})." ". 258 &icmptype_input("icmptype", $rule->{"icmp${ipvx_icmp}-type"}->[1])); 259 260# MAC address 261print &ui_table_row($text{'edit_mac'}, 262 &print_mode("macsource", $rule->{'mac-source'})." ". 263 &ui_textbox("macsource", $rule->{'mac-source'}->[1], 18)); 264 265print &ui_table_hr(); 266 267# Packet flow limit 268($n, $u) = $rule->{'limit'} && 269 $rule->{'limit'}->[1] =~ /^(\d+)\/(\S+)$/ ? ($1, $2) : (); 270print &ui_table_row($text{'edit_limit'}, 271 &print_mode("limit", $rule->{'limit'}, 272 $text{'edit_below'}, $text{'edit_above'}, 1)." ". 273 &ui_textbox("limit0", $n, 6)." / ". 274 &ui_select("limit1", $u, ['second', 'minute', 'hour', 'day'])); 275 276# Packet burst rate 277print &ui_table_row($text{'edit_limitburst'}, 278 &print_mode("limitburst", $rule->{'limit-burst'}, 279 $text{'edit_below'}, $text{'edit_above'}, 1)." ". 280 &ui_textbox("limitburst", $rule->{'limit-burst'}->[1], 6)); 281 282if ($rule->{'chain'} eq 'OUTPUT') { 283 print &ui_table_hr(); 284 285 # Sending UID 286 print &ui_table_row($text{'edit_uidowner'}, 287 &print_mode("uidowner", $rule->{'uid-owner'})." ". 288 &ui_user_textbox("uidowner", $rule->{'uid-owner'}->[1])); 289 290 # Sending GID 291 print &ui_table_row($text{'edit_gidowner'}, 292 &print_mode("gidowner", $rule->{'gid-owner'})." ". 293 &ui_group_textbox("gidowner", $rule->{'gid-owner'}->[1])); 294 295 # Sending process ID 296 print &ui_table_row($text{'edit_pidowner'}, 297 &print_mode("pidowner", $rule->{'pid-owner'})." ". 298 &ui_textbox("pidowner", $rule->{'pid-owner'}->[1], 6)); 299 300 # Sending process group 301 print &ui_table_row($text{'edit_sidowner'}, 302 &print_mode("sidowner", $rule->{'sid-owner'})." ". 303 &ui_textbox("sidowner", $rule->{'sid-owner'}->[1], 6)); 304 } 305 306print &ui_table_hr(); 307 308# Connection states 309my $sd = &supports_conntrack() ? "ctstate" : "state"; 310print &ui_table_row($text{'edit_state'}, 311 "<table cellpadding=0 cellspacing=0><tr><td valign=top>". 312 &print_mode($sd, $rule->{$sd})."</td>\n". 313 "<td> ". 314 &ui_select($sd, [ split(/,/, $rule->{$sd}->[1]) ], 315 [ map { [ $_, $text{"edit_state_".lc($_)} ] } 316 ('NEW', 'ESTABLISHED', 'RELATED', 'INVALID', 'UNTRACKED', 317 $sd eq "state" ? ( ) : ('SNAT', 'DNAT')) ], 5, 1). 318 "</td></tr></table>"); 319 320# Type of service 321print &ui_table_row($text{'edit_tos'}, 322 &print_mode("tos", $rule->{'tos'})." ". 323 &tos_input("tos", $rule->{'tos'}->[1])); 324 325print &ui_table_hr(); 326 327# Input physical device 328print &ui_table_row($text{'edit_physdevin'}, 329 &print_mode("physdevin", $rule->{'physdev-in'})." ". 330 &interface_choice("physdevin", $rule->{'physdev-in'}->[1])); 331 332# Output physical device 333print &ui_table_row($text{'edit_physdevout'}, 334 &print_mode("physdevout", $rule->{'physdev-out'})." ". 335 &interface_choice("physdevout", $rule->{'physdev-out'}->[1])); 336 337# Physdev match modes 338print &ui_table_row($text{'edit_physdevisin'}, 339 &print_mode("physdevisin", $rule->{'physdev-is-in'}, 340 $text{'yes'}, $text{'no'})); 341print &ui_table_row($text{'edit_physdevisout'}, 342 &print_mode("physdevisout", $rule->{'physdev-is-out'}, 343 $text{'yes'}, $text{'no'})); 344print &ui_table_row($text{'edit_physdevisbridged'}, 345 &print_mode("physdevisbridged", $rule->{'physdev-is-bridged'}, 346 $text{'yes'}, $text{'no'})); 347 348print &ui_table_hr(); 349 350# Show unknown modules 351@mods = grep { !/^(tcp|udp|icmp${ipvx_icmp}|multiport|mac|limit|owner|state|conntrack|tos|comment|physdev)$/ } map { $_->[1] } @{$rule->{'m'}}; 352print &ui_table_row($text{'edit_mods'}, 353 &ui_textbox("mods", join(" ", @mods), 60)); 354 355# Show unknown parameters 356$rule->{'args'} =~ s/^\s+//; 357$rule->{'args'} =~ s/\s+$//; 358print &ui_table_row($text{'edit_args'}, 359 &ui_textbox("args", $rule->{'args'}, 60)); 360 361print &ui_table_end(); 362if ($in{'new'}) { 363 print &ui_form_end([ [ undef, $text{'create'} ] ]); 364 } 365else { 366 print &ui_form_end([ [ undef, $text{'save'} ], 367 [ 'clone', $text{'edit_clone'} ], 368 [ 'delete', $text{'delete'} ] ]); 369 } 370 371&ui_print_footer("index.cgi?version=${ipvx_arg}", $text{'index_return'}); 372 373# print_mode(name, &value, [yes-option, no-option], [no-no-option]) 374sub print_mode 375{ 376local ($name, $value, $yes_opt, $no_opt, $no_no_opt) = @_; 377local $m = !$value ? 0 : 378 $value->[0] eq "!" ? 2 : 1; 379return &ui_select($name."_mode", $m, 380 [ [ 0, "<$text{'edit_ignore'}>" ], 381 [ 1, $yes_opt || $text{'edit_is'} ], 382 !$no_no_opt || $m == 2 ? ( [ 2, $no_opt || $text{'edit_not'} ] ) 383 : ( ) ]); 384} 385 386# port_input(name, value) 387sub port_input 388{ 389local ($name, $value) = @_; 390local ($s, $e, $p); 391if ($value =~ /^(\d*):(\d*)$/) { 392 $s = $1; $e = $2; 393 } 394else { 395 $p = $value || ""; 396 } 397return &ui_radio($name."_type", defined($p) ? 0 : 1, 398 [ [ 0, $text{'edit_port0'}." ". 399 &ui_textbox($name, $p, 15) ], 400 [ 1, &text('edit_port1', 401 &ui_textbox($name."_from", $s, 6), 402 &ui_textbox($name."_to", $e, 6)) ] ]); 403} 404 405# tcpflag_input(name, value) 406sub tcpflag_input 407{ 408local ($name, $value) = @_; 409local %flags = map { $_, 1 } split(/,/, $value); 410local $f; 411local $rv = "<font size=-1>\n"; 412foreach $f ('SYN', 'ACK', 'FIN', 'RST', 'URG', 'PSH') { 413 $rv .= &ui_checkbox($name, $f, "<tt>$f</tt>", 414 $flags{$f} || $flags{'ALL'})."\n"; 415 } 416$rv .= "</font>\n"; 417return $rv; 418} 419 420# icmptype_input(name, value, [&types]) 421sub icmptype_input 422{ 423local ($name, $value, $types) = @_; 424local ($started, @types, $major, $minor); 425$major = -1; 426if ($types) { 427 @types = @$types; 428 } 429else { 430 open(IPTABLES, "ip${ipvx}tables -p icmp${ipvx_icmp} -h 2>/dev/null |"); 431 while(<IPTABLES>) { 432 if (/valid\s+icmp\s+types:/i) { 433 $started = 1; 434 } 435 elsif (!/\S/) { 436 $started = 0; 437 } 438 elsif ($started && /^\s*(\S+)/) { 439 push(@types, $1); 440 } 441 } 442 close(IPTABLES); 443 } 444if (@types && $value !~ /^\d+$/ && $value !~ /^\d+\/\d+$/) { 445 return &ui_select($name, $value, \@types); 446 } 447else { 448 return &ui_textbox($name, $value, 6); 449 } 450} 451 452# protocol_input(name, value) 453sub protocol_input 454{ 455local ($name, $value) = @_; 456local @stdprotos = ( 'tcp', 'udp', "icmp${ipvx_icmp}", undef ); 457$value ||= "tcp"; 458local @otherprotos; 459open(PROTOS, "</etc/protocols"); 460while(<PROTOS>) { 461 s/\r|\n//g; 462 s/#.*$//; 463 push(@otherprotos, $1) if (/^(\S+)\s+(\d+)/); 464 } 465close(PROTOS); 466@otherprotos = sort { lc($a) cmp lc($b) } @otherprotos; 467local $p; 468local @allprotos = &unique(@stdprotos, @otherprotos); 469local $found = &indexof($value, @allprotos) >= 0; 470return &ui_select($name, $found ? $value : "", 471 [ (map { [ $_, uc($_) || "-------" ] } @allprotos), 472 [ '', $text{'edit_oifc'} ] ])." ". 473 &ui_textbox($name."_other", $found ? undef : $value, 5); 474} 475 476# tos_input(name, value) 477sub tos_input 478{ 479local ($name, $value) = @_; 480local ($started, @opts); 481open(IPTABLES, "ip${ipvx}tables -m tos -h 2>/dev/null |"); 482while(<IPTABLES>) { 483 if (/TOS.*options:/i) { 484 $started = 1; 485 } 486 elsif ($started && /^\s+(\S+)\s+(\d+)\s+\((0x[0-9a-f]+)\)/i) { 487 push(@opts, [ $1, $3 ]); 488 } 489 } 490close(IPTABLES); 491if (@opts) { 492 return &ui_select($name, $value, 493 [ map { [ $o->[0], "$o->[0] ($o->[1])" ] } @opts ]); 494 } 495else { 496 return &ui_textbox($name, $value, 20); 497 } 498} 499 500