[Falcon] CORD-366 Implemented CORD service dependency API and pipeline
Done
- Implement service dependency APIs
- Populate or remove basic tenant connectivity rules when VM created or removed
- Populate direct/indirect service access rules when service dependency created
- Remove service dependency rules
Todo
- Add/remove bucket to proper group when a VM is created or terminated
- Populate service dependency rules for existing VMs when service is activated
- Cleanup flow rules remove
Change-Id: I1daaf7ac9b41d7f2694605cb9b75f12d42144dbd
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java b/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
index 9e22997..4e044e1 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
@@ -15,195 +15,859 @@
*/
package org.onosproject.cordvtn;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
+import org.onosproject.net.Host;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DefaultDriverData;
import org.onosproject.net.driver.DefaultDriverHandler;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.driver.DriverHandler;
import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.instructions.ExtensionPropertyException;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.openstackswitching.OpenstackNetwork;
+import org.onosproject.openstackswitching.OpenstackSubnet;
import org.slf4j.Logger;
import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
-import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.Device.Type.SWITCH;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
import static org.slf4j.LoggerFactory.getLogger;
/**
- * Populates rules for virtual tenant network.
+ * Populates rules for CORD VTN service.
*/
-public final class CordVtnRuleInstaller {
+public class CordVtnRuleInstaller {
+
protected final Logger log = getLogger(getClass());
+ private static final int TABLE_IN_PORT = 0;
+ private static final int TABLE_ACCESS_TYPE = 1;
+ private static final int TABLE_IN_SERVICE = 2;
+ private static final int TABLE_DST_IP = 3;
+ private static final int TABLE_TUNNEL_IN = 4;
+
private static final int DEFAULT_PRIORITY = 5000;
+ private static final int LOWER_PRIORITY = 4000;
+ private static final int LOWEST_PRIORITY = 0;
private final ApplicationId appId;
- private final FlowObjectiveService flowObjectiveService;
+ private final FlowRuleService flowRuleService;
+ private final DeviceService deviceService;
private final DriverService driverService;
+ private final GroupService groupService;
+ private final MastershipService mastershipService;
private final String tunnelType;
/**
- * Creates a new rule installer.
+ * Creates a new rule populator.
*
* @param appId application id
- * @param flowObjectiveService flow objective service
+ * @param flowRuleService flow rule service
+ * @param deviceService device service
* @param driverService driver service
* @param tunnelType tunnel type
*/
public CordVtnRuleInstaller(ApplicationId appId,
- FlowObjectiveService flowObjectiveService,
+ FlowRuleService flowRuleService,
+ DeviceService deviceService,
DriverService driverService,
+ GroupService groupService,
+ MastershipService mastershipService,
String tunnelType) {
this.appId = appId;
- this.flowObjectiveService = flowObjectiveService;
+ this.flowRuleService = flowRuleService;
+ this.deviceService = deviceService;
this.driverService = driverService;
+ this.groupService = groupService;
+ this.mastershipService = mastershipService;
this.tunnelType = checkNotNull(tunnelType);
}
/**
- * Installs flow rules for tunnel in traffic.
+ * Installs table miss rule to a give device.
*
- * @param deviceId device id to install flow rules
- * @param inPort in port
- * @param dstInfos list of destination info
+ * @param deviceId device id to install the rules
+ * @param tunnelPort tunnel port number of the device
*/
- public void installFlowRulesTunnelIn(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
- dstInfos.stream().forEach(dstInfo -> {
- ForwardingObjective.Builder fBuilder = vtnRulesSameNode(inPort, dstInfo);
- if (fBuilder != null) {
- flowObjectiveService.forward(deviceId, fBuilder.add());
- }
- });
+ public void init(DeviceId deviceId, PortNumber tunnelPort) {
+ // default is drop packets which can be accomplished without
+ // a table miss entry for all table.
+ populateTunnelInPortRule(deviceId, tunnelPort);
+ processAccessTypeTable(deviceId);
}
/**
- * Installs flow rules for local in traffic.
+ * Populates basic rules that connect a VM to the other VMs in the system.
*
- * @param deviceId device id to install flow rules
- * @param inPort in port
- * @param dstInfos list of destination info
+ * @param host host
+ * @param hostIp host ip
+ * @param tunnelIp tunnel ip
+ * @param vNet openstack network
*/
- public void installFlowRulesLocalIn(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
- dstInfos.stream().forEach(dstInfo -> {
- ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ?
- vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo);
+ public void populateBasicConnectionRules(Host host, IpAddress hostIp, IpAddress tunnelIp,
+ OpenstackNetwork vNet) {
+ // TODO we can get host ip from host.ip() after applying NetworkConfig host provider
+ checkNotNull(host);
+ checkNotNull(vNet);
- if (fBuilder != null) {
- flowObjectiveService.forward(deviceId, fBuilder.add());
- }
- });
- }
-
- /**
- * Uninstalls flow rules associated with a given port from a given device.
- *
- * @param deviceId device id
- * @param inPort port associated with removed host
- * @param dstInfos list of destination info
- */
- public void uninstallFlowRules(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
- dstInfos.stream().forEach(dstInfo -> {
- ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ?
- vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo);
-
- if (fBuilder != null) {
- flowObjectiveService.forward(deviceId, fBuilder.remove());
- }
- });
- }
-
- /**
- * Returns forwarding objective builder to provision basic virtual tenant network.
- * This method cares for the traffics whose source and destination device is the same.
- *
- * @param inPort in port
- * @param dstInfo destination information
- * @return forwarding objective builder
- */
- private ForwardingObjective.Builder vtnRulesSameNode(Port inPort, DestinationInfo dstInfo) {
- checkArgument(inPort.element().id().equals(dstInfo.output().element().id()));
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- sBuilder.matchInPort(inPort.number())
- .matchEthDst(dstInfo.mac());
- if (isTunnelPort(inPort)) {
- sBuilder.matchTunnelId(dstInfo.tunnelId());
+ DeviceId deviceId = host.location().deviceId();
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ return;
}
- tBuilder.setOutput(dstInfo.output().number());
+ PortNumber inPort = host.location().port();
+ MacAddress dstMac = host.mac();
+ long tunnelId = Long.parseLong(vNet.segmentId());
- return DefaultForwardingObjective.builder()
- .withSelector(sBuilder.build())
- .withTreatment(tBuilder.build())
- .withPriority(DEFAULT_PRIORITY)
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .fromApp(appId)
- .makePermanent();
+ OpenstackSubnet subnet = vNet.subnets().stream()
+ .findFirst()
+ .orElse(null);
+
+ if (subnet == null) {
+ log.error("Failed to get subnet for {}", host.id());
+ return;
+ }
+
+ populateLocalInPortRule(deviceId, inPort, hostIp);
+ populateDirectAccessRule(Ip4Prefix.valueOf(subnet.cidr()), Ip4Prefix.valueOf(subnet.cidr()));
+ populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, tunnelIp);
+ populateTunnelInRule(deviceId, inPort, dstMac, tunnelId);
}
/**
- * Returns forwarding objective builder to provision basic virtual tenant network.
- * This method cares for the traffics whose source and destination is not the same.
+ * Populates service dependency rules.
*
- * @param deviceId device id to install flow rules
- * @param inPort in port
- * @param dstInfo destination information
- * @return forwarding objective, or null if it fails to build it
+ * @param tService tenant cord service
+ * @param pService provider cord service
*/
- private ForwardingObjective.Builder vtnRulesRemoteNode(DeviceId deviceId, Port inPort, DestinationInfo dstInfo) {
- checkArgument(isTunnelPort(dstInfo.output()));
+ public void populateServiceDependencyRules(CordService tService, CordService pService) {
+ checkNotNull(tService);
+ checkNotNull(pService);
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
+ Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
+ Ip4Address serviceIp = pService.serviceIp().getIp4Address();
- ExtensionTreatment extTreatment =
- getTunnelDstInstruction(deviceId, dstInfo.remoteIp().getIp4Address());
- if (extTreatment == null) {
+ Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
+ Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
+
+ for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+ GroupId groupId = createServiceGroup(device.id(), pService);
+ outGroups.put(device.id(), groupId);
+
+ Set<PortNumber> vms = tService.hosts().keySet()
+ .stream()
+ .filter(host -> host.location().deviceId().equals(device.id()))
+ .map(host -> host.location().port())
+ .collect(Collectors.toSet());
+ inPorts.put(device.id(), vms);
+ }
+
+ populateIndirectAccessRule(srcRange, serviceIp, outGroups);
+ populateDirectAccessRule(srcRange, dstRange);
+ populateInServiceRule(inPorts, outGroups);
+ }
+
+ /**
+ * Removes basic rules related to a given flow information.
+ *
+ * @param host host to be removed
+ */
+ public void removeBasicConnectionRules(Host host) {
+ checkNotNull(host);
+
+ DeviceId deviceId = host.location().deviceId();
+ MacAddress mac = host.mac();
+ PortNumber port = host.location().port();
+ IpAddress ip = host.ipAddresses().stream().findFirst().orElse(null);
+
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ return;
+ }
+
+ for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
+ if (flowRule.deviceId().equals(deviceId)) {
+ PortNumber inPort = getInPort(flowRule);
+ if (inPort != null && inPort.equals(port)) {
+ processFlowRule(false, flowRule);
+ continue;
+ }
+ }
+
+ MacAddress dstMac = getDstMacFromTreatment(flowRule);
+ if (dstMac != null && dstMac.equals(mac)) {
+ processFlowRule(false, flowRule);
+ continue;
+ }
+
+ dstMac = getDstMacFromSelector(flowRule);
+ if (dstMac != null && dstMac.equals(mac)) {
+ processFlowRule(false, flowRule);
+ continue;
+ }
+
+ IpPrefix dstIp = getDstIpFromSelector(flowRule);
+ if (dstIp != null && dstIp.equals(ip.toIpPrefix())) {
+ processFlowRule(false, flowRule);
+ }
+
+ }
+
+ // TODO uninstall same network access rule in access table if no vm exists in the network
+ }
+
+ /**
+ * Removes service dependency rules.
+ *
+ * @param tService tenant cord service
+ * @param pService provider cord service
+ */
+ public void removeServiceDependencyRules(CordService tService, CordService pService) {
+ checkNotNull(tService);
+ checkNotNull(pService);
+
+ Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
+ Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
+ IpPrefix serviceIp = pService.serviceIp().toIpPrefix();
+
+ Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
+ GroupKey groupKey = new DefaultGroupKey(pService.id().id().getBytes());
+
+ deviceService.getAvailableDevices(SWITCH).forEach(device -> {
+ Group group = groupService.getGroup(device.id(), groupKey);
+ if (group != null) {
+ outGroups.put(device.id(), group.id());
+ }
+ });
+
+ for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
+ IpPrefix dstIp = getDstIpFromSelector(flowRule);
+ IpPrefix srcIp = getSrcIpFromSelector(flowRule);
+
+ if (dstIp != null && dstIp.equals(serviceIp)) {
+ processFlowRule(false, flowRule);
+ continue;
+ }
+
+ if (dstIp != null && srcIp != null) {
+ if (dstIp.equals(dstRange) && srcIp.equals(srcRange)) {
+ processFlowRule(false, flowRule);
+ continue;
+ }
+
+ if (dstIp.equals(srcRange) && srcIp.equals(dstRange)) {
+ processFlowRule(false, flowRule);
+ continue;
+ }
+ }
+
+ GroupId groupId = getGroupIdFromTreatment(flowRule);
+ if (groupId != null && groupId.equals(outGroups.get(flowRule.deviceId()))) {
+ processFlowRule(false, flowRule);
+ }
+ }
+
+ // TODO remove the group if it is not in use
+ }
+
+ /**
+ * Creates a new group for a given service.
+ *
+ * @param deviceId device id to create a group
+ * @param service cord service
+ * @return group id, or null if it fails to create
+ */
+ private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
+ checkNotNull(service);
+
+ GroupKey groupKey = getGroupKey(service.id());
+ Group group = groupService.getGroup(deviceId, groupKey);
+ GroupId groupId = getGroupId(service.id(), deviceId);
+
+ if (group != null) {
+ log.debug("Group {} is already exist in {}", service.id(), deviceId);
+ return groupId;
+ }
+
+ GroupBuckets buckets = getServiceGroupBuckets(deviceId, service.segmentationId(), service.hosts());
+ GroupDescription groupDescription = new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.SELECT,
+ buckets,
+ groupKey,
+ groupId.id(),
+ appId);
+
+ groupService.addGroup(groupDescription);
+
+ return groupId;
+ }
+
+ /**
+ * Returns group buckets for a given device.
+ *
+ * @param deviceId device id
+ * @param tunnelId tunnel id
+ * @param hosts list of host
+ * @return group buckets
+ */
+ private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, Map<Host, IpAddress> hosts) {
+ List<GroupBucket> buckets = Lists.newArrayList();
+
+ for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
+ Host host = entry.getKey();
+ Ip4Address remoteIp = entry.getValue().getIp4Address();
+ DeviceId hostDevice = host.location().deviceId();
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+ .builder()
+ .setEthDst(host.mac());
+
+ if (deviceId.equals(hostDevice)) {
+ tBuilder.setOutput(host.location().port());
+ } else {
+ ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
+ if (tunnelDst == null) {
+ continue;
+ }
+
+ tBuilder.extension(tunnelDst, deviceId)
+ .setTunnelId(tunnelId)
+ .setOutput(getTunnelPort(hostDevice));
+ }
+
+ buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
+ }
+
+ return new GroupBuckets(buckets);
+ }
+
+ /**
+ * Returns globally unique group ID.
+ *
+ * @param serviceId service id
+ * @param deviceId device id
+ * @return group id
+ */
+ private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
+ return new DefaultGroupId(Objects.hash(serviceId, deviceId));
+ }
+
+ /**
+ * Returns group key of a service.
+ *
+ * @param serviceId service id
+ * @return group key
+ */
+ private GroupKey getGroupKey(CordServiceId serviceId) {
+ return new DefaultGroupKey(serviceId.id().getBytes());
+ }
+
+ /**
+ * Forward table miss rules in ACCESS_TYPE table to IN_SERVICE table.
+ *
+ * @param deviceId device id
+ */
+ private void processAccessTypeTable(DeviceId deviceId) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .transition(TABLE_IN_SERVICE)
+ .build();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(LOWEST_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_ACCESS_TYPE)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ }
+
+ /**
+ * Populates rules for tunnel flows in port in IN_PORT table.
+ * All flows from tunnel port are forwarded to TUNNEL_ID table.
+ *
+ * @param deviceId device id to install the rules
+ * @param tunnelPort tunnel port
+ */
+ private void populateTunnelInPortRule(DeviceId deviceId, PortNumber tunnelPort) {
+ checkNotNull(tunnelPort);
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(tunnelPort)
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .transition(TABLE_TUNNEL_IN)
+ .build();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_IN_PORT)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ }
+
+ /**
+ * Populates rules for local in port in IN_PORT table.
+ * Flows from a given in port, whose source IP is service IP transition
+ * to DST_TYPE table. Other flows transition to IN_SERVICE table.
+ *
+ * @param deviceId device id to install the rules
+ * @param inPort in port
+ * @param srcIp source ip
+ */
+ private void populateLocalInPortRule(DeviceId deviceId, PortNumber inPort, IpAddress srcIp) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(inPort)
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPSrc(srcIp.toIpPrefix())
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .transition(TABLE_ACCESS_TYPE)
+ .build();
+
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_IN_PORT)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+
+ selector = DefaultTrafficSelector.builder()
+ .matchInPort(inPort)
+ .build();
+ treatment = DefaultTrafficTreatment.builder()
+ .transition(TABLE_IN_SERVICE)
+ .build();
+
+ flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(LOWER_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_IN_PORT)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ }
+
+ /**
+ * Populates direct VM access rules for ACCESS_TYPE table.
+ * These rules are installed to all devices.
+ *
+ * @param srcRange source ip range
+ * @param dstRange destination ip range
+ */
+ private void populateDirectAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPSrc(srcRange)
+ .matchIPDst(dstRange)
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .transition(TABLE_DST_IP)
+ .build();
+
+ for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+ FlowRule flowRuleDirect = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(LOWER_PRIORITY)
+ .forDevice(device.id())
+ .forTable(TABLE_ACCESS_TYPE)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRuleDirect);
+ }
+ }
+
+ /**
+ * Populates indirect service access rules for ACCESS_TYPE table.
+ * These rules are installed to all devices.
+ *
+ * @param srcRange source range
+ * @param serviceIp service ip
+ * @param outGroups list of output group
+ */
+ private void populateIndirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
+ Map<DeviceId, GroupId> outGroups) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPSrc(srcRange)
+ .matchIPDst(serviceIp.toIpPrefix())
+ .build();
+
+ for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .group(outGroup.getValue())
+ .build();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(outGroup.getKey())
+ .forTable(TABLE_ACCESS_TYPE)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ }
+ }
+
+ /**
+ * Populates flow rules for IN_SERVICE table.
+ *
+ * @param inPorts list of inports related to the service for each device
+ * @param outGroups set of output groups
+ */
+ private void populateInServiceRule(Map<DeviceId, Set<PortNumber>> inPorts, Map<DeviceId, GroupId> outGroups) {
+ checkNotNull(inPorts);
+ checkNotNull(outGroups);
+
+ for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
+ Set<PortNumber> ports = entry.getValue();
+ DeviceId deviceId = entry.getKey();
+
+ GroupId groupId = outGroups.get(deviceId);
+ if (groupId == null) {
+ continue;
+ }
+
+ ports.stream().forEach(port -> {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(port)
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .group(groupId)
+ .build();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_IN_SERVICE)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ });
+ }
+ }
+
+ /**
+ * Populates flow rules for DST_IP table.
+ *
+ * @param deviceId device id
+ * @param inPort in port
+ * @param dstMac mac address
+ * @param dstIp destination ip
+ * @param tunnelId tunnel id
+ * @param tunnelIp tunnel remote ip
+ */
+ private void populateDstIpRule(DeviceId deviceId, PortNumber inPort, MacAddress dstMac,
+ IpAddress dstIp, long tunnelId, IpAddress tunnelIp) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(dstIp.toIpPrefix())
+ .build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(dstMac)
+ .setOutput(inPort)
+ .build();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_DST_IP)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+
+ for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+ if (device.id().equals(deviceId)) {
+ continue;
+ }
+
+ ExtensionTreatment tunnelDst = getTunnelDst(device.id(), tunnelIp.getIp4Address());
+ if (tunnelDst == null) {
+ continue;
+ }
+
+ treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(dstMac)
+ .setTunnelId(tunnelId)
+ .extension(tunnelDst, device.id())
+ .setOutput(getTunnelPort(device.id()))
+ .build();
+
+ flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(device.id())
+ .forTable(TABLE_DST_IP)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ }
+ }
+
+ /**
+ * Populates flow rules for TUNNEL_ID table.
+ *
+ * @param deviceId device id
+ * @param inPort in port
+ * @param mac mac address
+ * @param tunnelId tunnel id
+ */
+ private void populateTunnelInRule(DeviceId deviceId, PortNumber inPort, MacAddress mac, long tunnelId) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchTunnelId(tunnelId)
+ .matchEthDst(mac)
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(inPort)
+ .build();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_TUNNEL_IN)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+ }
+
+ /**
+ * Installs or uninstall a given rule.
+ *
+ * @param install true to install, false to uninstall
+ * @param rule rule
+ */
+ private void processFlowRule(boolean install, FlowRule rule) {
+ FlowRuleOperations.Builder oBuilder = FlowRuleOperations.builder();
+ oBuilder = install ? oBuilder.add(rule) : oBuilder.remove(rule);
+
+ flowRuleService.apply(oBuilder.build(new FlowRuleOperationsContext() {
+ @Override
+ public void onError(FlowRuleOperations ops) {
+ log.error(String.format("Failed %s, %s", ops.toString(), rule.toString()));
+ }
+ }));
+ }
+
+ /**
+ * Returns tunnel port of the device.
+ *
+ * @param deviceId device id
+ * @return tunnel port number, or null if no tunnel port exists on a given device
+ */
+ private PortNumber getTunnelPort(DeviceId deviceId) {
+ try {
+ return deviceService.getPorts(deviceId).stream()
+ .filter(p -> p.annotations().value("portName").contains(tunnelType))
+ .findFirst().get().number();
+ } catch (NoSuchElementException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the inport from a given flow rule if the rule contains the match of it.
+ *
+ * @param flowRule flow rule
+ * @return port number, or null if the rule doesn't have inport match
+ */
+ private PortNumber getInPort(FlowRule flowRule) {
+ Criterion criterion = flowRule.selector().getCriterion(IN_PORT);
+ if (criterion != null && criterion instanceof PortCriterion) {
+ PortCriterion port = (PortCriterion) criterion;
+ return port.port();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the destination mac address from a given flow rule if the rule
+ * contains the instruction of it.
+ *
+ * @param flowRule flow rule
+ * @return mac address, or null if the rule doesn't have destination mac instruction
+ */
+ private MacAddress getDstMacFromTreatment(FlowRule flowRule) {
+ Instruction instruction = flowRule.treatment().allInstructions().stream()
+ .filter(inst -> inst instanceof ModEtherInstruction &&
+ ((ModEtherInstruction) inst).subtype().equals(ETH_DST))
+ .findFirst()
+ .orElse(null);
+
+ if (instruction == null) {
return null;
}
- sBuilder.matchInPort(inPort.number())
- .matchEthDst(dstInfo.mac());
-
- tBuilder.extension(extTreatment, deviceId)
- .setTunnelId(dstInfo.tunnelId())
- .setOutput(dstInfo.output().number());
-
- return DefaultForwardingObjective.builder()
- .withSelector(sBuilder.build())
- .withTreatment(tBuilder.build())
- .withPriority(DEFAULT_PRIORITY)
- .withFlag(ForwardingObjective.Flag.VERSATILE)
- .fromApp(appId)
- .makePermanent();
+ return ((ModEtherInstruction) instruction).mac();
}
/**
- * Checks if a given port is tunnel interface or not.
- * It assumes the tunnel interface contains tunnelType string in its name.
+ * Returns the destination mac address from a given flow rule if the rule
+ * contains the match of it.
*
- * @param port port
- * @return true if the port is tunnel interface, false otherwise.
+ * @param flowRule flow rule
+ * @return mac address, or null if the rule doesn't have destination mac match
*/
- private boolean isTunnelPort(Port port) {
- return port.annotations().value("portName").contains(tunnelType);
+ private MacAddress getDstMacFromSelector(FlowRule flowRule) {
+ Criterion criterion = flowRule.selector().getCriterion(Criterion.Type.ETH_DST);
+ if (criterion != null && criterion instanceof EthCriterion) {
+ EthCriterion eth = (EthCriterion) criterion;
+ return eth.mac();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the destination IP from a given flow rule if the rule contains
+ * the match of it.
+ *
+ * @param flowRule flow rule
+ * @return ip prefix, or null if the rule doesn't have ip match
+ */
+ private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
+ Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
+ if (criterion != null && criterion instanceof IPCriterion) {
+ IPCriterion ip = (IPCriterion) criterion;
+ return ip.ip();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the source IP from a given flow rule if the rule contains
+ * the match of it.
+ *
+ * @param flowRule flow rule
+ * @return ip prefix, or null if the rule doesn't have ip match
+ */
+ private IpPrefix getSrcIpFromSelector(FlowRule flowRule) {
+ Criterion criterion = flowRule.selector().getCriterion(IPV4_SRC);
+ if (criterion != null && criterion instanceof IPCriterion) {
+ IPCriterion ip = (IPCriterion) criterion;
+ return ip.ip();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the group ID from a given flow rule if the rule contains the
+ * treatment of it.
+ *
+ * @param flowRule flow rule
+ * @return group id, or null if the rule doesn't have group instruction
+ */
+ private GroupId getGroupIdFromTreatment(FlowRule flowRule) {
+ Instruction instruction = flowRule.treatment().allInstructions().stream()
+ .filter(inst -> inst instanceof Instructions.GroupInstruction)
+ .findFirst()
+ .orElse(null);
+
+ if (instruction == null) {
+ return null;
+ }
+
+ return ((Instructions.GroupInstruction) instruction).groupId();
}
/**
@@ -213,19 +877,23 @@
* @param remoteIp tunnel destination address
* @return extension treatment or null if it fails to get instruction
*/
- private ExtensionTreatment getTunnelDstInstruction(DeviceId deviceId, Ip4Address remoteIp) {
+ private ExtensionTreatment getTunnelDst(DeviceId deviceId, Ip4Address remoteIp) {
try {
Driver driver = driverService.getDriver(deviceId);
- DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId));
- ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
+ DefaultDriverData driverData = new DefaultDriverData(driver, deviceId);
+ DriverHandler handler = new DefaultDriverHandler(driverData);
+ ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
- ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+ ExtensionTreatment treatment =
+ resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
treatment.setPropertyValue("tunnelDst", remoteIp);
return treatment;
- } catch (ItemNotFoundException | UnsupportedOperationException | ExtensionPropertyException e) {
- log.error("Failed to get extension instruction to set tunnel dst {}", deviceId);
+ } catch (ItemNotFoundException | UnsupportedOperationException |
+ ExtensionPropertyException e) {
+ log.error("Failed to get extension instruction {}", deviceId);
return null;
}
}
}
+