[ONOS-3953] Implements Security Group of Openstack

Change-Id: I30766097a2894a26e46a7a399176d99e95af6abf
diff --git a/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java b/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java
new file mode 100644
index 0000000..b16a6bf
--- /dev/null
+++ b/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java
@@ -0,0 +1,274 @@
+/*
+* Copyright 2016 Open Networking Laboratory
+*
+* Licensed 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.onosproject.openstacknetworking.switching;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.TpPort;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackSecurityGroup;
+import org.onosproject.openstackinterface.OpenstackSecurityGroupRule;
+import org.onosproject.openstacknetworking.OpenstackPortInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+/**
+ * Populates flows rules for Security Groups of VMs.
+ *
+ */
+public class OpenstackSecurityGroupRulePopulator {
+
+    private static Logger log = LoggerFactory
+            .getLogger(OpenstackSecurityGroupRulePopulator.class);
+
+    private OpenstackInterfaceService openstackService;
+    private FlowObjectiveService flowObjectiveService;
+
+    private ApplicationId appId;
+
+    private static final String PROTO_ICMP = "ICMP";
+    private static final String PROTO_TCP = "TCP";
+    private static final String PROTO_UDP = "UDP";
+
+    private static final String ETHTYPE_IPV4 = "IPV4";
+
+    private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
+
+    private static final int ACL_RULE_PRIORITY = 30000;
+
+    /**
+     * Constructor.
+     *
+     * @param appId
+     * @param openstackService
+     * @param flowObjectiveService
+     */
+    public OpenstackSecurityGroupRulePopulator(ApplicationId appId, OpenstackInterfaceService openstackService,
+                                               FlowObjectiveService flowObjectiveService) {
+        this.appId = appId;
+        this.openstackService = openstackService;
+        this.flowObjectiveService = flowObjectiveService;
+    }
+
+    /**
+     * Populates flow rules for security groups.
+     *
+     * @param id Device ID
+     * @param sgId Security Group ID
+     * @param vmIp VM IP address
+     * @param portInfoMap Port Info map
+     */
+    public void populateSecurityGroupRules(DeviceId id, String sgId, Ip4Address vmIp,
+                                           Map<String, OpenstackPortInfo> portInfoMap) {
+        OpenstackSecurityGroup securityGroup = openstackService.getSecurityGroup(sgId);
+        if (securityGroup != null) {
+            securityGroup.rules().stream().forEach(sgRule -> {
+                if (sgRule.remoteGroupId() != null && !sgRule.remoteGroupId().equals("null")) {
+                    openstackService.ports().stream()
+                        .filter(port -> port.securityGroups().contains(sgRule.remoteGroupId()))
+                        .flatMap(port -> port.fixedIps().values().stream())
+                        .forEach(remoteIp -> setSecurityGroupRule(id, sgRule,
+                                vmIp, IpPrefix.valueOf((IpAddress) remoteIp, 32)));
+                } else {
+                    setSecurityGroupRule(id, sgRule, vmIp, sgRule.remoteIpPrefix());
+                }
+            });
+
+            openstackService.ports().stream().forEach(osPort ->
+                osPort.securityGroups().stream().forEach(remoteVmSgId -> {
+                    OpenstackSecurityGroup remoteVmSg = openstackService.getSecurityGroup(remoteVmSgId);
+                    remoteVmSg.rules().stream()
+                        .filter(remoteVmSgRule -> remoteVmSgRule.remoteGroupId().equals(sgId))
+                        .forEach(remoteVmSgRule -> {
+                            Ip4Address remoteVmIp =
+                                    (Ip4Address) osPort.fixedIps().values().stream().findAny().orElse(null);
+                            OpenstackPortInfo osPortInfo = portInfoMap.get(OpenstackSwitchingManager.PORTNAME_PREFIX_VM
+                                    + osPort.id().substring(0, 11));
+                            if (osPortInfo != null && remoteVmIp != null) {
+                                setSecurityGroupRule(osPortInfo.deviceId(), remoteVmSgRule, remoteVmIp,
+                                        IpPrefix.valueOf(vmIp, 32));
+                            }
+                        });
+                }));
+        }
+    }
+
+    /**
+     * Removes flow rules for security groups.
+     *
+     * @param id Device ID
+     * @param sgId Security Group ID to remove
+     * @param vmIp VM IP address
+     * @param portInfoMap port info map
+     * @param securityGroupMap security group info map
+     */
+    public void removeSecurityGroupRules(DeviceId id, String sgId, Ip4Address vmIp,
+                                         Map<String, OpenstackPortInfo> portInfoMap,
+                                         Map<String, OpenstackSecurityGroup> securityGroupMap) {
+        OpenstackSecurityGroup securityGroup = securityGroupMap.get(sgId);
+        if (securityGroup != null) {
+            securityGroup.rules().stream().forEach(sgRule -> {
+                if (sgRule.remoteGroupId() != null && !sgRule.remoteGroupId().equals("null")) {
+                    portInfoMap.values().stream()
+                            .filter(portInfo -> portInfo.securityGroups().contains(sgRule.remoteGroupId()))
+                            .map(OpenstackPortInfo::ip)
+                            .forEach(remoteIp -> {
+                                removeSecurityGroupRule(id, sgRule, vmIp, IpPrefix.valueOf(remoteIp, 32));
+                            });
+                } else {
+                    removeSecurityGroupRule(id, sgRule, vmIp, sgRule.remoteIpPrefix());
+                }
+            });
+
+            portInfoMap.values().stream()
+                .forEach(portInfo -> portInfo.securityGroups()
+                    .forEach(remoteVmSgId -> {
+                        OpenstackSecurityGroup remoteVmSg = securityGroupMap.get(remoteVmSgId);
+                        remoteVmSg.rules().stream()
+                            .filter(remoteVmSgRule -> remoteVmSgRule.remoteGroupId().equals(sgId))
+                            .forEach(remoteVmSgRule -> removeSecurityGroupRule(portInfo.deviceId(),
+                                    remoteVmSgRule, portInfo.ip(), IpPrefix.valueOf(vmIp, 32)));
+                    }));
+        }
+    }
+
+    private void setSecurityGroupRule(DeviceId id, OpenstackSecurityGroupRule sgRule,
+                                      Ip4Address vmIp, IpPrefix remoteIp) {
+        ForwardingObjective.Builder foBuilder = buildFlowObjective(id, sgRule, vmIp, remoteIp);
+        if (foBuilder != null) {
+            flowObjectiveService.forward(id, foBuilder.add());
+        }
+    }
+
+    private void removeSecurityGroupRule(DeviceId id, OpenstackSecurityGroupRule sgRule,
+                                      Ip4Address vmIp, IpPrefix remoteIp) {
+        ForwardingObjective.Builder foBuilder = buildFlowObjective(id, sgRule, vmIp, remoteIp);
+        if (foBuilder != null) {
+            flowObjectiveService.forward(id, foBuilder.remove());
+        }
+    }
+
+    ForwardingObjective.Builder buildFlowObjective(DeviceId id, OpenstackSecurityGroupRule sgRule,
+                                           Ip4Address vmIp, IpPrefix remoteIp) {
+        if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
+            return null;
+        }
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        buildMatchs(sBuilder, sgRule, vmIp, remoteIp);
+
+        ForwardingObjective.Builder foBuilder = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(tBuilder.build())
+                .withPriority(ACL_RULE_PRIORITY)
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .fromApp(appId);
+
+        return foBuilder;
+    }
+
+    private void buildMatchs(TrafficSelector.Builder sBuilder, OpenstackSecurityGroupRule sgRule,
+                             Ip4Address vmIp, IpPrefix remoteIp) {
+        buildMatchEthType(sBuilder, sgRule.ethertype());
+        buildMatchDirection(sBuilder, sgRule.direction(), vmIp);
+        buildMatchProto(sBuilder, sgRule.protocol());
+        buildMatchPort(sBuilder, sgRule.protocol(), sgRule.direction(), sgRule.portRangeMax(), sgRule.portRangeMin());
+        buildMatchRemoteIp(sBuilder, remoteIp, sgRule.direction());
+    }
+
+    private void buildMatchDirection(TrafficSelector.Builder sBuilder,
+                                     OpenstackSecurityGroupRule.Direction direction, Ip4Address vmIp) {
+        if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
+            sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
+        } else {
+            sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
+        }
+    }
+
+    private void buildMatchEthType(TrafficSelector.Builder sBuilder, String ethertype) {
+        // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4);
+        if (ethertype != null && ethertype != "null" &&
+                !ethertype.toUpperCase().equals(ETHTYPE_IPV4)) {
+            log.error("EthType {} is not supported yet in Security Group", ethertype);
+        }
+    }
+
+    private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder, IpPrefix remoteIpPrefix,
+                                                       OpenstackSecurityGroupRule.Direction direction) {
+        if (remoteIpPrefix != null && !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
+            if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
+                sBuilder.matchIPDst(remoteIpPrefix);
+            } else {
+                sBuilder.matchIPSrc(remoteIpPrefix);
+            }
+        }
+    }
+
+    private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
+        if (protocol != null) {
+            switch (protocol.toUpperCase()) {
+                case PROTO_ICMP:
+                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
+                    break;
+                case PROTO_TCP:
+                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
+                    break;
+                case PROTO_UDP:
+                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
+                    break;
+                default:
+            }
+        }
+    }
+
+    private void buildMatchPort(TrafficSelector.Builder sBuilder, String protocol,
+                                                   OpenstackSecurityGroupRule.Direction direction,
+                                                   int portMin, int portMax) {
+        if (portMin > 0 && portMax > 0 && portMin == portMax) {
+            if (protocol.toUpperCase().equals(PROTO_TCP)) {
+                if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
+                    sBuilder.matchTcpDst(TpPort.tpPort(portMax));
+                } else {
+                    sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
+                }
+            } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
+                if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
+                    sBuilder.matchUdpDst(TpPort.tpPort(portMax));
+                } else {
+                    sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
+                }
+            }
+        }
+    }
+}