+sub delete_rule_properties {
+ my ($rule, $delete_str) = @_;
+
+ foreach my $opt (PVE::Tools::split_list($delete_str)) {
+ raise_param_exc({ 'delete' => "no such property ('$opt')"})
+ if !defined($rule_properties->{$opt});
+ raise_param_exc({ 'delete' => "unable to delete required property '$opt'"})
+ if $opt eq 'type' || $opt eq 'action';
+ delete $rule->{$opt};
+ }
+
+ return $rule;
+}
+
+my $apply_macro = sub {
+ my ($macro_name, $param, $verify) = @_;
+
+ my $macro_rules = $pve_fw_parsed_macros->{$macro_name};
+ die "unknown macro '$macro_name'\n" if !$macro_rules; # should not happen
+
+ my $rules = [];
+
+ foreach my $templ (@$macro_rules) {
+ my $rule = {};
+ my $param_used = {};
+ foreach my $k (keys %$templ) {
+ my $v = $templ->{$k};
+ if ($v eq 'PARAM') {
+ $v = $param->{$k};
+ $param_used->{$k} = 1;
+ } elsif ($v eq 'DEST') {
+ $v = $param->{dest};
+ $param_used->{dest} = 1;
+ } elsif ($v eq 'SOURCE') {
+ $v = $param->{source};
+ $param_used->{source} = 1;
+ }
+
+ if (!defined($v)) {
+ my $msg = "missing parameter '$k' in macro '$macro_name'";
+ raise_param_exc({ macro => $msg }) if $verify;
+ die "$msg\n";
+ }
+ $rule->{$k} = $v;
+ }
+ foreach my $k (keys %$param) {
+ next if $k eq 'macro';
+ next if !defined($param->{$k});
+ next if $param_used->{$k};
+ if (defined($rule->{$k})) {
+ if ($rule->{$k} ne $param->{$k}) {
+ my $msg = "parameter '$k' already define in macro (value = '$rule->{$k}')";
+ raise_param_exc({ $k => $msg }) if $verify;
+ die "$msg\n";
+ }
+ } else {
+ $rule->{$k} = $param->{$k};
+ }
+ }
+ push @$rules, $rule;
+ }
+
+ return $rules;
+};
+
+sub verify_rule {
+ my ($rule, $allow_groups) = @_;
+
+ my $type = $rule->{type};
+
+ raise_param_exc({ type => "missing property"}) if !$type;
+ raise_param_exc({ action => "missing property"}) if !$rule->{action};
+
+ if ($type eq 'in' || $type eq 'out') {
+ raise_param_exc({ action => "unknown action '$rule->{action}'"})
+ if $rule->{action} !~ m/^(ACCEPT|DROP|REJECT)$/;
+ } elsif ($type eq 'group') {
+ raise_param_exc({ type => "security groups not allowed"})
+ if !$allow_groups;
+ raise_param_exc({ action => "invalid characters in security group name"})
+ if $rule->{action} !~ m/^${security_group_name_pattern}$/;
+ } else {
+ raise_param_exc({ type => "unknown rule type '$type'"});
+ }
+
+ # fixme: verify $rule->{iface}?
+
+ if ($rule->{macro}) {
+ my $preferred_name = $pve_fw_preferred_macro_names->{lc($rule->{macro})};
+ raise_param_exc({ macro => "unknown macro '$rule->{macro}'"}) if !$preferred_name;
+ $rule->{macro} = $preferred_name;
+ }
+
+ if ($rule->{dport}) {
+ eval { parse_port_name_number_or_range($rule->{dport}); };
+ raise_param_exc({ dport => $@ }) if $@;
+ }
+
+ if ($rule->{sport}) {
+ eval { parse_port_name_number_or_range($rule->{sport}); };
+ raise_param_exc({ sport => $@ }) if $@;
+ }
+
+ if ($rule->{source}) {
+ eval { parse_address_list($rule->{source}); };
+ raise_param_exc({ source => $@ }) if $@;
+ }
+
+ if ($rule->{dest}) {
+ eval { parse_address_list($rule->{dest}); };
+ raise_param_exc({ dest => $@ }) if $@;
+ }
+
+ if ($rule->{macro}) {
+ &$apply_macro($rule->{macro}, $rule, 1);
+ }
+
+ return $rule;
+}
+