Marc De Leenheer

Support lambda's in selector & treatment

package org.onlab.onos.optical.testapp;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.HashMap;
import java.util.Map;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.CoreService;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.device.DeviceEvent;
import org.onlab.onos.net.device.DeviceListener;
import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.flow.DefaultFlowRule;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.FlowRule;
import org.onlab.onos.net.flow.FlowRuleService;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.slf4j.Logger;
/**
* Sample reactive forwarding application.
*/
@Component(immediate = true)
public class LambdaForwarding {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
private ApplicationId appId;
private final InternalDeviceListener listener = new InternalDeviceListener();
private final Map<DeviceId, Integer> uglyMap = new HashMap<>();
@Activate
public void activate() {
appId = coreService.registerApplication("org.onlab.onos.fwd");
deviceService.addListener(listener);
uglyMap.put(DeviceId.deviceId("of:0000ffffffffff01"), 1);
uglyMap.put(DeviceId.deviceId("of:0000ffffffffff02"), 2);
uglyMap.put(DeviceId.deviceId("of:0000ffffffffff03"), 3);
log.info("Started with Application ID {}", appId.id());
}
@Deactivate
public void deactivate() {
flowRuleService.removeFlowRulesById(appId);
log.info("Stopped");
}
private void pushRules(Device device) {
TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
int inport;
int outport;
short lambda = 10;
int switchNumber = uglyMap.get(device.id());
switch (switchNumber) {
case 1:
inport = 10;
outport = 20;
sbuilder.matchInport(PortNumber.portNumber(inport));
tbuilder.setOutput(PortNumber.portNumber(outport)).setLambda(lambda);
break;
case 2:
inport = 21;
outport = 11;
sbuilder.matchLambda(lambda).matchInport(PortNumber.portNumber(inport)); // match sigtype
tbuilder.setOutput(PortNumber.portNumber(outport));
break;
case 3:
inport = 30;
outport = 31;
sbuilder.matchLambda(lambda).matchInport(PortNumber.portNumber(inport));
tbuilder.setOutput(PortNumber.portNumber(outport)).setLambda(lambda);
break;
default:
}
sbuilder.matchLambda((short) 25).matchInport(PortNumber.portNumber(5));
tbuilder.setOutput(PortNumber.portNumber(5));
TrafficTreatment treatement = tbuilder.build();
TrafficSelector selector = sbuilder.build();
FlowRule f = new DefaultFlowRule(device.id(), selector,
treatement, 100, appId, 600, false);
flowRuleService.applyFlowRules(f);
}
public class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
switch (event.type()) {
case DEVICE_ADDED:
pushRules(event.subject());
break;
case DEVICE_AVAILABILITY_CHANGED:
break;
case DEVICE_MASTERSHIP_CHANGED:
break;
case DEVICE_REMOVED:
break;
case DEVICE_SUSPENDED:
break;
case DEVICE_UPDATED:
break;
case PORT_ADDED:
break;
case PORT_REMOVED:
break;
case PORT_UPDATED:
break;
default:
break;
}
}
}
}
......@@ -176,6 +176,11 @@ public final class DefaultTrafficSelector implements TrafficSelector {
}
@Override
public Builder matchLambda(short lambda) {
return add(Criteria.matchLambda(lambda));
}
@Override
public TrafficSelector build() {
return new DefaultTrafficSelector(ImmutableSet.copyOf(selector.values()));
}
......
......@@ -137,6 +137,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
case OUTPUT:
outputs.add(instruction);
break;
case L0MODIFICATION:
case L2MODIFICATION:
case L3MODIFICATION:
// TODO: enforce modification order if any
......@@ -193,6 +194,11 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
}
@Override
public Builder setLambda(short lambda) {
return add(Instructions.modL0Lambda(lambda));
}
@Override
public TrafficTreatment build() {
//If we are dropping should we just return an emptry list?
......
......@@ -130,6 +130,13 @@ public interface TrafficSelector {
public Builder matchTcpDst(Short tcpPort);
/**
* Matches an optical signal ID or lambda.
* @param lambda
* @return a selection builder
*/
public Builder matchLambda(short lambda);
/**
* Builds an immutable traffic selector.
*
* @return traffic selector
......
......@@ -105,6 +105,13 @@ public interface TrafficTreatment {
public Builder setIpDst(IpPrefix addr);
/**
* Sets the optical channel ID or lambda.
* @param lambda optical channel ID
* @return a treatment builder
*/
public Builder setLambda(short lambda);
/**
* Builds an immutable traffic treatment descriptor.
*
* @return traffic treatment
......
......@@ -151,10 +151,19 @@ public final class Criteria {
return new TcpPortCriterion(tcpPort, Type.TCP_DST);
}
/*
* Implementations of criteria.
/**
* Creates a match on lambda field using the specified value.
*
* @param lambda
* @return match criterion
*/
public static Criterion matchLambda(Short lambda) {
return new LambdaCriterion(lambda, Type.OCH_SIGID);
}
/**
* Implementations of criteria.
*/
public static final class PortCriterion implements Criterion {
private final PortNumber port;
......@@ -523,4 +532,49 @@ public final class Criteria {
return false;
}
}
public static final class LambdaCriterion implements Criterion {
private final short lambda;
private final Type type;
public LambdaCriterion(short lambda, Type type) {
this.lambda = lambda;
this.type = type;
}
@Override
public Type type() {
return this.type;
}
public Short lambda() {
return this.lambda;
}
@Override
public String toString() {
return toStringHelper(type().toString())
.add("lambda", lambda).toString();
}
@Override
public int hashCode() {
return Objects.hash(lambda, type);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof LambdaCriterion) {
LambdaCriterion that = (LambdaCriterion) obj;
return Objects.equals(lambda, that.lambda) &&
Objects.equals(type, that.type);
}
return false;
}
}
}
......
......@@ -108,7 +108,11 @@ public interface Criterion {
/** Logical Port Metadata. */
TUNNEL_ID,
/** IPv6 Extension Header pseudo-field. */
IPV6_EXTHDR
IPV6_EXTHDR,
/** Optical channel signal ID (lambda). */
OCH_SIGID,
/** Optical channel signal type (fixed or flexible). */
OCH_SIGTYPE
}
/**
......
......@@ -43,6 +43,11 @@ public interface Instruction {
GROUP,
/**
* Signifies that the traffic should be modified in L0 way.
*/
L0MODIFICATION,
/**
* Signifies that the traffic should be modified in L2 way.
*/
L2MODIFICATION,
......
......@@ -24,6 +24,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Objects;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.instructions.L0ModificationInstruction.L0SubType;
import org.onlab.onos.net.flow.instructions.L0ModificationInstruction.ModLambdaInstruction;
import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.L2SubType;
import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
import org.onlab.onos.net.flow.instructions.L3ModificationInstruction.L3SubType;
......@@ -62,6 +64,16 @@ public final class Instructions {
}
/**
* Creates a l0 modification.
* @param lambda the lambda to modify to.
* @return a l0 modification
*/
public static L0ModificationInstruction modL0Lambda(short lambda) {
checkNotNull(lambda, "L0 lambda cannot be null");
return new ModLambdaInstruction(L0SubType.LAMBDA, lambda);
}
/**
* Creates a l2 src modification.
* @param addr the mac address to modify to.
* @return a l2 modification
......
package org.onlab.onos.net.flow.instructions;
import static com.google.common.base.MoreObjects.toStringHelper;
import java.util.Objects;
public abstract class L0ModificationInstruction implements Instruction {
/**
* Represents the type of traffic treatment.
*/
public enum L0SubType {
/**
* Lambda modification.
*/
LAMBDA
//TODO: remaining types
}
public abstract L0SubType subtype();
@Override
public Type type() {
return Type.L0MODIFICATION;
}
/**
* Represents a L0 lambda modification instruction.
*/
public static final class ModLambdaInstruction extends L0ModificationInstruction {
private final L0SubType subtype;
private final short lambda;
public ModLambdaInstruction(L0SubType subType, short lambda) {
this.subtype = subType;
this.lambda = lambda;
}
@Override
public L0SubType subtype() {
return this.subtype;
}
public short lambda() {
return this.lambda;
}
@Override
public String toString() {
return toStringHelper(subtype().toString())
.add("lambda", lambda).toString();
}
@Override
public int hashCode() {
return Objects.hash(lambda, type(), subtype);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ModLambdaInstruction) {
ModLambdaInstruction that = (ModLambdaInstruction) obj;
return Objects.equals(lambda, that.lambda) &&
Objects.equals(this.type(), that.type()) &&
Objects.equals(subtype, that.subtype);
}
return false;
}
}
}
package org.onlab.onos.openflow.controller;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import org.onlab.packet.Ethernet;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
......@@ -9,9 +13,6 @@ import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext {
private final AtomicBoolean free = new AtomicBoolean(true);
......
......@@ -23,6 +23,8 @@ import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
import org.projectfloodlight.openflow.protocol.OFInstructionType;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc;
......@@ -34,6 +36,7 @@ import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
import org.projectfloodlight.openflow.protocol.match.Match;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.Masked;
import org.slf4j.Logger;
......@@ -166,6 +169,15 @@ public class FlowEntryBuilder {
builder.setIpSrc(IpPrefix.valueOf(si.getInt()));
}
break;
case EXPERIMENTER:
OFActionExperimenter exp = (OFActionExperimenter) act;
if (exp.getExperimenter() == 0x80005A06) {
OFActionCircuit ct = (OFActionCircuit) exp;
builder.setLambda(((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber());
} else {
log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter());
}
break;
case SET_TP_DST:
case SET_TP_SRC:
case POP_MPLS:
......@@ -188,7 +200,7 @@ public class FlowEntryBuilder {
case DEC_MPLS_TTL:
case DEC_NW_TTL:
case ENQUEUE:
case EXPERIMENTER:
case GROUP:
default:
log.warn("Action type {} not yet implemented.", act.getType());
......@@ -268,6 +280,10 @@ public class FlowEntryBuilder {
case TCP_SRC:
builder.matchTcpSrc((short) match.get(MatchField.TCP_SRC).getPort());
break;
case OCH_SIGID:
builder.matchLambda(match.get(MatchField.OCH_SIGID).getChannelNumber());
break;
case OCH_SIGTYPE_BASIC:
case ARP_OP:
case ARP_SHA:
case ARP_SPA:
......
......@@ -14,6 +14,7 @@ import org.onlab.onos.net.flow.criteria.Criteria.EthCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.EthTypeCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.IPProtocolCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.LambdaCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.PortCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.TcpPortCriterion;
import org.onlab.onos.net.flow.criteria.Criteria.VlanIdCriterion;
......@@ -21,6 +22,8 @@ import org.onlab.onos.net.flow.criteria.Criteria.VlanPcpCriterion;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.flow.instructions.Instructions.OutputInstruction;
import org.onlab.onos.net.flow.instructions.L0ModificationInstruction;
import org.onlab.onos.net.flow.instructions.L0ModificationInstruction.ModLambdaInstruction;
import org.onlab.onos.net.flow.instructions.L2ModificationInstruction;
import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
import org.onlab.onos.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
......@@ -35,6 +38,7 @@ import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.match.Match;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.CircuitSignalID;
import org.projectfloodlight.openflow.types.EthType;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.IpProtocol;
......@@ -137,6 +141,8 @@ public class FlowModBuilder {
case DROP:
log.warn("Saw drop action; assigning drop action");
return new LinkedList<>();
case L0MODIFICATION:
acts.add(buildL0Modification(i));
case L2MODIFICATION:
acts.add(buildL2Modification(i));
break;
......@@ -157,6 +163,20 @@ public class FlowModBuilder {
return acts;
}
private OFAction buildL0Modification(Instruction i) {
L0ModificationInstruction l0m = (L0ModificationInstruction) i;
switch (l0m.subtype()) {
case LAMBDA:
ModLambdaInstruction ml = (ModLambdaInstruction) i;
return factory.actions().circuit(factory.oxms().ochSigidBasic(
new CircuitSignalID((byte) 1, (byte) 2, ml.lambda(), (short) 1)));
default:
log.warn("Unimplemented action type {}.", l0m.subtype());
break;
}
return null;
}
private OFAction buildL3Modification(Instruction i) {
L3ModificationInstruction l3m = (L3ModificationInstruction) i;
ModIPInstruction ip;
......@@ -261,6 +281,11 @@ public class FlowModBuilder {
tp = (TcpPortCriterion) c;
mBuilder.setExact(MatchField.TCP_SRC, TransportPort.of(tp.tcpPort()));
break;
case OCH_SIGID:
LambdaCriterion lc = (LambdaCriterion) c;
mBuilder.setExact(MatchField.OCH_SIGID,
new CircuitSignalID((byte) 1, (byte) 2, lc.lambda(), (short) 1));
break;
case ARP_OP:
case ARP_SHA:
case ARP_SPA:
......