2 # Create, update or delete a firewall rule
4 require './ipfw-lib.pl';
6 &error_setup($text{'save_err'});
8 $rules = &get_config();
10 # Find the last editable rule
11 if ($rules->[@$rules-1]->{'num'} == 65535 &&
13 $lastidx = $rules->[@$rules-2]->{'index'};
16 $lastidx = $rules->[@$rules-1]->{'index'};
19 # Work out where to insert, and what number to use
20 if ($in{'before'} ne '') {
21 # Adding before some rule
22 local $pn = $in{'before'} == 0 ? 0 :
23 $rules->[$in{'before'}-1]->{'num'};
24 $rule = { 'num' => ($rules->[$in{'before'}]->{'num'}+$pn)/2 };
25 splice(@$rules, $in{'before'}, 0, $rule);
27 elsif ($in{'after'} ne '') {
28 # Adding after some rule
29 local $nn = $in{'after'} == $lastidx ?
30 $rules->[$in{'after'}]->{'num'}+200 :
31 $rules->[$in{'after'}+1]->{'num'};
32 $rule = { 'num' => ($rules->[$in{'after'}]->{'num'}+$nn)/2 };
33 splice(@$rules, $in{'after'}+1, 0, $rule);
35 elsif (!$in{'num_def'}) {
37 $in{'num'} =~ /^\d+$/ && $in{'num'} >= 0 && $in{'num'} < 65536
38 || &error($text{'save_enum'});
39 $rule = { 'num' => $in{'num'} };
41 for(my $i=0; $i<@$rules; $i++) {
42 if ($rules->[$i]->{'num'} >= $in{'num'}) {
43 splice(@$rules, $i, 0, $rule);
48 push(@$rules, $rule) if (!$found);
52 $rule = { 'num' => '00100' };
56 # At end or before last deny-all rule
57 $rule = { 'num' => $rules->[$lastidx]->{'num'}+100 };
58 splice(@$rules, $lastidx+1, 0, $rule);
60 $rule->{'num'} = sprintf "%5.5d", $rule->{'num'};
63 $rule = $rules->[$in{'idx'}];
64 delete($rule->{'text'});
68 # Just remove this rule
69 splice(@$rules, $in{'idx'}, 1);
72 # Validate inputs and contruct the rule object
73 $in{'cmt'} =~ s/\r//g;
74 $rule->{'cmt'} = $in{'cmt'};
76 # Parse rule action and arg
77 $rule->{'action'} = $in{'action'};
78 if ($in{'action'} eq "skipto") {
79 $in{'action_skipto'} =~ /^\d+$/ ||
80 &error($text{'save_eskipto'});
81 $rule->{'aarg'} = $in{'action_skipto'};
83 elsif ($in{'action'} eq "fwd") {
84 &check_ipaddress($in{'action_fwdip'}) ||
85 &error($text{'save_efwdip'});
86 if ($in{'action_fwdport'} eq "") {
87 $rule->{'aarg'} = $in{'action_fwdip'};
90 $in{'action_fwdport'} =~ /^\d+$/ ||
91 &error($text{'save_efwdport'});
92 $rule->{'aarg'} = $in{'action_fwdip'}.",".
93 $in{'action_fwdport'};
96 elsif ($in{'action'} eq "divert" || $in{'action'} eq "pipe" ||
97 $in{'action'} eq "queue" || $in{'action'} eq "tee") {
98 $in{'action_port'} =~ /^\d+$/ ||
99 &error($text{'save_eteeport'});
100 $rule->{'aarg'} = $in{'action_port'};
102 elsif ($in{'action'} eq "unreach") {
103 $rule->{'aarg'} = $in{'action_unreach'};
106 delete($rule->{'aarg'});
110 if ($in{'proto_orblock'}) {
111 $rule->{'proto'} = &parse_orblock("proto");
114 $rule->{'proto'} = $in{'proto'};
117 # Parse in/out option
118 delete($rule->{'in'});
119 delete($rule->{'out'});
120 delete($rule->{'in_not'});
121 delete($rule->{'out_not'});
122 if ($in{'inout'} == 1) {
125 elsif ($in{'inout'} == 2) {
129 # Parse via interface
130 $rule->{'via'} = &parse_interface("via");
132 # Parse logging level
135 if ($in{'logamount'} ne "") {
136 $in{'logamount'} =~ /^\d+$/ ||
137 &error($text{'save_elogamount'});
138 $rule->{'logamount'} = $in{'logamount'};
141 delete($rule->{'logamount'});
148 # Parse source and destination
149 foreach $s ("from", "to") {
151 if ($in{$s."_orblock"}) {
152 $rule->{$s} = &parse_orblock($s);
154 elsif ($in{$s."_mode"} == 0) {
157 elsif ($in{$s."_mode"} == 1) {
161 &to_ipaddress($in{$s}) ||
162 ($in{$s} =~ /^([0-9\.]+)\/(\d+)$/ &&
163 &check_ipaddress("$1")) ||
164 ($in{$s} =~ /^([0-9\.]+)\/(\d+)\{([0-9,]+)\}$/ &&
165 &check_ipaddress("$1") &&
166 $ipfw_version >= 2) ||
167 &error($text{'save_e'.$s});
168 $rule->{$s} = $in{$s};
172 if ($in{$s."_ports_orblock"}) {
173 # XXX could be optional?
174 $rule->{$s."_ports"} = &parse_orblock($s."_ports");
176 elsif ($in{$s."_ports_mode"} == 0) {
177 delete($rule->{$s."_ports"});
180 local $p = $rule->{'proto'};
181 $p eq "tcp" || $p eq "udp" || $p eq "ip" ||
182 $ipfw_version >= 2 ||
183 &error($text{'save_eportsproto'.$s});
184 $in{$s."_ports"} =~ /^\d+$/ ||
185 getservbyname($in{$s."_ports"}, $p) ||
186 $in{$s."_ports"} =~ /^\d+\-\d+$/ ||
187 ($in{$s."_ports"} =~ /^([a-z0-9]+)\-([a-z0-9]+)$/i &&
188 getservbyname($1, $p) && getservbyname($2, $p)) ||
189 $in{$s."_ports"} =~ /^([a-z0-9]+)(,[a-z0-9]+)*$/ ||
190 ($in{$s."_ports"} =~ /^([a-z0-9]+|([a-z0-9]+)\-([a-z0-9]+))(,[a-z0-9]+|,([a-z0-9]+)\-([a-z0-9]+))*$/ &&
191 $ipfw_version >= 2) ||
192 &error($text{'save_eports'.$s});
193 $rule->{$s."_ports"} = $in{$s."_ports"};
194 $rule->{$s."_ports_not"} = $in{$s."_ports_not"}
195 if ($ipfw_version >= 2);
198 $rule->{'xmit'} = &parse_interface("xmit");
199 $rule->{'recv'} = &parse_interface("recv");
201 # XXX multiple options
203 # Parse various options
204 &parse_yes_no_ignored("established");
205 &parse_yes_no_ignored("keep-state");
206 &parse_yes_no_ignored("bridged");
207 &parse_yes_no_ignored("frag");
208 &parse_yes_no_ignored("setup");
211 if ($ipfw_version >= 2) {
212 if ($in{'mac1_def'} && $in{'mac2_def'}) {
213 delete($rule->{'mac'});
217 if ($in{'mac2_def'}) {
221 $in{'mac2'} =~ /^[0-9a-f]{2}(:[0-9a-f]{2}){5}(\/\d+)?$/ || &error($text{'save_emac2'});
222 push(@mac, $in{'mac2'});
224 if ($in{'mac1_def'}) {
228 $in{'mac1'} =~ /^[0-9a-f]{2}(:[0-9a-f]{2}){5}(\/\d+)?$/ || &error($text{'save_emac1'});
229 push(@mac, $in{'mac1'});
231 $rule->{'mac'} = \@mac;
236 if ($in{'uid_def'}) {
237 delete($rule->{'uid'});
239 elsif ($in{'uid'} =~ /^#(\d+)$/) {
243 defined($rule->{'uid'} = getpwnam($in{'uid'})) ||
244 &error($text{'save_euid'});
246 if ($in{'gid_def'}) {
247 delete($rule->{'gid'});
249 elsif ($in{'gid'} =~ /^#(\d+)$/) {
253 defined($rule->{'gid'} = getgrnam($in{'gid'})) ||
254 &error($text{'save_egid'});
258 if ($in{'icmptypes'}) {
259 $rule->{'proto'} eq 'icmp' || &error($text{'save_eicmptypes'});
260 $rule->{'icmptypes'} = join(",", split(/\0/, $in{'icmptypes'}));
263 delete($rule->{'icmptypes'});
267 if ($in{'tcpflags'}) {
268 $rule->{'proto'} eq 'tcp' || &error($text{'save_etcpflags'});
269 $rule->{'tcpflags'} = join(",", split(/\0/, $in{'tcpflags'}));
272 delete($rule->{'tcpflags'});
275 # Parse limit directive
277 $in{'limit2'} =~ /^\d+$/ || &error($text{'save_elimit'});
278 $rule->{'limit'} = [ $in{'limit'}, $in{'limit2'} ];
281 delete($rule->{'limit'});
284 # Parse dst-port and src-port directive
285 foreach $ds ('dst', 'src') {
286 if (!$in{$ds.'port_def'}) {
287 local @dstports = split(/[ ,]+/, $in{$ds.'port'});
288 foreach $p (@dstports) {
289 &valid_port($p, $rule->{'proto'}) ||
290 &error($text{'save_e'.$ds.'port'});
292 $rule->{$ds.'-port'} = \@dstports;
295 delete($rule->{$ds.'-port'});
301 &lock_file($ipfw_file);
302 &save_config($rules);
303 &unlock_file($ipfw_file);
305 &webmin_log($in{'delete'} ? "delete" : $in{'new'} ? "create" : "modify",
306 "rule", $rule->{'action'}, $rule);
309 # parse_interface(name)
312 local $iface = $in{$_[0]} eq "other" ? $in{$_[0]."_other"} : $in{$_[0]};
313 return undef if (!$iface);
314 $iface =~ /^\S+$/ || &error($text{'save_e'.$_[0]});
318 # parse_orblock(name)
321 $in{$_[0]} =~ /\S/ || &error(&text('save_eorblock'.$_[0]));
322 return [ split(/\s+/, $in{$_[0]}) ];
325 # parse_yes_no_ignored(name)
326 sub parse_yes_no_ignored
328 if ($in{$_[0]} == 0) {
329 delete($rule->{$_[0]});
331 elsif ($in{$_[0]} == 1) {
333 $rule->{$_[0]."_not"} = 0;
335 elsif ($in{$_[0]} == 2) {
337 $rule->{$_[0]."_not"} = 1;