Charles Chan
Committed by Gerrit Code Review

CORD-349 Support VLAN cross-connect traffic

Change related to this topic:
- Support VLAN cross-connect traffic
    Utilize ports subjectClass to achieve. For non-xConnect port, set interface VLAN to -1
- Remove VLAN checking since we have multiple VLANs per port
- Hash the L2 interface group key generation to include VLAN as well
- Update the network-cfg.json sample

Other refactoring changes:
- Read next objective stores from srManager directly
- Use constant for flow priority
- CORD-267 Javadoc fix

Change-Id: I4ca8c2d9c8b3633a4a0101c5070d19343f7e5b90
Showing 31 changed files with 803 additions and 298 deletions
......@@ -41,6 +41,10 @@ import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handler of ARP packets that responses or forwards ARP packets that
* are sent to the controller.
*/
public class ArpHandler {
private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
......
......@@ -37,6 +37,10 @@ import java.util.concurrent.locks.ReentrantLock;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Default routing handler that is responsible for route computing and
* routing rule population.
*/
public class DefaultRoutingHandler {
private static Logger log = LoggerFactory
......@@ -512,6 +516,7 @@ public class DefaultRoutingHandler {
*/
public void populatePortAddressingRules(DeviceId deviceId) {
rulePopulator.populateRouterMacVlanFilters(deviceId);
rulePopulator.populateXConnectVlanFilters(deviceId);
rulePopulator.populateRouterIpPunts(deviceId);
}
......
......@@ -20,7 +20,7 @@ import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Tunnel class.
* Default Tunnel class.
*/
public class DefaultTunnel implements Tunnel {
......
......@@ -38,6 +38,10 @@ import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handler of ICMP packets that responses or forwards ICMP packets that
* are sent to the controller.
*/
public class IcmpHandler {
private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
......
......@@ -37,6 +37,10 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handler of IP packets that forwards IP packets that are sent to the controller,
* except the ICMP packets which are processed by @link{IcmpHandler}.
*/
public class IpHandler {
private static Logger log = LoggerFactory.getLogger(IpHandler.class);
......
......@@ -24,16 +24,24 @@ public interface Policy {
* Enums for policy type.
*/
enum Type {
// Tunnel flow policy type
/**
* Tunnel flow policy type.
*/
TUNNEL_FLOW,
// Load balancing policy type
/**
* Load balancing policy type.
*/
LOADBALANCE,
// policy to avoid specific routers or links
/**
* policy to avoid specific routers or links.
*/
AVOID,
// Access Control policy type
/**
* Access Control policy type.
*/
DENY
}
......
......@@ -48,18 +48,43 @@ public class PolicyHandler {
private FlowObjectiveService flowObjectiveService;
private TunnelHandler tunnelHandler;
private final EventuallyConsistentMap<String, Policy> policyStore;
/**
* Result of policy creation.
*/
public enum Result {
/**
* Success.
*/
SUCCESS,
/**
* The same policy exists already.
*/
POLICY_EXISTS,
/**
* The policy ID exists already.
*/
ID_EXISTS,
/**
* Cannot find associated tunnel.
*/
TUNNEL_NOT_FOUND,
/**
* Policy was not found.
*/
POLICY_NOT_FOUND,
/**
* Policy type {} is not supported yet.
*/
UNSUPPORTED_TYPE
}
/**
* Creates a reference.
* Constructs policy handler.
*
* @param appId segment routing application ID
* @param deviceConfiguration DeviceConfiguration reference
......
......@@ -23,6 +23,7 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.MplsLabel;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceConfiguration;
import org.onosproject.segmentrouting.grouphandler.NeighborSet;
......@@ -49,11 +50,15 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Populator of segment routing flow rules.
*/
public class RoutingRulePopulator {
private static final Logger log = LoggerFactory
.getLogger(RoutingRulePopulator.class);
......@@ -63,6 +68,10 @@ public class RoutingRulePopulator {
private DeviceConfiguration config;
private static final int HIGHEST_PRIORITY = 0xffff;
//
private static final int XCONNECT_PRIORITY = 1000;
private static final int DEFAULT_PRIORITY = 100;
private static final int FLOOD_PRIORITY = 5;
private static final long OFPP_MAX = 0xffffff00L;
......@@ -120,6 +129,14 @@ public class RoutingRulePopulator {
rulePopulationCounter.incrementAndGet();
}
/**
* Removes IP rules for host when the host is gone.
*
* @param deviceId device ID of the device that host attaches to
* @param hostIp IP address of the host
* @param hostMac MAC address of the host
* @param outPort port that host attaches to
*/
public void revokeIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
MacAddress hostMac, PortNumber outPort) {
log.debug("Revoke IP table entry for host {} at {}:{}",
......@@ -175,7 +192,7 @@ public class RoutingRulePopulator {
.withSelector(selector)
.nextStep(portNextObjId)
.fromApp(srManager.appId).makePermanent()
.withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
.withPriority(DEFAULT_PRIORITY).withFlag(ForwardingObjective.Flag.SPECIFIC);
}
/**
......@@ -369,7 +386,7 @@ public class RoutingRulePopulator {
for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
.makePermanent()).withSelector(selector)
.withPriority(100))
.withPriority(DEFAULT_PRIORITY))
.withFlag(ForwardingObjective.Flag.SPECIFIC);
srManager.flowObjectiveService.
forward(deviceId,
......@@ -464,7 +481,8 @@ public class RoutingRulePopulator {
FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
fob.withKey(Criteria.matchInPort(port.number()))
.addCondition(Criteria.matchEthDst(deviceMac))
.addCondition(Criteria.matchVlanId(VlanId.NONE));
.addCondition(Criteria.matchVlanId(VlanId.NONE))
.withPriority(DEFAULT_PRIORITY);
// vlan assignment is valid only if this instance is master
if (srManager.mastershipService.isLocalMaster(deviceId)) {
TrafficTreatment tt = DefaultTrafficTreatment.builder()
......@@ -558,7 +576,7 @@ public class RoutingRulePopulator {
fob.withFlag(Flag.SPECIFIC)
.withSelector(sbuilder.build())
.nextStep(nextId)
.withPriority(5)
.withPriority(FLOOD_PRIORITY)
.fromApp(srManager.appId)
.makePermanent();
......@@ -572,6 +590,86 @@ public class RoutingRulePopulator {
});
}
/**
* Creates a filtering objective to permit VLAN cross-connect traffic.
*
* @param deviceId the DPID of the switch
*/
public void populateXConnectVlanFilters(DeviceId deviceId) {
Map<VlanId, List<ConnectPoint>> xConnectsForDevice =
config.getXConnects();
xConnectsForDevice.forEach((vlanId, connectPoints) -> {
// Only proceed the xConnect for given device
for (ConnectPoint connectPoint : connectPoints) {
if (!connectPoint.deviceId().equals(deviceId)) {
return;
}
}
connectPoints.forEach(connectPoint -> {
FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
fob.withKey(Criteria.matchInPort(connectPoint.port()))
.addCondition(Criteria.matchVlanId(vlanId))
.addCondition(Criteria.matchEthDst(MacAddress.NONE))
.withPriority(XCONNECT_PRIORITY);
fob.permit().fromApp(srManager.appId);
srManager.flowObjectiveService
.filter(deviceId, fob.add(new SRObjectiveContext(deviceId,
SRObjectiveContext.ObjectiveType.FILTER)));
});
});
}
/**
* Populates a forwarding objective that points the VLAN cross-connect
* packets to a broadcast group.
*
* @param deviceId switch ID to set the rules
*/
public void populateXConnectBroadcastRule(DeviceId deviceId) {
Map<VlanId, List<ConnectPoint>> xConnects =
config.getXConnects();
xConnects.forEach((vlanId, connectPoints) -> {
// Only proceed the xConnect for given device
for (ConnectPoint connectPoint : connectPoints) {
if (!connectPoint.deviceId().equals(deviceId)) {
return;
}
}
int nextId = srManager.getXConnectNextObjectiveId(deviceId, vlanId);
if (nextId < 0) {
log.error("Cannot install cross-connect broadcast rule in dev:{} " +
"due to missing nextId:{}", deviceId, nextId);
return;
}
/*
* Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
* as the VLAN cross-connect broadcast rules
*/
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
sbuilder.matchVlanId(vlanId);
sbuilder.matchEthDst(MacAddress.NONE);
ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
fob.withFlag(Flag.SPECIFIC)
.withSelector(sbuilder.build())
.nextStep(nextId)
.withPriority(DEFAULT_PRIORITY)
.fromApp(srManager.appId)
.makePermanent();
srManager.flowObjectiveService.forward(
deviceId,
fob.add(new SRObjectiveContext(
deviceId,
SRObjectiveContext.ObjectiveType.FORWARDING)
)
);
});
}
private static class SRObjectiveContext implements ObjectiveContext {
enum ObjectiveType {
......
......@@ -69,7 +69,6 @@ import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.link.LinkListener;
import org.onosproject.net.link.LinkService;
......@@ -77,8 +76,8 @@ import org.onosproject.net.packet.InboundPacket;
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.segmentrouting.grouphandler.XConnectNextObjectiveStoreKey;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.store.service.EventuallyConsistentMapBuilder;
import org.onosproject.store.service.StorageService;
......@@ -102,6 +101,9 @@ import java.util.concurrent.TimeUnit;
@Service
@Component(immediate = true)
/**
* Segment routing manager.
*/
public class SegmentRoutingManager implements SegmentRoutingService {
private static Logger log = LoggerFactory
......@@ -111,15 +113,9 @@ public class SegmentRoutingManager implements SegmentRoutingService {
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
......@@ -157,16 +153,27 @@ public class SegmentRoutingManager implements SegmentRoutingService {
@SuppressWarnings("rawtypes")
private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<Event>();
private Map<DeviceId, DefaultGroupHandler> groupHandlerMap =
new ConcurrentHashMap<DeviceId, DefaultGroupHandler>();
// Per device next objective ID store with (device id + neighbor set) as key
private EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
new ConcurrentHashMap<>();
/**
* Per device next objective ID store with (device id + neighbor set) as key.
*/
public EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
nsNextObjStore = null;
// Per device next objective ID store with (device id + subnet) as key
private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
/**
* Per device next objective ID store with (device id + subnet) as key.
*/
public EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
subnetNextObjStore = null;
// Per device next objective ID store with (device id + port) as key
private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
/**
* Per device next objective ID store with (device id + port) as key.
*/
public EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
portNextObjStore = null;
/**
* Per cross-connect objective ID store with VLAN ID as key.
*/
public EventuallyConsistentMap<XConnectNextObjectiveStoreKey, Integer>
xConnectNextObjStore = null;
// Per device, per-subnet assigned-vlans store, with (device id + subnet
// IPv4 prefix) as key
private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId>
......@@ -204,7 +211,13 @@ public class SegmentRoutingManager implements SegmentRoutingService {
private KryoNamespace.Builder kryoBuilder = null;
/**
* The starting value of per-subnet VLAN ID assignment.
*/
private static final short ASSIGNED_VLAN_START = 4093;
/**
* The default VLAN ID assigned to the interfaces without subnet config.
*/
public static final short ASSIGNED_VLAN_NO_SUBNET = 4094;
@Activate
......@@ -262,6 +275,15 @@ public class SegmentRoutingManager implements SegmentRoutingService {
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
log.debug("Creating EC map xconnectnextobjectivestore");
EventuallyConsistentMapBuilder<XConnectNextObjectiveStoreKey, Integer>
xConnectNextObjStoreBuilder = storageService.eventuallyConsistentMapBuilder();
xConnectNextObjStore = xConnectNextObjStoreBuilder
.withName("xconnectnextobjectivestore")
.withSerializer(kryoBuilder)
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
storageService.eventuallyConsistentMapBuilder();
tunnelStore = tunnelMapBuilder
......@@ -394,9 +416,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
* 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 deviceId switch dpid
* @param subnet IPv4 prefix for which assigned vlan is desired
......@@ -404,6 +423,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
* null if no vlan assignment was found and this instance is not
* the master for the device.
*/
// TODO: We should avoid assigning VLAN IDs that are used by VLAN cross-connection.
public VlanId getSubnetAssignedVlanId(DeviceId deviceId, Ip4Prefix subnet) {
VlanId assignedVid = subnetVidStore.get(new SubnetAssignedVidStoreKey(
deviceId, subnet));
......@@ -514,6 +534,25 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
}
/**
* Returns the next objective ID of type broadcast associated with the VLAN
* cross-connection.
*
* @param deviceId Device ID for the cross-connection
* @param vlanId VLAN ID for the cross-connection
* @return next objective ID or -1 if it was not found
*/
public int getXConnectNextObjectiveId(DeviceId deviceId, VlanId vlanId) {
DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
if (ghdlr != null) {
return ghdlr.getXConnectNextObjectiveId(vlanId);
} else {
log.warn("getPortNextObjectiveId query - groupHandler for device {}"
+ " not found", deviceId);
return -1;
}
}
private class InternalPacketProcessor implements PacketProcessor {
@Override
public void process(PacketContext context) {
......@@ -707,9 +746,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
deviceConfiguration,
linkService,
flowObjectiveService,
nsNextObjStore,
subnetNextObjStore,
portNextObjStore,
this);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting processDeviceAdded.");
......@@ -727,6 +763,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(device.id());
groupHandler.createGroupsForXConnect(device.id());
routingRulePopulator.populateXConnectBroadcastRule(device.id());
}
}
......@@ -742,10 +780,18 @@ public class SegmentRoutingManager implements SegmentRoutingService {
private class InternalConfigListener implements NetworkConfigListener {
SegmentRoutingManager segmentRoutingManager;
/**
* Constructs the internal network config listener.
*
* @param srMgr segment routing manager
*/
public InternalConfigListener(SegmentRoutingManager srMgr) {
this.segmentRoutingManager = srMgr;
}
/**
* Reads network config and initializes related data structure accordingly.
*/
public void configureNetwork() {
deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
......@@ -777,9 +823,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
deviceConfiguration,
linkService,
flowObjectiveService,
nsNextObjStore,
subnetNextObjStore,
portNextObjStore,
segmentRoutingManager);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting configureNetwork.");
......@@ -798,6 +841,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
groupHandler.createGroupsFromSubnetConfig();
routingRulePopulator.populateSubnetBroadcastRule(device.id());
groupHandler.createGroupsForXConnect(device.id());
routingRulePopulator.populateXConnectBroadcastRule(device.id());
}
}
......
......@@ -6,13 +6,18 @@ import org.onlab.packet.Ip4Prefix;
import org.onosproject.net.DeviceId;
/**
* Class definition for key used to map per device subnets to assigned Vlan ids.
*
* Key of assigned VLAN ID store.
*/
public class SubnetAssignedVidStoreKey {
private final DeviceId deviceId;
private final Ip4Prefix subnet;
/**
* Constructs the key of per subnet VLAN ID store.
*
* @param deviceId device ID of the VLAN cross-connection
* @param subnet subnet information
*/
public SubnetAssignedVidStoreKey(DeviceId deviceId, Ip4Prefix subnet) {
this.deviceId = deviceId;
this.subnet = subnet;
......
......@@ -43,16 +43,54 @@ public class TunnelHandler {
private Map<DeviceId, DefaultGroupHandler> groupHandlerMap;
private LinkService linkService;
/**
* Result of tunnel creation or removal.
*/
public enum Result {
/**
* Success.
*/
SUCCESS,
/**
* More than one router needs to specified to created a tunnel.
*/
WRONG_PATH,
/**
* The same tunnel exists already.
*/
TUNNEL_EXISTS,
/**
* The same tunnel ID exists already.
*/
ID_EXISTS,
/**
* Tunnel not found.
*/
TUNNEL_NOT_FOUND,
/**
* Cannot remove the tunnel used by a policy.
*/
TUNNEL_IN_USE,
/**
* Failed to create/remove groups for the tunnel.
*/
INTERNAL_ERROR
}
/**
* Constructs tunnel handler.
*
* @param linkService link service
* @param deviceConfiguration device configuration
* @param groupHandlerMap group handler map
* @param tunnelStore tunnel store
*/
public TunnelHandler(LinkService linkService,
DeviceConfiguration deviceConfiguration,
Map<DeviceId, DefaultGroupHandler> groupHandlerMap,
......
......@@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableSet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.incubator.net.config.basics.ConfigException;
import org.onosproject.incubator.net.config.basics.InterfaceConfig;
import org.onosproject.incubator.net.intf.Interface;
......@@ -33,6 +34,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
......@@ -48,8 +50,8 @@ public class DeviceConfiguration implements DeviceProperties {
private static final Logger log = LoggerFactory
.getLogger(DeviceConfiguration.class);
private final List<Integer> allSegmentIds = new ArrayList<>();
private final ConcurrentHashMap<DeviceId, SegmentRouterInfo> deviceConfigMap
= new ConcurrentHashMap<>();
private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>();
private final Map<VlanId, List<ConnectPoint>> xConnects = new ConcurrentHashMap<>();
private class SegmentRouterInfo {
int nodeSid;
......@@ -62,14 +64,14 @@ public class DeviceConfiguration implements DeviceProperties {
Map<Integer, Set<Integer>> adjacencySids;
public SegmentRouterInfo() {
this.gatewayIps = new HashMap<>();
this.subnets = new HashMap<>();
gatewayIps = new HashMap<>();
subnets = new HashMap<>();
}
}
/**
* Constructor. Reads all the configuration for all devices of type
* Segment Router and organizes into various maps for easier access.
* Constructs device configuration for all Segment Router devices,
* organizing the data into various maps for easier access.
*
* @param cfgService config service
*/
......@@ -88,8 +90,8 @@ public class DeviceConfiguration implements DeviceProperties {
info.isEdge = config.isEdgeRouter();
info.adjacencySids = config.adjacencySids();
this.deviceConfigMap.put(info.deviceId, info);
this.allSegmentIds.add(info.nodeSid);
deviceConfigMap.put(info.deviceId, info);
allSegmentIds.add(info.nodeSid);
});
// Read gatewayIps and subnets from port subject.
......@@ -106,17 +108,42 @@ public class DeviceConfiguration implements DeviceProperties {
return;
}
networkInterfaces.forEach(networkInterface -> {
DeviceId dpid = networkInterface.connectPoint().deviceId();
PortNumber port = networkInterface.connectPoint().port();
SegmentRouterInfo info = this.deviceConfigMap.get(dpid);
VlanId vlanId = networkInterface.vlan();
ConnectPoint connectPoint = networkInterface.connectPoint();
DeviceId dpid = connectPoint.deviceId();
PortNumber port = connectPoint.port();
SegmentRouterInfo info = deviceConfigMap.get(dpid);
// skip if there is no corresponding device for this ConenctPoint
if (info != null) {
// Extract subnet information
Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
interfaceAddresses.forEach(interfaceAddress -> {
info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
});
// Extract VLAN cross-connect information
// Do not setup cross-connect if VLAN is NONE
if (vlanId.equals(VlanId.NONE)) {
return;
}
List<ConnectPoint> connectPoints = xConnects.get(vlanId);
if (connectPoints != null) {
if (connectPoints.size() != 1) {
log.warn("Cross-connect should only have two endpoints. Aborting.");
return;
}
if (!connectPoints.get(0).deviceId().equals(connectPoint.deviceId())) {
log.warn("Cross-connect endpoints must be on the same switch. Aborting.");
return;
}
connectPoints.add(connectPoint);
} else {
connectPoints = new LinkedList<>();
connectPoints.add(connectPoint);
xConnects.put(vlanId, connectPoints);
}
}
});
......@@ -235,6 +262,11 @@ public class DeviceConfiguration implements DeviceProperties {
return subnetPortMap;
}
@Override
public Map<VlanId, List<ConnectPoint>> getXConnects() {
return xConnects;
}
/**
* Returns the device identifier or data plane identifier (dpid)
* of a segment router given its segment id.
......
......@@ -21,6 +21,8 @@ import java.util.Map;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
......@@ -93,4 +95,11 @@ public interface DeviceProperties {
* @return a map that contains all subnet-to-ports mapping of given device
*/
Map<Ip4Prefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId);
/**
* Returns the VLAN cross-connect configuration.
*
* @return A map of that maps VLAN ID to a list of cross-connect endpoints
*/
Map<VlanId, List<ConnectPoint>> getXConnects();
}
......
......@@ -35,24 +35,24 @@ import java.util.Set;
* Configuration object for Segment Routing Application.
*/
public class SegmentRoutingConfig extends Config<DeviceId> {
public static final String NAME = "name";
public static final String IP = "routerIp";
public static final String MAC = "routerMac";
public static final String SID = "nodeSid";
public static final String EDGE = "isEdgeRouter";
public static final String ADJSIDS = "adjacencySids";
public static final String ADJSID = "adjSid";
public static final String PORTS = "ports";
private static final String NAME = "name";
private static final String IP = "routerIp";
private static final String MAC = "routerMac";
private static final String SID = "nodeSid";
private static final String EDGE = "isEdgeRouter";
private static final String ADJSIDS = "adjacencySids";
private static final String ADJSID = "adjSid";
private static final String PORTS = "ports";
@Override
public boolean isValid() {
return hasOnlyFields(NAME, IP, MAC, SID, EDGE, ADJSIDS, ADJSID, PORTS) &&
this.name() != null &&
this.routerIp() != null &&
this.routerMac() != null &&
this.nodeSid() != -1 &&
this.isEdgeRouter() != null &&
this.adjacencySids() != null;
name() != null &&
routerIp() != null &&
routerMac() != null &&
nodeSid() != -1 &&
isEdgeRouter() != null &&
adjacencySids() != null;
}
/**
......
......@@ -26,7 +26,6 @@ import org.onosproject.net.flowobjective.FlowObjectiveService;
import org.onosproject.net.link.LinkService;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceProperties;
import org.onosproject.store.service.EventuallyConsistentMap;
/**
* Default ECMP group handler creation module for an edge device.
......@@ -47,22 +46,13 @@ import org.onosproject.store.service.EventuallyConsistentMap;
* 8) what about ecmp no label case
*/
public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
// TODO Access stores through srManager
protected DefaultEdgeGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<
NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore,
SegmentRoutingManager srManager) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
super(deviceId, appId, config, linkService, flowObjService, srManager);
}
@Override
......
......@@ -35,6 +35,7 @@ import org.onlab.packet.MplsLabel;
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
......@@ -48,8 +49,6 @@ import org.onosproject.net.flowobjective.NextObjective;
import org.onosproject.net.flowobjective.Objective;
import org.onosproject.net.flowobjective.ObjectiveContext;
import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.net.group.DefaultGroupKey;
import org.onosproject.net.group.GroupKey;
import org.onosproject.net.link.LinkService;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
......@@ -81,12 +80,14 @@ public class DefaultGroupHandler {
//local store for ports on this device connected to neighbor-device-id
protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
new ConcurrentHashMap<>();
protected EventuallyConsistentMap<
NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null;
protected EventuallyConsistentMap<
SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null;
protected EventuallyConsistentMap<
PortNextObjectiveStoreKey, Integer> portNextObjStore = null;
protected EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
nsNextObjStore = null;
protected EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
subnetNextObjStore = null;
protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
portNextObjStore = null;
protected EventuallyConsistentMap<XConnectNextObjectiveStoreKey, Integer>
xConnectNextObjStore = null;
private SegmentRoutingManager srManager;
protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
......@@ -97,17 +98,10 @@ public class DefaultGroupHandler {
.register(GroupBucketIdentifier.class)
.register(GroupBucketIdentifier.BucketOutputType.class);
// TODO Access stores through srManager
protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore,
SegmentRoutingManager srManager) {
this.deviceId = checkNotNull(deviceId);
this.appId = checkNotNull(appId);
......@@ -123,9 +117,10 @@ public class DefaultGroupHandler {
+ " Skipping value assignment in DefaultGroupHandler");
}
this.flowObjectiveService = flowObjService;
this.nsNextObjStore = nsNextObjStore;
this.subnetNextObjStore = subnetNextObjStore;
this.portNextObjStore = portNextObjStore;
this.nsNextObjStore = srManager.nsNextObjStore;
this.subnetNextObjStore = srManager.subnetNextObjStore;
this.portNextObjStore = srManager.portNextObjStore;
this.xConnectNextObjStore = srManager.xConnectNextObjStore;
this.srManager = srManager;
populateNeighborMaps();
......@@ -141,8 +136,7 @@ public class DefaultGroupHandler {
* @param config interface to retrieve the device properties
* @param linkService link service object
* @param flowObjService flow objective service object
* @param nsNextObjStore NeighborSet next objective store map
* @param subnetNextObjStore subnet next objective store map
* @param srManager segment routing manager
* @throws DeviceConfigNotFoundException if the device configuration is not found
* @return default group handler type
*/
......@@ -152,12 +146,6 @@ public class DefaultGroupHandler {
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore,
SegmentRoutingManager srManager)
throws DeviceConfigNotFoundException {
// handle possible exception in the caller
......@@ -165,18 +153,12 @@ public class DefaultGroupHandler {
return new DefaultEdgeGroupHandler(deviceId, appId, config,
linkService,
flowObjService,
nsNextObjStore,
subnetNextObjStore,
portNextObjStore,
srManager
);
} else {
return new DefaultTransitGroupHandler(deviceId, appId, config,
linkService,
flowObjService,
nsNextObjStore,
subnetNextObjStore,
portNextObjStore,
srManager);
}
}
......@@ -194,6 +176,8 @@ public class DefaultGroupHandler {
* discovered on this device.
*
* @param newLink new neighbor link
* @param isMaster true if local instance is the master
*
*/
public void linkUp(Link newLink, boolean isMaster) {
......@@ -296,6 +280,7 @@ public class DefaultGroupHandler {
* Performs group recovery procedures when a port goes down on this device.
*
* @param port port number that has gone down
* @param isMaster true if local instance is the master
*/
public void portDown(PortNumber port, boolean isMaster) {
if (portDeviceMap.get(port) == null) {
......@@ -448,8 +433,8 @@ public class DefaultGroupHandler {
*/
public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
TrafficSelector meta) {
Integer nextId = portNextObjStore.
get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
Integer nextId = portNextObjStore
.get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
if (nextId == null) {
log.trace("getPortNextObjectiveId in device{}: Next objective id "
+ "not found for {} and {} creating", deviceId, portNum);
......@@ -458,7 +443,33 @@ public class DefaultGroupHandler {
new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
if (nextId == null) {
log.warn("getPortNextObjectiveId: unable to create next obj"
+ "for dev:{} port{}", deviceId, portNum);
+ "for dev:{} port:{}", deviceId, portNum);
return -1;
}
}
return nextId;
}
/**
* Returns the next objective ID of type broadcast associated with the VLAN
* cross-connection.
*
* @param vlanId VLAN ID for the cross-connection
* @return int if found or created, -1 if there are errors during the
* creation of the next objective
*/
public int getXConnectNextObjectiveId(VlanId vlanId) {
Integer nextId = xConnectNextObjStore
.get(new XConnectNextObjectiveStoreKey(deviceId, vlanId));
if (nextId == null) {
log.trace("getXConnectNextObjectiveId: Next objective id "
+ "not found for device {} and vlan {}. Creating", deviceId, vlanId);
createGroupsForXConnect(deviceId);
nextId = xConnectNextObjStore.get(
new XConnectNextObjectiveStoreKey(deviceId, vlanId));
if (nextId == null) {
log.warn("getXConnectNextObjectiveId: Next objective id "
+ "not found for device {} and vlan {}.", deviceId, vlanId);
return -1;
}
}
......@@ -655,7 +666,6 @@ public class DefaultGroupHandler {
/**
* Creates broadcast groups for all ports in the same configured subnet.
*
*/
public void createGroupsFromSubnetConfig() {
Map<Ip4Prefix, List<PortNumber>> subnetPortMap =
......@@ -700,6 +710,54 @@ public class DefaultGroupHandler {
});
}
/**
* Creates broadcast groups for VLAN cross-connect ports.
*
* @param deviceId the DPID of the switch
*/
public void createGroupsForXConnect(DeviceId deviceId) {
Map<VlanId, List<ConnectPoint>> xConnectsForDevice = deviceConfig.getXConnects();
xConnectsForDevice.forEach((vlanId, connectPoints) -> {
// Only proceed the xConnect for given device
for (ConnectPoint connectPoint : connectPoints) {
if (!connectPoint.deviceId().equals(deviceId)) {
return;
}
}
// Check if the next obj is already in the store
XConnectNextObjectiveStoreKey key =
new XConnectNextObjectiveStoreKey(deviceId, vlanId);
if (xConnectNextObjStore.containsKey(key)) {
log.debug("Cross-connect Broadcast group for device {} and vlanId {} exists",
deviceId, vlanId);
return;
}
TrafficSelector metadata =
DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
int nextId = flowObjectiveService.allocateNextId();
NextObjective.Builder nextObjBuilder = DefaultNextObjective
.builder().withId(nextId)
.withType(NextObjective.Type.BROADCAST).fromApp(appId)
.withMeta(metadata);
connectPoints.forEach(connectPoint -> {
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
tBuilder.setOutput(connectPoint.port());
nextObjBuilder.addTreatment(tBuilder.build());
});
NextObjective nextObj = nextObjBuilder.add();
flowObjectiveService.next(deviceId, nextObj);
log.debug("createGroupsForXConnect: Submited next objective {} in device {}",
nextId, deviceId);
xConnectNextObjStore.put(key, nextId);
});
}
/**
* Create simple next objective for a single port. The treatments can include
......@@ -730,11 +788,6 @@ public class DefaultGroupHandler {
portNextObjStore.put(key, nextId);
}
public GroupKey getGroupKey(Object obj) {
return new DefaultGroupKey(kryo.build().serialize(obj));
}
/**
* Removes groups for the next objective ID given.
*
......@@ -766,6 +819,9 @@ public class DefaultGroupHandler {
return false;
}
/**
* Removes all groups from all next objective stores.
*/
public void removeAllGroups() {
for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry:
nsNextObjStore.entrySet()) {
......
......@@ -26,7 +26,6 @@ import org.onosproject.net.link.LinkService;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceProperties;
import org.onosproject.store.service.EventuallyConsistentMap;
/**
* Default ECMP group handler creation module for a transit device.
......@@ -41,22 +40,13 @@ import org.onosproject.store.service.EventuallyConsistentMap;
* 2) all ports to D3 + with no label push,
*/
public class DefaultTransitGroupHandler extends DefaultGroupHandler {
// TODO Access stores through srManager
protected DefaultTransitGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<
NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore,
SegmentRoutingManager srManager) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
super(deviceId, appId, config, linkService, flowObjService, srManager);
}
@Override
......
......@@ -21,22 +21,38 @@ import java.util.Objects;
import org.onosproject.net.DeviceId;
/**
* Class definition of Key for Neighborset to NextObjective store.
* Key of Neighborset next objective store.
*/
public class NeighborSetNextObjectiveStoreKey {
private final DeviceId deviceId;
private final NeighborSet ns;
/**
* Constructs the key of neighbor set next objective store.
*
* @param deviceId device ID
* @param ns neighbor set
*/
public NeighborSetNextObjectiveStoreKey(DeviceId deviceId,
NeighborSet ns) {
this.deviceId = deviceId;
this.ns = ns;
}
/**
* Returns the device ID in the key.
*
* @return device ID
*/
public DeviceId deviceId() {
return this.deviceId;
}
/**
* Returns the neighbor set in the key.
*
* @return neighbor set
*/
public NeighborSet neighborSet() {
return this.ns;
}
......
......@@ -31,7 +31,6 @@ import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceProperties;
import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficTreatment;
......@@ -51,33 +50,31 @@ public class PolicyGroupHandler extends DefaultGroupHandler {
private HashMap<PolicyGroupIdentifier, PolicyGroupIdentifier> dependentGroups = new HashMap<>();
/**
* Policy group handler constructor.
* Constructs policy group handler.
*
* @param deviceId device identifier
* @param appId application identifier
* @param config interface to retrieve the device properties
* @param linkService link service object
* @param flowObjService flow objective service object
* @param nsNextObjStore NeighborSet next objective store map
* @param subnetNextObjStore subnet next objective store map
* @param srManager segment routing manager
*/
// TODO Access stores through srManager
public PolicyGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore,
SegmentRoutingManager srManager) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
super(deviceId, appId, config, linkService, flowObjService, srManager);
}
/**
* Creates policy group chain.
*
* @param id unique identifier associated with the policy group
* @param params a list of policy group params
* @return policy group identifier
*/
public PolicyGroupIdentifier createPolicyGroupChain(String id,
List<PolicyGroupParams> params) {
List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
......@@ -222,69 +219,18 @@ public class PolicyGroupHandler extends DefaultGroupHandler {
}
//TODO: Use nextObjective APIs to handle the group chains
/*@Override
protected void handleGroupEvent(GroupEvent event) {
if (event.type() == GroupEvent.Type.GROUP_ADDED) {
if (dependentGroups.get(event.subject().appCookie()) != null) {
PolicyGroupIdentifier dependentGroupKey = dependentGroups.get(event.subject().appCookie());
dependentGroups.remove(event.subject().appCookie());
boolean fullyResolved = true;
for (GroupBucketIdentifier bucketId:
dependentGroupKey.bucketIds()) {
if (bucketId.type() != BucketOutputType.GROUP) {
continue;
}
if (dependentGroups.containsKey(bucketId.outGroup())) {
fullyResolved = false;
break;
}
}
if (fullyResolved) {
List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
for (GroupBucketIdentifier bucketId:
dependentGroupKey.bucketIds()) {
TrafficTreatment.Builder tBuilder =
DefaultTrafficTreatment.builder();
if (bucketId.label() != NeighborSet.NO_EDGE_LABEL) {
tBuilder.pushMpls()
.setMpls(MplsLabel.
mplsLabel(bucketId.label()));
}
//TODO: BoS
if (bucketId.type() == BucketOutputType.PORT) {
DeviceId neighbor = portDeviceMap.
get(bucketId.outPort());
tBuilder.setOutput(bucketId.outPort())
.setEthDst(deviceConfig.
getDeviceMac(neighbor))
.setEthSrc(nodeMacAddr);
} else {
if (groupService.
getGroup(deviceId,
getGroupKey(bucketId.
outGroup())) == null) {
throw new IllegalStateException();
}
GroupId indirectGroupId = groupService.
getGroup(deviceId,
getGroupKey(bucketId.
outGroup())).id();
tBuilder.group(indirectGroupId);
}
outBuckets.add(DefaultGroupBucket.
createSelectGroupBucket(tBuilder.build()));
}
GroupDescription desc = new
DefaultGroupDescription(deviceId,
GroupDescription.Type.SELECT,
new GroupBuckets(outBuckets));
groupService.addGroup(desc);
}
}
}
}*/
/*
@Override
protected void handleGroupEvent(GroupEvent event) {}
*/
/**
* Generates policy group key.
*
* @param id unique identifier associated with the policy group
* @param params a list of policy group params
* @return policy group identifier
*/
public PolicyGroupIdentifier generatePolicyGroupKey(String id,
List<PolicyGroupParams> params) {
List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
......@@ -354,6 +300,11 @@ public class PolicyGroupHandler extends DefaultGroupHandler {
return innermostGroupkey;
}
/**
* Removes policy group chain.
*
* @param key policy group identifier
*/
public void removeGroupChain(PolicyGroupIdentifier key) {
checkArgument(key != null);
List<PolicyGroupIdentifier> groupsToBeDeleted = new ArrayList<>();
......
......@@ -28,7 +28,7 @@ public class PolicyGroupIdentifier {
private List<GroupBucketIdentifier> bucketIds;
/**
* Constructor.
* Constructs policy group identifier.
*
* @param id unique identifier associated with the policy group
* @param input policy group params associated with this group
......
......@@ -7,15 +7,23 @@ import org.onosproject.net.flow.TrafficTreatment;
import java.util.Objects;
/**
* Class definition of Key for Device/Port to NextObjective store. Since there
* can be multiple next objectives to the same physical port, we differentiate
* between them by including the treatment in the key.
* Key of Device/Port to NextObjective store.
*
* Since there can be multiple next objectives to the same physical port,
* we differentiate between them by including the treatment in the key.
*/
public class PortNextObjectiveStoreKey {
private final DeviceId deviceId;
private final PortNumber portNum;
private final TrafficTreatment treatment;
/**
* Constructs the key of port next objective store.
*
* @param deviceId device ID
* @param portNum port number
* @param treatment treatment that will be applied to the interface
*/
public PortNextObjectiveStoreKey(DeviceId deviceId, PortNumber portNum,
TrafficTreatment treatment) {
this.deviceId = deviceId;
......
......@@ -22,12 +22,18 @@ import org.onosproject.net.DeviceId;
import java.util.Objects;
/**
* Class definition of Key for Subnet to NextObjective store.
* Key of Subnet to NextObjective store.
*/
public class SubnetNextObjectiveStoreKey {
private final DeviceId deviceId;
private final IpPrefix prefix;
/**
* Constructs the key of subnet next objective store.
*
* @param deviceId device ID
* @param prefix subnet information
*/
public SubnetNextObjectiveStoreKey(DeviceId deviceId,
IpPrefix prefix) {
this.deviceId = deviceId;
......
/*
* Copyright 2015 Open Networking Laboratory
*
* 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.grouphandler;
import org.onlab.packet.VlanId;
import org.onosproject.net.DeviceId;
import java.util.Objects;
/**
* Key of VLAN cross-connect next objective store.
*/
public class XConnectNextObjectiveStoreKey {
private final DeviceId deviceId;
private final VlanId vlanId;
/**
* Constructs the key of cross-connect next objective store.
*
* @param deviceId device ID of the VLAN cross-connection
* @param vlanId VLAN ID of the VLAN cross-connection
*/
public XConnectNextObjectiveStoreKey(DeviceId deviceId, VlanId vlanId) {
this.deviceId = deviceId;
this.vlanId = vlanId;
}
/**
* Returns the device ID of this key.
*
* @return device ID
*/
public DeviceId deviceId() {
return this.deviceId;
}
/**
* Returns the VLAN ID of this key.
*
* @return VLAN ID
*/
public VlanId vlanId() {
return this.vlanId;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof XConnectNextObjectiveStoreKey)) {
return false;
}
XConnectNextObjectiveStoreKey that =
(XConnectNextObjectiveStoreKey) o;
return (Objects.equals(this.deviceId, that.deviceId) &&
Objects.equals(this.vlanId, that.vlanId));
}
// The list of neighbor ids and label are used for comparison.
@Override
public int hashCode() {
return Objects.hash(deviceId, vlanId);
}
@Override
public String toString() {
return "Device: " + deviceId + " VlanId: " + vlanId;
}
}
......@@ -21,6 +21,9 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.segmentrouting.Policy;
import org.onosproject.segmentrouting.TunnelPolicy;
/**
* Codec of Policy class.
*/
public final class PolicyCodec extends JsonCodec<Policy> {
// JSON field names
......
......@@ -26,6 +26,9 @@ import org.onosproject.segmentrouting.Tunnel;
import java.util.ArrayList;
import java.util.List;
/**
* Codec of Tunnel class.
*/
public final class TunnelCodec extends JsonCodec<Tunnel> {
// JSON field names
......
package org.onosproject.driver.pipeline;
import com.google.common.base.Objects;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalCause;
......@@ -69,12 +70,18 @@ public class OFDPA2GroupHandler {
* L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index>
* L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
*/
private static final int L2INTERFACEMASK = 0x0;
private static final int L3UNICASTMASK = 0x20000000;
private static final int MPLSINTERFACEMASK = 0x90000000;
private static final int L3ECMPMASK = 0x70000000;
private static final int L2FLOODMASK = 0x40000000;
private static final int L3VPNMASK = 0x92000000;
private static final int L2_INTERFACE_TYPE = 0x00000000;
private static final int L3_UNICAST_TYPE = 0x20000000;
private static final int MPLS_INTERFACE_TYPE = 0x90000000;
private static final int MPLS_L3VPN_SUBTYPE = 0x92000000;
private static final int L3_ECMP_TYPE = 0x70000000;
private static final int L2_FLOOD_TYPE = 0x40000000;
private static final int TYPE_MASK = 0x0fffffff;
private static final int SUBTYPE_MASK = 0x00ffffff;
private static final int PORT_LOWER_BITS_MASK = 0x3f;
private static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
private final Logger log = getLogger(getClass());
private ServiceDirectory serviceDirectory;
......@@ -252,18 +259,9 @@ public class OFDPA2GroupHandler {
+ " instruction in simple nextObjectives: {}", ins.type());
}
}
//use the vlanid associated with the port
VlanId vlanid = port2Vlan.get(portNum);
if (vlanid == null && nextObj.meta() != null) {
// use metadata vlan info if available
Criterion vidCriterion = nextObj.meta().getCriterion(Criterion.Type.VLAN_VID);
if (vidCriterion != null) {
vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
}
}
if (vlanid == null) {
VlanId vlanId = readVlanFromMeta(nextObj);
if (vlanId == null) {
log.error("Driver cannot process an L2/L3 group chain without "
+ "egress vlan information for dev: {} port:{}",
deviceId, portNum);
......@@ -271,11 +269,11 @@ public class OFDPA2GroupHandler {
}
// assemble information for ofdpa l2interface group
Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum.toLong();
int l2groupId = L2_INTERFACE_TYPE | (vlanId.toShort() << 16) | (int) portNum.toLong();
// a globally unique groupkey that is different for ports in the same devices
// but different for the same portnumber on different devices. Also different
// for the various group-types created out of the same next objective.
int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong());
int l2gk = l2InterfaceGroupKey(deviceId, vlanId, portNum.toLong());
final GroupKey l2groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2gk));
// create group description for the l2interfacegroup
......@@ -399,20 +397,20 @@ public class OFDPA2GroupHandler {
}
// assemble information for ofdpa l2interface group
Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
int l2groupId = L2_INTERFACE_TYPE | (vlanid.toShort() << 16) | (int) portNum;
// a globally unique groupkey that is different for ports in the same devices
// but different for the same portnumber on different devices. Also different
// for the various group-types created out of the same next objective.
int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum);
int l2gk = l2InterfaceGroupKey(deviceId, vlanid, portNum);
final GroupKey l2groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2gk));
// assemble information for outer group
GroupDescription outerGrpDesc = null;
if (mpls) {
// outer group is MPLSInteface
Integer mplsgroupId = MPLSINTERFACEMASK | (int) portNum;
int mplsgroupId = MPLS_INTERFACE_TYPE | (int) portNum;
// using mplsinterfacemask in groupkey to differentiate from l2interface
int mplsgk = MPLSINTERFACEMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
int mplsgk = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & (deviceId.hashCode() << 8 | (int) portNum));
final GroupKey mplsgroupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(mplsgk));
outerTtb.group(new DefaultGroupId(l2groupId));
// create the mpls-interface group description to wait for the
......@@ -432,8 +430,8 @@ public class OFDPA2GroupHandler {
mplsgroupkey, nextId);
} else {
// outer group is L3Unicast
Integer l3groupId = L3UNICASTMASK | (int) portNum;
int l3gk = L3UNICASTMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
int l3groupId = L3_UNICAST_TYPE | (int) portNum;
int l3gk = L3_UNICAST_TYPE | (TYPE_MASK & (deviceId.hashCode() << 8 | (int) portNum));
final GroupKey l3groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3gk));
outerTtb.group(new DefaultGroupId(l2groupId));
// create the l3unicast group description to wait for the
......@@ -488,14 +486,11 @@ public class OFDPA2GroupHandler {
// break up broadcast next objective to multiple groups
Collection<TrafficTreatment> buckets = nextObj.next();
// Read VLAN information from the metadata
TrafficSelector metadata = nextObj.meta();
Criterion criterion = metadata.getCriterion(Criterion.Type.VLAN_VID);
if (criterion == null) {
VlanId vlanId = readVlanFromMeta(nextObj);
if (vlanId == null) {
log.warn("Required VLAN ID info in nextObj metadata but not found. Aborting");
return;
}
VlanId vlanId = ((VlanIdCriterion) criterion).vlanId();
// each treatment is converted to an L2 interface group
List<GroupDescription> l2interfaceGroupDescs = new ArrayList<>();
......@@ -520,26 +515,15 @@ public class OFDPA2GroupHandler {
portNum = ((Instructions.OutputInstruction) ins).port();
newTreatment.add(ins);
} else {
log.debug("TrafficTreatment of type {} not permitted in "
+ " broadcast nextObjective", ins.type());
log.debug("TrafficTreatment of type {} not permitted in " +
" broadcast nextObjective", ins.type());
}
}
// Ensure that all ports of this broadcast nextObj are in the same vlan
// XXX maybe HA issue here?
VlanId expectedVlanId = port2Vlan.putIfAbsent(portNum, vlanId);
if (expectedVlanId != null && !vlanId.equals(expectedVlanId)) {
log.error("Driver requires all ports in a broadcast nextObj "
+ "to be in the same vlan. Different vlans found "
+ "{} and {}. Aborting group creation", vlanId, expectedVlanId);
return;
}
// assemble info for l2 interface group
int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong());
int l2gk = l2InterfaceGroupKey(deviceId, vlanId, portNum.toLong());
final GroupKey l2groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2gk));
Integer l2groupId = L2INTERFACEMASK | (vlanId.toShort() << 16) |
int l2groupId = L2_INTERFACE_TYPE | (vlanId.toShort() << 16) |
(int) portNum.toLong();
GroupBucket l2interfaceGroupBucket =
DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
......@@ -565,8 +549,8 @@ public class OFDPA2GroupHandler {
}
// assemble info for l2 flood group
Integer l2floodgroupId = L2FLOODMASK | (vlanId.toShort() << 16) | nextObj.id();
int l2floodgk = L2FLOODMASK | nextObj.id() << 12;
Integer l2floodgroupId = L2_FLOOD_TYPE | (vlanId.toShort() << 16) | nextObj.id();
int l2floodgk = L2_FLOOD_TYPE | nextObj.id() << 12;
final GroupKey l2floodgroupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2floodgk));
// collection of group buckets pointing to all the l2 interface groups
List<GroupBucket> l2floodBuckets = new ArrayList<>();
......@@ -610,8 +594,6 @@ public class OFDPA2GroupHandler {
}
}
/**
* As per the OFDPA 2.0 TTP, packets are sent out of ports by using
* a chain of groups. The hashed Next Objective passed in by the application
......@@ -643,7 +625,7 @@ public class OFDPA2GroupHandler {
.createSelectGroupBucket(ttb.build());
l3ecmpGroupBuckets.add(sbucket);
}
int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12;
int l3ecmpGroupId = L3_ECMP_TYPE | nextObj.id() << 12;
GroupKey l3ecmpGroupKey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3ecmpGroupId));
GroupDescription l3ecmpGroupDesc =
new DefaultGroupDescription(
......@@ -752,8 +734,8 @@ public class OFDPA2GroupHandler {
onelabelGroupInfo.outerGrpDesc.givenGroupId()));
GroupBucket l3vpnGrpBkt =
DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
int l3vpngroupId = L3VPNMASK | l3vpnindex.incrementAndGet();
int l3vpngk = L3VPNMASK | nextObj.id() << 12 | l3vpnindex.get();
int l3vpngroupId = MPLS_L3VPN_SUBTYPE | l3vpnindex.incrementAndGet();
int l3vpngk = MPLS_L3VPN_SUBTYPE | nextObj.id() << 12 | l3vpnindex.get();
GroupKey l3vpngroupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3vpngk));
GroupDescription l3vpnGroupDesc =
new DefaultGroupDescription(
......@@ -821,7 +803,7 @@ public class OFDPA2GroupHandler {
GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build());
// recreate the original L3 ECMP group id and description
int l3ecmpGroupId = L3ECMPMASK | nextObjective.id() << 12;
int l3ecmpGroupId = L3_ECMP_TYPE | nextObjective.id() << 12;
GroupKey l3ecmpGroupKey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3ecmpGroupId));
// Although GroupDescriptions are not necessary for adding buckets to
......@@ -1049,6 +1031,32 @@ public class OFDPA2GroupHandler {
}
}
private VlanId readVlanFromMeta(NextObjective nextObj) {
TrafficSelector metadata = nextObj.meta();
Criterion criterion = metadata.getCriterion(Criterion.Type.VLAN_VID);
return (criterion == null)
? null : ((VlanIdCriterion) criterion).vlanId();
}
/**
* Returns a hash as the L2 Interface Group Key.
*
* Keep the lower 6-bit for port since port number usually smaller than 64.
* Hash other information into remaining 28 bits.
*
* @param deviceId Device ID
* @param vlanId VLAN ID
* @param portNumber Port number
* @return L2 interface group key
*/
private int l2InterfaceGroupKey(
DeviceId deviceId, VlanId vlanId, long portNumber) {
int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
int hash = Objects.hashCode(deviceId, vlanId, portHigherBits);
return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
}
private class InnerGroupListener implements GroupListener {
@Override
public void event(GroupEvent event) {
......
......@@ -305,11 +305,16 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
}
VlanId assignedVlan = null;
if (vidCriterion != null && vidCriterion.vlanId() == VlanId.NONE) {
// untagged packets are assigned vlans in OF-DPA
// For VLAN cross-connect packets, use the configured VLAN
if (vidCriterion != null) {
if (vidCriterion.vlanId() != VlanId.NONE) {
assignedVlan = vidCriterion.vlanId();
// For untagged packets, assign a VLAN ID
} else {
if (filt.meta() == null) {
log.error("Missing metadata in filtering objective required "
+ "for vlan assignment in dev {}", deviceId);
log.error("Missing metadata in filtering objective required " +
"for vlan assignment in dev {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
......@@ -326,8 +331,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
return;
}
}
}
if (ethCriterion == null) {
if (ethCriterion == null || ethCriterion.mac().equals(MacAddress.NONE)) {
log.debug("filtering objective missing dstMac, cannot program TMAC table");
} else {
for (FlowRule tmacRule : processEthDstFilter(portCriterion, ethCriterion,
......@@ -340,8 +346,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
}
if (ethCriterion == null || vidCriterion == null) {
log.debug("filtering objective missing dstMac or vlan, cannot program"
+ "Vlan Table");
log.debug("filtering objective missing dstMac or VLAN, "
+ "cannot program VLAN Table");
} else {
for (FlowRule vlanRule : processVlanIdFilter(portCriterion, vidCriterion,
assignedVlan,
......
......@@ -837,7 +837,6 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
if (vlanIdCriterion.vlanId() != VlanId.NONE) {
selector.matchVlanId(vlanIdCriterion.vlanId());
selector.matchInPort(p.port());
treatment.deferred().popVlan();
} else {
selector.matchInPort(p.port());
treatment.immediate().pushVlan().setVlanId(assignedVlan);
......@@ -887,11 +886,16 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
VlanId assignedVlan = null;
if (vlanIdCriterion != null && vlanIdCriterion.vlanId() == VlanId.NONE) {
// Assign a VLAN ID to untagged packets
if (vlanIdCriterion != null) {
// For VLAN cross-connect packets, use the configured VLAN
if (vlanIdCriterion.vlanId() != VlanId.NONE) {
assignedVlan = vlanIdCriterion.vlanId();
// For untagged packets, assign a VLAN ID
} else {
if (filt.meta() == null) {
log.error("Missing metadata in filtering objective required "
+ "for vlan assignment in dev {}", deviceId);
log.error("Missing metadata in filtering objective required " +
"for vlan assignment in dev {}", deviceId);
fail(filt, ObjectiveError.BADPARAMS);
return;
}
......@@ -908,6 +912,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
return;
}
}
}
if (ethCriterion == null) {
log.debug("filtering objective missing dstMac, cannot program TMAC table");
......@@ -923,9 +928,9 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
}
if (ethCriterion == null || vlanIdCriterion == null) {
log.debug("filtering objective missing dstMac or vlan, cannot program"
+ "Vlan Table");
if (vlanIdCriterion == null) {
log.debug("filtering objective missing VLAN ID criterion, "
+ "cannot program VLAN Table");
} else {
for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
filt,
......
{
"ports" : {
"of:0000000000000001/3" : {
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"vlan" : "-1"
},
{
"vlan" : "100"
}
]
},
"of:0000000000000001/4" : {
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"vlan" : "-1"
}
]
},
"of:0000000000000001/5" : {
"interfaces" : [
{
"vlan" : "100"
}
]
},
"of:0000000000000002/3" : {
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"vlan" : "-1"
}
]
},
"of:0000000000000002/4" : {
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"vlan" : "-1"
}
]
}
},
"devices" : {
"of:0000000000000001" : {
"segmentrouting" : {
"name" : "Leaf-R1",
"nodeSid" : 101,
"routerIp" : "10.0.1.254",
"routerMac" : "00:00:00:00:01:80",
"isEdgeRouter" : true,
"adjacencySids" : []
}
},
"of:0000000000000002" : {
"segmentrouting" : {
"name" : "Leaf-R2",
"nodeSid" : 102,
"routerIp" : "10.0.2.254",
"routerMac" : "00:00:00:00:02:80",
"isEdgeRouter" : true,
"adjacencySids" : []
}
},
"of:0000000000000191" : {
"segmentrouting" : {
"name" : "Spine-R1",
"nodeSid" : 103,
"routerIp" : "192.168.0.11",
"routerMac" : "00:00:01:00:11:80",
"isEdgeRouter" : false,
"adjacencySids" : []
}
},
"of:0000000000000192" : {
"segmentrouting" : {
"name" : "Spine-R2",
"nodeSid" : 104,
"routerIp" : "192.168.0.22",
"routerMac" : "00:00:01:00:22:80",
"isEdgeRouter" : false,
"adjacencySids" : []
}
}
},
"hosts" : {
"00:00:00:00:00:01/4093" : {
"basic": {
"ips": ["10.0.1.1"],
"location": "of:0000000000000001/3"
}
},
"00:00:00:00:00:02/4093" : {
"basic": {
"ips": ["10.0.1.2"],
"location": "of:0000000000000001/4"
}
},
"00:00:00:00:00:03/4093" : {
"basic": {
"ips": ["10.0.2.1"],
"location": "of:0000000000000002/3"
}
},
"00:00:00:00:00:04/4093" : {
"basic": {
"ips": ["10.0.2.2"],
"location": "of:0000000000000002/4"
}
}
}
}
......@@ -4,8 +4,7 @@
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -13,8 +12,7 @@
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -22,8 +20,7 @@
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -31,8 +28,7 @@
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
}
......
......@@ -4,8 +4,7 @@
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -13,8 +12,7 @@
"interfaces" : [
{
"ips" : [ "10.0.1.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -22,8 +20,7 @@
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -31,8 +28,7 @@
"interfaces" : [
{
"ips" : [ "10.0.2.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -40,8 +36,7 @@
"interfaces" : [
{
"ips" : [ "10.0.3.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -49,8 +44,7 @@
"interfaces" : [
{
"ips" : [ "10.0.3.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -58,8 +52,7 @@
"interfaces" : [
{
"ips" : [ "10.0.4.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
},
......@@ -67,8 +60,7 @@
"interfaces" : [
{
"ips" : [ "10.0.4.254/24" ],
"mac" : "08:9e:01:82:38:68",
"vlan" : "100"
"vlan" : "-1"
}
]
}
......