diff options
Diffstat (limited to 'framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java')
-rw-r--r-- | framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java | 190 |
1 files changed, 162 insertions, 28 deletions
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java index eb2cf569..9d60b279 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java @@ -22,11 +22,17 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onlab.packet.Ethernet; +import org.onlab.packet.VlanId; import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; import org.onlab.util.KryoNamespace; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.event.Event; +import org.onosproject.net.ConnectPoint; import org.onosproject.net.config.ConfigFactory; import org.onosproject.net.config.NetworkConfigEvent; import org.onosproject.net.config.NetworkConfigRegistry; @@ -45,7 +51,6 @@ import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; import org.onosproject.net.flowobjective.FlowObjectiveService; -import org.onosproject.net.group.GroupKey; import org.onosproject.net.host.HostService; import org.onosproject.net.intent.IntentService; import org.onosproject.net.link.LinkEvent; @@ -56,6 +61,7 @@ import org.onosproject.net.packet.PacketContext; import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; import org.onosproject.net.topology.TopologyService; +import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey; import org.onosproject.store.service.EventuallyConsistentMap; import org.onosproject.store.service.EventuallyConsistentMapBuilder; import org.onosproject.store.service.StorageService; @@ -64,9 +70,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; @@ -133,8 +141,13 @@ public class SegmentRoutingManager implements SegmentRoutingService { // Per device next objective ID store with (device id + neighbor set) as key private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null; + private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null; private EventuallyConsistentMap<String, Tunnel> tunnelStore = null; private EventuallyConsistentMap<String, Policy> policyStore = null; + // Per device, per-subnet assigned-vlans store, with (device id + subnet + // IPv4 prefix) as key + private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId> + subnetVidStore = null; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected StorageService storageService; @@ -163,6 +176,9 @@ public class SegmentRoutingManager implements SegmentRoutingService { private KryoNamespace.Builder kryoBuilder = null; + private static final short ASSIGNED_VLAN_START = 4093; + public static final short ASSIGNED_VLAN_NO_SUBNET = 4094; + @Activate protected void activate() { appId = coreService @@ -170,6 +186,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { kryoBuilder = new KryoNamespace.Builder() .register(NeighborSetNextObjectiveStoreKey.class, + SubnetNextObjectiveStoreKey.class, + SubnetAssignedVidStoreKey.class, NeighborSet.class, DeviceId.class, URI.class, @@ -180,7 +198,12 @@ public class SegmentRoutingManager implements SegmentRoutingService { DefaultTunnel.class, Policy.class, TunnelPolicy.class, - Policy.Type.class + Policy.Type.class, + VlanId.class, + Ip4Address.class, + Ip4Prefix.class, + IpAddress.Version.class, + ConnectPoint.class ); log.debug("Creating EC map nsnextobjectivestore"); @@ -194,6 +217,16 @@ public class SegmentRoutingManager implements SegmentRoutingService { .build(); log.trace("Current size {}", nsNextObjStore.size()); + log.debug("Creating EC map subnetnextobjectivestore"); + EventuallyConsistentMapBuilder<SubnetNextObjectiveStoreKey, Integer> + subnetNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder(); + + subnetNextObjStore = subnetNextObjMapBuilder + .withName("subnetnextobjectivestore") + .withSerializer(kryoBuilder) + .withTimestampProvider((k, v) -> new WallClockTimestamp()) + .build(); + EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder = storageService.eventuallyConsistentMapBuilder(); @@ -212,6 +245,15 @@ public class SegmentRoutingManager implements SegmentRoutingService { .withTimestampProvider((k, v) -> new WallClockTimestamp()) .build(); + EventuallyConsistentMapBuilder<SubnetAssignedVidStoreKey, VlanId> + subnetVidStoreMapBuilder = storageService.eventuallyConsistentMapBuilder(); + + subnetVidStore = subnetVidStoreMapBuilder + .withName("subnetvidstore") + .withSerializer(kryoBuilder) + .withTimestampProvider((k, v) -> new WallClockTimestamp()) + .build(); + cfgService.addListener(cfgListener); cfgService.registerConfigFactory(cfgFactory); @@ -296,23 +338,72 @@ public class SegmentRoutingManager implements SegmentRoutingService { } /** - * Returns the GroupKey object for the device and the NeighborSet given. - * XXX is this called + * Returns the vlan-id assigned to the subnet configured for a device. + * If no vlan-id has been assigned, a new one is assigned out of a pool of ids, + * if and only if this controller instance is the master for the device. + * <p> + * USAGE: The assigned vlans are meant to be applied to untagged packets on those + * switches/pipelines that need this functionality. These vids are meant + * to be used internally within a switch, and thus need to be unique only + * on a switch level. Note that packets never go out on the wire with these + * vlans. Currently, vlan ids are assigned from value 4093 down. + * Vlan id 4094 expected to be used for all ports that are not assigned subnets. + * Vlan id 4095 is reserved and unused. Only a single vlan id is assigned + * per subnet. + * XXX This method should avoid any vlans configured on the ports, but + * currently the app works only on untagged packets and as a result + * ignores any vlan configuration. * - * @param ns NeightborSet object for the GroupKey - * @return GroupKey object for the NeighborSet + * @param deviceId switch dpid + * @param subnet IPv4 prefix for which assigned vlan is desired + * @return VlanId assigned for the subnet on the device, or + * null if no vlan assignment was found and this instance is not + * the master for the device. */ - public GroupKey getGroupKey(NeighborSet ns) { - for (DefaultGroupHandler groupHandler : groupHandlerMap.values()) { - return groupHandler.getGroupKey(ns); + public VlanId getSubnetAssignedVlanId(DeviceId deviceId, Ip4Prefix subnet) { + VlanId assignedVid = subnetVidStore.get(new SubnetAssignedVidStoreKey( + deviceId, subnet)); + if (assignedVid != null) { + log.debug("Query for subnet:{} on device:{} returned assigned-vlan " + + "{}", subnet, deviceId, assignedVid); + return assignedVid; + } + //check mastership for the right to assign a vlan + if (!mastershipService.isLocalMaster(deviceId)) { + log.warn("This controller instance is not the master for device {}. " + + "Cannot assign vlan-id for subnet {}", deviceId, subnet); + return null; + } + // vlan assignment is expensive but done only once + Set<Ip4Prefix> configuredSubnets = deviceConfiguration.getSubnets(deviceId); + Set<Short> assignedVlans = new HashSet<>(); + Set<Ip4Prefix> unassignedSubnets = new HashSet<>(); + for (Ip4Prefix sub : configuredSubnets) { + VlanId v = subnetVidStore.get(new SubnetAssignedVidStoreKey(deviceId, + sub)); + if (v != null) { + assignedVlans.add(v.toShort()); + } else { + unassignedSubnets.add(sub); + } + } + short nextAssignedVlan = ASSIGNED_VLAN_START; + if (!assignedVlans.isEmpty()) { + nextAssignedVlan = (short) (Collections.min(assignedVlans) - 1); + } + for (Ip4Prefix unsub : unassignedSubnets) { + subnetVidStore.put(new SubnetAssignedVidStoreKey(deviceId, unsub), + VlanId.vlanId(nextAssignedVlan--)); + log.info("Assigned vlan: {} to subnet: {} on device: {}", + nextAssignedVlan + 1, unsub, deviceId); } - return null; + return subnetVidStore.get(new SubnetAssignedVidStoreKey(deviceId, subnet)); } /** - * Returns the next objective ID for the NeighborSet given. If the nextObjectiveID does not exist, - * a new one is created and returned. + * Returns the next objective ID for the given NeighborSet. + * If the nextObjectiveID does not exist, a new one is created and returned. * * @param deviceId Device ID * @param ns NegighborSet @@ -329,6 +420,25 @@ public class SegmentRoutingManager implements SegmentRoutingService { } } + /** + * Returns the next objective ID for the Subnet given. If the nextObjectiveID does not exist, + * a new one is created and returned. + * + * @param deviceId Device ID + * @param prefix Subnet + * @return next objective ID + */ + public int getSubnetNextObjectiveId(DeviceId deviceId, IpPrefix prefix) { + if (groupHandlerMap.get(deviceId) != null) { + log.trace("getSubnetNextObjectiveId query in device {}", deviceId); + return groupHandlerMap + .get(deviceId).getSubnetNextObjectiveId(prefix); + } else { + log.warn("getSubnetNextObjectiveId query in device {} not found", deviceId); + return -1; + } + } + private class InternalPacketProcessor implements PacketProcessor { @Override public void process(PacketContext context) { @@ -423,6 +533,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED || event.type() == DeviceEvent.Type.DEVICE_UPDATED) { if (deviceService.isAvailable(((Device) event.subject()).id())) { + log.info("Processing device event {} for available device {}", + event.type(), ((Device) event.subject()).id()); processDeviceAdded((Device) event.subject()); } } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) { @@ -484,20 +596,31 @@ public class SegmentRoutingManager implements SegmentRoutingService { private void processDeviceAdded(Device device) { log.debug("A new device with ID {} was added", device.id()); - //Irrespective whether the local is a MASTER or not for this device, - //create group handler instance and push default TTP flow rules. - //Because in a multi-instance setup, instances can initiate - //groups for any devices. Also the default TTP rules are needed - //to be pushed before inserting any IP table entries for any device - DefaultGroupHandler dgh = DefaultGroupHandler. + // Irrespective of whether the local is a MASTER or not for this device, + // we need to create a SR-group-handler instance. This is because in a + // multi-instance setup, any instance can initiate forwarding/next-objectives + // for any switch (even if this instance is a SLAVE or not even connected + // to the switch). To handle this, a default-group-handler instance is necessary + // per switch. + DefaultGroupHandler groupHandler = DefaultGroupHandler. createGroupHandler(device.id(), appId, deviceConfiguration, linkService, flowObjectiveService, - nsNextObjStore); - groupHandlerMap.put(device.id(), dgh); - defaultRoutingHandler.populateTtpRules(device.id()); + nsNextObjStore, + subnetNextObjStore); + groupHandlerMap.put(device.id(), groupHandler); + + // Also, in some cases, drivers may need extra + // information to process rules (eg. Router IP/MAC); and so, we send + // port addressing rules to the driver as well irrespective of whether + // this instance is the master or not. + defaultRoutingHandler.populatePortAddressingRules(device.id()); + + if (mastershipService.isLocalMaster(device.id())) { + groupHandler.createGroupsFromSubnetConfig(); + } } private void processPortRemoved(Device device, Port port) { @@ -531,18 +654,29 @@ public class SegmentRoutingManager implements SegmentRoutingService { tunnelHandler, policyStore); for (Device device : deviceService.getDevices()) { - //Irrespective whether the local is a MASTER or not for this device, - //create group handler instance and push default TTP flow rules. - //Because in a multi-instance setup, instances can initiate - //groups for any devices. Also the default TTP rules are needed - //to be pushed before inserting any IP table entries for any device + // Irrespective of whether the local is a MASTER or not for this device, + // we need to create a SR-group-handler instance. This is because in a + // multi-instance setup, any instance can initiate forwarding/next-objectives + // for any switch (even if this instance is a SLAVE or not even connected + // to the switch). To handle this, a default-group-handler instance is necessary + // per switch. DefaultGroupHandler groupHandler = DefaultGroupHandler .createGroupHandler(device.id(), appId, deviceConfiguration, linkService, flowObjectiveService, - nsNextObjStore); + nsNextObjStore, + subnetNextObjStore); groupHandlerMap.put(device.id(), groupHandler); - defaultRoutingHandler.populateTtpRules(device.id()); + + // Also, in some cases, drivers may need extra + // information to process rules (eg. Router IP/MAC); and so, we send + // port addressing rules to the driver as well, irrespective of whether + // this instance is the master or not. + defaultRoutingHandler.populatePortAddressingRules(device.id()); + + if (mastershipService.isLocalMaster(device.id())) { + groupHandler.createGroupsFromSubnetConfig(); + } } defaultRoutingHandler.startPopulationProcess(); |