From ef0761c2118a363c199433e106094de8ac9f6c43 Mon Sep 17 00:00:00 2001 From: Ruchi Sahota Date: Mon, 28 Jan 2019 01:08:18 +0000 Subject: [PATCH] Route reprogamming using group substitution during next hop movement Change-Id: Idf8362dac522722ca67747e245bfd836e6ee6292 --- .../segmentrouting/DefaultRoutingHandler.java | 10 +- .../segmentrouting/HostHandler.java | 8 +- .../segmentrouting/RouteHandler.java | 43 +++- .../segmentrouting/RoutingRulePopulator.java | 44 ++-- .../segmentrouting/SegmentRoutingManager.java | 125 +++++++++++- .../segmentrouting/SegmentRoutingService.java | 8 + .../cli/NextMacVlanCommand.java | 59 ++++++ .../grouphandler/DefaultGroupHandler.java | 182 +++++++++++++++++ .../DestinationSetNextObjectiveStoreKey.java | 7 +- .../MacVlanNextObjectiveStoreKey.java | 102 ++++++++++ .../storekey/PortNextObjectiveStoreKey.java | 11 +- .../storekey/VlanNextObjectiveStoreKey.java | 7 +- .../segmentrouting/HostHandlerTest.java | 2 +- .../MockRoutingRulePopulator.java | 6 +- .../segmentrouting/RouteHandlerTest.java | 2 +- .../pipeline/ofdpa/Ofdpa2GroupHandler.java | 192 +++++++++++++++++- .../driver/pipeline/ofdpa/Ofdpa2Pipeline.java | 4 +- .../src/main/resources/onos/suppressions.xml | 1 + tools/package/runtime/bin/onos-diagnostics | 1 + 19 files changed, 762 insertions(+), 52 deletions(-) create mode 100644 apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/cli/NextMacVlanCommand.java create mode 100644 apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/storekey/MacVlanNextObjectiveStoreKey.java 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 a5a90a7587..7dfa632bf1 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 @@ public class DefaultRoutingHandler { * @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 @@ public class DefaultRoutingHandler { * @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 eee990054b..296fbcd41d 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 @@ public class HostHandler { 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 @@ public class HostHandler { 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 40cdf53f13..7dbbbeb580 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.onosproject.routeservice.RouteInfo; 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 @@ public class RouteHandler { 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 @@ public class RouteHandler { 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 @@ public class RouteHandler { 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 @@ public class RouteHandler { log.info("processHostMovedEvent {}", event); MacAddress hostMac = event.subject().mac(); VlanId hostVlanId = event.subject().vlan(); + // map of nextId for prev port in each device + Map nextIdMap = new HashMap<>(); affectedRoutes(hostMac, hostVlanId).forEach(affectedRoute -> { IpPrefix prefix = affectedRoute.prefix(); @@ -245,8 +249,22 @@ public class RouteHandler { Set 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 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 @@ public class RouteHandler { 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 @@ public class RouteHandler { 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 de0eb58c65..b864a08cd4 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 @@ public class RoutingRulePopulator { * @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 @@ public class RoutingRulePopulator { * @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 @@ public class RoutingRulePopulator { * @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 @@ public class RoutingRulePopulator { 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 @@ public class RoutingRulePopulator { )); }); 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 @@ public class RoutingRulePopulator { 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 6c72a06393..e78b1521e4 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.IPv6; 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.DestinationSetNextObjectiveStoreK 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 @@ public class SegmentRoutingManager implements SegmentRoutingService { 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 portNextObjStore = null; @@ -358,6 +360,13 @@ public class SegmentRoutingManager implements SegmentRoutingService { private EventuallyConsistentMap 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 + macVlanNextObjStore = null; + private EventuallyConsistentMap tunnelStore = null; private EventuallyConsistentMap policyStore = null; @@ -471,6 +480,15 @@ public class SegmentRoutingManager implements SegmentRoutingService { .withTimestampProvider((k, v) -> new WallClockTimestamp()) .build(); + log.debug("Creating EC map macvlannextobjectivestore"); + EventuallyConsistentMapBuilder + macVlanNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder(); + macVlanNextObjStore = macVlanNextObjMapBuilder + .withName("macvlannextobjectivestore") + .withSerializer(createSerializer()) + .withTimestampProvider((k, v) -> new WallClockTimestamp()) + .build(); + log.debug("Creating EC map subnetnextobjectivestore"); EventuallyConsistentMapBuilder portNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder(); @@ -602,7 +620,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { L2TunnelPolicy.class, DefaultL2Tunnel.class, DefaultL2TunnelPolicy.class, - DummyVlanIdStoreKey.class + DummyVlanIdStoreKey.class, + MacVlanNextObjectiveStoreKey.class ); } @@ -650,6 +669,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { dsNextObjStore.destroy(); vlanNextObjStore.destroy(); + macVlanNextObjStore.destroy(); portNextObjStore.destroy(); dummyVlanIdStore.destroy(); tunnelStore.destroy(); @@ -946,6 +966,15 @@ public class SegmentRoutingManager implements SegmentRoutingService { } } + @Override + public ImmutableMap getMacVlanNextObjStore() { + if (macVlanNextObjStore != null) { + return ImmutableMap.copyOf(macVlanNextObjStore.entrySet()); + } else { + return ImmutableMap.of(); + } + } + @Override public ImmutableMap getPortNextObjStore() { if (portNextObjStore != null) { @@ -989,6 +1018,13 @@ public class SegmentRoutingManager implements SegmentRoutingService { } }); } + 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) { @@ -1108,9 +1144,19 @@ public class SegmentRoutingManager implements SegmentRoutingService { return vlanNextObjStore; } + /** + * 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 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. */ @@ -1253,6 +1299,76 @@ public class SegmentRoutingManager implements SegmentRoutingService { } } + /** + * 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. * @@ -1584,6 +1700,9 @@ public class SegmentRoutingManager implements SegmentRoutingService { 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 1b28998a68..74db140f3b 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 com.google.common.collect.ImmutableMap; 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; @@ -225,6 +226,13 @@ public interface SegmentRoutingService { */ ImmutableMap getVlanNextObjStore(); + /** + * Returns the Mac Vlan next objective store. + * + * @return current contents of the macVlanNextObjStore + */ + ImmutableMap getMacVlanNextObjStore(); + /** * Returns the port next objective store. * 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 0000000000..e31e6931ed --- /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 macVlanNextObjStore) { + ArrayList 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 02f2b1974f..669e136798 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.DefaultRoutingHandler; 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 @@ public class DefaultGroupHandler { 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 @@ public class DefaultGroupHandler { // distributed store for (device+subnet-ip-prefix) mapped to next-id protected EventuallyConsistentMap vlanNextObjStore = null; + // distributed store for (device+mac+vlan+treatment) mapped to next-id + protected EventuallyConsistentMap + macVlanNextObjStore = null; // distributed store for (device+port+treatment) mapped to next-id protected EventuallyConsistentMap portNextObjStore = null; @@ -145,6 +152,7 @@ public class DefaultGroupHandler { 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, @@ -815,6 +823,52 @@ public class DefaultGroupHandler { return (nextId != null) ? nextId : -1; } + /** + * 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 @@ -1176,6 +1230,45 @@ public class DefaultGroupHandler { .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId)); } + /** + * 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 @@ public class DefaultGroupHandler { portNextObjStore.remove(key); } } + /** * Removes groups for the next objective ID given. * @@ -1401,6 +1495,94 @@ public class DefaultGroupHandler { flowObjectiveService.next(deviceId, nextObjBuilder.modify(context)); } + /** + * 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. * 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 9b6e62112d..0baa769b52 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 java.util.Objects; 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 @@ public class DestinationSetNextObjectiveStoreKey { @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 0000000000..4a1ab78549 --- /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 9aa41dbb9e..1269bce762 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 org.onosproject.net.flow.TrafficTreatment; import java.util.Objects; +import static com.google.common.base.MoreObjects.toStringHelper; + /** * Key of Device/Port to NextObjective store. * @@ -111,8 +113,11 @@ public class PortNextObjectiveStoreKey { @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 5d9f2fd15f..55a95cd07f 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 org.onosproject.net.DeviceId; import java.util.Objects; +import static com.google.common.base.MoreObjects.toStringHelper; + /** * Key of VLAN to NextObjective store. */ @@ -79,6 +81,9 @@ public class VlanNextObjectiveStoreKey { @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 f72c264f33..d0ca6cd869 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 @@ public class HostHandlerTest { // 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 4eab7c04cf..77eeed2b59 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 @@ public class MockRoutingRulePopulator extends RoutingRulePopulator { @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 @@ public class MockRoutingRulePopulator extends RoutingRulePopulator { @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 494b9eccf7..ade5d0c1f5 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 @@ public class RouteHandlerTest { 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 7a51f17d40..52d5382d62 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 @@ public class Ofdpa2GroupHandler { } } 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 @@ public class Ofdpa2GroupHandler { 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 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 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> 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 groupInfos) { // assemble info for l2 flood group. Since there can be only one flood @@ -1794,6 +1871,64 @@ public class Ofdpa2GroupHandler { flowObjectiveStore.removeNextGroup(nextObjective.id()); } + /** + * 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 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. * @@ -1802,13 +1937,8 @@ public class Ofdpa2GroupHandler { * @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 @@ public class Ofdpa2GroupHandler { 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 gkeyChain = new ArrayDeque<>(); + gkeyChain.addFirst(groupInfo.innerMostGroupDesc().appCookie()); + gkeyChain.addFirst(groupInfo.nextGroupDesc().appCookie()); + List> 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> modifiedGroupKeys = Lists.newArrayList(); + ArrayDeque 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 eb86f118d8..851b24bfa0 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 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline 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 fcb8a06000..d1c613b64a 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 @@ + diff --git a/tools/package/runtime/bin/onos-diagnostics b/tools/package/runtime/bin/onos-diagnostics index 4120b8f51b..9a085eef32 100755 --- a/tools/package/runtime/bin/onos-diagnostics +++ b/tools/package/runtime/bin/onos-diagnostics @@ -109,6 +109,7 @@ CLI_COMMANDS=( "sr-next-vlan" "sr-next-pw" "sr-next-xconnect" + "sr-next-mac-vlan" "dhcp-relay" "mcast-host-routes"