+ eval{
+ iptables("-C $rule");
+ };
+ return undef if $@;
+
+ return 1;
+}
+
+sub ruleset_generate_rule {
+ my ($ruleset, $chain, $rule, $goto) = @_;
+
+ my $cmd = '';
+
+ $cmd .= " -m iprange --src-range" if $rule->{nbsource} && $rule->{nbsource} > 1;
+ $cmd .= " -s $rule->{source}" if $rule->{source};
+ $cmd .= " -m iprange --dst-range" if $rule->{nbdest} && $rule->{nbdest} > 1;
+ $cmd .= " -d $rule->{dest}" if $rule->{destination};
+ $cmd .= " -p $rule->{proto}" if $rule->{proto};
+ $cmd .= " --match multiport" if $rule->{nbdport} && $rule->{nbdport} > 1;
+ $cmd .= " --dport $rule->{dport}" if $rule->{dport};
+ $cmd .= " --match multiport" if $rule->{nbsport} && $rule->{nbsport} > 1;
+ $cmd .= " --sport $rule->{sport}" if $rule->{sport};
+
+ if (my $action = $rule->{action}) {
+ $goto = 1 if !defined($goto) && $action eq 'PVEFW-SET-ACCEPT-MARK';
+ $cmd .= $goto ? " -g $action" : " -j $action";
+ };
+
+ ruleset_addrule($ruleset, $chain, $cmd) if $cmd;
+}
+
+sub ruleset_create_chain {
+ my ($ruleset, $chain) = @_;
+
+ die "Invalid chain name '$chain' (28 char max)\n" if length($chain) > 28;
+
+ die "chain '$chain' already exists\n" if $ruleset->{$chain};
+
+ $ruleset->{$chain} = [];
+}
+
+sub ruleset_chain_exist {
+ my ($ruleset, $chain) = @_;
+
+ return $ruleset->{$chain} ? 1 : undef;
+}
+
+sub ruleset_addrule {
+ my ($ruleset, $chain, $rule) = @_;
+
+ die "no such chain '$chain'\n" if !$ruleset->{$chain};
+
+ push @{$ruleset->{$chain}}, "-A $chain $rule";
+}
+
+sub ruleset_insertrule {
+ my ($ruleset, $chain, $rule) = @_;
+
+ die "no such chain '$chain'\n" if !$ruleset->{$chain};
+
+ unshift @{$ruleset->{$chain}}, "-A $chain $rule";
+}
+
+sub generate_bridge_chains {
+ my ($ruleset, $bridge) = @_;
+
+ if (!ruleset_chain_exist($ruleset, "PVEFW-BRIDGE-IN")){
+ ruleset_create_chain($ruleset, "PVEFW-BRIDGE-IN");
+ }
+
+ if (!ruleset_chain_exist($ruleset, "PVEFW-BRIDGE-OUT")){
+ ruleset_create_chain($ruleset, "PVEFW-BRIDGE-OUT");
+ }
+
+ if (!ruleset_chain_exist($ruleset, "PVEFW-FORWARD")){
+ ruleset_create_chain($ruleset, "PVEFW-FORWARD");
+
+ ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m state --state RELATED,ESTABLISHED -j ACCEPT");
+ ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m physdev --physdev-is-in --physdev-is-bridged -j PVEFW-BRIDGE-OUT");
+ ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m physdev --physdev-is-out --physdev-is-bridged -j PVEFW-BRIDGE-IN");
+ }
+
+ if (!ruleset_chain_exist($ruleset, "$bridge-IN")) {
+ ruleset_create_chain($ruleset, "$bridge-IN");
+ ruleset_addrule($ruleset, "PVEFW-FORWARD", "-i $bridge -j DROP"); # disable interbridge routing
+ ruleset_addrule($ruleset, "PVEFW-BRIDGE-IN", "-j $bridge-IN");
+ ruleset_addrule($ruleset, "$bridge-IN", "-j ACCEPT");
+ }
+
+ if (!ruleset_chain_exist($ruleset, "$bridge-OUT")) {
+ ruleset_create_chain($ruleset, "$bridge-OUT");
+ ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o $bridge -j DROP"); # disable interbridge routing
+ ruleset_addrule($ruleset, "PVEFW-BRIDGE-OUT", "-j $bridge-OUT");
+ }
+}
+
+sub generate_tap_rules_direction {
+ my ($ruleset, $group_rules, $iface, $netid, $macaddr, $rules, $bridge, $direction) = @_;
+
+ my $tapchain = "$iface-$direction";
+
+ ruleset_create_chain($ruleset, $tapchain);
+
+ ruleset_addrule($ruleset, $tapchain, "-m state --state INVALID -j DROP");
+ ruleset_addrule($ruleset, $tapchain, "-m state --state RELATED,ESTABLISHED -j ACCEPT");
+
+ if ($direction eq 'OUT' && defined($macaddr)) {
+ ruleset_addrule($ruleset, $tapchain, "-m mac ! --mac-source $macaddr -j DROP");
+ }
+
+ if ($rules) {
+ foreach my $rule (@$rules) {
+ next if $rule->{iface} && $rule->{iface} ne $netid;
+ # we go to $bridge-IN if accept in out rules
+ if($rule->{action} =~ m/^(GROUP-(\S+))$/){
+ $rule->{action} .= "-$direction";
+ # generate empty group rule if don't exist
+ if(!ruleset_chain_exist($ruleset, $rule->{action})){
+ generate_group_rules($ruleset, $group_rules, $2);
+ }
+ ruleset_generate_rule($ruleset, $tapchain, $rule);
+ ruleset_addrule($ruleset, $tapchain, "-m mark --mark 1 -g $bridge-IN")
+ if $direction eq 'OUT';
+ } else {
+ $rule->{action} = "$bridge-IN" if $rule->{action} eq 'ACCEPT' && $direction eq 'OUT';
+ ruleset_generate_rule($ruleset, $tapchain, $rule);
+ }