blob: c3c54adebf4e9a7bbb57daaa6da0c38aa948b745 [file] [log] [blame]
/*
* 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 application ID
* @param openstackService OpenStack interface service
* @param flowObjectiveService flow objective service
*/
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));
}
}
}
}
}