ONOS-5629: Enable encapsulation in SDN-IP
Change-Id: I4c3dbe877fd009302938b228fc9af40225d75329
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
index aa646ba..1275bd2 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
@@ -39,23 +39,34 @@
import org.onosproject.incubator.net.routing.RouteListener;
import org.onosproject.incubator.net.routing.RouteService;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.EncapsulationType;
import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
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.intent.ConnectivityIntent;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
import org.onosproject.net.intent.constraint.PartialFailureConstraint;
import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.sdnip.config.SdnIpConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import static org.onosproject.net.EncapsulationType.NONE;
+
/**
* FIB component of SDN-IP.
*/
@@ -73,10 +84,15 @@
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected RouteService routeService;
private final InternalRouteListener routeListener = new InternalRouteListener();
private final InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
+ private final InternalNetworkConfigListener networkConfigListener =
+ new InternalNetworkConfigListener();
private static final int PRIORITY_OFFSET = 100;
private static final int PRIORITY_MULTIPLIER = 5;
@@ -91,9 +107,8 @@
@Activate
public void activate() {
appId = coreService.getAppId(SdnIp.SDN_IP_APP);
-
interfaceService.addListener(interfaceListener);
-
+ networkConfigService.addListener(networkConfigListener);
routeService.addListener(routeListener);
}
@@ -106,11 +121,15 @@
private void update(ResolvedRoute route) {
synchronized (this) {
IpPrefix prefix = route.prefix();
+ EncapsulationType encap = encap();
MultiPointToSinglePointIntent intent =
- generateRouteIntent(prefix, route.nextHop(), route.nextHopMac());
+ generateRouteIntent(prefix,
+ route.nextHop(),
+ route.nextHopMac(),
+ encap);
if (intent == null) {
- log.debug("SDN-IP no interface found for route {}", route);
+ log.debug("No interface found for route {}", route);
return;
}
@@ -124,8 +143,8 @@
IpPrefix prefix = route.prefix();
MultiPointToSinglePointIntent intent = routeIntents.remove(prefix);
if (intent == null) {
- log.trace("SDN-IP no intent in routeIntents to delete " +
- "for prefix: {}", prefix);
+ log.trace("No intent in routeIntents to delete for prefix: {}",
+ prefix);
return;
}
intentSynchronizer.withdraw(intent);
@@ -140,15 +159,17 @@
* Intent will match dst IP prefix and rewrite dst MAC address at all other
* border switches, then forward packets according to dst MAC address.
*
- * @param prefix IP prefix of the route to add
- * @param nextHopIpAddress IP address of the next hop
- * @param nextHopMacAddress MAC address of the next hop
+ * @param prefix the IP prefix of the route to add
+ * @param nextHopIpAddress the IP address of the next hop
+ * @param nextHopMacAddress the MAC address of the next hop
+ * @param encap the encapsulation type in use
* @return the generated intent, or null if no intent should be submitted
*/
private MultiPointToSinglePointIntent generateRouteIntent(
IpPrefix prefix,
IpAddress nextHopIpAddress,
- MacAddress nextHopMacAddress) {
+ MacAddress nextHopMacAddress,
+ EncapsulationType encap) {
// Find the attachment point (egress interface) of the next hop
Interface egressInterface =
@@ -194,15 +215,19 @@
// Set key
Key key = Key.of(prefix.toString(), appId);
- return MultiPointToSinglePointIntent.builder()
- .appId(appId)
- .key(key)
- .filteredIngressPoints(ingressFilteredCPs)
- .filteredEgressPoint(egressFilteredCP)
- .treatment(treatment.build())
- .priority(priority)
- .constraints(CONSTRAINTS)
- .build();
+ MultiPointToSinglePointIntent.Builder intentBuilder =
+ MultiPointToSinglePointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .filteredIngressPoints(ingressFilteredCPs)
+ .filteredEgressPoint(egressFilteredCP)
+ .treatment(treatment.build())
+ .priority(priority)
+ .constraints(CONSTRAINTS);
+
+ setEncap(intentBuilder, CONSTRAINTS, encap);
+
+ return intentBuilder.build();
}
private void addInterface(Interface intf) {
@@ -350,6 +375,87 @@
return false;
}
+ /*
+ * Triggered when the network configuration configuration is modified.
+ * It checks if the encapsulation type has changed from last time, and in
+ * case modifies all intents.
+ */
+ private void encapUpdate() {
+ synchronized (this) {
+ // Get the encapsulation type just set from the configuration
+ EncapsulationType encap = encap();
+
+
+ for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry : routeIntents.entrySet()) {
+ // Get each intent currently registered by SDN-IP
+ MultiPointToSinglePointIntent intent = entry.getValue();
+
+ // Make sure the same constraint is not already part of the
+ // intent constraints
+ List<Constraint> constraints = intent.constraints();
+ if (!constraints.stream()
+ .filter(c -> c instanceof EncapsulationConstraint &&
+ new EncapsulationConstraint(encap).equals(c))
+ .findAny()
+ .isPresent()) {
+ MultiPointToSinglePointIntent.Builder intentBuilder =
+ MultiPointToSinglePointIntent.builder(intent);
+
+ // Set the new encapsulation constraint
+ setEncap(intentBuilder, constraints, encap);
+
+ // Build and submit the new intent
+ MultiPointToSinglePointIntent newIntent =
+ intentBuilder.build();
+
+ routeIntents.put(entry.getKey(), newIntent);
+ intentSynchronizer.submit(newIntent);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets an encapsulation constraint to the intent builder given.
+ *
+ * @param builder the intent builder
+ * @param constraints the existing intent constraints
+ * @param encap the encapsulation type to be set
+ */
+ private static void setEncap(ConnectivityIntent.Builder builder,
+ List<Constraint> constraints,
+ EncapsulationType encap) {
+ // Constraints might be an immutable list, so a new modifiable list
+ // is created
+ List<Constraint> newConstraints = new ArrayList<>(constraints);
+
+ // Remove any encapsulation constraint if already in the list
+ constraints.stream()
+ .filter(c -> c instanceof EncapsulationConstraint)
+ .forEach(c -> newConstraints.remove(c));
+
+ // if the new encapsulation is different from NONE, a new encapsulation
+ // constraint should be added to the list
+ if (!encap.equals(NONE)) {
+ newConstraints.add(new EncapsulationConstraint(encap));
+ }
+
+ // Submit new constraint list as immutable list
+ builder.constraints(ImmutableList.copyOf(newConstraints));
+ }
+
+ private EncapsulationType encap() {
+ SdnIpConfig sdnIpConfig =
+ networkConfigService.getConfig(appId, SdnIpConfig.class);
+
+ if (sdnIpConfig == null) {
+ log.debug("No SDN-IP config available");
+ return EncapsulationType.NONE;
+ } else {
+ return sdnIpConfig.encap();
+ }
+ }
+
private class InternalRouteListener implements RouteListener {
@Override
public void event(RouteEvent event) {
@@ -367,8 +473,28 @@
}
}
- private class InternalInterfaceListener implements InterfaceListener {
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+ @Override
+ public void event(NetworkConfigEvent event) {
+ switch (event.type()) {
+ case CONFIG_REGISTERED:
+ break;
+ case CONFIG_UNREGISTERED:
+ break;
+ case CONFIG_ADDED:
+ case CONFIG_UPDATED:
+ case CONFIG_REMOVED:
+ if (event.configClass() == SdnIpConfig.class) {
+ encapUpdate();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ private class InternalInterfaceListener implements InterfaceListener {
@Override
public void event(InterfaceEvent event) {
switch (event.type()) {