From 48fdc25daa3b98c30767b2ee267e5af03828a1f4 Mon Sep 17 00:00:00 2001 From: Jayapal Date: Thu, 31 Jan 2013 10:52:21 +0530 Subject: [PATCH] CLOUDSTACK-299: Egress firewall rules feature for guest network on VR --- .../firewall/CreateEgressFirewallRuleCmd.java | 341 ++++++++++++++++++ .../firewall/DeleteEgressFirewallRuleCmd.java | 120 ++++++ .../firewall/ListEgressFirewallRulesCmd.java | 89 +++++ .../debian/config/root/firewallRule_egress.sh | 171 +++++++++ 4 files changed, 721 insertions(+) create mode 100644 api/src/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java create mode 100755 patches/systemvm/debian/config/root/firewallRule_egress.sh diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java new file mode 100644 index 00000000000..097b2c55f80 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateEgressFirewallRuleCmd.java @@ -0,0 +1,341 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.package org.apache.cloudstack.api.command.user.firewall; + +package org.apache.cloudstack.api.command.user.firewall; + +import java.util.ArrayList; +import java.util.List; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.utils.net.NetUtils; + +@APICommand(name = "createEgressFirewallRule", description = "Creates a egress firewall rule for a given network ", responseObject = FirewallResponse.class) +public class CreateEgressFirewallRuleCmd extends BaseAsyncCreateCmd implements FirewallRule { + public static final Logger s_logger = Logger.getLogger(CreateEgressFirewallRuleCmd.class.getName()); + + private static final String s_name = "createegressfirewallruleresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter (name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network id of the port forwarding rule") + private Long networkId; + + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the firewall rule. Valid values are TCP/UDP/ICMP.") + private String protocol; + + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, description = "the starting port of firewall rule") + private Integer publicStartPort; + + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of firewall rule") + private Integer publicEndPort; + + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic from") + private List cidrlist; + + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the icmp message being sent") + private Integer icmpType; + + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message") + private Integer icmpCode; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "type of firewallrule: system/user") + private String type; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getIpAddressId() { + return null; + } + + @Override + public String getProtocol() { + return protocol.trim(); + } + + @Override + public List getSourceCidrList() { + if (cidrlist != null) { + return cidrlist; + } else { + List oneCidrList = new ArrayList(); + oneCidrList.add(_networkService.getNetwork(networkId).getCidr()); + return oneCidrList; + } + } + + public Long getVpcId() { + Network network = _networkService.getNetwork(getNetworkId()); + if (network == null) { + throw new InvalidParameterValueException("Invalid networkId is given"); + } + + Long vpcId = network.getVpcId(); + return vpcId; + } + + + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public void setSourceCidrList(List cidrs){ + cidrlist = cidrs; + } + + @Override + public void execute() throws ResourceUnavailableException { + UserContext callerContext = UserContext.current(); + boolean success = false; + FirewallRule rule = _entityMgr.findById(FirewallRule.class, getEntityId()); + try { + UserContext.current().setEventDetails("Rule Id: " + getEntityId()); + success = _firewallService.applyEgressFirewallRules (rule, callerContext.getCaller()); + // State is different after the rule is applied, so get new object here + rule = _entityMgr.findById(FirewallRule.class, getEntityId()); + FirewallResponse fwResponse = new FirewallResponse(); + if (rule != null) { + fwResponse = _responseGenerator.createFirewallResponse(rule); + setResponseObject(fwResponse); + } + fwResponse.setResponseName(getCommandName()); + } finally { + if (!success || rule == null) { + _firewallService.revokeFirewallRule(getEntityId(), true); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create firewall rule"); + } + } + } + + @Override + public long getId() { + throw new UnsupportedOperationException("database id can only provided by VO objects"); + } + + @Override + public String getXid() { + // FIXME: We should allow for end user to specify Xid. + return null; + } + + @Override + public Long getSourceIpAddressId() { + return null; + } + + @Override + public Integer getSourcePortStart() { + if (publicStartPort != null) { + return publicStartPort.intValue(); + } + return null; + } + + @Override + public Integer getSourcePortEnd() { + if (publicEndPort == null) { + if (publicStartPort != null) { + return publicStartPort.intValue(); + } + } else { + return publicEndPort.intValue(); + } + + return null; + } + + @Override + public Purpose getPurpose() { + return Purpose.Firewall; + } + + @Override + public State getState() { + throw new UnsupportedOperationException("Should never call me to find the state"); + } + + @Override + public long getNetworkId() { + return networkId; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public long getDomainId() { + Network network =_networkService.getNetwork(networkId); + return network.getDomainId(); + } + + @Override + public void create() { + if (getSourceCidrList() != null) { + String guestCidr = _networkService.getNetwork(getNetworkId()).getCidr(); + + for (String cidr: getSourceCidrList()){ + if (!NetUtils.isValidCIDR(cidr)){ + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Source cidrs formatting error " + cidr); + } + if (cidr.equals(NetUtils.ALL_CIDRS)) { + continue; + } + if(!NetUtils.isNetworkAWithinNetworkB(cidr, guestCidr)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, cidr + "is not within the guest cidr " + guestCidr); + } + } + } + if (getProtocol().equalsIgnoreCase(NetUtils.ALL_PROTO)) { + if (getSourcePortStart() != null && getSourcePortEnd() != null) { + throw new InvalidParameterValueException("Do not pass ports to protocol ALL, porotocol ALL do not require ports. Unable to create " + +"firewall rule for the network id=" + networkId); + } + } + + if (getVpcId() != null ){ + throw new InvalidParameterValueException("Unable to create firewall rule for the network id=" + networkId + + " as firewall egress rule can be created only for non vpc networks."); + } + + try { + FirewallRule result = _firewallService.createEgressFirewallRule(this); + setEntityId(result.getId()); + } catch (NetworkRuleConflictException ex) { + s_logger.info("Network rule conflict: " + ex.getMessage()); + s_logger.trace("Network Rule Conflict: ", ex); + throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, ex.getMessage()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_FIREWALL_OPEN; + } + + @Override + public String getEventDescription() { + Network network = _networkService.getNetwork(networkId); + return ("Creating firewall rule for network: " + network + " for protocol:" + this.getProtocol()); + } + + + @Override + public long getAccountId() { + Network network = _networkService.getNetwork(networkId); + return network.getAccountId(); + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return getNetworkId(); + } + + + @Override + public Integer getIcmpCode() { + if (icmpCode != null) { + return icmpCode; + } else if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + } + return null; + } + + @Override + public Integer getIcmpType() { + if (icmpType != null) { + return icmpType; + } else if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + + } + return null; + } + + @Override + public Long getRelated() { + return null; + } + + @Override + public FirewallRuleType getType() { + if (type != null && type.equalsIgnoreCase("system")) { + return FirewallRuleType.System; + } else { + return FirewallRuleType.User; + } + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.FirewallRule; + } + + @Override + public TrafficType getTrafficType() { + return TrafficType.Egress; + } + + @Override + public String getUuid() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java new file mode 100644 index 00000000000..4646cdbcf69 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/DeleteEgressFirewallRuleCmd.java @@ -0,0 +1,120 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.package org.apache.cloudstack.api.command.user.firewall; + +package org.apache.cloudstack.api.command.user.firewall; + +import org.apache.cloudstack.api.APICommand; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.UserContext; + +@APICommand(name = "deleteEgressFirewallRule", description="Deletes an ggress firewall rule", responseObject=SuccessResponse.class) +public class DeleteEgressFirewallRuleCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeleteEgressFirewallRuleCmd.class.getName()); + private static final String s_name = "deleteegressfirewallruleresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required=true, description="the ID of the firewall rule") + private Long id; + + // unexposed parameter needed for events logging + @Parameter(name=ApiConstants.ACCOUNT_ID, type=CommandType.LONG, expose=false) + private Long ownerId; + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public String getCommandName() { + return s_name; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_FIREWALL_CLOSE; + } + + @Override + public String getEventDescription() { + return ("Deleting egress firewall rule id=" + id); + } + + @Override + public long getEntityOwnerId() { + if (ownerId == null) { + FirewallRule rule = _entityMgr.findById(FirewallRule.class, id); + if (rule == null) { + throw new InvalidParameterValueException("Unable to find egress firewall rule by id"); + } else { + ownerId = _entityMgr.findById(FirewallRule.class, id).getAccountId(); + } + } + return ownerId; + } + + @Override + public void execute() throws ResourceUnavailableException { + UserContext.current().setEventDetails("Rule Id: " + id); + boolean result = _firewallService.revokeFirewallRule(id, true); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete egress firewall rule"); + } + } + + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return _firewallService.getFirewallRule(id).getNetworkId(); + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.FirewallRule; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java new file mode 100644 index 00000000000..6eeb53648f5 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/ListEgressFirewallRulesCmd.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.package org.apache.cloudstack.api.command.user.firewall; + +package org.apache.cloudstack.api.command.user.firewall; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.ListResponse; +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.Pair; + +@APICommand(name = "listEgressFirewallRules", description="Lists all egress firewall rules for network id.", responseObject=FirewallResponse.class) +public class ListEgressFirewallRulesCmd extends ListFirewallRulesCmd { + public static final Logger s_logger = Logger.getLogger(ListEgressFirewallRulesCmd.class.getName()); + private static final String s_name = "listegressfirewallrulesresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, description="Lists rule with the specified ID.") + private Long id; + + @Parameter(name=ApiConstants.NETWORK_ID, type=CommandType.LONG, description="the id network network for the egress firwall services") + private Long networkId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getNetworkId() { + return networkId; + } + + public FirewallRule.TrafficType getTrafficType () { + return FirewallRule.TrafficType.Egress; + } + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + Pair, Integer> result = _firewallService.listFirewallRules(this); + ListResponse response = new ListResponse(); + List fwResponses = new ArrayList(); + + for (FirewallRule fwRule : result.first()) { + FirewallResponse ruleData = _responseGenerator.createFirewallResponse(fwRule); + ruleData.setObjectName("firewallrule"); + fwResponses.add(ruleData); + } + response.setResponses(fwResponses, result.second()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/patches/systemvm/debian/config/root/firewallRule_egress.sh b/patches/systemvm/debian/config/root/firewallRule_egress.sh new file mode 100755 index 00000000000..0da7718741d --- /dev/null +++ b/patches/systemvm/debian/config/root/firewallRule_egress.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# $Id: firewallRule_egress.sh 9947 2013-01-17 19:34:24Z manuel $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/patches/xenserver/root/firewallRule_egress.sh $ +# firewallRule_egress.sh -- allow some ports / protocols from vm instances +# @VERSION@ + +source /root/func.sh + +lock="biglock" +locked=$(getLockFile $lock) +if [ "$locked" != "1" ] +then + exit 1 +fi +#set -x +usage() { + printf "Usage: %s: -a protocol:startport:endport:sourcecidrs> \n" $(basename $0) >&2 + printf "sourcecidrs format: cidr1-cidr2-cidr3-...\n" +} + +fw_egress_remove_backup() { + sudo iptables -D FW_OUTBOUND -j _FW_EGRESS_RULES + sudo iptables -F _FW_EGRESS_RULES + sudo iptables -X _FW_EGRESS_RULES +} + +fw_egress_save() { + sudo iptables -E FW_EGRESS_RULES _FW_EGRESS_RULES +} + +fw_egress_chain () { +#supress errors 2>/dev/null + fw_egress_remove_backup + fw_egress_save + sudo iptables -N FW_EGRESS_RULES + sudo iptables -A FW_OUTBOUND -j FW_EGRESS_RULES +} + +fw_egress_backup_restore() { + sudo iptables -A FW_OUTBOUND -j FW_EGRESS_RULES + sudo iptables -E _FW_EGRESS_RULES FW_EGRESS_RULES + fw_egress_remove_backup +} + + +fw_entry_for_egress() { + local rule=$1 + + local prot=$(echo $rule | cut -d: -f2) + local sport=$(echo $rule | cut -d: -f3) + local eport=$(echo $rule | cut -d: -f4) + local cidrs=$(echo $rule | cut -d: -f5 | sed 's/-/ /g') + if [ "$sport" == "0" -a "$eport" == "0" ] + then + DPORT="" + else + DPORT="--dport $sport:$eport" + fi + logger -t cloud "$(basename $0): enter apply fw egress rules for guest $prot:$sport:$eport:$cidrs" + + for lcidr in $cidrs + do + [ "$prot" == "reverted" ] && continue; + if [ "$prot" == "icmp" ] + then + typecode="$sport/$eport" + [ "$eport" == "-1" ] && typecode="$sport" + [ "$sport" == "-1" ] && typecode="any" + sudo iptables -A FW_EGRESS_RULES -p $prot -s $lcidr --icmp-type $typecode \ + -j ACCEPT + result=$? + elif [ "$prot" == "all" ] + then + sudo iptables -A FW_EGRESS_RULES -p $prot -s $lcidr -j ACCEPT + result=$? + else + sudo iptables -A FW_EGRESS_RULES -p $prot -s $lcidr \ + $DPORT -j ACCEPT + result=$? + fi + + [ $result -gt 0 ] && + logger -t cloud "Error adding iptables entry for guest network $prot:$sport:$eport:$cidrs" && + break + done + + logger -t cloud "$(basename $0): exit apply egress firewall rules for guest network" + return $result +} + + +aflag=0 +rules="" +rules_list="" +ip="" +dev="" +shift +shift +while getopts 'a:' OPTION +do + case $OPTION in + a) aflag=1 + rules="$OPTARG" + ;; + ?) usage + unlock_exit 2 $lock $locked + ;; + esac +done + +if [ "$aflag" != "1" ] +then + usage + unlock_exit 2 $lock $locked +fi + +if [ -n "$rules" ] +then + rules_list=$(echo $rules | cut -d, -f1- --output-delimiter=" ") +fi + +# rule format +# protocal:sport:eport:cidr +#-a tcp:80:80:0.0.0.0/0::tcp:220:220:0.0.0.0/0:,tcp:222:222:192.168.10.0/24-75.57.23.0/22-88.100.33.1/32 +# if any entry is reverted , entry will be in the format reverted:0:0:0 +# example : tcp:80:80:0.0.0.0/0:, tcp:220:220:0.0.0.0/0:,200.1.1.2:reverted:0:0:0 + +success=0 + +fw_egress_chain +for r in $rules_list +do + fw_entry_for_egress $r + success=$? + if [ $success -gt 0 ] + then + logger -t cloud "failure to apply fw egress rules " + break + else + logger -t cloud "successful in applying fw egress rules" + fi +done + +if [ $success -gt 0 ] +then + logger -t cloud "restoring from backup for guest network" + fw_egress_backup_restore +else + logger -t cloud "deleting backup for guest network" +fi + +fw_egress_remove_backup + +unlock_exit $success $lock $locked + +