Route reprogamming using group substitution during next hop movement
Change-Id: Idf8362dac522722ca67747e245bfd836e6ee6292
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index a5a90a7..7dfa632 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -1118,11 +1118,12 @@
* @param hostMac MAC address of the next hop
* @param hostVlanId Vlan ID of the nexthop
* @param outPort port where the next hop attaches to
+ * @param directHost host is of type direct or indirect
*/
void populateRoute(DeviceId deviceId, IpPrefix prefix,
- MacAddress hostMac, VlanId hostVlanId, PortNumber outPort) {
+ MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
if (shouldProgram(deviceId)) {
- srManager.routingRulePopulator.populateRoute(deviceId, prefix, hostMac, hostVlanId, outPort);
+ srManager.routingRulePopulator.populateRoute(deviceId, prefix, hostMac, hostVlanId, outPort, directHost);
}
}
@@ -1135,11 +1136,12 @@
* @param hostMac MAC address of the next hop
* @param hostVlanId Vlan ID of the nexthop
* @param outPort port that next hop attaches to
+ * @param directHost host is of type direct or indirect
*/
void revokeRoute(DeviceId deviceId, IpPrefix prefix,
- MacAddress hostMac, VlanId hostVlanId, PortNumber outPort) {
+ MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
if (shouldProgram(deviceId)) {
- srManager.routingRulePopulator.revokeRoute(deviceId, prefix, hostMac, hostVlanId, outPort);
+ srManager.routingRulePopulator.revokeRoute(deviceId, prefix, hostMac, hostVlanId, outPort, directHost);
}
}
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java
index eee9900..296fbcd 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java
@@ -525,9 +525,9 @@
log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
if (revoke) {
- srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
+ srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
} else {
- srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
+ srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
}
}
@@ -675,10 +675,10 @@
ipPrefixSet.forEach(ipPrefix -> {
if (install && ipPrefix.contains(hostIpAddress)) {
srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
- host.mac(), host.vlan(), cp.port());
+ host.mac(), host.vlan(), cp.port(), true);
} else if (!install && ipPrefix.contains(hostIpAddress)) {
srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
- host.mac(), host.vlan(), cp.port());
+ host.mac(), host.vlan(), cp.port(), true);
}
});
}));
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RouteHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
index 40cdf53..7dbbbeb 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
@@ -32,6 +32,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
@@ -101,7 +103,7 @@
srManager.deviceConfiguration.addSubnet(location, prefix);
log.debug("RouteAdded populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
- nextHopMac, nextHopVlan, location.port());
+ nextHopMac, nextHopVlan, location.port(), false);
});
});
}
@@ -175,7 +177,7 @@
srManager.deviceConfiguration.addSubnet(location, prefix);
log.debug("RouteUpdated. populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
- nextHopMac, nextHopVlan, location.port());
+ nextHopMac, nextHopVlan, location.port(), false);
});
});
}
@@ -221,7 +223,7 @@
log.debug("RouteRemoved. revokeRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
srManager.defaultRoutingHandler.revokeRoute(pairDeviceId.get(), prefix,
- nextHopMac, vlanId, pairLocalPort.get());
+ nextHopMac, vlanId, pairLocalPort.get(), false);
}
});
});
@@ -231,6 +233,8 @@
log.info("processHostMovedEvent {}", event);
MacAddress hostMac = event.subject().mac();
VlanId hostVlanId = event.subject().vlan();
+ // map of nextId for prev port in each device
+ Map<DeviceId, Integer> nextIdMap = new HashMap<>();
affectedRoutes(hostMac, hostVlanId).forEach(affectedRoute -> {
IpPrefix prefix = affectedRoute.prefix();
@@ -245,8 +249,22 @@
Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
.collect(Collectors.toSet());
+ // Set of deviceIDs of the previous locations where the host was connected
+ // Used to determine if host moved to different connect points
+ // on same device or moved to a different device altogether
+ Set<DeviceId> oldDeviceIds = prevLocations.stream().map(HostLocation::deviceId)
+ .collect(Collectors.toSet());
+
// For each old location
Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
+ //find next Id for each old port, add to map
+ int nextId = srManager.getNextIdForHostPort(prevLocation.deviceId(), hostMac,
+ hostVlanId, prevLocation.port());
+ log.debug("HostMoved. NextId For Host {}, {}, {}, {} {}", prevLocation, prefix,
+ hostMac, hostVlanId, nextId);
+
+ nextIdMap.put(prevLocation.deviceId(), nextId);
+
// Remove flows for unchanged IPs only when the host moves from a switch to another.
// Otherwise, do not remove and let the adding part update the old flow
if (newDeviceIds.contains(prevLocation.deviceId())) {
@@ -265,7 +283,7 @@
log.debug("HostMoved. revokeRoute {}, {}, {}, {}", prevLocation, prefix, hostMac, hostVlanId);
srManager.defaultRoutingHandler.revokeRoute(prevLocation.deviceId(), prefix,
- hostMac, hostVlanId, prevLocation.port());
+ hostMac, hostVlanId, prevLocation.port(), false);
});
// For each new location, add all new IPs.
@@ -273,9 +291,20 @@
log.debug("HostMoved. addSubnet {}, {}", newLocation, prefix);
srManager.deviceConfiguration.addSubnet(newLocation, prefix);
- log.debug("HostMoved. populateRoute {}, {}, {}, {}", newLocation, prefix, hostMac, hostVlanId);
- srManager.defaultRoutingHandler.populateRoute(newLocation.deviceId(), prefix,
- hostMac, hostVlanId, newLocation.port());
+ //its a new connect point, not a move from an existing device, populateRoute
+ if (!oldDeviceIds.contains(newLocation.deviceId())) {
+ log.debug("HostMoved. populateRoute {}, {}, {}, {}", newLocation, prefix, hostMac, hostVlanId);
+ srManager.defaultRoutingHandler.populateRoute(newLocation.deviceId(), prefix,
+ hostMac, hostVlanId, newLocation.port(), false);
+ } else {
+ // update same flow to point to new nextObj
+ VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(newLocation)).orElse(hostVlanId);
+ //use nextIdMap to send new port details to update the nextId group bucket
+ log.debug("HostMoved. update L3 Ucast Group Bucket {}, {}, {} --> {}",
+ newLocation, hostMac, vlanId, nextIdMap.get(newLocation.deviceId()));
+ srManager.updateMacVlanTreatment(newLocation.deviceId(), hostMac, vlanId,
+ newLocation.port(), nextIdMap.get(newLocation.deviceId()));
+ }
});
});
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index de0eb58..b864a08 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -303,15 +303,16 @@
* @param hostMac MAC address of the next hop
* @param hostVlanId Vlan ID of the nexthop
* @param outPort port where the next hop attaches to
+ * @param directHost host is of type direct or indirect
*/
void populateRoute(DeviceId deviceId, IpPrefix prefix,
- MacAddress hostMac, VlanId hostVlanId, PortNumber outPort) {
+ MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
log.debug("Populate direct routing entry for route {} at {}:{}",
prefix, deviceId, outPort);
ForwardingObjective.Builder fwdBuilder;
try {
fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac,
- hostVlanId, outPort, false);
+ hostVlanId, outPort, directHost, false);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting direct populateRoute");
return;
@@ -342,15 +343,16 @@
* @param hostMac MAC address of the next hop
* @param hostVlanId Vlan ID of the nexthop
* @param outPort port that next hop attaches to
+ * @param directHost host is of type direct or indirect
*/
void revokeRoute(DeviceId deviceId, IpPrefix prefix,
- MacAddress hostMac, VlanId hostVlanId, PortNumber outPort) {
+ MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
log.debug("Revoke IP table entry for route {} at {}:{}",
prefix, deviceId, outPort);
ForwardingObjective.Builder fwdBuilder;
try {
fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac,
- hostVlanId, outPort, true);
+ hostVlanId, outPort, directHost, true);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting revokeIpRuleForHost.");
return;
@@ -379,16 +381,22 @@
* @param hostVlanId Vlan ID of the nexthop
* @param outPort port where the nexthop attaches to
* @param revoke true if forwarding objective is meant to revoke forwarding rule
+ * @param directHost host is direct or indirect
* @return forwarding objective builder
* @throws DeviceConfigNotFoundException if given device is not configured
*/
- private ForwardingObjective.Builder routingFwdObjBuilder(
+
+ private ForwardingObjective.Builder
+
+
+ routingFwdObjBuilder(
DeviceId deviceId, IpPrefix prefix,
MacAddress hostMac, VlanId hostVlanId, PortNumber outPort,
- boolean revoke)
+ boolean directHost, boolean revoke)
throws DeviceConfigNotFoundException {
MacAddress deviceMac;
deviceMac = config.getDeviceMac(deviceId);
+ int nextObjId = -1;
ConnectPoint connectPoint = new ConnectPoint(deviceId, outPort);
VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
@@ -435,18 +443,26 @@
return null;
}
}
- // if the objective is to revoke an existing rule, and for some reason
- // the next-objective does not exist, then a new one should not be created
- int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outPort,
+
+ if (directHost) {
+ // if the objective is to revoke an existing rule, and for some reason
+ // the next-objective does not exist, then a new one should not be created
+ nextObjId = srManager.getPortNextObjectiveId(deviceId, outPort,
tbuilder.build(), mbuilder.build(), !revoke);
- if (portNextObjId == -1) {
- // Warning log will come from getPortNextObjective method
+ } else {
+ // if the objective is to revoke an existing rule, and for some reason
+ // the next-objective does not exist, then a new one should not be created
+ nextObjId = srManager.getMacVlanNextObjectiveId(deviceId, hostMac, hostVlanId,
+ tbuilder.build(), mbuilder.build(), !revoke);
+ }
+ if (nextObjId == -1) {
+ // Warning log will come from getMacVlanNextObjective method
return null;
}
return DefaultForwardingObjective.builder()
.withSelector(sbuilder.build())
- .nextStep(portNextObjId)
+ .nextStep(nextObjId)
.fromApp(srManager.appId).makePermanent()
.withPriority(getPriorityFromPrefix(prefix))
.withFlag(ForwardingObjective.Flag.SPECIFIC);
@@ -1711,7 +1727,7 @@
));
});
try {
- fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac, dummyVlan, outPort, false);
+ fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac, dummyVlan, outPort, false, false);
} catch (DeviceConfigNotFoundException e) {
log.error(e.getMessage() + " Aborting populateDoubleTaggedRoute");
return;
@@ -1750,7 +1766,7 @@
void revokeDoubleTaggedRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac,
VlanId hostVlan, VlanId innerVlan, VlanId outerVlan,
EthType outerTpid, PortNumber outPort) {
- revokeRoute(deviceId, prefix, hostMac, hostVlan, outPort);
+ revokeRoute(deviceId, prefix, hostMac, hostVlan, outPort, false);
DummyVlanIdStoreKey key = new DummyVlanIdStoreKey(
new ConnectPoint(deviceId, outPort), prefix.address());
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 6c72a06..e78b152 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -27,6 +27,7 @@
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.VlanId;
+import org.onlab.packet.MacAddress;
import org.onlab.util.KryoNamespace;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
@@ -115,6 +116,7 @@
import org.onosproject.segmentrouting.storekey.DummyVlanIdStoreKey;
import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
+import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
import org.onosproject.segmentrouting.xconnect.api.XconnectService;
import org.onosproject.store.serializers.KryoNamespaces;
@@ -346,7 +348,7 @@
vlanNextObjStore = null;
/**
* Per device next objective ID store with (device id + port + treatment + meta) as key.
- * Used to keep track on L2 interface group and L3 unicast group information.
+ * Used to keep track on L2 interface group and L3 unicast group information for direct hosts.
*/
private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
portNextObjStore = null;
@@ -358,6 +360,13 @@
private EventuallyConsistentMap<DummyVlanIdStoreKey, VlanId>
dummyVlanIdStore = null;
+ /**
+ * Per device next objective ID store with (device id + MAC address + vlan) as key.
+ * Used to keep track of L3 unicast group for indirect hosts.
+ */
+ private EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer>
+ macVlanNextObjStore = null;
+
private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
private EventuallyConsistentMap<String, Policy> policyStore = null;
@@ -471,6 +480,15 @@
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
+ log.debug("Creating EC map macvlannextobjectivestore");
+ EventuallyConsistentMapBuilder<MacVlanNextObjectiveStoreKey, Integer>
+ macVlanNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
+ macVlanNextObjStore = macVlanNextObjMapBuilder
+ .withName("macvlannextobjectivestore")
+ .withSerializer(createSerializer())
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .build();
+
log.debug("Creating EC map subnetnextobjectivestore");
EventuallyConsistentMapBuilder<PortNextObjectiveStoreKey, Integer>
portNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
@@ -602,7 +620,8 @@
L2TunnelPolicy.class,
DefaultL2Tunnel.class,
DefaultL2TunnelPolicy.class,
- DummyVlanIdStoreKey.class
+ DummyVlanIdStoreKey.class,
+ MacVlanNextObjectiveStoreKey.class
);
}
@@ -650,6 +669,7 @@
dsNextObjStore.destroy();
vlanNextObjStore.destroy();
+ macVlanNextObjStore.destroy();
portNextObjStore.destroy();
dummyVlanIdStore.destroy();
tunnelStore.destroy();
@@ -947,6 +967,15 @@
}
@Override
+ public ImmutableMap<MacVlanNextObjectiveStoreKey, Integer> getMacVlanNextObjStore() {
+ if (macVlanNextObjStore != null) {
+ return ImmutableMap.copyOf(macVlanNextObjStore.entrySet());
+ } else {
+ return ImmutableMap.of();
+ }
+ }
+
+ @Override
public ImmutableMap<PortNextObjectiveStoreKey, Integer> getPortNextObjStore() {
if (portNextObjStore != null) {
return ImmutableMap.copyOf(portNextObjStore.entrySet());
@@ -989,6 +1018,13 @@
}
});
}
+ if (macVlanNextObjStore != null) {
+ macVlanNextObjStore.entrySet().forEach(e -> {
+ if (e.getValue() == nextId) {
+ macVlanNextObjStore.remove(e.getKey());
+ }
+ });
+ }
if (portNextObjStore != null) {
portNextObjStore.entrySet().forEach(e -> {
if (e.getValue() == nextId) {
@@ -1109,8 +1145,18 @@
}
/**
+ * Per device next objective ID store with (device id + MAC address + vlan) as key.
+ * Used to keep track on L3 Unicast group information for indirect hosts.
+ *
+ * @return mac vlan next object store
+ */
+ public EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer> macVlanNextObjStore() {
+ return macVlanNextObjStore;
+ }
+
+ /**
* Per device next objective ID store with (device id + port + treatment + meta) as key.
- * Used to keep track on L2 interface group and L3 unicast group information.
+ * Used to keep track on L2 interface group and L3 unicast group information for direct hosts.
*
* @return port next object store.
*/
@@ -1254,6 +1300,76 @@
}
/**
+ * Returns the next Objective ID for the given mac and vlan, given the treatment.
+ * There could be multiple different treatments to the same outport, which
+ * would result in different objectives. If the next object does not exist,
+ * and should be created, a new one is created and its id is returned.
+ *
+ * @param deviceId Device ID
+ * @param macAddr mac of host for which Next ID is required.
+ * @param vlanId vlan of host for which Next ID is required.
+ * @param treatment the actions to apply on the packets (should include outport)
+ * @param meta metadata passed into the creation of a Next Objective if necessary
+ * @param createIfMissing true if a next object should be created if not found
+ * @return next objective ID or -1 if an error occurred during retrieval or creation
+ */
+ public int getMacVlanNextObjectiveId(DeviceId deviceId, MacAddress macAddr, VlanId vlanId,
+ TrafficTreatment treatment,
+ TrafficSelector meta,
+ boolean createIfMissing) {
+ DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
+ if (ghdlr != null) {
+ return ghdlr.getMacVlanNextObjectiveId(macAddr, vlanId, treatment, meta, createIfMissing);
+ } else {
+ log.warn("getMacVlanNextObjectiveId query - groupHandler for device {}"
+ + " not found", deviceId);
+ return -1;
+ }
+ }
+
+ /**
+ * Returns the next ID for the given host port from the store.
+ *
+ * @param deviceId Device ID
+ * @param hostMac mac of host for which Next ID is required.
+ * @param hostVlanId vlan of host for which Next ID is required.
+ * @param port port of device for which next ID is required.
+ * @return next objective ID or -1 if an error occurred during retrieval
+ */
+ public int getNextIdForHostPort(DeviceId deviceId, MacAddress hostMac,
+ VlanId hostVlanId, PortNumber port) {
+ DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
+ if (ghdlr != null) {
+ return ghdlr.getNextIdForHostPort(hostMac, hostVlanId, port);
+ } else {
+ log.warn("getNextIdForHostPort query - groupHandler for device {}"
+ + " not found", deviceId);
+ return -1;
+ }
+ }
+
+ /**
+ * Updates the next objective for the given nextId .
+ *
+ * @param deviceId Device ID
+ * @param hostMac mac of host for which Next obj is to be updated.
+ * @param hostVlanId vlan of host for which Next obj is to be updated.
+ * @param port port with which to update the Next Obj.
+ * @param nextId of Next Obj which needs to be updated.
+ */
+ public void updateMacVlanTreatment(DeviceId deviceId, MacAddress hostMac,
+ VlanId hostVlanId, PortNumber port, int nextId) {
+ DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
+ if (ghdlr != null) {
+ ghdlr.updateL3UcastGroupBucket(hostMac, hostVlanId, port, nextId);
+ } else {
+ log.warn("updateL3UcastGroupBucket query - groupHandler for device {}"
+ + " not found", deviceId);
+ }
+ }
+
+
+ /**
* Returns the group handler object for the specified device id.
*
* @param devId the device identifier
@@ -1584,6 +1700,9 @@
vlanNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
.forEach(entry -> vlanNextObjStore.remove(entry.getKey()));
+ macVlanNextObjStore.entrySet().stream()
+ .filter(entry -> entry.getKey().deviceId().equals(device.id()))
+ .forEach(entry -> macVlanNextObjStore.remove(entry.getKey()));
portNextObjStore.entrySet().stream()
.filter(entry -> entry.getKey().deviceId().equals(device.id()))
.forEach(entry -> portNextObjStore.remove(entry.getKey()));
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
index 1b28998..74db140 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
@@ -42,6 +42,7 @@
import org.onosproject.segmentrouting.mcast.McastStoreKey;
import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
+import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
import java.util.List;
import java.util.Map;
@@ -226,6 +227,13 @@
ImmutableMap<VlanNextObjectiveStoreKey, Integer> getVlanNextObjStore();
/**
+ * Returns the Mac Vlan next objective store.
+ *
+ * @return current contents of the macVlanNextObjStore
+ */
+ ImmutableMap<MacVlanNextObjectiveStoreKey, Integer> getMacVlanNextObjStore();
+
+ /**
* Returns the port next objective store.
*
* @return current contents of the portNextObjStore
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/NextMacVlanCommand.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/NextMacVlanCommand.java
new file mode 100644
index 0000000..e31e693
--- /dev/null
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/NextMacVlanCommand.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.segmentrouting.cli;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.segmentrouting.SegmentRoutingService;
+import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Map;
+
+/**
+ * Command to read the current state of the macVlanNextObjStore.
+ */
+@Service
+@Command(scope = "onos", name = "sr-next-mac-vlan",
+ description = "Displays the current next-hop / next-id it mapping")
+public class NextMacVlanCommand extends AbstractShellCommand {
+ @Override
+ protected void doExecute() {
+ SegmentRoutingService srService =
+ AbstractShellCommand.get(SegmentRoutingService.class);
+ print(srService.getMacVlanNextObjStore());
+ }
+
+ private void print(Map<MacVlanNextObjectiveStoreKey, Integer> macVlanNextObjStore) {
+ ArrayList<MacVlanNextObjectiveStoreKey> a = new ArrayList<>(macVlanNextObjStore.keySet());
+ a.sort(Comparator
+ .comparing((MacVlanNextObjectiveStoreKey o) -> o.deviceId().toString())
+ .thenComparing((MacVlanNextObjectiveStoreKey o) -> o.vlanId().toString())
+ .thenComparing((MacVlanNextObjectiveStoreKey o) -> o.macAddr().toString()));
+
+ StringBuilder builder = new StringBuilder();
+ a.forEach(k ->
+ builder.append("\n")
+ .append(k)
+ .append(" --> ")
+ .append(macVlanNextObjStore.get(k))
+ );
+ print(builder.toString());
+ }
+}
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 02f2b19..669e136 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -44,9 +44,11 @@
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceProperties;
+import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
+import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.slf4j.Logger;
@@ -88,6 +90,8 @@
protected MacAddress nodeMacAddr = null;
protected LinkService linkService;
protected FlowObjectiveService flowObjectiveService;
+ private DeviceConfiguration config;
+
/**
* local store for neighbor-device-ids and the set of ports on this device
* that connect to the same neighbor.
@@ -106,6 +110,9 @@
// distributed store for (device+subnet-ip-prefix) mapped to next-id
protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
vlanNextObjStore = null;
+ // distributed store for (device+mac+vlan+treatment) mapped to next-id
+ protected EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer>
+ macVlanNextObjStore = null;
// distributed store for (device+port+treatment) mapped to next-id
protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
portNextObjStore = null;
@@ -145,6 +152,7 @@
this.dsNextObjStore = srManager.dsNextObjStore();
this.vlanNextObjStore = srManager.vlanNextObjStore();
this.portNextObjStore = srManager.portNextObjStore();
+ this.macVlanNextObjStore = srManager.macVlanNextObjStore();
this.srManager = srManager;
executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
VERIFY_INTERVAL,
@@ -816,6 +824,52 @@
}
/**
+ * Returns the next objective of type simple associated with the mac/vlan on the
+ * device, given the treatment. Different treatments to the same mac/vlan result
+ * in different next objectives. If no such objective exists, this method
+ * creates one (if requested) and returns the id. Optionally metadata can be passed in for
+ * the creation of the objective. Typically this is used for L2 and L3 forwarding
+ * to compute nodes and containers/VMs on the compute nodes directly attached
+ * to the switch.
+ *
+ * @param macAddr the mac addr for the simple next objective
+ * @param vlanId the vlan for the simple next objective
+ * @param treatment the actions to apply on the packets (should include outport)
+ * @param meta optional metadata passed into the creation of the next objective
+ * @param createIfMissing true if a next object should be created if not found
+ * @return int if found or created, -1 if there are errors during the
+ * creation of the next objective.
+ */
+ public int getMacVlanNextObjectiveId(MacAddress macAddr, VlanId vlanId, TrafficTreatment treatment,
+ TrafficSelector meta, boolean createIfMissing) {
+
+ Integer nextId = macVlanNextObjStore
+ .get(new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId));
+
+ if (nextId != null) {
+ return nextId;
+ }
+
+ log.debug("getMacVlanNextObjectiveId in device {}: Next objective id "
+ + "not found for host : {}/{} .. {}", deviceId, macAddr, vlanId,
+ (createIfMissing) ? "creating" : "aborting");
+
+ if (!createIfMissing) {
+ return -1;
+ }
+
+ /* create missing next objective */
+ nextId = createGroupFromMacVlan(macAddr, vlanId, treatment, meta);
+ if (nextId == null) {
+ log.warn("getMacVlanNextObjectiveId: unable to create next obj"
+ + "for dev:{} host:{}/{}", deviceId, macAddr, vlanId);
+ return -1;
+ }
+ return nextId;
+ }
+
+
+ /**
* Returns the next objective of type simple associated with the port on the
* device, given the treatment. Different treatments to the same port result
* in different next objectives. If no such objective exists, this method
@@ -1177,6 +1231,45 @@
}
/**
+ * Create simple next objective for an indirect host mac/vlan. The treatments can include
+ * all outgoing actions that need to happen on the packet.
+ *
+ * @param macAddr the mac address of the host
+ * @param vlanId the vlan of the host
+ * @param treatment the actions to apply on the packets (should include outport)
+ * @param meta optional data to pass to the driver
+ * @return next objective ID
+ */
+ public int createGroupFromMacVlan(MacAddress macAddr, VlanId vlanId, TrafficTreatment treatment,
+ TrafficSelector meta) {
+ int nextId = flowObjectiveService.allocateNextId();
+ MacVlanNextObjectiveStoreKey key = new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId);
+
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.SIMPLE)
+ .addTreatment(treatment)
+ .fromApp(appId)
+ .withMeta(meta);
+
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) ->
+ log.debug("createGroupFromMacVlan installed "
+ + "NextObj {} on {}", nextId, deviceId),
+ (objective, error) -> {
+ log.warn("createGroupFromMacVlan failed to install NextObj {} on {}: {}", nextId, deviceId, error);
+ srManager.invalidateNextObj(objective.id());
+ });
+ NextObjective nextObj = nextObjBuilder.add(context);
+ flowObjectiveService.next(deviceId, nextObj);
+ log.debug("createGroupFromMacVlan: Submited next objective {} in device {} "
+ + "for host {}/{}", nextId, deviceId, macAddr, vlanId);
+
+ macVlanNextObjStore.put(key, nextId);
+ return nextId;
+ }
+
+ /**
* Create simple next objective for a single port. The treatments can include
* all outgoing actions that need to happen on the packet.
*
@@ -1257,6 +1350,7 @@
portNextObjStore.remove(key);
}
}
+
/**
* Removes groups for the next objective ID given.
*
@@ -1402,6 +1496,94 @@
}
/**
+ * Returns the next ID for the given host port from the store.
+ *
+ * @param hostMac mac of host for which Next ID is required.
+ * @param hostVlanId vlan of host for which Next ID is required.
+ * @param port port of device for which next ID is required.
+ * @return next objective ID or -1 if an error occurred during retrieval
+ */
+ public int getNextIdForHostPort(MacAddress hostMac, VlanId hostVlanId, PortNumber port) {
+
+ MacAddress deviceMac;
+ try {
+ deviceMac = deviceConfig.getDeviceMac(deviceId);
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage() + " in getNextIdForHostPort");
+ return -1;
+ }
+
+ TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+ tbuilder.deferred()
+ .setEthDst(hostMac)
+ .setEthSrc(deviceMac)
+ .setVlanId(hostVlanId)
+ .setOutput(port);
+
+ TrafficSelector metadata =
+ DefaultTrafficSelector.builder().matchVlanId(hostVlanId).build();
+
+ int nextId = getMacVlanNextObjectiveId(hostMac, hostVlanId,
+ tbuilder.build(), metadata, true);
+
+ log.debug("getNextId : hostMac {}, deviceMac {}, port {}, hostVlanId {} Treatment {} Meta {} nextId {} ",
+ hostMac, deviceMac, port, hostVlanId, tbuilder.build(), metadata, nextId);
+
+ return nextId;
+ }
+
+ /**
+ * Updates the next objective for the given nextId .
+ *
+ * @param hostMac mac of host for which Next obj is to be updated.
+ * @param hostVlanId vlan of host for which Next obj is to be updated.
+ * @param port port with which to update the Next Obj.
+ * @param nextId of Next Obj which needs to be updated.
+ */
+ public void updateL3UcastGroupBucket(MacAddress hostMac, VlanId hostVlanId, PortNumber port, int nextId) {
+
+ MacAddress deviceMac;
+ try {
+ deviceMac = deviceConfig.getDeviceMac(deviceId);
+ } catch (DeviceConfigNotFoundException e) {
+ log.warn(e.getMessage() + " in updateL3UcastGroupBucket");
+ return;
+ }
+
+ TrafficSelector metadata =
+ DefaultTrafficSelector.builder().matchVlanId(hostVlanId).build();
+
+ TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+ tbuilder.deferred()
+ .setEthDst(hostMac)
+ .setEthSrc(deviceMac)
+ .setVlanId(hostVlanId)
+ .setOutput(port);
+
+ log.debug(" update L3Ucast : deviceMac {}, port {}, host {}/{}, nextid {}, Treatment {} Meta {}",
+ deviceMac, port, hostMac, hostVlanId, nextId, tbuilder.build(), metadata);
+
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.SIMPLE).fromApp(appId)
+ .addTreatment(tbuilder.build())
+ .withMeta(metadata);
+
+ ObjectiveContext context = new DefaultObjectiveContext(
+ (objective) -> log.debug(" NextId {} successfully updated host {} vlan {} with port {}",
+ nextId, hostMac, hostVlanId, port),
+ (objective, error) -> {
+ log.warn(" NextId {} failed to update host {} vlan {} with port {}, error : {}",
+ nextId, hostMac, hostVlanId, port, error);
+ srManager.invalidateNextObj(objective.id());
+ });
+
+ NextObjective nextObj = nextObjBuilder.modify(context);
+ flowObjectiveService.next(deviceId, nextObj);
+
+ }
+
+ /**
* Adds a single port to the L2FG or removes it from the L2FG.
*
* @param vlanId the vlan id corresponding to this port
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/DestinationSetNextObjectiveStoreKey.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/DestinationSetNextObjectiveStoreKey.java
index 9b6e621..0baa769 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/DestinationSetNextObjectiveStoreKey.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/DestinationSetNextObjectiveStoreKey.java
@@ -21,6 +21,8 @@
import org.onosproject.net.DeviceId;
import org.onosproject.segmentrouting.grouphandler.DestinationSet;
+import static com.google.common.base.MoreObjects.toStringHelper;
+
/**
* Key of Destination set next objective store.
*/
@@ -84,6 +86,9 @@
@Override
public String toString() {
- return "Device: " + deviceId + " " + ds;
+ return toStringHelper(getClass())
+ .add("deviceId", deviceId)
+ .add("ds", ds)
+ .toString();
}
}
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/MacVlanNextObjectiveStoreKey.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/MacVlanNextObjectiveStoreKey.java
new file mode 100644
index 0000000..4a1ab78
--- /dev/null
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/MacVlanNextObjectiveStoreKey.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * 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.segmentrouting.storekey;
+
+import org.onlab.packet.VlanId;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Key of Device/Vlan/MacAddr to NextObjective store.
+ */
+public class MacVlanNextObjectiveStoreKey {
+ private final DeviceId deviceId;
+ private final MacAddress macAddr;
+ private final VlanId vlanId;
+
+ /**
+ * Constructs the key of the next objective store.
+ *
+ * @param deviceId device ID
+ * @param macAddr mac of host
+ * @param vlanId vlan of host
+ */
+ public MacVlanNextObjectiveStoreKey(DeviceId deviceId, MacAddress macAddr, VlanId vlanId) {
+ this.deviceId = deviceId;
+ this.macAddr = macAddr;
+ this.vlanId = vlanId;
+ }
+
+ /**
+ * Gets device id in this MacVlanNextObjectiveStoreKey.
+ *
+ * @return device id
+ */
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ /**
+ * Gets vlan information in this MacVlanNextObjectiveStoreKey.
+ *
+ * @return vlan information
+ */
+ public VlanId vlanId() {
+ return vlanId;
+ }
+
+ /**
+ * Gets mac information in this MacVlanNextObjectiveStoreKey.
+ *
+ * @return mac information
+ */
+ public MacAddress macAddr() {
+ return macAddr;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof MacVlanNextObjectiveStoreKey)) {
+ return false;
+ }
+ MacVlanNextObjectiveStoreKey that =
+ (MacVlanNextObjectiveStoreKey) o;
+ return (Objects.equals(this.deviceId, that.deviceId) &&
+ Objects.equals(this.vlanId, that.vlanId) &&
+ Objects.equals(this.macAddr, that.macAddr));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(deviceId, vlanId, macAddr);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("deviceId", deviceId)
+ .add("vlanId", vlanId)
+ .add("macAddr", macAddr)
+ .toString();
+ }
+}
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/PortNextObjectiveStoreKey.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/PortNextObjectiveStoreKey.java
index 9aa41db..1269bce 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/PortNextObjectiveStoreKey.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/PortNextObjectiveStoreKey.java
@@ -22,6 +22,8 @@
import java.util.Objects;
+import static com.google.common.base.MoreObjects.toStringHelper;
+
/**
* Key of Device/Port to NextObjective store.
*
@@ -111,8 +113,11 @@
@Override
public String toString() {
- return "ConnectPoint: " + deviceId + "/" + portNum +
- " Treatment: " + treatment +
- " Meta: " + meta;
+ return toStringHelper(getClass())
+ .add("deviceId", deviceId)
+ .add("portNum", portNum)
+ .add("treatment", treatment)
+ .add("meta", meta)
+ .toString();
}
}
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java
index 5d9f2fd..55a95cd 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java
@@ -21,6 +21,8 @@
import java.util.Objects;
+import static com.google.common.base.MoreObjects.toStringHelper;
+
/**
* Key of VLAN to NextObjective store.
*/
@@ -79,6 +81,9 @@
@Override
public String toString() {
- return "deviceId: " + deviceId + " vlanId: " + vlanId;
+ return toStringHelper(getClass())
+ .add("deviceId", deviceId)
+ .add("vlanId", vlanId)
+ .toString();
}
}
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
index f72c264..d0ca6cd 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
@@ -563,7 +563,7 @@
// We should expect only one bridging flow and one routing flow programmed on 1A
mockDefaultRoutingHandler.populateBridging(DEV3, P2, HOST_MAC, HOST_VLAN_UNTAGGED);
expectLastCall().times(1);
- mockDefaultRoutingHandler.populateRoute(DEV3, HOST_IP11.toIpPrefix(), HOST_MAC, HOST_VLAN_UNTAGGED, P2);
+ mockDefaultRoutingHandler.populateRoute(DEV3, HOST_IP11.toIpPrefix(), HOST_MAC, HOST_VLAN_UNTAGGED, P2, true);
expectLastCall().times(1);
replay(mockDefaultRoutingHandler);
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockRoutingRulePopulator.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockRoutingRulePopulator.java
index 4eab7c0..77eeed2 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockRoutingRulePopulator.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockRoutingRulePopulator.java
@@ -38,7 +38,7 @@
@Override
public void populateRoute(DeviceId deviceId, IpPrefix prefix,
- MacAddress hostMac, VlanId hostVlanId, PortNumber outPort) {
+ MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
MockRoutingTableKey rtKey = new MockRoutingTableKey(deviceId, prefix);
MockRoutingTableValue rtValue = new MockRoutingTableValue(outPort, hostMac, hostVlanId);
routingTable.put(rtKey, rtValue);
@@ -46,9 +46,9 @@
@Override
public void revokeRoute(DeviceId deviceId, IpPrefix prefix,
- MacAddress hostMac, VlanId hostVlanId, PortNumber outPort) {
+ MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
MockRoutingTableKey rtKey = new MockRoutingTableKey(deviceId, prefix);
MockRoutingTableValue rtValue = new MockRoutingTableValue(outPort, hostMac, hostVlanId);
routingTable.remove(rtKey, rtValue);
}
-}
\ No newline at end of file
+}
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
index 494b9ec..ade5d0c 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
@@ -595,4 +595,4 @@
verify(srManager.deviceConfiguration);
}
-}
\ No newline at end of file
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
index 7a51f17..52d5382 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
@@ -854,7 +854,7 @@
}
}
if (portNum == null) {
- log.warn("Can't find output port for the bucket {}.", treatment);
+ log.debug("Can't find output port for the bucket {}.", treatment);
continue;
}
// assemble info for l2 interface group
@@ -880,12 +880,89 @@
log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
deviceId, Integer.toHexString(l2InterfaceGroupId),
l2InterfaceGroupKey, nextObj.id());
+
groupInfoBuilder.add(new GroupInfo(l2InterfaceGroupDescription,
l2InterfaceGroupDescription));
}
return groupInfoBuilder.build();
}
+ private GroupInfo prepareL3UnicastGroup(NextObjective nextObj, NextGroup next) {
+
+ ImmutableList.Builder<GroupInfo> groupInfoBuilder = ImmutableList.builder();
+ TrafficTreatment treatment = nextObj.next().iterator().next();
+
+ VlanId assignedVlan = readVlanFromSelector(nextObj.meta());
+ if (assignedVlan == null) {
+ log.warn("VLAN ID required by next obj is missing. Abort.");
+ return null;
+ }
+
+ List<GroupInfo> l2GroupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
+ GroupDescription l2InterfaceGroupDesc = l2GroupInfos.get(0).innerMostGroupDesc();
+ GroupKey l2groupkey = l2InterfaceGroupDesc.appCookie();
+
+ TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
+ VlanId vlanid = null;
+ MacAddress srcMac;
+ MacAddress dstMac;
+ for (Instruction ins : treatment.allInstructions()) {
+ if (ins.type() == Instruction.Type.L2MODIFICATION) {
+ L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
+ switch (l2ins.subtype()) {
+ case ETH_DST:
+ dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
+ outerTtb.setEthDst(dstMac);
+ break;
+ case ETH_SRC:
+ srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
+ outerTtb.setEthSrc(srcMac);
+ break;
+ case VLAN_ID:
+ vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
+ outerTtb.setVlanId(vlanid);
+ break;
+ default:
+ break;
+ }
+ } else {
+ log.debug("Driver does not handle this type of TrafficTreatment"
+ + " instruction in l2l3chain: {} - {}", ins.type(), ins);
+ }
+ }
+
+ GroupId l2groupId = new GroupId(l2InterfaceGroupDesc.givenGroupId());
+ outerTtb.group(l2groupId);
+
+ // we need the top level group's key to point the flow to it
+ List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
+ GroupKey l3groupkey = gkeys.get(0).peekFirst();
+ GroupId grpId = groupService.getGroup(deviceId, l3groupkey).id();
+ int l3groupId = grpId.id();
+
+ // create the l3unicast group description to wait for the
+ // l2 interface group to be processed
+ GroupBucket l3UnicastGroupBucket = DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
+
+ GroupDescription l3UnicastGroupDescription = new DefaultGroupDescription(deviceId,
+ GroupDescription.Type.INDIRECT,
+ new GroupBuckets(Collections.singletonList(
+ l3UnicastGroupBucket)), l3groupkey,
+ l3groupId, nextObj.appId());
+
+ // store l2groupkey with the groupChainElem for the outer-group that depends on it
+ GroupChainElem gce = new GroupChainElem(l3UnicastGroupDescription, 1, false, deviceId);
+ updatePendingGroups(l2groupkey, gce);
+
+ log.debug("Trying L3-Interface: device:{} gid:{} gkey:{} nextid:{}",
+ deviceId, Integer.toHexString(l3groupId), l3groupkey, nextObj.id());
+
+ groupInfoBuilder.add(new GroupInfo(l2InterfaceGroupDesc,
+ l3UnicastGroupDescription));
+
+ return groupInfoBuilder.build().iterator().next();
+ }
+
private void createL2FloodGroup(NextObjective nextObj, VlanId vlanId,
List<GroupInfo> groupInfos) {
// assemble info for l2 flood group. Since there can be only one flood
@@ -1795,6 +1872,64 @@
}
/**
+ * modifies group with next objective.
+ *
+ * @param nextObjective the NextObjective
+ * @param nextGroup the NextGroup
+ */
+ protected void modifyBucketFromGroup(NextObjective nextObjective, NextGroup nextGroup) {
+ switch (nextObjective.type()) {
+ case SIMPLE:
+ Collection<TrafficTreatment> treatments = nextObjective.next();
+ if (treatments.size() != 1) {
+ log.error("Next Objectives of type Simple should only have a "
+ + "single Traffic Treatment. Next Objective Id:{}",
+ nextObjective.id());
+ fail(nextObjective, ObjectiveError.BADPARAMS);
+ return;
+ }
+ modifySimpleNextObjective(nextObjective, nextGroup);
+ break;
+ default:
+ fail(nextObjective, ObjectiveError.UNKNOWN);
+ log.warn("Unknown next objective type {}", nextObjective.type());
+ }
+ }
+
+ /**
+ * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
+ * a chain of groups. The simple Next Objective passed in by the application
+ * is broken up into a group chain. The following chains can be modified
+ * depending on the parameters in the Next Objective.
+ * 1. L2 Interface group (no chaining)
+ * 2. L3 Unicast group -> L2 Interface group
+ *
+ * @param nextObj the nextObjective of type SIMPLE
+ */
+ private void modifySimpleNextObjective(NextObjective nextObj, NextGroup nextGroup) {
+ TrafficTreatment treatment = nextObj.next().iterator().next();
+ // determine if plain L2 or L3->L2 chain
+ boolean plainL2 = true;
+ for (Instruction ins : treatment.allInstructions()) {
+ if (ins.type() == Instruction.Type.L2MODIFICATION) {
+ L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
+ if (l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_DST ||
+ l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_SRC ||
+ l2ins.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
+ plainL2 = false;
+ }
+ }
+ }
+ if (plainL2) {
+ modifyBucketInL2Group(nextObj, nextGroup);
+ } else {
+ modifyBucketInL3Group(nextObj, nextGroup);
+ }
+ return;
+ }
+
+
+ /**
* Modify buckets in the L2 interface group.
*
* @param nextObjective a next objective that contains information for the
@@ -1802,13 +1937,8 @@
* @param next the representation of the existing group-chains for this next
* objective, from which the innermost group buckets to remove are determined
*/
- protected void modifyBucketFromGroup(NextObjective nextObjective, NextGroup next) {
- if (nextObjective.type() != NextObjective.Type.SIMPLE) {
- log.warn("ModifyBucketFromGroup cannot be applied to nextType:{} in dev:{} for next:{}",
- nextObjective.type(), deviceId, nextObjective.id());
- fail(nextObjective, ObjectiveError.UNSUPPORTED);
- return;
- }
+
+ protected void modifyBucketInL2Group(NextObjective nextObjective, NextGroup next) {
VlanId assignedVlan = readVlanFromSelector(nextObjective.meta());
if (assignedVlan == null) {
@@ -1842,8 +1972,54 @@
new OfdpaNextGroup(modifiedGroupKeys,
nextObjective));
}
+
}
+ protected void modifyBucketInL3Group(NextObjective nextObjective, NextGroup next) {
+
+ //get l3 group
+ GroupInfo groupInfo = prepareL3UnicastGroup(nextObjective, next);
+ if (groupInfo == null) {
+ log.warn("Null groupInfo retrieved for next obj. Abort.");
+ fail(nextObjective, ObjectiveError.BADPARAMS);
+ return;
+ }
+
+ GroupDescription l3UnicastGroupDesc = groupInfo.nextGroupDesc();
+
+ // Replace group bucket for L3 UC interface group
+ groupService.setBucketsForGroup(deviceId, l3UnicastGroupDesc.appCookie(),
+ l3UnicastGroupDesc.buckets(), l3UnicastGroupDesc.appCookie(),
+ l3UnicastGroupDesc.appId());
+
+ // create object for local and distributed storage
+ Deque<GroupKey> gkeyChain = new ArrayDeque<>();
+ gkeyChain.addFirst(groupInfo.innerMostGroupDesc().appCookie());
+ gkeyChain.addFirst(groupInfo.nextGroupDesc().appCookie());
+ List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
+ allGroupKeys.add(gkeyChain);
+ OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObjective);
+ // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
+ updatePendingNextObjective(groupInfo.nextGroupDesc().appCookie(), ofdpaGrp);
+
+ // update store - synchronize access as there may be multiple threads
+ // trying to update bucket from the same group, each with its own
+ // potentially stale copy of allActiveKeys
+ synchronized (flowObjectiveStore) {
+
+ List<Deque<GroupKey>> modifiedGroupKeys = Lists.newArrayList();
+ ArrayDeque<GroupKey> top = new ArrayDeque<>();
+ top.add(l3UnicastGroupDesc.appCookie());
+ top.add(groupInfo.innerMostGroupDesc().appCookie()); //l2 group key
+ modifiedGroupKeys.add(top);
+
+ flowObjectiveStore.putNextGroup(nextObjective.id(),
+ new OfdpaNextGroup(modifiedGroupKeys,
+ nextObjective));
+ }
+ }
+
+
/**
* Checks existing buckets in {@link NextGroup} to verify if they match
* the buckets in the given {@link NextObjective}. Adds or removes buckets
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
index eb86f11..851b24b 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
@@ -425,8 +425,8 @@
nextObjective.id(), deviceId);
return;
}
- log.debug("Processing NextObjective id {} in dev {} - modify bucket",
- nextObjective.id(), deviceId);
+ log.debug("Processing NextObjective id {} in dev {} group {} - modify bucket",
+ nextObjective.id(), deviceId, nextGroup);
groupHandler.modifyBucketFromGroup(nextObjective, nextGroup);
break;
case VERIFY:
diff --git a/tools/build/conf/src/main/resources/onos/suppressions.xml b/tools/build/conf/src/main/resources/onos/suppressions.xml
index fcb8a06..d1c613b 100644
--- a/tools/build/conf/src/main/resources/onos/suppressions.xml
+++ b/tools/build/conf/src/main/resources/onos/suppressions.xml
@@ -30,6 +30,7 @@
<suppress files="org.onlab.packet.*" checks="AbbreviationAsWordInName" />
<suppress files="org.onlab.jdvue.*" checks="AbbreviationAsWordInName" />
<suppress files="org.onosproject.driver.pipeline.*" checks="AbbreviationAsWordInName" />
+ <suppress files="org.onosproject.driver.pipeline.ofdpa.Ofdpa2GroupHandler.java" checks="FileLength" />
<suppress files="org.onosproject.segmentrouting.*" checks="AbbreviationAsWordInName" />
<!-- These files carry AT&T copyrights -->
diff --git a/tools/package/runtime/bin/onos-diagnostics b/tools/package/runtime/bin/onos-diagnostics
index 4120b8f..9a085ee 100755
--- a/tools/package/runtime/bin/onos-diagnostics
+++ b/tools/package/runtime/bin/onos-diagnostics
@@ -109,6 +109,7 @@
"sr-next-vlan"
"sr-next-pw"
"sr-next-xconnect"
+ "sr-next-mac-vlan"
"dhcp-relay"
"mcast-host-routes"