A few things:
1) Moved to loxigen-openflowj version 0.4.0 which supports MPLS BoS indicator matching and set-action
2) Fixed OFMessageDecoder/OFChannelHandler to not crash when openflowj cannot decode a message from a switch
3) Fixed ACL flows to use writeActions instead of applyActions to affect the packet's action-set
4) Tested multiple-label operation using group-chaining and BoS indicator bit
Change-Id: Ia68997e57374ecc7a397f2319166484a257e0d8c
diff --git a/pom.xml b/pom.xml
index ea83b8e..6b61e07 100644
--- a/pom.xml
+++ b/pom.xml
@@ -711,7 +711,7 @@
<dependency>
<groupId>org.projectfloodlight</groupId>
<artifactId>openflowj</artifactId>
- <version>0.3.8-SNAPSHOT</version>
+ <version>0.4.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
index 45753f9..5f844f5 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
@@ -2077,12 +2077,15 @@
counters.switchDisconnectSwitchStateException.updateCounterWithFlush();
ctx.getChannel().close();
} else if (e.getCause() instanceof OFParseError) {
- log.error("Disconnecting switch "
+ log.error("Parse failure in switch "
+ getSwitchInfoString() +
" due to message parse failure",
e.getCause());
counters.switchDisconnectParseError.updateCounterWithFlush();
- ctx.getChannel().close();
+ // OFParse errors should now be handled in the OFMessageDecoder
+ // So it should never get here. Nevertheless we should not close
+ // channels for parse errors.
+ // ctx.getChannel().close();
} else if (e.getCause() instanceof RejectedExecutionException) {
log.warn("Could not process message: queue full");
counters.rejectedExecutionException.updateCounterWithFlush();
@@ -2387,6 +2390,10 @@
return state;
}
+ public String getChannelSwitchInfo() {
+ return sw.getStringId();
+ }
+
void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) {
roleChanger = new RoleChanger(roleTimeoutMs);
}
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java b/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java
index 43257ca..0dde61c 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFMessageDecoder.java
@@ -17,21 +17,22 @@
package net.floodlightcontroller.core.internal;
-import java.util.List;
-
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
import org.projectfloodlight.openflow.protocol.OFFactories;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFMessageReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Decode an openflow message from a Channel, for use in a netty pipeline
*/
public class OFMessageDecoder extends FrameDecoder {
-
+ private static final Logger log = LoggerFactory.getLogger(OFMessageDecoder.class);
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer) throws Exception {
@@ -49,9 +50,23 @@
// a list of the parsed messages to the controller.
// The performance *may or may not* not be as good as before.
OFMessageReader<OFMessage> reader = OFFactories.getGenericReader();
- OFMessage message = reader.readFrom(buffer);
+ OFMessage message = null;
+ try {
+ message = reader.readFrom(buffer);
+ } catch (OFParseError e) {
+ OFChannelHandler ofch = (OFChannelHandler) ctx.getPipeline().getLast();
+ log.error("Parse failure of incoming message from switch "
+ + ofch.getChannelSwitchInfo() + " Index:Byte ==> {}:{} {}",
+ buffer.readerIndex(),
+ buffer.getByte(buffer.readerIndex()),
+ buffer.array());
- return message;
+ buffer.clear(); // MUST CLEAR BUFFER or next message will be read
+ // incorrectly
+ return null;
+ }
+
+ return message;
}
}
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
index cde23c4..8ebe8cc 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCPqD13.java
@@ -77,12 +77,14 @@
import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4DstMasked;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsBos;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmMplsLabel;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid;
import org.projectfloodlight.openflow.types.EthType;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.IpProtocol;
import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.OFBooleanValue;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFGroup;
import org.projectfloodlight.openflow.types.OFPort;
@@ -1260,7 +1262,9 @@
.ethType(EthType.MPLS_UNICAST);
OFOxmMplsLabel labelid = factory.oxms()
.mplsLabel(U32.of(mplsm.getMplsLabel()));
- OFOxmList oxmList = OFOxmList.of(ethTypeMpls, labelid);
+ // OFOxmMplsBos bos = factory.oxms()
+ // .mplsBos(OFBooleanValue.of(mplsm.isBos()));
+ OFOxmList oxmList = OFOxmList.of(ethTypeMpls, labelid); // XXX add bos
OFMatchV3 matchlabel = factory.buildMatchV3()
.setOxmList(oxmList).build();
@@ -1369,21 +1373,21 @@
}
// set actions
- List<OFAction> applyActions = new ArrayList<OFAction>();
+ List<OFAction> writeActions = new ArrayList<OFAction>();
for (Action action : ma.getActions()) {
OFAction ofAction = getOFAction(action);
if (ofAction != null) {
- applyActions.add(ofAction);
+ writeActions.add(ofAction);
}
}
// set instructions
OFInstruction clearInstr = factory.instructions().clearActions();
- OFInstruction applyInstr = factory.instructions().buildApplyActions()
- .setActions(applyActions).build();
+ OFInstruction writeInstr = factory.instructions().buildWriteActions()
+ .setActions(writeActions).build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
instructions.add(clearInstr);
- instructions.add(applyInstr);
+ instructions.add(writeInstr);
// set flow-mod
OFFlowMod.Builder fmBuilder = null;
@@ -1467,6 +1471,24 @@
}
}
+ @Override
+ public TableId getTableId(String tableType) {
+ tableType = tableType.toLowerCase();
+ if (tableType.contentEquals("ip")) {
+ return TableId.of(OFSwitchImplCPqD13.TABLE_IPv4_UNICAST);
+ }
+ else if (tableType.contentEquals("mpls")) {
+ return TableId.of(OFSwitchImplCPqD13.TABLE_MPLS);
+ }
+ else if (tableType.contentEquals("acl")) {
+ return TableId.of(OFSwitchImplCPqD13.TABLE_ACL);
+ }
+ else {
+ log.warn("Invalid tableType: {}", tableType);
+ return null;
+ }
+ }
+
// *****************************
// Unused
// *****************************
@@ -1549,21 +1571,185 @@
log.info("Sw: {} Group Features {}", getStringId(), gfsr);
}
- @Override
- public TableId getTableId(String tableType) {
- tableType = tableType.toLowerCase();
- if(tableType.contentEquals("ip")){
- return TableId.of(OFSwitchImplCPqD13.TABLE_IPv4_UNICAST);
- }
- else if (tableType.contentEquals("mpls")){
- return TableId.of(OFSwitchImplCPqD13.TABLE_MPLS);
- }
- else if (tableType.contentEquals("acl")){
- return TableId.of(OFSwitchImplCPqD13.TABLE_ACL);
- }
- else{
- log.warn("Invalid tableType: {}", tableType);
- return null;
+ @SuppressWarnings("unused")
+ private void testMultipleLabels() {
+ if (getId() == 1) {
+ List<OFMessage> msglist = new ArrayList<OFMessage>();
+
+ // first all the indirect groups
+
+ // the group to switch 2 with outer label
+ OFGroup g1 = OFGroup.of(201);
+ OFOxmEthDst dmac1 = factory.oxms().ethDst(MacAddress.of("00:00:02:02:02:80"));
+ OFAction push1 = factory.actions().pushMpls(EthType.MPLS_UNICAST);
+ OFOxmMplsLabel lid1 = factory.oxms()
+ .mplsLabel(U32.of(105)); // outer label
+ OFAction setMpls1 = factory.actions().buildSetField()
+ .setField(lid1).build();
+ OFOxmMplsBos bos1 = factory.oxms()
+ .mplsBos(OFBooleanValue.FALSE);
+ OFAction setB1 = factory.actions().buildSetField()
+ .setField(bos1).build();
+ OFAction setDA1 = factory.actions().buildSetField()
+ .setField(dmac1).build();
+ OFAction outp1 = factory.actions().buildOutput()
+ .setPort(OFPort.of(2))
+ .build();
+ List<OFAction> a1 = new ArrayList<OFAction>();
+ a1.add(push1);
+ a1.add(setMpls1);
+ a1.add(setB1);
+ a1.add(setDA1);
+ a1.add(outp1);
+ OFBucket b1 = factory.buildBucket()
+ .setActions(a1)
+ .build();
+ OFMessage gm1 = factory.buildGroupAdd()
+ .setGroup(g1)
+ .setBuckets(Collections.singletonList(b1))
+ .setGroupType(OFGroupType.INDIRECT)
+ .setXid(getNextTransactionId())
+ .build();
+ msglist.add(gm1);
+
+ // the group to switch 3 with outer label
+ OFGroup g2 = OFGroup.of(301);
+ OFOxmEthDst dmac2 = factory.oxms().ethDst(MacAddress.of("00:00:03:03:03:80"));
+ OFAction push2 = factory.actions().pushMpls(EthType.MPLS_UNICAST);
+ OFOxmMplsLabel lid2 = factory.oxms()
+ .mplsLabel(U32.of(104)); // outer label
+ OFAction setMpls2 = factory.actions().buildSetField()
+ .setField(lid2).build();
+ OFOxmMplsBos bos2 = factory.oxms()
+ .mplsBos(OFBooleanValue.FALSE);
+ OFAction setB2 = factory.actions().buildSetField()
+ .setField(bos2).build();
+ OFAction setDA2 = factory.actions().buildSetField()
+ .setField(dmac2).build();
+ OFAction outp2 = factory.actions().buildOutput()
+ .setPort(OFPort.of(3))
+ .build();
+ List<OFAction> a2 = new ArrayList<OFAction>();
+ a2.add(push2);
+ a2.add(setMpls2);
+ a2.add(setB2);
+ a2.add(setDA2);
+ a2.add(outp2);
+ OFBucket b2 = factory.buildBucket()
+ .setActions(a2)
+ .build();
+ OFMessage gm2 = factory.buildGroupAdd()
+ .setGroup(g2)
+ .setBuckets(Collections.singletonList(b2))
+ .setGroupType(OFGroupType.INDIRECT)
+ .setXid(getNextTransactionId())
+ .build();
+ msglist.add(gm2);
+
+ // now add main ECMP group with inner labels
+ OFGroup group = OFGroup.of(786);
+ List<OFBucket> buckets = new ArrayList<OFBucket>();
+ for (int i = 0; i < 2; i++) { // 2 buckets
+
+ List<OFAction> actions = new ArrayList<OFAction>();
+ OFOxmEthSrc smac = factory.oxms()
+ .ethSrc(MacAddress.of("00:00:01:01:01:80"));
+ OFAction setSA = factory.actions().buildSetField()
+ .setField(smac).build();
+ actions.add(setSA);
+
+ if (i == 0) {
+ // send to switch 2
+ OFAction pushX = factory.actions().pushMpls(EthType.MPLS_UNICAST);
+ OFOxmMplsLabel lidX = factory.oxms()
+ .mplsLabel(U32.of(106)); // inner label
+ OFAction setX = factory.actions().buildSetField()
+ .setField(lidX).build();
+ OFOxmMplsBos bosX = factory.oxms()
+ .mplsBos(OFBooleanValue.TRUE);
+ OFAction setBX = factory.actions().buildSetField()
+ .setField(bosX).build();
+ OFAction ogX = factory.actions().buildGroup()
+ .setGroup(g1).build();
+ actions.add(pushX);
+ actions.add(setX);
+ actions.add(setBX);
+ actions.add(ogX);
+
+ } else {
+ // send to switch 3
+ OFAction pushY = factory.actions().pushMpls(EthType.MPLS_UNICAST);
+ OFOxmMplsLabel lidY = factory.oxms()
+ .mplsLabel(U32.of(106)); // inner label
+ OFAction setY = factory.actions().buildSetField()
+ .setField(lidY).build();
+ OFOxmMplsBos bosY = factory.oxms()
+ .mplsBos(OFBooleanValue.TRUE);
+ OFAction setBY = factory.actions().buildSetField()
+ .setField(bosY).build();
+ OFAction ogY = factory.actions().buildGroup()
+ .setGroup(g2).build();
+ actions.add(pushY);
+ actions.add(setY);
+ actions.add(setBY);
+ actions.add(ogY);
+ }
+
+ OFBucket ofb = factory.buildBucket()
+ .setWeight(1)
+ .setActions(actions)
+ .build();
+ buckets.add(ofb);
+ }
+
+ OFMessage gm = factory.buildGroupAdd()
+ .setGroup(group)
+ .setBuckets(buckets)
+ .setGroupType(OFGroupType.SELECT)
+ .setXid(getNextTransactionId())
+ .build();
+ msglist.add(gm);
+
+ // create an ACL entry to use this ecmp group
+ Builder matchBuilder = factory.buildMatch();
+ matchBuilder.setExact(MatchField.ETH_TYPE, EthType.of(0x800));
+ matchBuilder.setMasked(MatchField.IPV4_DST,
+ IPv4Address.of("7.7.7.0")
+ .withMaskOfLength(24));
+
+ OFAction grp = factory.actions().buildGroup()
+ .setGroup(OFGroup.of(786))
+ .build();
+ List<OFAction> writeActions = Collections.singletonList(grp);
+
+ OFInstruction clearInstr = factory.instructions().clearActions();
+ OFInstruction writeInstr = factory.instructions().buildWriteActions()
+ .setActions(writeActions).build();
+ List<OFInstruction> instructions = new ArrayList<OFInstruction>();
+ instructions.add(clearInstr);
+ instructions.add(writeInstr);
+
+ OFFlowMod.Builder fmBuilder = factory.buildFlowAdd();
+
+ OFMessage aclFlow = fmBuilder
+ .setTableId(TableId.of(TABLE_ACL))
+ .setMatch(matchBuilder.build())
+ .setInstructions(instructions)
+ .setPriority(10) // TODO: wrong - should be MA
+ // priority
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setIdleTimeout(0)
+ .setHardTimeout(0)
+ .setXid(getNextTransactionId())
+ .build();
+ msglist.add(aclFlow);
+
+ try {
+ write(msglist);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
}
}