[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));
+ }
+ }
+ }
+ }
+}