Saurav Das
Committed by Gerrit Code Review

CORD-367 L2 bridging and L3 routing support with internal VLANs in OF-DPA.

Also includes:
     All forwarding in app is now via nextObjectives (not treatments) - Spring Open driver converts
     non-ECMP forwarding to flow-actions, while OF-DPA driver continues to use groups.
     Convert 'setMeta' methods to 'withMeta' in Flow Objectives API.
     Bug fix in Flow Objective Manager - set of PendingNext is now threadsafe.
     Bug fix in ArpHandler - now recognizes routerIp in addition to gatewayIps
     Removed a bunch of testcode
     Added group count in CLI

Change-Id: Id3b879c5dda78151ca0ec359179f1604066d39fc
Showing 20 changed files with 761 additions and 608 deletions
......@@ -107,7 +107,7 @@ public class ArpHandler {
vlanId);
// ARP request for router. Send ARP reply.
if (isArpReqForRouter(deviceId, arpRequest)) {
if (isArpForRouter(deviceId, arpRequest)) {
Ip4Address targetAddress = Ip4Address.valueOf(arpRequest.getTargetProtocolAddress());
sendArpResponse(arpRequest, config.getRouterMacForAGatewayIp(targetAddress), vlanId);
} else {
......@@ -130,7 +130,7 @@ public class ArpHandler {
vlanId);
// ARP reply for router. Process all pending IP packets.
if (isArpReqForRouter(deviceId, arpReply)) {
if (isArpForRouter(deviceId, arpReply)) {
Ip4Address hostIpAddress = Ip4Address.valueOf(arpReply.getSenderProtocolAddress());
srManager.ipHandler.forwardPackets(deviceId, hostIpAddress);
} else {
......@@ -141,7 +141,8 @@ public class ArpHandler {
// ARP reply for unknown host, Flood in the subnet.
} else {
// Don't flood to non-edge ports
if (vlanId.equals(VlanId.vlanId(srManager.ASSIGNED_VLAN_NO_SUBNET))) {
if (vlanId.equals(
VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
return;
}
removeVlanAndFlood(payload, inPort);
......@@ -150,14 +151,21 @@ public class ArpHandler {
}
private boolean isArpReqForRouter(DeviceId deviceId, ARP arpRequest) {
Set<Ip4Address> gatewayIpAddresses = config.getPortIPs(deviceId);
if (gatewayIpAddresses != null) {
Ip4Address targetProtocolAddress = Ip4Address.valueOf(arpRequest
.getTargetProtocolAddress());
if (gatewayIpAddresses.contains(targetProtocolAddress)) {
private boolean isArpForRouter(DeviceId deviceId, ARP arpMsg) {
Ip4Address targetProtocolAddress = Ip4Address.valueOf(
arpMsg.getTargetProtocolAddress());
Set<Ip4Address> gatewayIpAddresses = null;
try {
if (targetProtocolAddress.equals(config.getRouterIp(deviceId))) {
return true;
}
gatewayIpAddresses = config.getPortIPs(deviceId);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
}
if (gatewayIpAddresses != null &&
gatewayIpAddresses.contains(targetProtocolAddress)) {
return true;
}
return false;
}
......
......@@ -88,10 +88,10 @@ public class IcmpHandler {
(destinationAddress.equals(routerIpAddress) ||
gatewayIpAddresses.contains(destinationAddress))) {
sendICMPResponse(ethernet, connectPoint);
// TODO: do we need to set the flow rule again ??
// ICMP for any known host
} else if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
// TODO: known host packet should not be coming to controller - resend flows?
srManager.ipHandler.forwardPackets(deviceId, destinationAddress);
// ICMP for an unknown host in the subnet of the router
......
......@@ -98,7 +98,7 @@ public class IpHandler {
*/
public void addToPacketBuffer(IPv4 ipPacket) {
// Better not buffer TPC packets due to out-of-order packet transfer
// Better not buffer TCP packets due to out-of-order packet transfer
if (ipPacket.getProtocol() == IPv4.PROTOCOL_TCP) {
return;
}
......
......@@ -147,20 +147,34 @@ public class RoutingRulePopulator {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, IpPrefix.MAX_INET_MASK_LENGTH));
sbuilder.matchEthType(Ethernet.TYPE_IPV4);
sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, IpPrefix.MAX_INET_MASK_LENGTH));
TrafficSelector selector = sbuilder.build();
tbuilder.deferred()
.setEthDst(hostMac)
.setEthSrc(deviceMac)
.setOutput(outPort);
TrafficTreatment treatment = tbuilder.build();
TrafficSelector selector = sbuilder.build();
// All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
// for switch pipelines that need it, provide outgoing vlan as metadata
VlanId outvlan = null;
Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outPort);
if (subnet == null) {
outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
} else {
outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
}
TrafficSelector meta = DefaultTrafficSelector.builder()
.matchVlanId(outvlan).build();
int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outPort,
treatment, meta);
return DefaultForwardingObjective.builder()
.withSelector(selector)
.nextStep(portNextObjId)
.fromApp(srManager.appId).makePermanent()
.withSelector(selector).withTreatment(treatment)
.withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
}
......@@ -454,7 +468,7 @@ public class RoutingRulePopulator {
if (srManager.mastershipService.isLocalMaster(deviceId)) {
TrafficTreatment tt = DefaultTrafficTreatment.builder()
.pushVlan().setVlanId(assignedVlan).build();
fob.setMeta(tt);
fob.withMeta(tt);
}
fob.permit().fromApp(srManager.appId);
srManager.flowObjectiveService.
......@@ -559,6 +573,12 @@ public class RoutingRulePopulator {
int nextId = srManager.getSubnetNextObjectiveId(deviceId, subnet);
VlanId vlanId = srManager.getSubnetAssignedVlanId(deviceId, subnet);
if (nextId < 0 || vlanId == null) {
log.error("Cannot install subnet broadcast rule in dev:{} due"
+ "to vlanId:{} or nextId:{}", vlanId, nextId);
return;
}
/* Driver should treat objective with MacAddress.NONE as the
* subnet broadcast rule
*/
......
......@@ -57,6 +57,7 @@ import org.onosproject.segmentrouting.config.SegmentRoutingConfig;
import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
import org.onosproject.segmentrouting.grouphandler.NeighborSet;
import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey;
import org.onosproject.segmentrouting.grouphandler.PortNextObjectiveStoreKey;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
......@@ -97,7 +98,6 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("ALL")
@Service
@Component(immediate = true)
public class SegmentRoutingManager implements SegmentRoutingService {
......@@ -150,21 +150,27 @@ public class SegmentRoutingManager implements SegmentRoutingService {
private ScheduledExecutorService executorService = Executors
.newScheduledThreadPool(1);
@SuppressWarnings("unused")
private static ScheduledFuture<?> eventHandlerFuture = null;
@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>
nsNextObjStore = null;
// Per device next objective ID store with (device id + subnet) as key
private EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
subnetNextObjStore = null;
private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
private EventuallyConsistentMap<String, Policy> policyStore = null;
// Per device next objective ID store with (device id + port) as key
private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
portNextObjStore = null;
// Per device, per-subnet assigned-vlans store, with (device id + subnet
// IPv4 prefix) as key
private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId>
subnetVidStore = null;
private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
private EventuallyConsistentMap<String, Policy> policyStore = null;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
......@@ -175,6 +181,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
private final InternalConfigListener cfgListener =
new InternalConfigListener(this);
@SuppressWarnings({ "unchecked", "rawtypes" })
private final ConfigFactory cfgFactory =
new ConfigFactory(SubjectFactories.DEVICE_SUBJECT_FACTORY,
SegmentRoutingConfig.class,
......@@ -228,7 +235,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
log.debug("Creating EC map nsnextobjectivestore");
EventuallyConsistentMapBuilder<NeighborSetNextObjectiveStoreKey, Integer>
nsNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
nsNextObjStore = nsNextObjMapBuilder
.withName("nsnextobjectivestore")
.withSerializer(kryoBuilder)
......@@ -239,16 +245,23 @@ public class SegmentRoutingManager implements SegmentRoutingService {
log.debug("Creating EC map subnetnextobjectivestore");
EventuallyConsistentMapBuilder<SubnetNextObjectiveStoreKey, Integer>
subnetNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
subnetNextObjStore = subnetNextObjMapBuilder
.withName("subnetnextobjectivestore")
.withSerializer(kryoBuilder)
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
log.debug("Creating EC map subnetnextobjectivestore");
EventuallyConsistentMapBuilder<PortNextObjectiveStoreKey, Integer>
portNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
portNextObjStore = portNextObjMapBuilder
.withName("portnextobjectivestore")
.withSerializer(kryoBuilder)
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
storageService.eventuallyConsistentMapBuilder();
tunnelStore = tunnelMapBuilder
.withName("tunnelstore")
.withSerializer(kryoBuilder)
......@@ -257,7 +270,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
EventuallyConsistentMapBuilder<String, Policy> policyMapBuilder =
storageService.eventuallyConsistentMapBuilder();
policyStore = policyMapBuilder
.withName("policystore")
.withSerializer(kryoBuilder)
......@@ -266,7 +278,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
EventuallyConsistentMapBuilder<SubnetAssignedVidStoreKey, VlanId>
subnetVidStoreMapBuilder = storageService.eventuallyConsistentMapBuilder();
subnetVidStore = subnetVidStoreMapBuilder
.withName("subnetvidstore")
.withSerializer(kryoBuilder)
......@@ -425,8 +436,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
/**
* Returns the next objective ID for the given NeighborSet.
* If the nextObjective does not exist, a new one is created and
* it's id is returned.
* TODO move the side-effect creation of a Next Objective into a new method
* its id is returned.
*
* @param deviceId Device ID
* @param ns NegighborSet
......@@ -441,18 +451,19 @@ public class SegmentRoutingManager implements SegmentRoutingService {
return groupHandlerMap
.get(deviceId).getNextObjectiveId(ns, meta);
} else {
log.warn("getNextObjectiveId query in device {} not found", deviceId);
log.warn("getNextObjectiveId query - groupHandler for device {} "
+ "not found", deviceId);
return -1;
}
}
/**
* Returns the next objective ID for the Subnet given. If the nextObjectiveID does not exist,
* a new one is created and returned.
* Returns the next objective ID for the given subnet prefix. It is expected
* that the next-objective has been pre-created from configuration.
*
* @param deviceId Device ID
* @param prefix Subnet
* @return next objective ID
* @return next objective ID or -1 if it was not found
*/
public int getSubnetNextObjectiveId(DeviceId deviceId, IpPrefix prefix) {
if (groupHandlerMap.get(deviceId) != null) {
......@@ -460,7 +471,33 @@ public class SegmentRoutingManager implements SegmentRoutingService {
return groupHandlerMap
.get(deviceId).getSubnetNextObjectiveId(prefix);
} else {
log.warn("getSubnetNextObjectiveId query in device {} not found", deviceId);
log.warn("getSubnetNextObjectiveId query - groupHandler for "
+ "device {} not found", deviceId);
return -1;
}
}
/**
* Returns the next objective ID for the given portNumber, 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, a new one is created and its id is returned.
*
* @param deviceId Device ID
* @param portNum port number on device for which NextObjective is queried
* @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
* @return next objective ID or -1 if it was not found
*/
public int getPortNextObjectiveId(DeviceId deviceId, PortNumber portNum,
TrafficTreatment treatment,
TrafficSelector meta) {
DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
if (ghdlr != null) {
return ghdlr.getPortNextObjectiveId(portNum, treatment, meta);
} else {
log.warn("getPortNextObjectiveId query - groupHandler for device {}"
+ " not found", deviceId);
return -1;
}
}
......@@ -475,7 +512,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
InboundPacket pkt = context.inPacket();
Ethernet ethernet = pkt.parsed();
log.trace("Rcvd pktin: {}", ethernet);
if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
arpHandler.processPacketIn(pkt);
} else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
......@@ -517,6 +554,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
}
@SuppressWarnings("rawtypes")
private void scheduleEventHandlerIfNotScheduled(Event event) {
synchronized (threadSchedulerLock) {
eventQueue.add(event);
......@@ -539,6 +577,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
public void run() {
try {
while (true) {
@SuppressWarnings("rawtypes")
Event event = null;
synchronized (threadSchedulerLock) {
if (!eventQueue.isEmpty()) {
......@@ -647,7 +686,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
linkService,
flowObjectiveService,
nsNextObjStore,
subnetNextObjStore);
subnetNextObjStore,
portNextObjStore);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting processDeviceAdded.");
return;
......@@ -714,7 +754,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
linkService,
flowObjectiveService,
nsNextObjStore,
subnetNextObjStore);
subnetNextObjStore,
portNextObjStore);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting configureNetwork.");
return;
......@@ -766,7 +807,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
// Populate bridging table entry
ForwardingObjective.Builder fob =
getForwardingObjectiveBuilder(mac, vlanId, port);
getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
flowObjectiveService.forward(deviceId, fob.add(
new BridgingTableObjectiveContext(mac, vlanId)
));
......@@ -782,20 +823,37 @@ public class SegmentRoutingManager implements SegmentRoutingService {
}
private ForwardingObjective.Builder getForwardingObjectiveBuilder(
MacAddress mac, VlanId vlanId, PortNumber port) {
DeviceId deviceId, MacAddress mac, VlanId vlanId,
PortNumber outport) {
// match rule
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
sbuilder.matchEthDst(mac);
sbuilder.matchVlanId(vlanId);
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
// TODO Move popVlan from flow action to group action
tbuilder.immediate().popVlan();
tbuilder.immediate().setOutput(port);
tbuilder.immediate().setOutput(outport);
// for switch pipelines that need it, provide outgoing vlan as metadata
VlanId outvlan = null;
Ip4Prefix subnet = deviceConfiguration.getPortSubnet(deviceId, outport);
if (subnet == null) {
outvlan = VlanId.vlanId(ASSIGNED_VLAN_NO_SUBNET);
} else {
outvlan = getSubnetAssignedVlanId(deviceId, subnet);
}
TrafficSelector meta = DefaultTrafficSelector.builder()
.matchVlanId(outvlan).build();
// All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
int portNextObjId = getPortNextObjectiveId(deviceId, outport,
tbuilder.build(),
meta);
return DefaultForwardingObjective.builder()
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.withSelector(sbuilder.build())
.withTreatment(tbuilder.build())
.nextStep(portNextObjId)
.withPriority(100)
.fromApp(appId)
.makePermanent();
......@@ -807,11 +865,13 @@ public class SegmentRoutingManager implements SegmentRoutingService {
DeviceId deviceId = event.subject().location().deviceId();
PortNumber port = event.subject().location().port();
Set<IpAddress> ips = event.subject().ipAddresses();
log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
// Populate bridging table entry
log.debug("Populate L2 table entry for host {} at {}:{}",
mac, deviceId, port);
ForwardingObjective.Builder fob =
getForwardingObjectiveBuilder(mac, vlanId, port);
getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
flowObjectiveService.forward(deviceId, fob.add(
new BridgingTableObjectiveContext(mac, vlanId)
));
......@@ -835,7 +895,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
// Revoke bridging table entry
ForwardingObjective.Builder fob =
getForwardingObjectiveBuilder(mac, vlanId, port);
getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
flowObjectiveService.forward(deviceId, fob.remove(
new BridgingTableObjectiveContext(mac, vlanId)
));
......@@ -863,7 +923,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
// Revoke previous bridging table entry
ForwardingObjective.Builder prevFob =
getForwardingObjectiveBuilder(mac, vlanId, prevPort);
getForwardingObjectiveBuilder(prevDeviceId, mac, vlanId, prevPort);
flowObjectiveService.forward(prevDeviceId, prevFob.remove(
new BridgingTableObjectiveContext(mac, vlanId)
));
......@@ -878,7 +938,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
// Populate new bridging table entry
ForwardingObjective.Builder newFob =
getForwardingObjectiveBuilder(mac, vlanId, prevPort);
getForwardingObjectiveBuilder(newDeviceId, mac, vlanId, newPort);
flowObjectiveService.forward(newDeviceId, newFob.add(
new BridgingTableObjectiveContext(mac, vlanId)
));
......
......@@ -56,9 +56,11 @@ public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore) {
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore);
nsNextObjStore, subnetNextObjStore, portNextObjStore);
}
@Override
......
......@@ -80,6 +80,8 @@ public class DefaultGroupHandler {
NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null;
protected EventuallyConsistentMap<
SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null;
protected EventuallyConsistentMap<
PortNextObjectiveStoreKey, Integer> portNextObjStore = null;
protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
.register(URI.class).register(HashSet.class)
......@@ -93,11 +95,12 @@ public class DefaultGroupHandler {
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<
NeighborSetNextObjectiveStoreKey,
EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore) {
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore) {
this.deviceId = checkNotNull(deviceId);
this.appId = checkNotNull(appId);
this.deviceConfig = checkNotNull(config);
......@@ -114,6 +117,7 @@ public class DefaultGroupHandler {
this.flowObjectiveService = flowObjService;
this.nsNextObjStore = nsNextObjStore;
this.subnetNextObjStore = subnetNextObjStore;
this.portNextObjStore = portNextObjStore;
populateNeighborMaps();
}
......@@ -133,30 +137,34 @@ public class DefaultGroupHandler {
* @throws DeviceConfigNotFoundException if the device configuration is not found
* @return default group handler type
*/
public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<
NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore)
throws DeviceConfigNotFoundException {
public static DefaultGroupHandler createGroupHandler(
DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore)
throws DeviceConfigNotFoundException {
// handle possible exception in the caller
if (config.isEdgeDevice(deviceId)) {
return new DefaultEdgeGroupHandler(deviceId, appId, config,
linkService,
flowObjService,
nsNextObjStore,
subnetNextObjStore);
subnetNextObjStore,
portNextObjStore);
} else {
return new DefaultTransitGroupHandler(deviceId, appId, config,
linkService,
flowObjService,
nsNextObjStore,
subnetNextObjStore);
subnetNextObjStore,
portNextObjStore);
}
}
......@@ -231,25 +239,21 @@ public class DefaultGroupHandler {
Integer nextId = nsNextObjStore.
get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
if (nextId != null) {
if (nextId != null && isMaster) {
NextObjective.Builder nextObjBuilder = DefaultNextObjective
.builder().withId(nextId)
.withType(NextObjective.Type.HASHED).fromApp(appId);
nextObjBuilder.addTreatment(tBuilder.build());
log.info("**linkUp in device {}: Adding Bucket "
+ "with Port {} to next object id {} and amIMaster:{}",
+ "with Port {} to next object id {}",
deviceId,
newLink.src().port(),
nextId, isMaster);
if (isMaster) {
NextObjective nextObjective = nextObjBuilder.
addToExisting(new SRNextObjectiveContext(deviceId));
flowObjectiveService.next(deviceId, nextObjective);
}
} else {
nextId);
NextObjective nextObjective = nextObjBuilder.
addToExisting(new SRNextObjectiveContext(deviceId));
flowObjectiveService.next(deviceId, nextObjective);
} else if (isMaster) {
log.warn("linkUp in device {}, but global store has no record "
+ "for neighbor-set {}", deviceId, ns);
}
......@@ -331,8 +335,8 @@ public class DefaultGroupHandler {
}
/**
* Returns the next objective associated with the neighborset.
* If there is no next objective for this neighborset, this API
* Returns the next objective of type hashed associated with the neighborset.
* If there is no next objective for this neighborset, this method
* would create a next objective and return. Optionally metadata can be
* passed in for the creation of the next objective.
*
......@@ -372,9 +376,10 @@ public class DefaultGroupHandler {
}
/**
* Returns the next objective associated with the subnet.
* If there is no next objective for this subnet, this API
* would create a next objective and return.
* Returns the next objective of type broadcast associated with the subnet,
* or -1 if no such objective exists. Note that this method does NOT create
* the next objective as a side-effect. It is expected that is objective is
* created at startup from network configuration.
*
* @param prefix subnet information
* @return int if found or -1
......@@ -387,6 +392,38 @@ public class DefaultGroupHandler {
}
/**
* Returns the next objective of type simple associated with the port on the
* device, given the treatment. Different treatments to the same port result
* in different next objectives. If no such objective exists, this method
* creates one and returns the id. Optionally metadata can be passed in for
* the creation of the objective.
*
* @param portNum the port number 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
* @return int if found or created, -1 if there are errors during the
* creation of the next objective.
*/
public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
TrafficSelector meta) {
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);
createGroupFromPort(portNum, treatment, meta);
nextId = portNextObjStore.get(
new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
if (nextId == null) {
log.warn("getPortNextObjectiveId: unable to create next obj"
+ "for dev:{} port{}", deviceId, portNum);
return -1;
}
}
return nextId;
}
/**
* Checks if the next objective ID (group) for the neighbor set exists or not.
*
* @param ns neighbor set to check
......@@ -561,7 +598,7 @@ public class DefaultGroupHandler {
}
}
if (meta != null) {
nextObjBuilder.setMeta(meta);
nextObjBuilder.withMeta(meta);
}
NextObjective nextObj = nextObjBuilder.
add(new SRNextObjectiveContext(deviceId));
......@@ -574,7 +611,10 @@ public class DefaultGroupHandler {
}
}
/**
* Creates broadcast groups for all ports in the same configured subnet.
*
*/
public void createGroupsFromSubnetConfig() {
Map<Ip4Prefix, List<PortNumber>> subnetPortMap =
this.deviceConfig.getSubnetPortsMap(this.deviceId);
......@@ -612,6 +652,37 @@ public class DefaultGroupHandler {
});
}
/**
* Create simple next objective for a single port. The treatments can include
* all outgoing actions that need to happen on the packet.
*
* @param portNum the outgoing port on the device
* @param treatment the actions to apply on the packets (should include outport)
* @param meta optional data to pass to the driver
*/
public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
TrafficSelector meta) {
int nextId = flowObjectiveService.allocateNextId();
PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
deviceId, portNum, treatment);
NextObjective.Builder nextObjBuilder = DefaultNextObjective
.builder().withId(nextId)
.withType(NextObjective.Type.SIMPLE)
.addTreatment(treatment)
.fromApp(appId)
.withMeta(meta);
NextObjective nextObj = nextObjBuilder.add();
flowObjectiveService.next(deviceId, nextObj);
log.debug("createGroupFromPort: Submited next objective {} in device {} "
+ "for port {}", nextId, deviceId, portNum);
portNextObjStore.put(key, nextId);
}
public GroupKey getGroupKey(Object obj) {
return new DefaultGroupKey(kryo.build().serialize(obj));
}
......
......@@ -50,9 +50,11 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler {
NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore) {
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore);
nsNextObjStore, subnetNextObjStore, portNextObjStore);
}
@Override
......
......@@ -68,9 +68,11 @@ public class PolicyGroupHandler extends DefaultGroupHandler {
EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
Integer> nsNextObjStore,
EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
Integer> subnetNextObjStore) {
Integer> subnetNextObjStore,
EventuallyConsistentMap<PortNextObjectiveStoreKey,
Integer> portNextObjStore) {
super(deviceId, appId, config, linkService, flowObjService,
nsNextObjStore, subnetNextObjStore);
nsNextObjStore, subnetNextObjStore, portNextObjStore);
}
public PolicyGroupIdentifier createPolicyGroupChain(String id,
......
package org.onosproject.segmentrouting.grouphandler;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
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.
*/
public class PortNextObjectiveStoreKey {
private final DeviceId deviceId;
private final PortNumber portNum;
private final TrafficTreatment treatment;
public PortNextObjectiveStoreKey(DeviceId deviceId, PortNumber portNum,
TrafficTreatment treatment) {
this.deviceId = deviceId;
this.portNum = portNum;
this.treatment = treatment;
}
/**
* Gets device id in this PortNextObjectiveStoreKey.
*
* @return device id
*/
public DeviceId deviceId() {
return deviceId;
}
/**
* Gets port information in this PortNextObjectiveStoreKey.
*
* @return port information
*/
public PortNumber portNumber() {
return portNum;
}
/**
* Gets treatment information in this PortNextObjectiveStoreKey.
*
* @return treatment information
*/
public TrafficTreatment treatment() {
return treatment;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PortNextObjectiveStoreKey)) {
return false;
}
PortNextObjectiveStoreKey that =
(PortNextObjectiveStoreKey) o;
return (Objects.equals(this.deviceId, that.deviceId) &&
Objects.equals(this.portNum, that.portNum) &&
Objects.equals(this.treatment, that.treatment));
}
@Override
public int hashCode() {
return Objects.hash(deviceId, portNum, treatment);
}
@Override
public String toString() {
return "Device: " + deviceId + " Port: " + portNum + " Treatment: " + treatment;
}
}
......@@ -119,7 +119,7 @@ public class GroupsListCommand extends AbstractShellCommand {
}
private void printGroups(DeviceId deviceId, List<Group> groups) {
print("deviceId=%s", deviceId);
print("deviceId=%s, groupCount=%s", deviceId, groups.size());
for (Group group : groups) {
print(FORMAT, Integer.toHexString(group.id().id()), group.state(), group.type(),
group.bytes(), group.packets(), group.appId().name());
......
......@@ -196,7 +196,7 @@ public final class DefaultFilteringObjective implements FilteringObjective {
}
@Override
public Builder setMeta(TrafficTreatment treatment) {
public Builder withMeta(TrafficTreatment treatment) {
this.meta = treatment;
return this;
}
......
......@@ -181,7 +181,7 @@ public final class DefaultNextObjective implements NextObjective {
}
@Override
public Builder setMeta(TrafficSelector meta) {
public Builder withMeta(TrafficSelector meta) {
this.meta = meta;
return this;
}
......
......@@ -133,7 +133,7 @@ public interface FilteringObjective extends Objective {
* @param treatment traffic treatment to use
* @return a filtering builder
*/
Builder setMeta(TrafficTreatment treatment);
Builder withMeta(TrafficTreatment treatment);
/**
* Assigns an application id.
......
......@@ -147,7 +147,7 @@ public interface NextObjective extends Objective {
* @param selector match conditions
* @return an objective builder
*/
Builder setMeta(TrafficSelector selector);
Builder withMeta(TrafficSelector selector);
/**
* Builds the next objective that will be added.
......
......@@ -16,7 +16,6 @@
package org.onosproject.net.flowobjective.impl;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -53,9 +52,11 @@ import org.onosproject.net.group.GroupService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -228,8 +229,10 @@ public class FlowObjectiveManager implements FlowObjectiveService {
flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
// TODO: change to computeIfAbsent
Set<PendingNext> pnext = pendingForwards.putIfAbsent(fwd.nextId(),
Sets.newHashSet(new PendingNext(deviceId, fwd)));
Set<PendingNext> newset = Collections.newSetFromMap(
new ConcurrentHashMap<PendingNext, Boolean>());
newset.add(new PendingNext(deviceId, fwd));
Set<PendingNext> pnext = pendingForwards.putIfAbsent(fwd.nextId(), newset);
if (pnext != null) {
pnext.add(new PendingNext(deviceId, fwd));
}
......
......@@ -350,7 +350,7 @@ public class DistributedGroupStore
// Check if a group is existing with the same key
Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
if (existingGroup != null) {
log.warn("Group already exists with the same key {} in dev:{} with id:{}",
log.warn("Group already exists with the same key {} in dev:{} with id:0x{}",
groupDesc.appCookie(), groupDesc.deviceId(),
Integer.toHexString(existingGroup.id().id()));
return;
......
......@@ -39,7 +39,9 @@ import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleOperationsContext;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.criteria.EthTypeCriterion;
import org.onosproject.net.flow.criteria.IPCriterion;
import org.onosproject.net.flow.criteria.MplsBosCriterion;
......@@ -62,6 +64,14 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
private final Logger log = getLogger(getClass());
/*
* Cpqd emulation does not require the non-OF standard rules for
* matching untagged packets.
*
* (non-Javadoc)
* @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
*/
@Override
protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
VlanIdCriterion vidCriterion,
......@@ -122,16 +132,101 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
return rules;
}
/*
* Cpqd emulation does not handle vlan tags and mpls labels correctly.
* Workaround requires popping off the VLAN tags in the TMAC table.
*
* (non-Javadoc)
* @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthDstFilter
*/
@Override
protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion,
EthCriterion ethCriterion,
VlanIdCriterion vidCriterion,
VlanId assignedVlan,
ApplicationId applicationId) {
//handling untagged packets via assigned VLAN
if (vidCriterion.vlanId() == VlanId.NONE) {
vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
}
// ofdpa cannot match on ALL portnumber, so we need to use separate
// rules for each port.
List<PortNumber> portnums = new ArrayList<PortNumber>();
if (portCriterion.port() == PortNumber.ALL) {
for (Port port : deviceService.getPorts(deviceId)) {
if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
portnums.add(port.number());
}
}
} else {
portnums.add(portCriterion.port());
}
List<FlowRule> rules = new ArrayList<FlowRule>();
for (PortNumber pnum : portnums) {
// for unicast IP packets
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchInPort(pnum);
selector.matchVlanId(vidCriterion.vlanId());
selector.matchEthType(Ethernet.TYPE_IPV4);
selector.matchEthDst(ethCriterion.mac());
/*
* Note: CpqD switches do not handle MPLS-related operation properly
* for a packet with VLAN tag. We pop VLAN here as a workaround.
* Side effect: HostService learns redundant hosts with same MAC but
* different VLAN. No known side effect on the network reachability.
*/
treatment.popVlan();
treatment.transition(UNICAST_ROUTING_TABLE);
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(DEFAULT_PRIORITY)
.fromApp(applicationId)
.makePermanent()
.forTable(TMAC_TABLE).build();
rules.add(rule);
//for MPLS packets
selector = DefaultTrafficSelector.builder();
treatment = DefaultTrafficTreatment.builder();
selector.matchInPort(pnum);
selector.matchVlanId(vidCriterion.vlanId());
selector.matchEthType(Ethernet.MPLS_UNICAST);
selector.matchEthDst(ethCriterion.mac());
// workaround here again
treatment.popVlan();
treatment.transition(MPLS_TABLE_0);
rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(DEFAULT_PRIORITY)
.fromApp(applicationId)
.makePermanent()
.forTable(TMAC_TABLE).build();
rules.add(rule);
}
return rules;
}
/*
* Cpqd emulation allows MPLS ecmp.
*
* (non-Javadoc)
* @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific
*/
@Override
protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthTypeCriterion ethType =
(EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
if ((ethType == null) ||
(ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
(ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)) {
log.warn("processSpecific: Unsupported "
+ "forwarding objective criteraia");
log.warn("processSpecific: Unsupported forwarding objective criteria"
+ "ethType:{} in dev:{}", ethType, deviceId);
fail(fwd, ObjectiveError.UNSUPPORTED);
return Collections.emptySet();
}
......@@ -143,8 +238,8 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
.matchIPDst(((IPCriterion)
selector.getCriterion(Criterion.Type.IPV4_DST)).ip());
forTableId = UNICAST_ROUTING_TABLE;
log.debug("processing IPv4 specific forwarding objective {} hash{} in dev:{}",
fwd.id(), fwd.hashCode(), deviceId);
log.debug("processing IPv4 specific forwarding objective {} -> next:{}"
+ " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
} else {
filteredSelector
.matchEthType(Ethernet.MPLS_UNICAST)
......@@ -156,8 +251,8 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
filteredSelector.matchMplsBos(bos.mplsBos());
}
forTableId = MPLS_TABLE_1;
log.debug("processing MPLS specific forwarding objective {} hash:{} in dev {}",
fwd.id(), fwd.hashCode(), deviceId);
log.debug("processing MPLS specific forwarding objective {} -> next:{}"
+ " in dev {}", fwd.id(), fwd.nextId(), deviceId);
}
TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
......@@ -197,7 +292,6 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
return Collections.singletonList(ruleBuilder.build());
}
@Override
protected void initializePipeline() {
processPortTable();
......@@ -210,7 +304,6 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
processAclTable();
}
@Override
protected void processPortTable() {
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
......@@ -239,7 +332,6 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
}));
}
@Override
protected void processTmacTable() {
//table miss entry
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
......@@ -270,7 +362,6 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
}));
}
@Override
protected void processIpTable() {
//table miss entry
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
......@@ -278,6 +369,7 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector = DefaultTrafficSelector.builder();
treatment = DefaultTrafficTreatment.builder();
treatment.deferred().setOutput(PortNumber.CONTROLLER);
treatment.transition(ACL_TABLE);
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
......@@ -301,7 +393,6 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
}));
}
@Override
protected void processMplsTable() {
//table miss entry
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
......@@ -374,7 +465,6 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
}));
}
@Override
protected void processAclTable() {
//table miss entry - catch all to executed action-set
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
......
......@@ -18,7 +18,6 @@ package org.onosproject.driver.pipeline;
import static org.onlab.util.Tools.groupedThreads;
import static org.slf4j.LoggerFactory.getLogger;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
......@@ -28,6 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
......@@ -35,14 +35,9 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.Data;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MPLS;
import org.onlab.packet.MacAddress;
import org.onlab.packet.MplsLabel;
import org.onlab.packet.UDP;
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
......@@ -99,10 +94,6 @@ import org.onosproject.net.group.GroupEvent;
import org.onosproject.net.group.GroupKey;
import org.onosproject.net.group.GroupListener;
import org.onosproject.net.group.GroupService;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.onosproject.store.serializers.KryoNamespaces;
import org.slf4j.Logger;
......@@ -160,7 +151,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
protected ApplicationId driverId;
protected PacketService packetService;
protected DeviceService deviceService;
private InternalPacketProcessor processor = new InternalPacketProcessor();
protected KryoNamespace appKryo = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(GroupKey.class)
......@@ -170,7 +160,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
.register(ArrayDeque.class)
.build();
private Cache<GroupKey, OfdpaNextGroup> pendingNextObjectives;
private Cache<GroupKey, List<OfdpaNextGroup>> pendingNextObjectives;
private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups;
private ScheduledExecutorService groupChecker =
......@@ -196,10 +186,12 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
pendingNextObjectives = CacheBuilder.newBuilder()
.expireAfterWrite(20, TimeUnit.SECONDS)
.removalListener((
RemovalNotification<GroupKey, OfdpaNextGroup> notification) -> {
RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> {
if (notification.getCause() == RemovalCause.EXPIRED) {
fail(notification.getValue().nextObjective(),
ObjectiveError.GROUPINSTALLATIONFAILED);
notification.getValue().forEach(ofdpaNextGrp ->
fail(ofdpaNextGrp.nextObj,
ObjectiveError.GROUPINSTALLATIONFAILED));
}
}).build();
......@@ -212,7 +204,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
flowObjectiveStore = context.store();
packetService = serviceDirectory.get(PacketService.class);
deviceService = serviceDirectory.get(DeviceService.class);
packetService.addProcessor(processor, PacketProcessor.director(2));
groupService.addListener(new InnerGroupListener());
driverId = coreService.registerApplication(
......@@ -271,7 +262,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
log.warn("Unknown forwarding type {}", fwd.op());
}
flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
......@@ -283,7 +273,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
}
}));
}
@Override
......@@ -697,17 +686,57 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
* returned if there is an issue in processing the objective.
*/
protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthTypeCriterion ethType =
(EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
if ((ethType == null) ||
(ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
(ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)) {
log.warn("processSpecific: Unsupported "
+ "forwarding objective criteraia");
log.trace("Processing specific fwd objective:{} in dev:{} with next:{}",
fwd.id(), deviceId, fwd.nextId());
boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
boolean isEthDstObj = isSupportedEthDstObjective(fwd);
if (isEthTypeObj) {
return processEthTypeSpecific(fwd);
} else if (isEthDstObj) {
return processEthDstSpecific(fwd);
} else {
log.warn("processSpecific: Unsupported forwarding objective "
+ "criteria fwd:{} in dev:{}", fwd.nextId(), deviceId);
fail(fwd, ObjectiveError.UNSUPPORTED);
return Collections.emptySet();
}
}
private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthTypeCriterion ethType = (EthTypeCriterion) selector
.getCriterion(Criterion.Type.ETH_TYPE);
if ((ethType == null) ||
((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
(ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
return false;
}
return true;
}
private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthCriterion ethDst = (EthCriterion) selector
.getCriterion(Criterion.Type.ETH_DST);
VlanIdCriterion vlanId = (VlanIdCriterion) selector
.getCriterion(Criterion.Type.VLAN_VID);
if (ethDst == null && vlanId == null) {
return false;
}
return true;
}
/**
* Handles forwarding rules to the IP and MPLS tables.
*
* @param fwd the forwarding objective
* @return A collection of flow rules, or an empty set
*/
protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
TrafficSelector selector = fwd.selector();
EthTypeCriterion ethType =
(EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
int forTableId = -1;
TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
......@@ -716,8 +745,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
.matchIPDst(((IPCriterion)
selector.getCriterion(Criterion.Type.IPV4_DST)).ip());
forTableId = UNICAST_ROUTING_TABLE;
log.debug("processing IPv4 specific forwarding objective {} in dev:{}",
fwd.id(), deviceId);
log.debug("processing IPv4 specific forwarding objective {} -> next:{}"
+ " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
} else {
filteredSelector
.matchEthType(Ethernet.MPLS_UNICAST)
......@@ -729,8 +758,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
filteredSelector.matchMplsBos(bos.mplsBos());
}
forTableId = MPLS_TABLE_1;
log.debug("processing MPLS specific forwarding objective {} in dev {}",
fwd.id(), deviceId);
log.debug("processing MPLS specific forwarding objective {} -> next:{}"
+ " in dev {}", fwd.id(), fwd.nextId(), deviceId);
}
TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
......@@ -754,6 +783,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
// MPLS interface, or a MPLS SWAP (with-same) but that would
// have to be handled in the next-objective. Also the pop-mpls
// logic used here won't work in non-BoS case.
fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
return Collections.emptySet();
}
......@@ -762,7 +792,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
// we only need the top level group's key to point the flow to it
Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
if (group == null) {
log.warn("The group left!");
log.warn("Group with key:{} for next-id:{} not found in dev:{}",
gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
fail(fwd, ObjectiveError.GROUPMISSING);
return Collections.emptySet();
}
......@@ -786,6 +817,88 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
return Collections.singletonList(ruleBuilder.build());
}
/**
* Handles forwarding rules to the L2 bridging table. Flow actions are not
* allowed in the bridging table - instead we use L2 Interface group or
* L2 flood group
*
* @param fwd the forwarding objective
* @return A collection of flow rules, or an empty set
*/
protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
List<FlowRule> rules = new ArrayList<>();
// Build filtered selector
TrafficSelector selector = fwd.selector();
EthCriterion ethCriterion = (EthCriterion) selector
.getCriterion(Criterion.Type.ETH_DST);
VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
.getCriterion(Criterion.Type.VLAN_VID);
if (vlanIdCriterion == null) {
log.warn("Forwarding objective for bridging requires vlan. Not "
+ "installing fwd:{} in dev:{}", fwd.id(), deviceId);
fail(fwd, ObjectiveError.BADPARAMS);
return Collections.emptySet();
}
TrafficSelector.Builder filteredSelectorBuilder =
DefaultTrafficSelector.builder();
// Do not match MacAddress for subnet broadcast entry
if (!ethCriterion.mac().equals(MacAddress.NONE)) {
filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
fwd.id(), fwd.nextId(), deviceId);
} else {
log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
+ "in dev:{} for vlan:{}",
fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
}
filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
TrafficSelector filteredSelector = filteredSelectorBuilder.build();
if (fwd.treatment() != null) {
log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
+ "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
}
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
if (fwd.nextId() != null) {
NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
if (next != null) {
List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
// we only need the top level group's key to point the flow to it
Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
if (group != null) {
treatmentBuilder.deferred().group(group.id());
} else {
log.warn("Group with key:{} for next-id:{} not found in dev:{}",
gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
fail(fwd, ObjectiveError.GROUPMISSING);
return Collections.emptySet();
}
}
}
treatmentBuilder.immediate().transition(ACL_TABLE);
TrafficTreatment filteredTreatment = treatmentBuilder.build();
// Build bridging table entries
FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
flowRuleBuilder.fromApp(fwd.appId())
.withPriority(fwd.priority())
.forDevice(deviceId)
.withSelector(filteredSelector)
.withTreatment(filteredTreatment)
.forTable(BRIDGING_TABLE);
if (fwd.permanent()) {
flowRuleBuilder.makePermanent();
} else {
flowRuleBuilder.makeTemporary(fwd.timeout());
}
rules.add(flowRuleBuilder.build());
return rules;
}
private void pass(Objective obj) {
if (obj.context().isPresent()) {
obj.context().get().onSuccess(obj);
......@@ -842,9 +955,26 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
* @param nextObj the nextObjective of type SIMPLE
*/
private void processSimpleNextObjective(NextObjective nextObj) {
// break up simple next objective to GroupChain objects
TrafficTreatment treatment = nextObj.next().iterator().next();
// determine if plain L2 or L3->L2
boolean plainL2 = true;
for (Instruction ins : treatment.allInstructions()) {
if (ins.type() == Instruction.Type.L2MODIFICATION) {
L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
if (l2ins.subtype() == L2SubType.ETH_DST ||
l2ins.subtype() == L2SubType.ETH_SRC) {
plainL2 = false;
break;
}
}
}
if (plainL2) {
createL2InterfaceGroup(nextObj);
return;
}
// break up simple next objective to GroupChain objects
GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
nextObj.appId(), false,
nextObj.meta());
......@@ -860,8 +990,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
Collections.singletonList(gkeyChain),
nextObj);
// store l3groupkey with the ofdpaGroupChain for the nextObjective that depends on it
pendingNextObjectives.put(groupInfo.outerGrpDesc.appCookie(), ofdpaGrp);
// store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
updatePendingNextObjective(groupInfo.outerGrpDesc.appCookie(), ofdpaGrp);
// now we are ready to send the l2 groupDescription (inner), as all the stores
// that will get async replies have been updated. By waiting to update
......@@ -869,6 +999,98 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
groupService.addGroup(groupInfo.innerGrpDesc);
}
private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) {
List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>();
nextList.add(value);
List<OfdpaNextGroup> ret = pendingNextObjectives.asMap()
.putIfAbsent(key, nextList);
if (ret != null) {
ret.add(value);
}
}
/**
* Creates a simple L2 Interface Group.
*
* @param nextObj the next Objective
*/
private void createL2InterfaceGroup(NextObjective nextObj) {
// only allowed actions are vlan pop and outport
TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
PortNumber portNum = null;
for (Instruction ins : nextObj.next().iterator().next().allInstructions()) {
if (ins.type() == Instruction.Type.L2MODIFICATION) {
L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
switch (l2ins.subtype()) {
case VLAN_POP:
ttb.add(l2ins);
break;
default:
break;
}
} else if (ins.type() == Instruction.Type.OUTPUT) {
portNum = ((OutputInstruction) ins).port();
ttb.add(ins);
} else {
log.warn("Driver does not handle this type of TrafficTreatment"
+ " 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(Type.VLAN_VID);
if (vidCriterion != null) {
vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
}
}
if (vlanid == null) {
log.error("Driver cannot process an L2/L3 group chain without "
+ "egress vlan information for dev: {} port:{}",
deviceId, portNum);
return;
}
// assemble information for ofdpa l2interface group
Integer l2groupId = L2INTERFACEMASK | (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());
final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
// create group description for the l2interfacegroup
GroupBucket l2interfaceGroupBucket =
DefaultGroupBucket.createIndirectGroupBucket(ttb.build());
GroupDescription l2groupDescription =
new DefaultGroupDescription(
deviceId,
GroupDescription.Type.INDIRECT,
new GroupBuckets(Collections.singletonList(
l2interfaceGroupBucket)),
l2groupkey,
l2groupId,
nextObj.appId());
log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
deviceId, Integer.toHexString(l2groupId),
l2groupkey, nextObj.id());
// create object for local and distributed storage
Deque<GroupKey> singleKey = new ArrayDeque<>();
singleKey.addFirst(l2groupkey);
OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
Collections.singletonList(singleKey),
nextObj);
// store l2groupkey for the nextObjective that depends on it
updatePendingNextObjective(l2groupkey, ofdpaGrp);
// send the group description to the group service
groupService.addGroup(l2groupDescription);
}
/**
* Creates one of two possible group-chains from the treatment
* passed in. Depending on the MPLS boolean, this method either creates
......@@ -895,6 +1117,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
VlanId vlanid = null;
long portNum = 0;
boolean setVlan = false, popVlan = false;
for (Instruction ins : treatment.allInstructions()) {
if (ins.type() == Instruction.Type.L2MODIFICATION) {
L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
......@@ -908,9 +1131,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
case VLAN_ID:
vlanid = ((ModVlanIdInstruction) l2ins).vlanId();
outerTtb.setVlanId(vlanid);
setVlan = true;
break;
case VLAN_POP:
innerTtb.popVlan();
popVlan = true;
break;
case DEC_MPLS_TTL:
case MPLS_LABEL:
......@@ -935,12 +1160,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
vlanid = port2Vlan.get(PortNumber.portNumber(portNum));
}
if (vlanid == null) {
// use metadata
for (Criterion metaCriterion : meta.criteria()) {
if (metaCriterion.type() == Type.VLAN_VID) {
vlanid = ((VlanIdCriterion) metaCriterion).vlanId();
}
if (vlanid == null && meta != null) {
// use metadata if available
Criterion vidCriterion = meta.getCriterion(Type.VLAN_VID);
if (vidCriterion != null) {
vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
}
}
......@@ -951,6 +1175,14 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
return null;
}
if (!setVlan && !popVlan) {
// untagged outgoing port
TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
temp.popVlan();
innerTtb.build().allInstructions().forEach(i -> temp.add(i));
innerTtb = temp;
}
// assemble information for ofdpa l2interface group
Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
// a globally unique groupkey that is different for ports in the same devices
......@@ -1077,6 +1309,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
}
// also ensure that all ports are in the same vlan
// XXX maybe HA issue here?
VlanId thisvlanid = port2Vlan.get(portNum);
if (vlanid == null) {
vlanid = thisvlanid;
......@@ -1151,7 +1384,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
// store l2floodgroupkey with the ofdpaGroupChain for the nextObjective
// that depends on it
pendingNextObjectives.put(l2floodgroupkey, ofdpaGrp);
updatePendingNextObjective(l2floodgroupkey, ofdpaGrp);
for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
// store all l2groupkeys with the groupChainElem for the l2floodgroup
......@@ -1336,7 +1569,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
// store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
// that depends on it
pendingNextObjectives.put(l3ecmpGroupKey, ofdpaGrp);
updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp);
log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
deviceId, Integer.toHexString(l3ecmpGroupId),
......@@ -1422,16 +1655,18 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
processGroupChain(gce);
}
} else {
OfdpaNextGroup obj = pendingNextObjectives.getIfPresent(key);
if (obj != null) {
log.info("Group service processed group key {} in device:{}. "
+ "Done implementing next objective: {} <<-->> gid:{}",
key, deviceId, obj.nextObjective().id(),
Integer.toHexString(groupService.getGroup(deviceId, key)
.givenGroupId()));
pass(obj.nextObjective());
List<OfdpaNextGroup> objList = pendingNextObjectives.getIfPresent(key);
if (objList != null) {
pendingNextObjectives.invalidate(key);
flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
objList.forEach(obj -> {
log.info("Group service processed group key {} in device:{}. "
+ "Done implementing next objective: {} <<-->> gid:{}",
key, deviceId, obj.nextObjective().id(),
Integer.toHexString(groupService.getGroup(deviceId, key)
.givenGroupId()));
pass(obj.nextObjective());
flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
});
}
}
});
......@@ -1455,16 +1690,18 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
processGroupChain(gce);
}
} else {
OfdpaNextGroup obj = pendingNextObjectives.getIfPresent(key);
if (obj != null) {
log.info("group ADDED with key {} in dev {}.. Done implementing next "
+ "objective: {} <<-->> gid:{}",
key, deviceId, obj.nextObjective().id(),
Integer.toHexString(groupService.getGroup(deviceId, key)
.givenGroupId()));
pass(obj.nextObjective());
List<OfdpaNextGroup> objList = pendingNextObjectives.getIfPresent(key);
if (objList != null) {
pendingNextObjectives.invalidate(key);
flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
objList.forEach(obj -> {
log.info("group ADDED with key {} in dev {}.. Done implementing next "
+ "objective: {} <<-->> gid:{}",
key, deviceId, obj.nextObjective().id(),
Integer.toHexString(groupService.getGroup(deviceId, key)
.givenGroupId()));
pass(obj.nextObjective());
flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
});
}
}
}
......@@ -1550,418 +1787,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
" waiting-on-groups: " + waitOnGroups.get() +
" device: " + deviceId);
}
}
//////////////////////////////////////
// Test code to be used for future
// static-flow-pusher app
//////////////////////////////////////
public void processStaticFlows() {
//processPortTable();
processGroupTable();
processVlanTable();
processTmacTable();
processIpTable();
//processMcastTable();
//processBridgingTable();
processAclTable();
sendPackets();
processMplsTable();
}
protected void processGroupTable() {
TrafficTreatment.Builder act = DefaultTrafficTreatment.builder();
act.popVlan(); // to send out untagged packets
act.setOutput(PortNumber.portNumber(24));
GroupBucket bucket =
DefaultGroupBucket.createIndirectGroupBucket(act.build());
final GroupKey groupkey = new DefaultGroupKey(appKryo.serialize(500));
Integer groupId = 0x00c80018; //l2 interface, vlan 200, port 24
GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
GroupDescription.Type.INDIRECT,
new GroupBuckets(Collections.singletonList(bucket)),
groupkey,
groupId,
driverId);
groupService.addGroup(groupDescription);
TrafficTreatment.Builder act2 = DefaultTrafficTreatment.builder();
act2.setOutput(PortNumber.portNumber(40));
GroupBucket bucket2 = DefaultGroupBucket.createIndirectGroupBucket(act2.build());
final GroupKey groupkey2 = new DefaultGroupKey(appKryo.serialize(502));
Integer groupId2 = 0x00c50028; //l2 interface, vlan 197, port 40
GroupDescription groupDescription2 = new DefaultGroupDescription(deviceId,
GroupDescription.Type.INDIRECT,
new GroupBuckets(Collections.singletonList(bucket2)),
groupkey2,
groupId2,
driverId);
groupService.addGroup(groupDescription2);
while (groupService.getGroup(deviceId, groupkey2) == null) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//Now for L3 Unicast group
TrafficTreatment.Builder act3 = DefaultTrafficTreatment.builder();
act3.setEthDst(MacAddress.valueOf(0x2020));
act3.setEthSrc(MacAddress.valueOf(0x1010));
act3.setVlanId(VlanId.vlanId((short) 200));
act3.group(new DefaultGroupId(0x00c80018)); // point to L2 interface
// MPLS interface group - does not work for popping single label
//Integer secGroupId = MPLSINTERFACEMASK | 38; // 0x90000026
Integer groupId3 = L3UNICASTMASK | 1; // 0x20000001
GroupBucket bucket3 =
DefaultGroupBucket.createIndirectGroupBucket(act3.build());
final GroupKey groupkey3 = new DefaultGroupKey(appKryo.serialize(503));
GroupDescription groupDescription3 = new DefaultGroupDescription(deviceId,
GroupDescription.Type.INDIRECT,
new GroupBuckets(Collections.singletonList(bucket3)),
groupkey3,
groupId3,
driverId);
groupService.addGroup(groupDescription3);
//Another L3 Unicast group
TrafficTreatment.Builder act4 = DefaultTrafficTreatment.builder();
act4.setEthDst(MacAddress.valueOf(0x3030));
act4.setEthSrc(MacAddress.valueOf(0x1010));
act4.setVlanId(VlanId.vlanId((short) 197));
act4.group(new DefaultGroupId(0x00c50028)); // point to L2 interface
Integer groupId4 = L3UNICASTMASK | 2; // 0x20000002
GroupBucket bucket4 =
DefaultGroupBucket.createIndirectGroupBucket(act4.build());
final GroupKey groupkey4 = new DefaultGroupKey(appKryo.serialize(504));
GroupDescription groupDescription4 = new DefaultGroupDescription(deviceId,
GroupDescription.Type.INDIRECT,
new GroupBuckets(Collections.singletonList(bucket4)),
groupkey4,
groupId4,
driverId);
groupService.addGroup(groupDescription4);
while (groupService.getGroup(deviceId, groupkey4) == null) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// L3 ecmp group
TrafficTreatment.Builder act5 = DefaultTrafficTreatment.builder();
act5.group(new DefaultGroupId(0x20000001));
TrafficTreatment.Builder act6 = DefaultTrafficTreatment.builder();
act6.group(new DefaultGroupId(0x20000002));
GroupBucket buckete1 =
DefaultGroupBucket.createSelectGroupBucket(act5.build());
GroupBucket buckete2 =
DefaultGroupBucket.createSelectGroupBucket(act6.build());
List<GroupBucket> bktlist = new ArrayList<GroupBucket>();
bktlist.add(buckete1);
bktlist.add(buckete2);
final GroupKey groupkey5 = new DefaultGroupKey(appKryo.serialize(505));
Integer groupId5 = L3ECMPMASK | 5; // 0x70000005
GroupDescription groupDescription5 = new DefaultGroupDescription(deviceId,
GroupDescription.Type.SELECT,
new GroupBuckets(bktlist),
groupkey5,
groupId5,
driverId);
groupService.addGroup(groupDescription5);
}
@SuppressWarnings("deprecation")
protected void processMplsTable() {
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchEthType(Ethernet.MPLS_UNICAST);
selector.matchMplsLabel(MplsLabel.mplsLabel(0xff)); //255
selector.matchMplsBos(true);
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.decMplsTtl(); // nw_ttl does not work
treatment.copyTtlIn();
treatment.popMpls(Ethernet.TYPE_IPV4);
treatment.deferred().group(new DefaultGroupId(0x20000001)); // point to L3 Unicast
//treatment.deferred().group(new DefaultGroupId(0x70000005)); // point to L3 ECMP
treatment.transition(ACL_TABLE);
FlowRule test = DefaultFlowRule.builder().forDevice(deviceId)
.withSelector(selector.build()).withTreatment(treatment.build())
.withPriority(DEFAULT_PRIORITY).fromApp(driverId).makePermanent()
.forTable(24).build();
ops = ops.add(test);
flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
log.info("Initialized mpls table");
}
@Override
public void onError(FlowRuleOperations ops) {
log.info("Failed to initialize mpls table");
}
}));
}
protected void processPortTable() {
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchInPort(PortNumber.portNumber(0)); // should be maskable?
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
treatment.transition(VLAN_TABLE);
FlowRule tmisse = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(LOWEST_PRIORITY)
.fromApp(driverId)
.makePermanent()
.forTable(PORT_TABLE).build();
ops = ops.add(tmisse);
flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
log.info("Initialized port table");
}
@Override
public void onError(FlowRuleOperations ops) {
log.info("Failed to initialize port table");
}
}));
}
private void processVlanTable() {
// Table miss entry is not required as ofdpa default is to drop
// In OF terms, the absence of a t.m.e. also implies drop
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchInPort(PortNumber.portNumber(12));
selector.matchVlanId(VlanId.vlanId((short) 100));
treatment.transition(TMAC_TABLE);
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(DEFAULT_PRIORITY)
.fromApp(driverId)
.makePermanent()
.forTable(VLAN_TABLE).build();
ops = ops.add(rule);
flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
log.info("Initialized vlan table");
}
@Override
public void onError(FlowRuleOperations ops) {
log.info("Failed to initialize vlan table");
}
}));
}
protected void processTmacTable() {
//table miss entry
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchInPort(PortNumber.portNumber(12));
selector.matchVlanId(VlanId.vlanId((short) 100));
selector.matchEthType(Ethernet.TYPE_IPV4);
selector.matchEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
treatment.transition(UNICAST_ROUTING_TABLE);
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(DEFAULT_PRIORITY)
.fromApp(driverId)
.makePermanent()
.forTable(TMAC_TABLE).build();
ops = ops.add(rule);
selector.matchEthType(Ethernet.MPLS_UNICAST);
treatment.transition(MPLS_TABLE_0);
FlowRule rulempls = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(DEFAULT_PRIORITY)
.fromApp(driverId)
.makePermanent()
.forTable(TMAC_TABLE).build();
ops = ops.add(rulempls);
flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
log.info("Initialized tmac table");
}
@Override
public void onError(FlowRuleOperations ops) {
log.info("Failed to initialize tmac table");
}
}));
}
protected void processIpTable() {
//table miss entry
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchEthType(Ethernet.TYPE_IPV4);
selector.matchIPDst(IpPrefix.valueOf("2.0.0.0/16"));
treatment.deferred().group(new DefaultGroupId(0x20000001));
treatment.transition(ACL_TABLE);
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(30000)
.fromApp(driverId)
.makePermanent()
.forTable(UNICAST_ROUTING_TABLE).build();
ops = ops.add(rule);
flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
log.info("Initialized IP table");
}
@Override
public void onError(FlowRuleOperations ops) {
log.info("Failed to initialize unicast IP table");
}
}));
}
protected void processAclTable() {
//table miss entry
FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
selector.matchEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
treatment.deferred().group(new DefaultGroupId(0x20000001));
FlowRule rule = DefaultFlowRule.builder()
.forDevice(deviceId)
.withSelector(selector.build())
.withTreatment(treatment.build())
.withPriority(60000)
.fromApp(driverId)
.makePermanent()
.forTable(ACL_TABLE).build();
ops = ops.add(rule);
flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
@Override
public void onSuccess(FlowRuleOperations ops) {
log.info("Initialized Acl table");
}
@Override
public void onError(FlowRuleOperations ops) {
log.info("Failed to initialize Acl table");
}
}));
}
private void sendPackets() {
Ethernet eth = new Ethernet();
eth.setDestinationMACAddress("00:00:00:00:00:02");
eth.setSourceMACAddress("00:00:00:11:22:33");
eth.setVlanID((short) 100);
eth.setEtherType(Ethernet.MPLS_UNICAST);
MPLS mplsPkt = new MPLS();
mplsPkt.setLabel(255);
mplsPkt.setTtl((byte) 5);
IPv4 ipv4 = new IPv4();
ipv4.setDestinationAddress("4.0.5.6");
ipv4.setSourceAddress("1.0.2.3");
ipv4.setTtl((byte) 64);
ipv4.setChecksum((short) 0);
UDP udp = new UDP();
udp.setDestinationPort(666);
udp.setSourcePort(333);
udp.setPayload(new Data(new byte[]{(byte) 1, (byte) 2}));
udp.setChecksum((short) 0);
ipv4.setPayload(udp);
mplsPkt.setPayload(ipv4);
eth.setPayload(mplsPkt);
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(PortNumber.portNumber(24))
.build();
OutboundPacket packet = new DefaultOutboundPacket(deviceId,
treatment,
ByteBuffer.wrap(eth.serialize()));
Ethernet eth2 = new Ethernet();
eth2.setDestinationMACAddress("00:00:00:00:00:02");
eth2.setSourceMACAddress("00:00:00:11:22:33");
eth2.setVlanID((short) 100);
eth2.setEtherType(Ethernet.TYPE_IPV4);
IPv4 ipv42 = new IPv4();
ipv42.setDestinationAddress("2.0.0.2");
ipv42.setSourceAddress("1.0.9.9");
ipv42.setTtl((byte) 64);
ipv42.setChecksum((short) 0);
UDP udp2 = new UDP();
udp2.setDestinationPort(999);
udp2.setSourcePort(333);
udp2.setPayload(new Data(new byte[]{(byte) 1, (byte) 2}));
udp2.setChecksum((short) 0);
ipv42.setPayload(udp2);
eth2.setPayload(ipv42);
TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
.setOutput(PortNumber.portNumber(26))
.build();
OutboundPacket packet2 = new DefaultOutboundPacket(deviceId,
treatment2,
ByteBuffer.wrap(eth2.serialize()));
log.info("Emitting packets now");
packetService.emit(packet);
packetService.emit(packet);
packetService.emit(packet2);
packetService.emit(packet);
packetService.emit(packet);
log.info("Done emitting packets");
}
private class InternalPacketProcessor implements PacketProcessor {
@Override
public void process(PacketContext context) {
}
}
}
......
......@@ -287,24 +287,14 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
case SIMPLE:
Collection<TrafficTreatment> treatments = nextObjective.next();
if (treatments.size() == 1) {
TrafficTreatment treatment = treatments.iterator().next();
GroupBucket bucket = DefaultGroupBucket
.createIndirectGroupBucket(treatment);
final GroupKey key = new DefaultGroupKey(
appKryo.serialize(nextObjective
.id()));
GroupDescription groupDescription = new DefaultGroupDescription(
deviceId,
GroupDescription.Type.INDIRECT,
new GroupBuckets(
Collections.singletonList(bucket)),
key,
null,
nextObjective.appId());
log.debug("Creating SIMPLE group for next objective id {} "
+ "in dev:{}", nextObjective.id(), deviceId);
pendingGroups.put(key, nextObjective);
groupService.addGroup(groupDescription);
// Spring Open TTP converts simple nextObjective to flow-actions
// in a dummy group
TrafficTreatment treatment = nextObjective.next().iterator().next();
log.debug("Converting SIMPLE group for next objective id {} " +
"to {} flow-actions in device:{}", nextObjective.id(),
treatment.allInstructions().size(), deviceId);
flowObjectiveStore.putNextGroup(nextObjective.id(),
new SpringOpenGroup(null, treatment));
}
break;
case HASHED:
......@@ -624,8 +614,9 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
if (next != null) {
SpringOpenGroup soGroup = appKryo.deserialize(next.data());
if (soGroup.dummy) {
log.debug("Adding flow-actions for fwd. obj. {} "
+ "in dev: {}", fwd.id(), deviceId);
log.debug("Adding {} flow-actions for fwd. obj. {} -> next:{} "
+ "in dev: {}", soGroup.treatment.allInstructions().size(),
fwd.id(), fwd.nextId(), deviceId);
for (Instruction ins : soGroup.treatment.allInstructions()) {
treatmentBuilder.add(ins);
}
......@@ -639,7 +630,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
}
treatmentBuilder.deferred().group(group.id());
log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
+ "in dev: {}", group.id(), fwd.id(), deviceId);
+ "for next:{} in dev: {}", group.id(), fwd.id(),
fwd.nextId(), deviceId);
}
} else {
log.warn("processSpecific: No associated next objective object");
......@@ -705,10 +697,11 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
if (next != null) {
SpringOpenGroup soGrp = appKryo.deserialize(next.data());
if (soGrp.dummy) {
log.debug("Adding flow-actions for fwd. obj. {} "
+ "in dev: {}", fwd.id(), deviceId);
log.debug("Adding {} flow-actions for fwd. obj. {} "
+ "in dev: {}", soGrp.treatment.allInstructions().size(),
fwd.id(), deviceId);
for (Instruction ins : soGrp.treatment.allInstructions()) {
treatmentBuilder.add(ins);
treatmentBuilder.deferred().add(ins);
}
} else {
GroupKey key = soGrp.key;
......@@ -773,6 +766,12 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
return rules;
}
/*
* Note: CpqD switches do not handle MPLS-related operation properly
* for a packet with VLAN tag. We pop VLAN here as a workaround.
* Side effect: HostService learns redundant hosts with same MAC but
* different VLAN. No known side effect on the network reachability.
*/
protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
VlanIdCriterion vlanIdCriterion,
FilteringObjective filt,
......@@ -783,12 +782,6 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
}
/*
* Note: CpqD switches do not handle MPLS-related operation properly
* for a packet with VLAN tag. We pop VLAN here as a workaround.
* Side effect: HostService learns redundant hosts with same MAC but
* different VLAN. No known side effect on the network reachability.
*/
List<FlowRule> rules = new ArrayList<>();
TrafficSelector.Builder selectorIp = DefaultTrafficSelector
.builder();
......