2 # Common functions for the shorewall configuration files
5 # - read_shorewall_config & standard_parser do not allow quoted comment characters
7 BEGIN { push(@INC, ".."); };
12 $shorewall_version = &get_shorewall_version(0);
13 %shorewall_config = &read_shorewall_config();
14 #&dump_shorewall_config();
16 # get access permissions
17 %access = &get_module_acl();
19 @shorewall_files = ( 'zones', 'interfaces', 'policy', 'rules', 'tos',
20 'masq', 'nat', 'proxyarp', 'routestopped',
21 'tunnels', 'hosts', 'blacklist',
22 ( &version_atleast(2, 3) ? ( 'providers' ) : ( ) ),
23 'params', 'shorewall.conf',
25 @comment_tables = ( 'masq', 'nat', 'rules', 'tcrules' );
29 print STDERR scalar(localtime).": shorewall-lib: @_\n";
32 # version_atleast(v1, v2, v3, ...)
33 # - Check if the Shorewall version is greater than or equal to the one supplied.
36 local @vsp = split(/\./, $shorewall_version);
38 for($i=0; $i<@vsp || $i<@_; $i++) {
39 return 0 if ($vsp[$i] < $_[$i]);
40 return 1 if ($vsp[$i] > $_[$i]);
45 sub read_shorewall_config
48 open(SHOREWALL_CONF, "$config{'config_dir'}/shorewall.conf");
49 while (<SHOREWALL_CONF>) {
53 @F = split( /=/, $_, 2 );
55 push @ret, ( $F[0], $F[1] );
57 close(SHOREWALL_CONF);
61 # dump_shorewall_config()
63 sub dump_shorewall_config
65 for (sort keys %shorewall_config) {
66 print STDERR "$_=$shorewall_config{$_}\n";
70 # shorewall_config(var)
73 if (exists $shorewall_config{$_[0]} && defined $shorewall_config{$_[0]}) {
74 return $shorewall_config{$_[0]};
79 # return true if new zones format is in use
82 # Shorewall 3.4.0 - 3.4.4 have a bug that prevents the old format from being used.
83 if (&version_atleast(3, 4) && !&version_atleast(3, 4, 5)) {
86 # Zones table is in new format in Shorewall 3, unless shorewall.conf has IPSECFILE=ipsec
87 if (!&version_atleast(3) || &shorewall_config('IPSECFILE') eq 'ipsec') {
93 # read_table_file(table, &parserfunc)
98 open(FILE, "$config{'config_dir'}/$_[0]");
101 local $l = &$func($_);
102 push(@rv, $l) if ($l);
108 # read_table_struct(table, &parserfunc)
109 sub read_table_struct
111 if (!defined($read_table_cache{$_[0]})) {
114 open(FILE, "$config{'config_dir'}/$_[0]");
122 local $l = &$func($_);
124 push(@rv, { 'line' => $lnum,
125 'file' => "$config{'config_dir'}/$_[0]",
127 'index' => scalar(@rv),
129 'comment' => $cmt });
134 $read_table_cache{$_[0]} = \@rv;
136 return $read_table_cache{$_[0]};
139 # find_line_num(&lref, &parserfunc, index)
145 for($i=0; $i<@$lref; $i++) {
146 if (&$func($lref->[$i])) {
147 if ($idx++ == $_[2]) {
155 # delete_table_row(table, &parserfunc, index)
158 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
159 local $lnum = &find_line_num($lref, $_[1], $_[2]);
160 splice(@$lref, $lnum, 1) if (defined($lnum));
164 # delete_table_struct(&struct)
165 sub delete_table_struct
167 local $lref = &read_file_lines($_[0]->{'file'});
168 splice(@$lref, $_[0]->{'line'}, 1);
170 local $cache = $read_table_cache{$_[0]->{'table'}};
171 local $idx = &indexof($_[0], @$cache);
173 splice(@$cache, $idx, 1);
176 foreach $c (@$cache) {
177 $c->{'line'}-- if ($c->{'line'} > $_[0]->{'line'});
178 $c->{'index'}-- if ($c->{'index'} > $_[0]->{'index'});
182 # create_table_row(table, &parserfunc, line, [insert-index])
185 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
187 for($i=0; $i<@$lref; $i++) {
188 if ($lref->[$i] =~ /^#+\s*LAST\s+LINE/) {
192 elsif ($lref->[$i] =~ /^SECTION\s+NEW/) {
197 if (defined($_[3])) {
198 local $lnum = &find_line_num($lref, $_[1], $_[3]);
199 $lnum = $idx if (!defined($lnum));
200 splice(@$lref, $lnum, 0, &simplify_line($_[2]));
203 splice(@$lref, $idx, 0, &simplify_line($_[2]));
208 # create_table_struct(&struct, parserfunc, [&insert-before])
209 sub create_table_struct
211 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]->{'table'}");
213 for($i=0; $i<@$lref; $i++) {
214 if ($lref->[$i] =~ /^#+\s*LAST\s+LINE/) {
219 if (!defined($idx)) {
222 local $cache = &read_table_struct($_[0]->{'table'}, $_[1]);
225 splice(@$lref, $_[2]->{'line'}, 0, &make_struct($_[0]));
226 $_[0]->{'file'} = "$config{'config_dir'}/$_[0]->{'table'}";
227 $_[0]->{'line'} = $_[2]->{'line'};
228 $_[0]->{'index'} = $_[2]->{'index'};
230 foreach $c (@$cache) {
231 $_[0]->{'line'}++ if ($c->{'line'} >= $_[2]->{'line'});
232 $_[0]->{'index'}++ if ($c->{'index'} >= $_[2]->{'index'});
234 local $iidx = &indexof($_[2], @$cache);
235 splice(@$cache, $iidx, 0, $_[0]);
239 splice(@$lref, $idx, 0, &make_struct($_[0]));
240 $_[0]->{'file'} = "$config{'config_dir'}/$_[0]->{'table'}";
241 $_[0]->{'line'} = $idx;
242 $_[0]->{'index'} = @$cache;
243 push(@$cache, $_[0]->{'index'});
248 # modify_table_row(table, &parserfunc, index, line)
251 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
252 local $lnum = &find_line_num($lref, $_[1], $_[2]);
253 $lref->[$lnum] = &simplify_line($_[3]) if (defined($lnum));
257 # modify_table_struct(&newstruct, &oldstruct)
258 sub modify_table_struct
260 local $lref = &read_file_lines("$config{'config_dir'}/$_[1]->{'table'}");
261 $lref->[$_[1]->{'line'}] = &make_struct($_[0]);
262 if ($_[0] ne $_[1]) {
263 $_[0]->{'line'} = $_[1]->{'line'};
264 $_[0]->{'index'} = $_[1]->{'index'};
265 local $cache = $read_table_cache{$_[1]->{'table'}};
266 local $idx = &indexof($_[1], @$cache);
267 $cache->[$idx] = $_[0];
272 # swap_table_rows(table, &parserfunc, index1, index2)
275 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
276 local $lnum1 = &find_line_num($lref, $_[1], $_[2]);
277 local $lnum2 = &find_line_num($lref, $_[1], $_[3]);
278 ($lref->[$lnum1], $lref->[$lnum2]) = ($lref->[$lnum2], $lref->[$lnum1]);
282 # make_struct(&struct)
285 local $line = join("\t", @{$_[0]->{'values'}});
286 if ($_[0]->{'comment'}) {
287 $line .= "\t# $_[0]->{'comment'}";
289 return &simplify_line($line);
292 # simplify_line(line)
293 # Removes blank fields from the end of a line
297 while($rv =~ s/\s+$// || $rv =~ s/\-$//) { }
303 &lock_file("$config{'config_dir'}/$_[0]");
308 &unlock_file("$config{'config_dir'}/$_[0]");
311 # parser for whitespace-separated config files
316 local @sp = split(/\s+/, $l);
317 return undef if ($sp[0] eq "SECTION");
318 return @sp ? \@sp : undef;
321 # parser for shell-style config files
325 $l =~ s/#\s*(.*?)\s*$//; # save the comment we strip
326 local @sp = split(/=/, $l, 2);
327 if ($#sp > -1 && defined $1) {
328 push @sp, $1; # add back the saved comment, if present
330 return @sp ? \@sp : undef;
333 # determine which parser function to use
336 local $hashref = $_[0];
337 &get_clean_table_name($hashref);
338 local $pfunc = $hashref->{'tableclean'}."_parser";
339 if (!defined(&$pfunc)) {
340 if ($hashref->{'tableclean'} =~ /^(params|shorewall_conf)$/) {
341 $pfunc = "config_parser";
344 $pfunc = "standard_parser";
350 # ensure that the passed string contains only characters valid in shell variable identifiers
358 # get a table name that is clean enough to use as a function prefix
359 sub get_clean_table_name
361 local $hashref = $_[0];
362 if (!exists hashref->{'tableclean'}) {
363 $hashref->{'tableclean'} = &clean_name($in{'table'});
367 # zone_field(name, value, othermode, simplemode)
370 local @ztable = &read_table_file("zones", \&zones_parser);
373 print "<select name=$_[0]>\n";
378 printf "<option value=- %s>%s\n",
379 $_[1] eq '-' ? "selected" : "", "<$text{'list_any'}>";
380 $found = !$_[1] || $_[1] eq '-';
383 printf "<option value=all %s>%s\n",
384 $_[1] eq 'all' ? "selected" : "", "<$text{'list_any'}>";
385 printf "<option value=\$FW %s>%s\n",
386 &is_fw($_[1]) ? "selected" : "", "<$text{'list_fw'}>";
387 $found = !$_[1] || $_[1] eq 'all' || &is_fw($_[1]);
389 foreach $z (@ztable) {
390 printf "<option value=%s %s>%s\n",
391 $z->[0], $_[1] eq $z->[0] ? "selected" : "", &convert_zone($z->[0]);
392 $found++ if ($_[1] eq $z->[0]);
395 printf "<option value='' %s>%s\n",
396 $found ? "" : "selected", $text{'list_other'};
399 print "<option value=$_[1] selected>$_[1]\n" if (!$found);
405 # iface_field(name, value)
408 local @itable = &read_table_file("interfaces", \&standard_parser);
409 print "<select name=$_[0]>\n";
410 local $found = !$_[1];
411 foreach $i (@itable) {
412 printf "<option value=%s %s>%s\n",
413 $i->[1], $_[1] eq $i->[1] ? "selected" : "", $i->[1];
414 $found++ if ($_[1] eq $i->[1]);
416 print "<option value=$_[1] selected>$_[1]\n" if (!$found);
421 # Given a zone name, returns a description
422 # FIXME: inefficient - should be able to pass ztable into this function
425 local @ztable = &read_table_file("zones", \&zones_parser);
426 foreach $z (@ztable) {
427 if ($_[0] eq $z->[0]) {
428 if (&new_zones_format()) {
429 # No descriptions in new format - use comment field if present
430 if (defined $z->[6] && $z->[6] ne "") {
431 $ret = $_[0]." - ".$z->[6];
443 $ret = $text{'list_fw'};
445 return $ret || $_[0];
448 # nice_host_list(list)
449 # Convert a comma-separate host string to space-separated
452 local @hosts = split(/,/, $_[0]);
454 return join(", ", @hosts[0..5]).", ...";
457 return join(", ", @hosts);
462 # - Checks if the supplied zone is the firewall zone.
463 # Now handles renaming of firewall zone in shorewall.conf.
466 local $fw = &shorewall_config('FW');
467 $fw = 'fw' if ($fw eq '');
468 return $_[0] eq '$FW' || $_[0] eq $fw;
471 ################################# zones #######################################
475 if (&new_zones_format()) {
478 $l =~ s/#\s*(.*?)\s*$//; # save the stripped comment
479 local $comment = $1 if defined $1;
480 local @r = split(/\s+/, $l, 6);
482 local $zone = shift @r;
484 # split out parent if it is present in the zone field
486 $zone =~ m/(.*?):(.*)/;
494 unshift @r, $zone, $parent;
496 # put the saved comment back
497 if (defined $comment) {
498 # ensure option fields are present
503 # add the comment field
507 return scalar(@r) ? \@r : undef;
513 if ($l =~ /^(\S+)\s+(\S+)\s*(.*)/) {
514 return [ $1, $2, $3 ];
524 return &new_zones_format() ? 4 : 3;
527 # format a parsed row for display in list form
530 if (&new_zones_format()) {
531 return ( $_[0], $_[1], $text{'zones_'.$_[2]} || $_[2], $_[6] );
540 if (&new_zones_format()) {
541 return ( $text{'zones_0'}, $text{'zones_1new'}, $text{'zones_2new'},
542 # The option fields are not displayed in the main list.
543 # $text{'zones_3new'}, $text{'zones_4new'}, $text{'zones_5new'},
544 $text{'zones_6new'} );
547 return ( $text{'zones_0'}, $text{'zones_1'}, $text{'zones_2'} );
553 if (&new_zones_format()) {
554 # Shorewall 3 zones format
555 print "<tr> <td><b>$text{'zones_0'}</b></td>\n";
556 print "<td>",&ui_textbox("id", $_[0], 8),"</td>\n";
558 print "<td><b>$text{'zones_1new'}</b></td>\n";
560 &zone_field("parent", $_[1], 0, 1);
561 print "</td> </tr>\n";
563 print "<td><b>$text{'zones_2new'}</b></td>\n";
564 print "<td>",&ui_select("type", $_[2],
565 [ [ "ipv4", $text{'zones_ipv4'} ],
566 [ "ipsec", $text{'zones_ipsec'} ],
567 [ "firewall", $text{'zones_firewall'} ] ]),"</td> </tr>\n";
569 print "<tr> <td><b>$text{'zones_3new'}</b></td>\n";
570 print "<td>",&ui_textbox("opts", $_[3], 50),"</td> </tr>\n";
572 print "<tr> <td><b>$text{'zones_4new'}</b></td>\n";
573 print "<td>",&ui_textbox("opts_in", $_[4], 50),"</td> </tr>\n";
575 print "<tr> <td><b>$text{'zones_5new'}</b></td>\n";
576 print "<td>",&ui_textbox("opts_out", $_[5], 50),"</td> </tr>\n";
578 print "<tr> <td><b>$text{'zones_6new'}</b></td>\n";
579 print "<td>",&ui_textbox("comment", $_[6], 50),"</td> </tr>\n";
583 # Shorewall 2 zones format
584 print "<tr> <td><b>$text{'zones_0'}</b></td>\n";
585 print "<td><input name=id size=8 value='$_[0]'></td> </tr>\n";
587 print "<tr> <td><b>$text{'zones_1'}</b></td>\n";
588 print "<td><input name=name size=15 value='$_[1]'></td> </tr>\n";
590 print "<tr> <td><b>$text{'zones_2'}</b></td>\n";
591 print "<td><input name=desc size=70 value='$_[2]'></td> </tr>\n";
597 $in{'id'} =~ /^\S+$/ || &error($text{'zones_eid'});
598 &is_fw($in{'id'}) && &error($text{'zones_efwid'});
599 if (&new_zones_format()) {
601 $in{'opts'} =~ /^\S*$/ || &error($text{'zones_eopts'});
602 $in{'opts_in'} =~ /^\S*$/ || &error($text{'zones_eopts_in'});
603 $in{'opts_out'} =~ /^\S*$/ || &error($text{'zones_eopts_out'});
604 if (!defined $in{'parent'} || $in{'parent'} eq "-") {
605 return ( $in{'id'}, $in{'type'}, $in{'opts'},
606 $in{'opts_in'}, $in{'opts_out'}, "# $in{'comment'}" );
609 return ( $in{'id'}.":".$in{'parent'}, $in{'type'}, $in{'opts'},
610 $in{'opts_in'}, $in{'opts_out'}, "# $in{'comment'}" );
615 $in{'name'} =~ /^\S+$/ || &error($text{'zones_ename'});
616 $in{'desc'} =~ /\S/ || &error($text{'zones_edesc'});
617 return ( $in{'id'}, $in{'name'}, $in{'desc'} );
621 ################################# interfaces ###################################
626 $_[0] eq '-' ? $text{'list_any'} : $_[0],
627 $_[2] eq 'detect' ? $text{'list_auto'} :
628 $_[2] eq '-' || $_[2] eq '' ? $text{'list_none'} : $_[2],
629 $_[3] ? $_[3] : $text{'list_none'} );
632 @interfaces_opts = ( 'dhcp', 'noping', 'filterping', 'routestopped', 'norfc1918',
633 'multi', 'routefilter', 'dropunclean', 'logunclean',
634 'blacklist', 'maclist', 'tcpflags', 'proxyarp' );
635 if (&version_atleast(3)) {
636 push(@interfaces_opts, "logmartians", "routeback", "arp_filter",
637 "arp_ignore", "nosmurfs", "detectnets", "upnp");
642 print "<tr> <td><b>$text{'interfaces_0'}</b></td>\n";
643 print "<td><input name=iface size=6 value='$_[1]'></td>\n";
645 local @ztable = &read_table_file("zones", \&zones_parser);
646 print "<td><b>$text{'interfaces_1'}</b></td>\n";
648 &zone_field("zone", $_[0], 0, 1);
649 print "</td> </tr>\n";
651 local $bmode = $_[2] eq 'detect' ? 2 :
652 $_[2] eq '-' || $_[2] eq '' ? 1 : 0;
653 print "<tr> <td><b>$text{'interfaces_2'}</b></td> <td colspan=3>\n";
654 printf "<input type=radio name=broad_mode value=1 %s> %s\n",
655 $bmode == 1 ? "checked" : "", $text{'list_none'};
656 printf "<input type=radio name=broad_mode value=2 %s> %s\n",
657 $bmode == 2 ? "checked" : "", $text{'list_auto'};
658 printf "<input type=radio name=broad_mode value=0 %s>\n",
659 $bmode == 0 ? "checked" : "";
660 printf "<input name=broad size=50 value='%s'></td> </tr>\n",
661 $bmode == 0 ? $_[2] : "";
664 local %opts = map { $_, 1 } split(/,/, $_[3]);
665 print "<tr> <td valign=top><b>$text{'interfaces_3'}</b></td> <td colspan=3>\n";
666 &options_input("opts", $_[3], \@interfaces_opts);
667 print "</td> </tr>\n";
670 sub interfaces_validate
672 $in{'iface'} =~ /^[a-z]+\d*(\.\d+)?$/ ||
673 $in{'iface'} =~ /^[a-z]+\+$/ || &error($text{'interfaces_eiface'});
674 $in{'broad_mode'} || $in{'broad'} =~ /^[0-9\.,]+$/ ||
675 &error($text{'interfaces_ebroad'});
676 return ( $in{'zone'}, $in{'iface'},
677 $in{'broad_mode'} == 2 ? 'detect' :
678 $in{'broad_mode'} == 1 ? '-' : $in{'broad'},
679 join(",", split(/\0/, $in{'opts'})) );
682 ################################# policy #######################################
686 return ( $_[0] eq 'all' ? $text{'list_any'} :
687 &is_fw($_[0]) ? $text{'list_fw'} : $_[0],
688 $_[1] eq 'all' ? $text{'list_any'} :
689 &is_fw($_[1]) ? $text{'list_fw'} : $_[1],
690 $_[2], $_[3] eq '-' || $_[3] eq '' ? $text{'list_none'} : $_[3],
691 $_[4] =~ /(\d+):(\d+)/ ? &text('policy_limit', "$1", "$2")
692 : $text{'list_none'} );
695 @policy_list = ( "ACCEPT", "DROP", "REJECT", "CONTINUE" );
701 print "<tr> <td><b>$text{'policy_0'}</b></td>\n";
703 &zone_field("source", $_[0], 0);
706 print "<td><b>$text{'policy_1'}</b></td>\n";
708 &zone_field("dest", $_[1], 0);
709 print "</td> </tr>\n";
711 print "<tr> <td><b>$text{'policy_2'}</b></td>\n";
712 print "<td><select name=policy>\n";
714 foreach $p (@policy_list) {
715 printf "<option value=%s %s>%s\n",
716 $p, lc($p) eq lc($_[2]) ? "selected" : "", $p;
717 $found++ if (lc($p) eq lc($_[2]));
719 print "<option value=$_[2] selected>$_[2]\n" if (!$found);
720 print "</select></td>\n";
722 print "<td><b>$text{'policy_3'}</b></td>\n";
723 print "<td><select name=log>\n";
724 printf "<option value=- %s>%s\n",
725 $_[3] eq '-' || !$_[3] ? "selected" : "", "<$text{'policy_nolog'}>";
726 printf "<option value=ULOG %s>%s\n",
727 $_[3] eq 'ULOG' ? "selected" : "", "<$text{'policy_ulog'}>";
728 $found = !$_[3] || $_[3] eq '-' || $_[3] eq 'ULOG';
729 &foreign_require("syslog", "syslog-lib.pl");
730 foreach $l (&syslog::list_priorities()) {
731 printf "<option value=%s %s>%s\n",
732 $l, $_[3] eq $l ? "selected" : "", $l;
733 $found++ if ($_[3] eq $l);
735 print "<option value=$_[3] selected>$_[3]\n" if (!$found);
736 print "</select></td> </tr>\n";
738 local ($l, $b) = $_[4] =~ /(\d+):(\d+)/ ? ($1, $2) : ( );
739 print "<tr> <td><b>$text{'policy_4'}</b></td> <td colspan=3>\n";
740 printf "<input type=radio name=limit_def value=1 %s> %s\n",
741 $l eq '' ? "checked" : "", $text{'list_none'};
742 printf "<input type=radio name=limit_def value=0 %s>\n",
743 $l eq '' ? "" : "checked";
744 print &text('policy_limit',
745 "<input name=limit size=5 value='$l'>",
746 "<input name=burst size=5 value='$b'>"),"</td> </tr>\n";
751 &is_fw($in{'source'}) && &is_fw($in{'dest'}) && &error($text{'policy_efw'});
752 if (!$in{'limit_def'}) {
753 $in{'limit'} =~ /^\d+$/ || &error($text{'policy_elimit'});
754 $in{'burst'} =~ /^\d+$/ || &error($text{'policy_eburst'});
756 return ( $in{'source'}, $in{'dest'}, $in{'policy'}, $in{'log'},
757 $in{'limit_def'} ? ( ) : ( "$in{'limit'}:$in{'burst'}" ) );
760 ################################# rules #######################################
764 return ( $_[0] =~ /^(\S+):/ ? "$1" : $_[0],
765 &is_fw($_[1]) ? $text{'list_fw'} :
766 $_[1] eq 'all' ? $text{'list_any'} :
767 $config{'display_zone_descriptions'} == 0 ? $_[1] :
768 $_[1] =~ /^([^:]+):(\S+)$/ ?
769 &text('rules_hosts', &convert_zone("$1"), &nice_host_list("$2")) :
770 &text('rules_zone', &convert_zone($_[1])),
771 &is_fw($_[2]) ? $text{'list_fw'} :
772 $_[2] eq 'all' ? $text{'list_any'} :
773 $_[2] =~ /^\d+$/ ? &text('rules_rport', $_[2]) :
774 $config{'display_zone_descriptions'} == 0 ? $_[2] :
775 $_[2] =~ /^([^:]+):(\S+)$/ ?
776 &text('rules_hosts', &convert_zone("$1"), &nice_host_list("$2")) :
777 &text('rules_zone', &convert_zone($_[2])),
778 $_[3] eq 'all' ? $text{'list_any'} :
779 $_[3] eq 'related' ? $text{'rules_related'} : uc($_[3]),
780 $_[3] eq 'all' || $_[3] eq 'related' ? "" :
781 $_[5] eq '-' || $_[5] eq '' ? $text{'list_any'} : $_[5],
782 $_[4] eq '-' || $_[4] eq '' ? "" : $_[4],
783 &version_atleast(1, 4, 7) ? (
784 $_[7] eq "-" ? "" : $_[7],
785 $_[8] eq "-" ? "" : $_[8] ) :
790 @rules_actions = ( 'ACCEPT', 'DROP', 'REJECT', 'DNAT', 'DNAT-', 'REDIRECT' );
791 if (&version_atleast(2, 0, 0)) {
792 push(@rules_actions, 'CONTINUE');
793 push(@rules_actions, 'ACCEPT+');
794 push(@rules_actions, 'NONAT');
795 push(@rules_actions, 'REDIRECT-');
796 push(@rules_actions, 'LOG');
798 if (&version_atleast(3)) {
799 push(@rules_actions, 'DNAT-');
800 push(@rules_actions, 'SAME');
801 push(@rules_actions, 'SAME-');
802 push(@rules_actions, 'QUEUE');
804 @rules_protos = ( 'all', 'related', 'tcp', 'udp', 'icmp' );
809 local @ztable = &read_table_file("zones", \&zones_parser);
811 local ($action, $log) = split(/:/, $_[0]);
813 if ($action =~ /^(.*)\/(.*)$/) {
819 print "<tr> <td><b>$text{'rules_0'}</b></td>\n";
820 print "<td colspan=3><select name=action>\n";
822 foreach $a ((sort { $a cmp $b } @rules_actions),
823 "-------- Actions --------",
824 &list_standard_actions(),
825 (&version_atleast(3) ? ( "-------- Macros --------",
826 &list_standard_macros() ) : ( ) )) {
827 printf "<option value=%s %s>%s\n",
828 $a, $action eq $a ? "selected" : "", $a;
829 $found++ if ($action eq $a);
831 print "<option value=$action selected>$action\n" if (!$found);
835 print "<b>$text{'rules_log'}</b> <select name=log>\n";
836 printf "<option value='' %s>%s\n",
837 !$log ? "selected" : "", "<$text{'rules_nolog'}>";
838 printf "<option value=ULOG %s>%s\n",
839 $log eq 'ULOG' ? "selected" : "", "<$text{'policy_ulog'}>";
840 $found = !$log || $log eq '-' || $log eq 'ULOG';
841 &foreign_require("syslog", "syslog-lib.pl");
842 foreach $l (&syslog::list_priorities()) {
843 printf "<option value=%s %s>%s\n",
844 $l, $log eq $l ? "selected" : "", $l;
845 $found++ if ($log eq $l);
847 print "<option value=$log selected>$log\n" if (!$found);
848 print "</select></td> </tr>\n";
850 if (&version_atleast(3)) {
851 print "<tr> <td valign=top><b>$text{'rules_macro'}</b></td>\n";
852 print "<td colspan=3 nowrap>\n";
853 print &ui_select("macro", $macroarg,
854 [ [ "", "<$text{'rules_none2'}>" ],
855 map { [ $_ ] } (sort { $a cmp $b } @rules_actions) ],
857 print "</td> </tr>\n";
860 # Source zone and hosts
861 local ($zone, $host) = split(/:/, $_[1], 2);
862 print "<tr> <td valign=top><b>$text{'rules_1z'}</b></td>\n";
863 print "<td colspan=3 nowrap>\n";
864 $found = &zone_field("source", $zone, 1);
865 printf "<input name=sother size=10 value='%s'>\n",
868 print "<br><b>$text{'rules_inzone'}</b>\n";
869 printf "<input type=checkbox name=sinzone_def value=1 %s> %s\n",
870 $host ? "checked" : "", $text{'rules_addr'};
871 printf "<input name=sinzone size=50 value='%s'></td> </tr>\n",
872 join(" ", split(/,/, $host));
874 ($zone, $host) = split(/:/, $_[2], 2);
875 print "<tr> <td valign=top><b>$text{'rules_2z'}</b></td>\n";
876 print "<td colspan=3 nowrap>\n";
877 $found = &zone_field("dest", $zone, 1);
878 printf "<input name=dother size=10 value='%s'>\n",
881 print "<br><b>$text{'rules_inzone'}</b>\n";
882 printf "<input type=checkbox name=dinzone_def value=1 %s> %s\n",
883 $host ? "checked" : "", $text{'rules_addr'};
884 printf "<input name=dinzone size=50 value='%s'>\n",
885 join(" ", split(/,/, $host));
886 print "<br>$text{'rules_dnat_dest'}</td> </tr>\n";
888 print "<tr> <td><b>$text{'rules_3'}</b></td>\n";
889 print "<td colspan=3><select name=proto>\n";
891 foreach $p (@rules_protos) {
892 printf "<option value=%s %s>%s\n",
893 $p, $p eq $_[3] ? "selected" : "",
894 $p eq 'all' ? "<$text{'list_any'}>" :
895 $p eq 'related' ? "<$text{'rules_related'}>" : uc($p);
896 $found++ if ($p eq $_[3]);
898 printf "<option value='' %s>%s\n",
899 $found ? "" : "selected", $text{'list_other'};
901 printf "<input name=pother size=5 value='%s'></td> </tr>\n",
904 print "<tr> <td><b>$text{'rules_4'}</b></td> <td colspan=3>\n";
905 printf "<input type=radio name=sport_def value=1 %s> %s\n",
906 $_[5] eq '' || $_[5] eq '-' ? "checked" : "", $text{'list_any'};
907 printf "<input type=radio name=sport_def value=0 %s> %s\n",
908 $_[5] eq '' || $_[5] eq '-' ? "" : "checked", $text{'rules_ranges'};
909 printf "<input name=sport size=30 value='%s'></td> </tr>\n",
910 $_[5] eq '' || $_[5] eq '-' ? "" : join(" ", split(/,/, $_[5]));
912 print "<tr> <td><b>$text{'rules_5'}</b></td> <td colspan=3>\n";
913 printf "<input type=radio name=dport_def value=1 %s> %s\n",
914 $_[4] eq '' || $_[4] eq '-' ? "checked" : "", $text{'list_any'};
915 printf "<input type=radio name=dport_def value=0 %s> %s\n",
916 $_[4] eq '' || $_[4] eq '-' ? "" : "checked", $text{'rules_ranges'};
917 printf "<input name=dport size=30 value='%s'>\n",
918 $_[4] eq '' || $_[4] eq '-' ? "" : join(" ", split(/,/, $_[4]));
919 print "<br>$text{'rules_dnat_port'}</td> </tr>\n";
921 print "<tr> <td><b>$text{'rules_dnat'}</b></td> <td colspan=3>\n";
922 printf "<input type=radio name=dnat_def value=1 %s> %s\n",
923 $_[6] eq '' || $_[6] eq '-' ? "checked" : "", $text{'list_none'};
924 printf "<input type=radio name=dnat_def value=0 %s>\n",
925 $_[6] eq '' || $_[6] eq '-' ? "" : "checked";
926 printf "<input name=dnat size=30 value='%s'></td> </tr>\n",
927 $_[6] eq '' || $_[6] eq '-' ? "" : $_[6];
929 if (&version_atleast(1, 4, 7)) {
930 print "<tr> <td><b>$text{'rules_rate'}</b></td> <td colspan=3>\n";
931 printf "<input type=radio name=rate_def value=1 %s> %s\n",
932 $_[7] eq "-" || !$_[7] ? "checked" : "", $text{'rules_norate'};
933 printf "<input type=radio name=rate_def value=0 %s>\n",
934 $_[7] eq "-" || !$_[7] ? "" : "checked";
935 printf "<input name=rate size=15 value='%s'></td> </tr>\n",
936 $_[7] eq "-" ? "" : $_[7];
938 print "<tr> <td><b>$text{'rules_set'}</b></td> <td colspan=3>\n";
939 printf "<input type=radio name=set_def value=1 %s> %s\n",
940 $_[8] eq "-" || !$_[8] ? "checked" : "", $text{'rules_noset'};
941 printf "<input type=radio name=set_def value=0 %s>\n",
942 $_[8] eq "-" || !$_[8] ? "" : "checked";
943 printf "<input name=set size=15 value='%s'></td> </tr>\n",
944 $_[8] eq "-" ? "" : $_[8];
950 $in{'action'} !~ /----/ || &error($text{'rules_eaction'});
951 $in{'source'} || $in{'sother'} =~ /^\S+$/ || &error($text{'rules_esother'});
952 !$in{'sinzone_def'} || $in{'sinzone'} =~ /\S/ || &error($text{'rules_esinzone'});
953 $in{'dest'} || $in{'dother'} =~ /^\S+$/ || &error($text{'rules_edother'});
954 !$in{'dinzone_def'} || $in{'dinzone'} =~ /\S/ || &error($text{'rules_edinzone'});
955 $in{'proto'} || $in{'pother'} =~ /^\S+$/ || &error($text{'rules_epother'});
956 $in{'sport_def'} || $in{'sport'} =~ /\S/ || &error($text{'rules_esport'});
957 $in{'dport_def'} || $in{'dport'} =~ /\S/ || &error($text{'rules_edport'});
958 $in{'dnat_def'} || &check_ipaddress($in{'dnat'}) ||
959 ($in{'dnat'} =~ /^([0-9\.]+):([0-9\.]+)$/ &&
960 &check_ipaddress("$1") && &check_ipaddress("$2")) ||
961 ($in{'dnat'} =~ /^\!([0-9\.]+)$/ && &check_ipaddress("$1")) ||
962 ($in{'dnat'} =~ /^\!([0-9\.]+),([0-9\.]+)(\/\d+)?$/ &&
963 &check_ipaddress("$1") && &check_ipaddress("$2")) ||
964 ($in{'dnat'} =~ /^\!([0-9\.,]+)$/ &&
965 scalar(grep { &check_ipaddress($_) } split(/,/, $1))) ||
966 &error($text{'rules_ednat'});
967 $in{'action'} ne 'DNAT' && $in{'action'} ne 'REDIRECT' && $in{'action'} ne 'DNAT-' &&
968 !$in{'dnat_def'} && &error($text{'rules_ednat2'});
970 $in{'sinzone'} =~ s/\s+/,/g;
971 $in{'dinzone'} =~ s/\s+/,/g;
972 $in{'sport'} =~ s/\s+/,/g;
973 $in{'dport'} =~ s/\s+/,/g;
974 if (&version_atleast(1, 4, 7)) {
975 $in{'rate_def'} || $in{'rate'} =~ /^\S+$/ ||
976 &error($text{'rules_erate'});
977 $in{'set_def'} || $in{'set'} =~ /^\S+$/ ||
978 &error($text{'rules_eset'});
980 if ($in{'macro'} && &indexof($in{'action'}, &list_standard_macros()) >= 0) {
981 $in{'action'} .= "/".$in{'macro'};
982 $in{'proto'} = $in{'pother'} = undef;
984 return ( $in{'log'} ? "$in{'action'}:$in{'log'}" : $in{'action'},
985 ($in{'source'} || $in{'sother'}).
986 ($in{'sinzone_def'} ? ":$in{'sinzone'}" : ""),
987 ($in{'dest'} || $in{'dother'}).
988 ($in{'dinzone_def'} ? ":$in{'dinzone'}" : ""),
989 $in{'proto'} || $in{'pother'} || '-',
990 $in{'dport_def'} ? "-" : $in{'dport'},
991 $in{'sport_def'} ? "-" : $in{'sport'},
992 $in{'dnat_def'} ? "-" : $in{'dnat'},
993 &version_atleast(1, 4, 7) ? (
994 ( $in{'rate_def'} ? "-" : $in{'rate'} ),
995 ( $in{'set_def'} ? "-" : $in{'set'} )
1002 return &version_atleast(1, 4, 7) ? 6 : 8;
1005 ################################# tos #########################################
1007 %tos_map = ( 0, 'Normal-Service',
1009 4, 'Maximize-Reliability',
1010 8, 'Maximize-Throughput',
1011 16, 'Minimize-Delay' );
1012 @tos_protos = ( 'tcp', 'udp', 'icmp' );
1016 return ( &is_fw($_[0]) ? $text{'list_fw'} :
1017 $_[0] eq 'all' ? $text{'list_any'} :
1018 $_[0] =~ /^([^:]+):(\S+)$/ ? &text('rules_hosts', "$1", "$2") :
1019 &text('rules_zone', $_[0]),
1020 &is_fw($_[1]) ? $text{'list_fw'} :
1021 $_[1] eq 'all' ? $text{'list_any'} :
1022 $_[1] =~ /^([^:]+):(\S+)$/ ? &text('rules_hosts', "$1", "$2") :
1023 &text('rules_zone', $_[1]),
1025 $_[3] eq '-' || $_[3] eq '' ? $text{'list_any'} : $_[3],
1026 $_[4] eq '-' || $_[4] eq '' ? $text{'list_any'} : $_[4],
1027 $tos_map{$_[5]} || $_[5],
1028 $_[6] eq '-' ? $text{'list_none'} : $_[6] );
1033 local ($zone, $host) = split(/:/, $_[0], 2);
1034 print "<tr> <td valign=top><b>$text{'tos_0z'}</b></td>\n";
1035 print "<td colspan=3 nowrap>\n";
1036 $found = &zone_field("source", $zone, 1);
1037 printf "<input name=sother size=10 value='%s'>\n",
1038 $found ? "" : $zone;
1040 print "<br><b>$text{'rules_inzone'}</b>\n";
1041 printf "<input type=checkbox name=sinzone_def value=1 %s> %s\n",
1042 $host ? "checked" : "", $text{'rules_addr'};
1043 printf "<input name=sinzone size=50 value='%s'></td> </tr>\n",
1044 join(" ", split(/,/, $host));
1046 ($zone, $host) = split(/:/, $_[1], 2);
1047 print "<tr> <td valign=top><b>$text{'tos_1z'}</b></td>\n";
1048 print "<td colspan=3 nowrap>\n";
1049 $found = &zone_field("dest", $zone, 1);
1050 printf "<input name=dother size=10 value='%s'>\n",
1051 $found ? "" : $zone;
1053 print "<br><b>$text{'rules_inzone'}</b>\n";
1054 printf "<input type=checkbox name=dinzone_def value=1 %s> %s\n",
1055 $host ? "checked" : "", $text{'rules_addr'};
1056 printf "<input name=dinzone size=50 value='%s'></td> </tr>\n",
1057 join(" ", split(/,/, $host));
1059 print "<tr> <td><b>$text{'tos_2'}</b></td>\n";
1060 print "<td><select name=proto>\n";
1062 foreach $p (@tos_protos) {
1063 printf "<option value=%s %s>%s\n",
1064 $p, $p eq $_[2] ? "selected" : "", uc($p);
1065 $found++ if ($p eq $_[2]);
1067 printf "<option value='' %s>%s\n",
1068 $found ? "" : "selected", $text{'list_other'};
1069 print "</select>\n";
1070 printf "<input name=pother size=5 value='%s'></td> </tr>\n",
1071 $found ? "" : $_[2];
1073 print "<tr> <td><b>$text{'tos_3'}</b></td> <td colspan=3>\n";
1074 printf "<input type=radio name=sport_def value=1 %s> %s\n",
1075 $_[3] eq '' || $_[3] eq '-' ? "checked" : "", $text{'list_any'};
1076 printf "<input type=radio name=sport_def value=0 %s> %s\n",
1077 $_[3] eq '' || $_[3] eq '-' ? "" : "checked", $text{'rules_ranges'};
1078 printf "<input name=sport size=30 value='%s'></td> </tr>\n",
1079 $_[3] eq '' || $_[3] eq '-' ? "" : join(" ", split(/,/, $_[3]));
1081 print "<tr> <td><b>$text{'tos_4'}</b></td> <td colspan=3>\n";
1082 printf "<input type=radio name=dport_def value=1 %s> %s\n",
1083 $_[4] eq '' || $_[4] eq '-' ? "checked" : "", $text{'list_any'};
1084 printf "<input type=radio name=dport_def value=0 %s> %s\n",
1085 $_[4] eq '' || $_[4] eq '-' ? "" : "checked", $text{'rules_ranges'};
1086 printf "<input name=dport size=30 value='%s'></td> </tr>\n",
1087 $_[4] eq '' || $_[4] eq '-' ? "" : join(" ", split(/,/, $_[4]));
1089 print "<tr> <td><b>$text{'tos_5'}</b></td>\n";
1090 print "<td><select name=tos>\n";
1092 foreach $t (sort { $a <=> $b } keys %tos_map) {
1093 printf "<option value=%s %s>%s\n",
1094 $t, $_[5] == $t ? "selected" : "", $tos_map{$t};
1095 $found++ if ($_[5] == $t);
1097 print "<option value=$_[5] selected>$_[5]\n" if (!$found);
1098 print "</select></td> </tr>\n";
1100 print "<tr> <td><b>$text{'tos_6'}</b></td>\n";
1101 printf "<td><input name=mark size=50 value='%s'></td> </tr>\n",
1102 $_[6] eq "-" ? "" : $_[6];
1107 $in{'source'} || $in{'sother'} =~ /^\S+$/ || &error($text{'rules_esother'});
1108 !$in{'sinzone_def'} || $in{'sinzone'} =~ /\S/ || &error($text{'rules_esinzone'});
1109 $in{'dest'} || $in{'dother'} =~ /^\S+$/ || &error($text{'rules_edother'});
1110 !$in{'dinzone_def'} || $in{'dinzone'} =~ /\S/ || &error($text{'rules_edinzone'});
1111 $in{'proto'} || $in{'pother'} =~ /^\S+$/ || &error($text{'rules_epother'});
1112 $in{'sport_def'} || $in{'sport'} =~ /\S/ || &error($text{'rules_esport'});
1113 $in{'dport_def'} || $in{'dport'} =~ /\S/ || &error($text{'rules_edport'});
1114 return ( ($in{'source'} || $in{'sother'}).
1115 ($in{'sinzone_def'} ? ":$in{'sinzone'}" : ""),
1116 ($in{'dest'} || $in{'dother'}).
1117 ($in{'dinzone_def'} ? ":$in{'dinzone'}" : ""),
1118 $in{'proto'} || $in{'pother'},
1119 $in{'sport_def'} ? "-" : join(",", split(/\s+/, $in{'sport'})),
1120 $in{'dport_def'} ? "-" : join(",", split(/\s+/, $in{'dport'})),
1122 $in{'mark'} || "-" );
1125 ################################# masq #########################################
1129 return ( $_[0] =~ /^(\S+):(\S+)$/ ? &text('masq_in', "$1", "$2") : $_[0],
1130 $_[1] =~ /^[0-9\.\/]+$/ ? $_[1] :
1131 $_[1] =~ /^([a-z]+\d*)\!(\S+)$/ ? &text('masq_ex', "$1", "$2") :
1132 &text('masq_iface', $_[1]),
1133 $_[2] eq "" ? "" : $_[2] );
1143 local ($iface, $net) = split(/:/, $_[0], 2);
1144 print "<tr> <td><b>$text{'masq_0'}</b></td> <td colspan=3>\n";
1145 &iface_field("iface", $iface);
1147 printf "<input type=checkbox name=net_def value=1 %s> %s\n",
1148 $net ? "checked" : "", $text{'masq_net'};
1149 print "<input name=net size=20 value='$net'></td> </tr>\n";
1151 local ($mnet, $miface, $mode);
1152 if ($_[1] =~ /^[0-9\.\/]+$/) {
1156 elsif ($_[1] =~ /^([a-z]+\d*)\!(\S+)$/) {
1165 print "<tr> <td valign=top><b>$text{'masq_1'}</b></td> <td colspan=3>\n";
1166 printf "<input type=radio name=mode value=0 %s> %s\n",
1167 $mode == 0 ? "checked" : "", $text{'masq_mode0'};
1168 printf "<input name=mnet size=20 value='%s'><br>\n",
1169 $mode == 0 ? $mnet : "";
1170 printf "<input type=radio name=mode value=1 %s> %s\n",
1171 $mode == 1 ? "checked" : "", $text{'masq_mode1'};
1172 &iface_field("miface", $mode == 1 ? $miface : undef);
1173 printf "<input type=checkbox name=mnet_def value=1 %s> %s\n",
1174 $mode == 1 && $mnet ? "checked" : "", $text{'masq_except'};
1175 printf "<input name=mnete size=20 value='%s'>\n",
1176 $mode == 1 ? join(" ", split(/,/, $mnet)) : "";
1177 print "</td> </tr>\n";
1179 print "<tr> <td><b>$text{'masq_2'}</b></td> <td colspan=3>\n";
1180 printf "<input type=radio name=snat_def value=1 %s> %s\n",
1181 $_[2] eq '' || $_[2] eq '-' ? "checked" : "", $text{'list_none'};
1182 printf "<input type=radio name=snat_def value=0 %s>\n",
1183 $_[2] eq '' || $_[2] eq '-' ? "" : "checked";
1184 printf "<input name=snat size=15 value='%s'></td> </tr>\n",
1185 $_[2] eq '' || $_[2] eq '-' ? "" : $_[2];
1187 if (&version_atleast(3)) {
1188 print "<tr> <td><b>$text{'masq_3'}</b></td> <td colspan=3>\n";
1189 print &ui_radio("proto_def", $_[3] ? 0 : 1,
1190 [ [ 1, $text{'masq_any'} ],
1192 &ui_select("proto", $_[3],
1193 [ map { [ $_, uc($_) ] } &list_protocols() ],
1194 1, 0, $_[3] ? 1 : 0),"</td> </tr>\n";
1196 print "<tr> <td><b>$text{'masq_4'}</b></td> <td colspan=3>\n";
1197 print &ui_opt_textbox("ports", $_[4], 40, $text{'masq_all'}),
1200 print "<tr> <td><b>$text{'masq_5'}</b></td> <td colspan=3>\n";
1201 print &ui_opt_textbox("ipsec", $_[5], 40, $text{'default'}),
1208 !$in{'net_def'} || $in{'net'} =~ /^\S+$/ || &error($text{'masq_enet'});
1209 if ($in{'mode'} == 0) {
1210 $in{'mnet'} =~ /^\S+$/ || &error($text{'masq_emnet'});
1213 !$in{'mnet_def'} || $in{'mnete'} =~ /\S/ ||&error($text{'masq_emnete'});
1215 $in{'snat_def'} || &check_ipaddress($in{'snat'}) || &error($text{'masq_esnat'});
1217 local @rv = ( $in{'iface'}.(!$in{'net_def'} ? "" : ":$in{'net'}"),
1218 $in{'mode'} == 0 ? $in{'mnet'} :
1219 $in{'miface'}.(!$in{'mnet_def'} ? "" :
1220 "!".join(",", split(/\s+/, $in{'mnete'}))),
1221 $in{'snat_def'} ? ( "" ) : ( $in{'snat'} ) );
1222 if (&version_atleast(3)) {
1223 push(@rv, $in{'proto_def'} ? "" : $in{'proto'});
1224 if ($in{'ports_def'}) {
1228 $in{'ports'} =~ /^\S+$/ || &error($text{'masq_eports'});
1229 push(@rv, $in{'ports'});
1231 if ($in{'ipsec_def'}) {
1235 $in{'ipsec'} =~ /^\S+$/ || &error($text{'masq_eipsec'});
1236 push(@rv, $in{'ipsec'});
1242 ################################# nat #########################################
1246 print "<tr> <td><b>$text{'nat_0'}</b></td>\n";
1247 print "<td><input name=ext size=15 value='$_[0]'></td>\n";
1249 print "<td><b>$text{'nat_1'}</b></td>\n";
1251 if (&version_atleast(1, 3, 14)) {
1252 local ($iface, $virt) = split(/:/, $_[1]);
1253 &iface_field("iface", $iface);
1254 print "<b>$text{'nat_virt'}</b>\n";
1255 print "<input name=virt size=3 value='$virt'>\n";
1256 print "</td> </tr>\n";
1259 &iface_field("iface", $_[1]);
1260 print "</td> </tr>\n";
1263 print "<tr> <td><b>$text{'nat_2'}</b></td>\n";
1264 print "<td><input name=int size=15 value='$_[2]'></td> </tr>\n";
1266 local $all = $_[3] eq '-' || $_[3] eq '' || $_[3] =~ /yes/i;
1267 print "<tr> <td><b>$text{'nat_all'}</b></td>\n";
1268 printf "<td><input type=radio name=all value=1 %s> %s\n",
1269 $all ? "checked" : "", $text{'yes'};
1270 printf "<input type=radio name=all value=0 %s> %s</td>\n",
1271 $all ? "" : "checked", $text{'no'};
1273 local $local = $_[4] =~ /yes/i;
1274 print "<td><b>$text{'nat_local'}</b></td>\n";
1275 printf "<td><input type=radio name=local value=1 %s> %s\n",
1276 $local ? "checked" : "", $text{'yes'};
1277 printf "<input type=radio name=local value=0 %s> %s</td> </tr>\n",
1278 $local ? "" : "checked", $text{'no'};
1283 &check_ipaddress($in{'ext'}) || &error($text{'nat_eext'});
1284 &check_ipaddress($in{'int'}) || &error($text{'nat_eint'});
1285 $in{'virt'} =~ /^\d*$/ || &error($text{'nat_evirt'});
1286 return ( $in{'ext'},
1287 $in{'virt'} ne '' ? $in{'iface'}.":".$in{'virt'} : $in{'iface'},
1289 $in{'all'} ? "yes" : "no",
1290 $in{'local'} ? "yes" : "no" );
1293 ################################# proxyarp #######################################
1298 $_[1] eq '-' || $_[1] eq '' ? $text{'list_auto'} : $_[1],
1300 &version_atleast(2, 0, 0) ?
1301 ( $_[4] =~ /yes/i ? $text{'yes'} : $text{'no'} ) : ( ) );
1306 print "<tr> <td><b>$text{'proxyarp_0'}</b></td>\n";
1307 print "<td><input name=addr size=15 value='$_[0]'></td>\n";
1309 print "<td><b>$text{'proxyarp_1'}</b></td>\n";
1310 printf "<td><input type=radio name=int_def value=1 %s> %s\n",
1311 $_[1] eq '-' || $_[1] eq '' ? "checked" : "", $text{'list_auto'};
1312 printf "<input type=radio name=int_def value=0 %s>\n",
1313 $_[1] eq '-' || $_[1] eq '' ? "" : "checked";
1314 &iface_field("int", $_[1] eq '-' ? undef : $_[1]);
1315 print "</td> </tr>";
1317 local $have = $_[3] =~ /yes/i;
1318 print "<tr> <td><b>$text{'proxyarp_have'}</b></td>\n";
1319 printf "<td><input type=radio name=have value=1 %s> %s\n",
1320 $have ? "checked" : "", $text{'yes'};
1321 printf "<input type=radio name=have value=0 %s> %s</td>\n",
1322 $have ? "" : "checked", $text{'no'};
1324 print "<td><b>$text{'proxyarp_2'}</b></td>\n";
1326 &iface_field("ext", $_[2]);
1327 print "</td> </tr>";
1329 if (&version_atleast(2, 0, 0)) {
1330 local $pers = $_[4] =~ /yes/i;
1331 print "<tr> <td><b>$text{'proxyarp_pers'}</b></td>\n";
1332 printf "<td><input type=radio name=pers value=1 %s> %s\n",
1333 $pers ? "checked" : "", $text{'yes'};
1334 printf "<input type=radio name=pers value=0 %s> %s</td>\n",
1335 $pers ? "" : "checked", $text{'no'};
1339 sub proxyarp_validate
1341 &check_ipaddress($in{'addr'}) || &error($text{'proxyarp_eaddr'});
1342 return ( $in{'addr'},
1343 $in{'int_def'} ? "-" : $in{'int'},
1345 $in{'have'} ? "yes" : "no",
1346 &version_atleast(2, 0, 0) ? ( $in{'pers'} ? "yes" : "no" ) : ( )
1351 sub proxyarp_columns
1353 return &version_atleast(2, 0, 0) ? 4 : 3;
1356 ################################ routestopped ##################################
1358 sub routestopped_row
1360 return ( $_[0], $_[1],
1361 $_[2] eq '-' || $_[2] eq '' ? $text{'default'} : $_[2],
1362 $_[3] eq '-' || $_[3] eq '' ? $text{'tunnels_gnone'} : $_[3] );
1365 sub routestopped_columns
1370 @routestopped_options = ( "routeback", "source", "dest", "critical" );
1372 sub routestopped_form
1374 print "<tr> <td valign=top><b>$text{'routestopped_0'}</b></td>\n";
1375 print "<td valign=top>";
1376 &iface_field("iface", $_[0]);
1379 local $none = $_[1] eq '' || $_[1] eq '-' || $_[1] eq '0.0.0.0/0';
1380 print "<td valign=top><b>$text{'routestopped_1'}</b></td>\n";
1381 printf "<td><input type=radio name=addr_def value=1 %s> %s<br>\n",
1382 $none ? "checked" : "", $text{'routestopped_all'};
1383 printf "<input type=radio name=addr_def value=0 %s> %s<br>\n",
1384 $none ? "" : "checked", $text{'routestopped_list'};
1385 print "<textarea name=addr rows=5 cols=20>",
1386 $none ? "" : join("\n", split(/,/, $_[1])),
1387 "</textarea></td> </tr>\n";
1389 if (&version_atleast(3)) {
1390 print "<tr> <td valign=top><b>$text{'routestopped_2'}</b></td>\n";
1391 print "<td colspan=3>\n";
1392 &options_input("opts", $_[2], \@routestopped_options);
1393 print "</td> </tr>\n";
1397 sub routestopped_validate
1399 $in{'addr_def'} || $in{'addr'} =~ /\S/ || &error($text{'routestopped_eaddr'});
1400 return ( $in{'iface'},
1401 $in{'addr_def'} ? "-" : join(",", split(/\s+/, $in{'addr'})),
1402 join(",", split(/\0/, $in{'opts'})) );
1405 ################################ tunnels ##################################
1410 $tt =~ s/^(openvpn|generic):.*$/$1/;
1411 return ( $text{'tunnels_'.$tt} || $tt,
1412 $_[1] eq '-' || $_[1] eq '' ? $text{'routestopped_all'} : $_[1],
1418 print "<tr> <td><b>$text{'tunnels_0'}</b></td>\n";
1419 print "<td><select name=type>\n";
1421 local $found = !$_[0];
1422 local $ttype = $_[0];
1424 if ($ttype =~ s/^(openvpn|generic):(.*)$/$1/) {
1427 foreach $tt ('ipsec', 'ipsecnat',
1428 (&version_atleast(2, 0, 0) ? ( 'ipsec:noah', 'ipsecnat:noah' )
1430 'ip', 'gre', 'pptpclient', 'pptpserver', 'generic',
1431 (&version_atleast(1, 3, 14) ? ( 'openvpn' ) : ( )) ) {
1432 printf "<option value=%s %s>%s\n",
1433 $tt, $ttype eq $tt ? "selected" : "",
1434 $text{'tunnels_'.$tt.'_l'} || $text{'tunnels_'.$tt};
1435 $found++ if ($ttype eq $tt);
1437 print "<option value=$ttype selected>",uc($ttype),"\n" if (!$found);
1438 print "</select>\n";
1439 print "<input name=tport size=10 value='$tport'>\n";
1442 print "<tr> <td><b>$text{'tunnels_1'}</b></td>\n";
1444 &zone_field("zone", $_[1], 0, 0);
1445 print "</td> </tr>\n";
1447 local $none = $_[2] eq '' || $_[2] eq '-';
1448 print "<tr> <td><b>$text{'tunnels_2'}</b></td> <td valign=top>\n";
1449 printf "<input type=radio name=gateway_def value=1 %s> %s\n",
1450 $none ? "checked" : "", $text{'default'};
1451 printf "<input type=radio name=gateway_def value=0 %s> %s\n",
1452 $none ? "" : "checked", $text{'tunnels_sel'};
1453 printf "<input name=gateway size=20 value='%s'></td> </tr>\n", $_[2];
1455 local $none = $_[2] eq '' || $_[2] eq '-';
1456 print "<tr> <td><b>$text{'tunnels_3'}</b></td> <td valign=top>\n";
1457 printf "<input type=radio name=gzones_def value=1 %s> %s\n",
1458 $none ? "checked" : "", $text{'tunnels_gnone'};
1459 printf "<input type=radio name=gzones_def value=0 %s> %s\n",
1460 $none ? "" : "checked", $text{'tunnels_gsel'};
1461 printf "<input name=gzones size=50 value='%s'></td> </tr>\n",
1462 join(" ", split(/,/, $_[3]));
1465 sub tunnels_validate
1467 $in{'gateway_def'} || &check_ipaddress($in{'gateway'}) ||
1468 ($in{'gateway'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1)) ||
1469 &error($text{'tunnels_egateway'});
1470 if ($in{'type'} eq "openvpn") {
1471 $in{'tport'} =~ /^\d*$/ || &error($text{'tunnels_eopenvpn'});
1472 $in{'type'} .= ":".$in{'tport'} if ($in{'tport'});
1474 elsif ($in{'type'} eq 'generic') {
1475 $in{'tport'} =~ /^\S+$/ || &error($text{'tunnels_egeneric'});
1476 $in{'type'} .= ":".$in{'tport'};
1478 return ( $in{'type'}, $in{'zone'},
1479 $in{'gateway_def'} ? '-' : $in{'gateway'},
1480 $in{'gzones_def'} ? '-' : join(",", split(/\s+/, $in{'gzones'})) );
1483 ################################ hosts ##################################
1487 return ( $_[0], $_[1] =~ /^(\S+):(\S+)$/ ? ( $1, $2 ) : ( undef, undef ) );
1490 @host_options = ( "maclist", "routeback" );
1491 if (&version_atleast(3)) {
1492 push(@host_options, "norfc1918", "blacklist", "tcpflags",
1493 "nosmurfs", "ipsec");
1498 print "<tr> <td><b>$text{'hosts_0'}</b></td>\n";
1500 &zone_field("zone", $_[0], 0, 2);
1501 print "</td> </tr>\n";
1503 local ($iface, $net) = split(/:/, $_[1]);
1504 print "<tr> <td><b>$text{'hosts_1'}</b></td>\n";
1506 &iface_field("iface", $iface);
1507 print "</td> </tr>\n";
1509 print "<tr> <td><b>$text{'hosts_2'}</b></td>\n";
1510 print "<td><input name=net size=50 value='$net'></td> </tr>\n";
1512 print "<tr> <td valign=top><b>$text{'hosts_opts'}</b></td> <td>\n";
1513 &options_input("opts", $_[2], \@host_options);
1514 print "</td> </tr>\n";
1519 &check_ipaddress($in{'net'}) ||
1520 $in{'net'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1) ||
1521 &error($text{'hosts_enet'});
1522 return ( $in{'zone'}, $in{'iface'}.":".$in{'net'},
1523 join(",", split(/\0/, $in{'opts'})) );
1526 ################################ blacklist ##################################
1530 return ( $_[0] eq '-' ? $text{'blacklist_any'} : $_[0],
1531 uc($_[1]) || $text{'blacklist_any'},
1532 $_[2] || $text{'blacklist_any'} );
1535 @blacklist_protos = ( undef, 'tcp', 'udp', 'icmp' );
1539 print "<tr> <td valign=top><b>$text{'blacklist_host'}</b></td> <td colspan=3>\n";
1540 local ($mode, $ipset, $mac, $ip);
1541 if ($_[0] =~ /^\+(.*)/) {
1542 $mode = 2; $ipset = $1;
1544 elsif ($_[0] =~ /^\~(.*)$/) {
1545 $mode = 1; $mac = $1;
1547 elsif ($_[0] eq '-') {
1551 $mode = 0; $ip = $_[0];
1553 print &ui_radio("host_def", $mode,
1554 [ [ 0, &text('hosts_ip', &ui_textbox("host", $ip, 30))."<br>" ],
1555 [ 1, &text('hosts_mac', &ui_textbox("mac", $mac, 30))."<br>" ],
1556 [ 3, $text{'hosts_any'}."<br>" ],
1557 &version_atleast(3) ?
1558 ( [ 2, &text('hosts_ipset', &ui_textbox("ipset", $ipset, 15)) ] ) : ( ),
1560 print "</td> </tr>\n";
1562 print "<tr> <td><b>$text{'blacklist_proto'}</b></td>\n";
1563 print "<td colspan=3><select name=proto>\n";
1565 foreach $p (@blacklist_protos) {
1566 printf "<option value='%s' %s>%s\n",
1567 $p, $p eq $_[1] ? "selected" : "",
1568 $p eq '' ? "<$text{'list_any'}>" : uc($p);
1569 $found++ if ($p eq $_[1]);
1571 printf "<option value='*' %s>%s\n",
1572 $found ? "" : "selected", $text{'list_other'};
1573 print "</select>\n";
1574 printf "<input name=pother size=5 value='%s'></td> </tr>\n",
1575 $found ? "" : $_[1];
1577 print "<tr> <td><b>$text{'blacklist_ports'}</b></td>\n";
1578 print "<td colspan=3><input name=ports size=20 value='$_[2]'></td> </tr>\n";
1581 sub blacklist_validate
1584 if ($in{'host_def'} == 0) {
1585 &check_ipaddress($in{'host'}) ||
1586 $in{'host'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1) ||
1587 &error($text{'blacklist_ehost'});
1588 $host = $in{'host'};
1590 elsif ($in{'host_def'} == 1) {
1591 $in{'mac'} =~ s/:/-/g;
1592 $in{'mac'} =~ /^[0-9a-f]{2}(\-[0-9a-f]{2}){5}$/ ||
1593 &error($text{'blacklist_emac'});
1594 $host = "~".$in{'mac'};
1596 elsif ($in{'host_def'} == 2) {
1597 $in{'ipset'} =~ /^\S+$/ || &error($text{'blacklist_eipset'});
1598 $host = "+".$in{'ipset'};
1600 elsif ($in{'host_def'} == 3) {
1604 if ($in{'proto'} eq '*') {
1605 $in{'pother'} =~ /^\d+$/ ||
1606 defined(getprotobyname($in{'pother'})) ||
1607 &error($text{'blacklist_eproto'});
1608 $proto = lc($in{'pother'});
1611 $proto = lc($in{'proto'});
1613 if ($proto eq "tcp" || $proto eq "udp") {
1614 $in{'ports'} =~ /^\S+$/ || &error($text{'blacklist_eports'});
1616 elsif ($in{'ports'}) {
1617 &error($text{'blacklist_eports2'});
1619 return ( $host, $proto, $in{'ports'} );
1622 ################################ providers ##################################
1626 return ( $_[0], $_[1], $_[2], $_[4], $_[5] );
1629 @providers_opts = ( "track", "balance", "loose" );
1633 print "<tr> <td><b>$text{'providers_name'}</b></td>\n";
1634 print "<td><input name=name size=20 value='$_[0]'></td>\n";
1636 print "<td><b>$text{'providers_number'}</b></td>\n";
1637 print "<td><input name=number size=4 value='$_[1]'></td> </tr>\n";
1639 print "<tr> <td><b>$text{'providers_iface'}</b></td>\n";
1641 &iface_field("iface", $_[4]);
1644 print "<td><b>$text{'providers_mark'}</b></td>\n";
1645 print "<td><input name=mark size=4 value='$_[2]'></td> </tr>\n";
1647 print "<tr> <td><b>$text{'providers_gateway'}</b></td>\n";
1648 print "<td><input name=gateway size=15 value='$_[5]'></td>\n";
1650 local $ddef = $_[3] eq "-" || $_[3] eq "" ? 0 : $_[3] eq "main" ? 1 : 2;
1651 print "<td><b>$text{'providers_dup'}</b></td>\n";
1652 print "<td>",&ui_radio("dup_def", $ddef,
1653 [ [ 0, $text{'default'} ],
1654 [ 1, $text{'providers_main'} ],
1655 [ 2, &ui_textbox("dup", $ddef == 2 ? $_[3] : "", 5) ] ]),
1658 local %opts = map { $_, 1 } split(/,/, $_[6]);
1659 print "<tr> <td valign=top><b>$text{'providers_opts'}</b></td> <td>\n";
1660 foreach my $o (@providers_opts) {
1661 print &ui_checkbox("opts", $o, $text{'providers_'.$o}, $opts{$o})."<br>\n";
1664 foreach my $o (keys %opts) {
1665 print &ui_hidden("opts", $o),"\n";
1669 print "<td valign=top><b>$text{'providers_copy'}</b></td>\n";
1670 print "<td valign=top><input name=copy size=15 value='$_[7]'></td> </tr>\n";
1673 sub providers_validate
1675 $in{'name'} =~ /^\S+$/ || &error($text{'providers_ename'});
1676 $in{'number'} =~ /^\d+$/ || &error($text{'providers_enumber'});
1677 $in{'mark'} =~ /^\d+$/ || &error($text{'providers_emark'});
1678 $in{'dup_def'} < 2 || $in{'dup'} =~ /^\S+$/ || &error($text{'providers_edup'});
1679 &check_ipaddress($in{'gateway'}) || &error($text{'providers_egateway'});
1680 return ( $in{'name'}, $in{'number'}, $in{'mark'},
1681 $in{'dup_def'} == 0 ? '-' : $in{'dup_def'} == 1 ? 'main' : $in{'dup'},
1682 $in{'iface'}, $in{'gateway'},
1683 join(",", split(/\0/, $in{'opts'})) || "-",
1684 $in{'copy'} || "-" );
1687 ################################ shorewall.conf ##################################
1691 local ($msg1, $msg2, $msg3, $field1, $field2, $field3, $dummy) = @_;
1693 $field1 =~ s/"/"/g;
1694 print "<tr><td><b>$msg1</b></td>\n";
1695 print "<td><input name=var size=50 value=\"$field1\"></td></tr>\n";
1697 $field2 =~ s/"/"/g;
1698 print "<tr><td><b>$msg2</b></td>\n";
1699 print "<td><input name=val size=50 value=\"$field2\"></td></tr>\n";
1701 $field3 =~ s/"/"/g;
1702 print "<tr><td><b>$msg3</b></td>\n";
1703 print "<td><input name=comment size=50 value=\"$field3\"></td></tr>\n";
1705 print "</td></tr>\n";
1708 ################################ shorewall.conf ##################################
1710 sub shorewall_conf_columns
1715 sub shorewall_conf_form
1717 &conf_form($text{'shorewall_conf_0'}, $text{'shorewall_conf_1'}, $text{'shorewall_conf_2'}, @_);
1720 sub shorewall_conf_validate
1722 &error($text{'shorewall_conf_varname'}) unless $in{'var'} =~ /^\w+$/;
1723 local $comment = "";
1724 $comment = "\t# ".$in{'comment'} if (exists $in{'comment'} and $in{'comment'} ne "");
1725 return ($in{'var'}.'='.$in{'val'}.$comment);
1728 ################################ params ##################################
1737 &conf_form($text{'params_0'}, $text{'params_1'}, $text{'params_2'}, @_);
1742 &error($text{'params_varname'}) unless $in{'var'} =~ /^\w+$/;
1743 local $comment = "";
1744 $comment = "\t# ".$in{'comment'} if (exists $in{'comment'} and $in{'comment'} ne "");
1745 return ($in{'var'}.'='.$in{'val'}.$comment);
1749 #############################################################################
1754 if ($access{'files'} eq '*') {
1758 local @acc = split(/\s+/, $access{'files'});
1759 return &indexof($_[0], @acc) >= 0;
1763 # run_before_apply_command()
1764 # Runs the before-applying command, if any. If it failes, returns the error
1766 sub run_before_apply_command
1768 if ($config{'before_apply_cmd'}) {
1769 local $out = &backquote_logged("($config{'before_apply_cmd'}) </dev/null 2>&1");
1770 return $out if ($?);
1775 # run_after_apply_command()
1776 # Runs the after-applying command, if any
1777 sub run_after_apply_command
1779 if ($config{'after_apply_cmd'}) {
1780 &system_logged("($config{'after_apply_cmd'}) </dev/null >/dev/null 2>&1");
1784 # list_standard_actions()
1785 # Returns a list of standard Shorewall actions
1786 sub list_standard_actions
1789 foreach my $a (split(/\t+/, $config{'actions'})) {
1801 if (&version_atleast(3)) {
1802 # Add built-in actions
1803 push(@rv, "allowBcast", "dropBcast", "dropNotSyn", "rejNotSyn",
1804 "dropInvalid", "allowInvalid", "allowoutUPnP", "allowinUPnP",
1807 return &unique(@rv);
1810 # list_standard_macros()
1811 # Returns a list of all macro. actions
1812 sub list_standard_macros
1815 foreach my $a ($config{'config_dir'}, $config{'macros'}) {
1817 foreach my $f (readdir(DIR)) {
1818 push(@rv, $1) if ($f =~ /^macro\.(.*)$/);
1822 return &unique(@rv);
1825 $BETA_STR = "-Beta";
1826 $BETA_NUM = "\.0000\.";
1828 # get_shorewall_version(nocache)
1829 sub get_shorewall_version
1831 local ($nocache) = @_;
1833 if (!$nocache && open(VERSION, "$module_config_directory/version")) {
1834 chop($version = <VERSION>);
1838 local $out = `$config{'shorewall'} version 2>&1`;
1840 $out =~ s/$BETA_STR/$BETA_NUM/i; # Convert beta string to version number.
1841 if ($out =~ /(\n|^)([0-9\.]+)\n/) {
1848 sub get_printable_version($)
1851 $out =~ s/$BETA_NUM/$BETA_STR/i; # Convert version number back to string.
1857 local @stdprotos = ( 'tcp', 'udp', 'icmp' );
1859 open(PROTOS, "/etc/protocols");
1863 push(@otherprotos, $1) if (/^(\S+)\s+(\d+)/);
1866 @otherprotos = sort { lc($a) cmp lc($b) } @otherprotos;
1867 return &unique(@stdprotos, @otherprotos);
1870 # options_input(name, value, &opts)
1873 local ($name, $value, $opts) = @_;
1874 local %opts = map { $_, 1 } split(/,/, $value);
1875 print "<table width=100%>\n";
1877 foreach my $o (@$opts) {
1878 print "<tr>\n" if ($i%3 == 0);
1879 printf "<td><input type=checkbox name=$name value=%s %s> %s</td>\n",
1880 $o, $opts{$o} ? "checked" : "", $text{'opts_'.$o} || $o;
1881 print "</tr>\n" if ($i%3 == 2);
1885 foreach $o (keys %opts) {
1886 print "<input type=hidden name=opts value=$o>\n";