[ONOS-6249] (vNet) Link mapping and end-to-end isolation for virtual network
A virtual tenant's topology graph should not be restricted to the substrate's subgraph.
Thus, we need to set up a tunnel on the physical path through which a virtual link passes.
Furthermore, isolating the traffic from different virtual networks is also needed.
Change-Id: Ia5dc49d34ec96d3a7b9c55ff7a6df3b10ad93697
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProvider.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProvider.java
index a9bff1d..ef8fa8c 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProvider.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProvider.java
@@ -34,6 +34,7 @@
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualNetworkService;
import org.onosproject.incubator.net.virtual.VirtualPort;
+import org.onosproject.incubator.net.virtual.VirtualLink;
import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
import org.onosproject.incubator.net.virtual.provider.InternalRoutingAlgorithm;
import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
@@ -73,6 +74,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.List;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -392,15 +394,62 @@
if (ingressPoint.deviceId().equals(egressPoint.deviceId()) ||
egressPoint.port().isLogical()) {
//Traffic is handled inside a single physical switch
- //No tunnel is needed.
TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector
.builder(commonSelector)
.matchInPort(ingressPoint.port());
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
- .builder(commonTreatment)
- .setOutput(egressPoint.port());
+ .builder(commonTreatment);
+
+ VirtualPort virtualIngressPort = vnService
+ .getVirtualPorts(networkId, flowRule.deviceId())
+ .stream()
+ .filter(p -> p.realizedBy().equals(ingressPoint))
+ .findFirst()
+ .get();
+
+ VirtualPort virtualEgressPort = vnService
+ .getVirtualPorts(networkId, flowRule.deviceId())
+ .stream()
+ .filter(p -> p.realizedBy().equals(egressPoint))
+ .findFirst()
+ .get();
+
+ ConnectPoint ingressCp = new ConnectPoint(virtualIngressPort.element().id(), virtualIngressPort.number());
+ ConnectPoint egressCp = new ConnectPoint(virtualEgressPort.element().id(), virtualEgressPort.number());
+
+ Optional<VirtualLink> optionalIngressLink = vnService
+ .getVirtualLinks(networkId)
+ .stream()
+ .filter(l -> l.dst().equals(ingressCp))
+ .findFirst();
+
+ Optional<VirtualLink> optionalEgressLink = vnService
+ .getVirtualLinks(networkId)
+ .stream()
+ .filter(l -> l.src().equals(egressCp))
+ .findFirst();
+
+ //Isolate traffic from different virtual networks with VLAN
+ if (!optionalIngressLink.isPresent() && !optionalEgressLink.isPresent()) {
+ treatmentBuilder.setOutput(egressPoint.port());
+ } else if (optionalIngressLink.isPresent() && !optionalEgressLink.isPresent()) {
+ selectorBuilder.matchVlanId(VlanId.vlanId(networkId.id().shortValue()));
+ treatmentBuilder.popVlan();
+ treatmentBuilder.setOutput(egressPoint.port());
+ } else if (!optionalIngressLink.isPresent() && optionalEgressLink.isPresent()) {
+ outRules.addAll(generateRulesOnPath(networkId, optionalEgressLink.get(),
+ commonSelector, commonTreatment, flowRule));
+ treatmentBuilder.pushVlan()
+ .setVlanId(VlanId.vlanId(networkId.id().shortValue()));
+ treatmentBuilder.setOutput(egressPoint.port());
+ } else if (optionalIngressLink.isPresent() && optionalEgressLink.isPresent()) {
+ outRules.addAll(generateRulesOnPath(networkId, optionalEgressLink.get(),
+ commonSelector, commonTreatment, flowRule));
+ selectorBuilder.matchVlanId(VlanId.vlanId(networkId.id().shortValue()));
+ treatmentBuilder.setOutput(egressPoint.port());
+ }
FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
.fromApp(vnService.getVirtualNetworkApplicationId(networkId))
@@ -522,6 +571,76 @@
return outRules;
}
+ /**
+ * Generate flow rules to the intermediate nodes on the physical path for a virtual link.
+ *
+ * @param networkId The virtual network identifier
+ * @param virtualLink A virtual link
+ * @param commonSelector A common traffic selector between the virtual
+ * and physical flow rules
+ * @param commonTreatment A common traffic treatment between the virtual
+ * and physical flow rules
+ * @param flowRule The virtual flow rule to be translated
+ * @return A set of flow rules for the path on physical network
+ */
+ private Set<FlowRule> generateRulesOnPath(NetworkId networkId,
+ VirtualLink virtualLink,
+ TrafficSelector commonSelector,
+ TrafficTreatment commonTreatment,
+ FlowRule flowRule) {
+
+ VirtualPort srcVirtualPort = vnService
+ .getVirtualPorts(networkId, virtualLink.src().deviceId())
+ .stream()
+ .filter(p -> p.number().equals(virtualLink.src().port()))
+ .findFirst()
+ .get();
+
+ VirtualPort dstVirtualPort = vnService
+ .getVirtualPorts(networkId, virtualLink.dst().deviceId())
+ .stream()
+ .filter(p -> p.number().equals(virtualLink.dst().port()))
+ .findFirst()
+ .get();
+ Set<FlowRule> outRules = new HashSet<>();
+ ConnectPoint srcCp = srcVirtualPort.realizedBy();
+ ConnectPoint dstCp = dstVirtualPort.realizedBy();
+
+ Path internalPath = internalRoutingAlgorithm
+ .findPath(srcCp, dstCp);
+ List<Link> links = internalPath.links();
+ if (internalPath != null && links.size() > 1) {
+ for (int i = 0; i < links.size() - 1; i++) {
+ ConnectPoint inCp = links.get(i).dst();
+ ConnectPoint outCp = links.get(i + 1).src();
+ TrafficSelector.Builder linkSelectorBuilder = DefaultTrafficSelector
+ .builder(commonSelector)
+ .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
+ .matchInPort(inCp.port());
+
+ TrafficTreatment.Builder linkTreatmentBuilder = DefaultTrafficTreatment
+ .builder(commonTreatment)
+ .setOutput(outCp.port());
+
+ FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+ .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
+ .forDevice(inCp.deviceId())
+ .withSelector(linkSelectorBuilder.build())
+ .withTreatment(linkTreatmentBuilder.build())
+ .withPriority(flowRule.priority());
+
+ if (flowRule.isPermanent()) {
+ ruleBuilder.makePermanent();
+ } else {
+ ruleBuilder.makeTemporary(flowRule.timeout());
+ }
+
+ outRules.add(ruleBuilder.build());
+ }
+ }
+ return outRules;
+ }
+
private class InternalFlowRuleListener implements FlowRuleListener {
@Override
public void event(FlowRuleEvent event) {
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java
index 2b010bb..e268f32 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/provider/DefaultVirtualFlowRuleProviderTest.java
@@ -347,7 +347,7 @@
@Override
public Set<VirtualLink> getVirtualLinks(NetworkId networkId) {
- return null;
+ return new HashSet<>();
}
@Override
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
index 4b79cf5..81a9a3c 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
@@ -514,8 +514,14 @@
if (virtualLinkSet == null) {
virtualLinkSet = new HashSet<>();
}
+
// validate that the link does not already exist in this network
- checkState(getLink(networkId, src, dst) == null, "The virtual link already exists");
+ checkState(getLink(networkId, src, dst) == null,
+ "The virtual link already exists");
+ checkState(getLink(networkId, src, null) == null,
+ "The source connection point has been used by another link");
+ checkState(getLink(networkId, null, dst) == null,
+ "The destination connection point has been used by another link");
VirtualLink virtualLink = DefaultVirtualLink.builder()
.networkId(networkId)
@@ -536,8 +542,14 @@
Set<VirtualLink> virtualLinkSet = networkIdVirtualLinkSetMap.get(virtualLink.networkId());
if (virtualLinkSet == null) {
virtualLinkSet = new HashSet<>();
+ networkIdVirtualLinkSetMap.put(virtualLink.networkId(), virtualLinkSet);
+ log.warn("The updated virtual link {} has not been added", virtualLink);
+ return;
}
- virtualLinkSet.remove(virtualLink);
+ if (!virtualLinkSet.remove(virtualLink)) {
+ log.warn("The updated virtual link {} does not exist", virtualLink);
+ return;
+ }
VirtualLink newVirtualLink = DefaultVirtualLink.builder()
.networkId(virtualLink.networkId())
@@ -557,6 +569,7 @@
final VirtualLink virtualLink = getLink(networkId, src, dst);
if (virtualLink == null) {
+ log.warn("The removed virtual link between {} and {} does not exist", src, dst);
return null;
}
Set<VirtualLink> virtualLinkSet = new HashSet<>();
@@ -742,7 +755,13 @@
VirtualLink virtualLink = null;
for (VirtualLink link : virtualLinkSet) {
- if (link.src().equals(src) && link.dst().equals(dst)) {
+ if (src == null && link.dst().equals(dst)) {
+ virtualLink = link;
+ break;
+ } else if (dst == null && link.src().equals(src)) {
+ virtualLink = link;
+ break;
+ } else if (link.src().equals(src) && link.dst().equals(dst)) {
virtualLink = link;
break;
}