alshabib

switch can now connect

......@@ -13,7 +13,7 @@ public interface OpenFlowSwitch {
*
* @param msg the message to write
*/
public void write(OFMessage msg);
public void sendMsg(OFMessage msg);
/**
* Handle a message from the switch.
......
......@@ -18,7 +18,9 @@
package org.onlab.onos.of.controller.impl.internal;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.jboss.netty.channel.Channel;
import org.onlab.onos.of.controller.Dpid;
......@@ -27,12 +29,14 @@ import org.onlab.onos.of.controller.RoleState;
import org.onlab.onos.of.controller.impl.internal.OpenFlowControllerImpl.OpenFlowSwitchAgent;
import org.onlab.onos.of.controller.impl.internal.RoleManager.RoleRecvStatus;
import org.onlab.onos.of.controller.impl.internal.RoleManager.RoleReplyInfo;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFErrorMsg;
import org.projectfloodlight.openflow.protocol.OFExperimenter;
import org.projectfloodlight.openflow.protocol.OFFactories;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFRoleReply;
import org.projectfloodlight.openflow.protocol.OFVersion;
......@@ -45,10 +49,13 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch {
private static Logger log =
LoggerFactory.getLogger(AbstractOpenFlowSwitch.class);
private Channel channel;
protected Channel channel;
protected boolean startDriverHandshakeCalled = false;
private boolean connected;
private Dpid dpid;
private OpenFlowSwitchAgent agent;
private AtomicInteger xidCounter = new AtomicInteger(0);
private OFVersion ofVersion;
......@@ -58,8 +65,12 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch {
private final RoleManager roleMan = new RoleManager(this);
protected AbstractOpenFlowSwitch(long dpid) {
this.dpid = new Dpid(dpid);
protected RoleState role;
protected OFFeaturesReply features;
protected AbstractOpenFlowSwitch(Dpid dp) {
this.dpid = dp;
}
//************************
......@@ -80,14 +91,16 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch {
*
* @param m the message to be written
*/
public abstract void write(OFMessage m);
public abstract void sendMsg(OFMessage m);
/**
* Writes to the OFMessage list to the output stream.
*
* @param msgs the messages to be written
*/
public abstract void write(List<OFMessage> msgs);
public void write(List<OFMessage> msgs) {
this.channel.write(msgs);
}
/**
......@@ -151,7 +164,9 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch {
this.tableFull = full;
}
public abstract void setFeaturesReply(OFFeaturesReply featuresReply);
public void setFeaturesReply(OFFeaturesReply featuresReply) {
this.features = featuresReply;
}
/**
* Let peoeple know if you support Nicira style role requests.
......@@ -172,7 +187,9 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch {
this.agent.processMessage(m);
}
public abstract RoleState getRole();
public RoleState getRole() {
return role;
};
final boolean addConnectedSwitch() {
return this.agent.addConnectedSwitch(this.getId(), this);
......@@ -214,7 +231,9 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch {
public void setRole(RoleState role) {
try {
this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE);
if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
this.role = role;
}
} catch (IOException e) {
log.error("Unable to write to switch {}.", this.dpid);
}
......@@ -236,23 +255,23 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch {
}
void handleNiciraRole(OFMessage m) throws SwitchStateException {
RoleState role = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
if (role == null) {
// The message wasn't really a Nicira role reply. We just
// dispatch it to the OFMessage listeners in this case.
this.handleMessage(m);
}
RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
new RoleReplyInfo(role, null, m.getXid()));
if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
if (role == RoleState.MASTER) {
this.transitionToMasterSwitch();
} else if (role == RoleState.EQUAL ||
role == RoleState.SLAVE) {
this.transitionToEqualSwitch();
}
}
RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
if (r == null) {
// The message wasn't really a Nicira role reply. We just
// dispatch it to the OFMessage listeners in this case.
this.handleMessage(m);
}
RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
new RoleReplyInfo(r, null, m.getXid()));
if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
if (r == RoleState.MASTER) {
this.transitionToMasterSwitch();
} else if (r == RoleState.EQUAL ||
r == RoleState.SLAVE) {
this.transitionToEqualSwitch();
}
}
}
boolean handleRoleError(OFErrorMsg error) {
......@@ -274,6 +293,16 @@ public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitch {
this.agent = ag;
}
public void setSwitchDescription(OFDescStatsReply desc) {
// TODO Auto-generated method stub
}
protected int getNextTransactionId() {
return this.xidCounter.getAndIncrement();
}
protected List<OFPortDesc> getPorts() {
return Collections.unmodifiableList(ports.getEntries());
}
}
......
......@@ -29,9 +29,11 @@ import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.onlab.onos.of.controller.Dpid;
import org.onlab.onos.of.controller.impl.annotations.LogMessageDoc;
import org.onlab.onos.of.controller.impl.annotations.LogMessageDocs;
import org.onlab.onos.of.controller.impl.internal.OpenFlowControllerImpl.OpenFlowSwitchAgent;
import org.onlab.onos.of.drivers.DriverManager;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFFactories;
import org.projectfloodlight.openflow.protocol.OFFactory;
......@@ -83,11 +85,6 @@ public class Controller {
// Getters/Setters
// ***************
public synchronized void setIOFSwitchManager(IOFSwitchManager swManager) {
this.switchManager = swManager;
}
public OFFactory getOFMessageFactory10() {
return FACTORY10;
}
......@@ -201,18 +198,6 @@ public class Controller {
}
/**
* Startup all of the controller's components.
*/
@LogMessageDoc(message = "Waiting for storage source",
explanation = "The system database is not yet ready",
recommendation = "If this message persists, this indicates " +
"that the system database has failed to start. " +
LogMessageDoc.CHECK_CONTROLLER)
public synchronized void startupComponents() {
//TODO do something maybe
}
// **************
// Utility methods
// **************
......@@ -236,9 +221,10 @@ public class Controller {
* @param desc
* @return switch instance
*/
protected AbstractOpenFlowSwitch getOFSwitchInstance(OFDescStatsReply desc, OFVersion ofv) {
AbstractOpenFlowSwitch sw = switchManager.getSwitchImpl(desc.getMfrDesc(), desc.getHwDesc(),
desc.getSwDesc(), ofv);
protected AbstractOpenFlowSwitch getOFSwitchInstance(long dpid,
OFDescStatsReply desc, OFVersion ofv) {
AbstractOpenFlowSwitch sw = DriverManager.getOFSwitchImpl(new Dpid(dpid),
desc, ofv);
sw.setAgent(agent);
return sw;
}
......@@ -247,7 +233,6 @@ public class Controller {
log.info("Initialising OpenFlow Lib and IO");
this.agent = ag;
this.init(new HashMap<String, String>());
this.startupComponents();
this.run();
}
......
......@@ -230,12 +230,6 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
h.thisdpid = m.getDatapathId().getLong();
log.info("Received features reply for switch at {} with dpid {}",
h.getSwitchInfoString(), h.thisdpid);
//update the controller about this connected switch
boolean success = h.sw.addConnectedSwitch();
if (!success) {
disconnectDuplicate(h);
return;
}
h.featuresReply = m; //temp store
if (h.ofVersion == OFVersion.OF_10) {
......@@ -419,7 +413,12 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
h.channel.getRemoteAddress());
OFDescStatsReply drep = (OFDescStatsReply) m;
// Here is where we differentiate between different kinds of switches
h.sw = h.controller.getOFSwitchInstance(drep, h.ofVersion);
h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
boolean success = h.sw.addConnectedSwitch();
if (!success) {
disconnectDuplicate(h);
return;
}
// set switch information
h.sw.setOFVersion(h.ofVersion);
h.sw.setFeaturesReply(h.featuresReply);
......@@ -433,7 +432,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
//Put switch in EQUAL mode until we hear back from the global registry
log.debug("Setting new switch {} to EQUAL and sending Role request",
h.sw.getStringId());
h.setSwitchRole(RoleState.EQUAL);
h.sw.addActivatedEqualSwitch();
//h.setSwitchRole(RoleState.EQUAL);
h.setSwitchRole(RoleState.MASTER);
h.sw.startDriverHandshake();
h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
......
......@@ -24,12 +24,19 @@ import org.slf4j.LoggerFactory;
@Service
public class OpenFlowControllerImpl implements OpenFlowController {
protected ConcurrentHashMap<Long, OpenFlowSwitch> connectedSwitches;
protected ConcurrentHashMap<Long, OpenFlowSwitch> activeMasterSwitches;
protected ConcurrentHashMap<Long, OpenFlowSwitch> activeEqualSwitches;
private static final Logger log =
LoggerFactory.getLogger(OpenFlowControllerImpl.class);
protected ConcurrentHashMap<Long, OpenFlowSwitch> connectedSwitches =
new ConcurrentHashMap<Long, OpenFlowSwitch>();
protected ConcurrentHashMap<Long, OpenFlowSwitch> activeMasterSwitches =
new ConcurrentHashMap<Long, OpenFlowSwitch>();
protected ConcurrentHashMap<Long, OpenFlowSwitch> activeEqualSwitches =
new ConcurrentHashMap<Long, OpenFlowSwitch>();
protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
protected ArrayList<OpenFlowSwitchListener> ofEventListener;
protected ArrayList<OpenFlowSwitchListener> ofEventListener =
new ArrayList<OpenFlowSwitchListener>();
private final Controller ctrl = new Controller();
......@@ -98,29 +105,17 @@ public class OpenFlowControllerImpl implements OpenFlowController {
@Override
public void write(Dpid dpid, OFMessage msg) {
this.getSwitch(dpid).write(msg);
this.getSwitch(dpid).sendMsg(msg);
}
@Override
public void processPacket(OFMessage msg) {
log.info("Got message {}", msg);
}
@Override
public void setRole(Dpid dpid, RoleState role) {
switch (role) {
case MASTER:
agent.transitionToMasterSwitch(dpid.value());
break;
case EQUAL:
agent.transitionToEqualSwitch(dpid.value());
break;
case SLAVE:
//agent.transitionToSlaveSwitch(dpid.value());
break;
default:
//WTF role is this?
}
((AbstractOpenFlowSwitch) getSwitch(dpid)).setRole(role);
}
public class OpenFlowSwitchAgent {
......@@ -189,6 +184,7 @@ public class OpenFlowControllerImpl implements OpenFlowController {
return false;
}
activeEqualSwitches.put(dpid, sw);
log.info("Added Activated EQUAL Switch {}", dpid);
return true;
} finally {
switchLock.unlock();
......@@ -203,6 +199,9 @@ public class OpenFlowControllerImpl implements OpenFlowController {
protected void transitionToMasterSwitch(long dpid) {
switchLock.lock();
try {
if (activeMasterSwitches.containsKey(dpid)) {
return;
}
OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
if (sw == null) {
log.error("Transition to master called on sw {}, but switch "
......@@ -224,6 +223,9 @@ public class OpenFlowControllerImpl implements OpenFlowController {
protected void transitionToEqualSwitch(long dpid) {
switchLock.lock();
try {
if (activeEqualSwitches.containsKey(dpid)) {
return;
}
OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
if (sw == null) {
log.error("Transition to equal called on sw {}, but switch "
......
......@@ -2,7 +2,6 @@ package org.onlab.onos.of.controller.impl.internal;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;
import org.onlab.onos.of.controller.RoleState;
import org.projectfloodlight.openflow.protocol.OFControllerRole;
......@@ -50,7 +49,6 @@ class RoleManager {
// the expectation set by the caller for the returned role
private RoleRecvStatus expectation;
private AtomicInteger xidCounter;
private AbstractOpenFlowSwitch sw;
......@@ -58,7 +56,6 @@ class RoleManager {
this.requestPending = false;
this.pendingXid = -1;
this.pendingRole = null;
this.xidCounter = new AtomicInteger(0);
this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
this.sw = sw;
}
......@@ -85,7 +82,7 @@ class RoleManager {
roleToSend = OFNiciraControllerRole.ROLE_SLAVE;
log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
}
int xid = xidCounter.getAndIncrement();
int xid = sw.getNextTransactionId();
OFExperimenter roleRequest = OFFactories.getFactory(OFVersion.OF_10)
.buildNiciraControllerRoleRequest()
.setXid(xid)
......@@ -113,7 +110,7 @@ class RoleManager {
+ " Should only be used for queries.", sw);
}
int xid = xidCounter.getAndIncrement();
int xid = sw.getNextTransactionId();
OFRoleRequest rrm = OFFactories.getFactory(OFVersion.OF_13)
.buildRoleRequest()
.setRole(roleToSend)
......@@ -121,7 +118,7 @@ class RoleManager {
//FIXME fix below when we actually use generation ids
.setGenerationId(U64.ZERO)
.build();
sw.write(rrm);
sw.sendMsg(rrm);
return xid;
}
......
package org.onlab.onos.of.drivers;
import java.util.List;
import org.onlab.onos.of.controller.Dpid;
import org.onlab.onos.of.controller.RoleState;
import org.onlab.onos.of.controller.impl.internal.AbstractOpenFlowSwitch;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple implementation of a driver manager that differentiates between
* connected switches using the OF Description Statistics Reply message.
*/
public final class DriverManager {
private static final Logger log = LoggerFactory.getLogger(DriverManager.class);
// Whether to use an OF 1.3 configured TTP, or to use an OF 1.0-style
// single table with packet-ins.
private static boolean cpqdUsePipeline13 = false;
/**
* Return an IOFSwitch object based on switch's manufacturer description
* from OFDescStatsReply.
*
* @param desc DescriptionStatistics reply from the switch
* @return A IOFSwitch instance if the driver found an implementation for
* the given description. Otherwise it returns OFSwitchImplBase
*/
public static AbstractOpenFlowSwitch getOFSwitchImpl(Dpid dpid,
OFDescStatsReply desc, OFVersion ofv) {
String vendor = desc.getMfrDesc();
String hw = desc.getHwDesc();
if (vendor.startsWith("Stanford University, Ericsson Research and CPqD Research")
&&
hw.startsWith("OpenFlow 1.3 Reference Userspace Switch")) {
return new OFSwitchImplCPqD13(dpid, desc, cpqdUsePipeline13);
}
if (vendor.startsWith("Nicira") &&
hw.startsWith("Open vSwitch")) {
if (ofv == OFVersion.OF_10) {
return new OFSwitchImplOVS10(dpid, desc);
} else if (ofv == OFVersion.OF_13) {
return new OFSwitchImplOVS13(dpid, desc);
}
}
log.warn("DriverManager could not identify switch desc: {}. "
+ "Assigning OFSwitchImplBase", desc);
AbstractOpenFlowSwitch base = new AbstractOpenFlowSwitch(dpid) {
@Override
public void write(List<OFMessage> msgs) {
// TODO Auto-generated method stub
}
@Override
public void sendMsg(OFMessage m) {
// TODO Auto-generated method stub
}
@Override
public Boolean supportNxRole() {
// TODO Auto-generated method stub
return null;
}
@Override
public void startDriverHandshake() {
// TODO Auto-generated method stub
}
@Override
public void setFeaturesReply(OFFeaturesReply featuresReply) {
// TODO Auto-generated method stub
}
@Override
public void processDriverHandshakeMessage(OFMessage m) {
// TODO Auto-generated method stub
}
@Override
public boolean isDriverHandshakeComplete() {
// TODO Auto-generated method stub
return false;
}
@Override
public RoleState getRole() {
// TODO Auto-generated method stub
return null;
}
};
base.setSwitchDescription(desc);
// XXX S must set counter here - unidentified switch
return base;
}
/**
* Private constructor to avoid instantiation.
*/
private DriverManager() {
}
/**
* Sets the configuration parameter which determines how the CPqD switch
* is set up. If usePipeline13 is true, a 1.3 pipeline will be set up on
* the switch. Otherwise, the switch will be set up in a 1.0 style with
* a single table where missed packets are sent to the controller.
*
* @param usePipeline13 whether to use a 1.3 pipeline or not
*/
public static void setConfigForCpqd(boolean usePipeline13) {
cpqdUsePipeline13 = usePipeline13;
}
}
package org.onlab.onos.of.drivers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import org.onlab.onos.of.controller.Dpid;
import org.onlab.onos.of.controller.RoleState;
import org.onlab.onos.of.controller.impl.internal.AbstractOpenFlowSwitch;
import org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeAlreadyStarted;
import org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeCompleted;
import org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeNotStarted;
import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
import org.projectfloodlight.openflow.protocol.OFBucket;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFErrorMsg;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFGroupFeaturesStatsReply;
import org.projectfloodlight.openflow.protocol.OFGroupType;
import org.projectfloodlight.openflow.protocol.OFMatchV3;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFOxmList;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthDst;
import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthSrc;
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.OFOxmMetadataMasked;
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.MacAddress;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFGroup;
import org.projectfloodlight.openflow.types.OFMetadata;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.OFVlanVidMatch;
import org.projectfloodlight.openflow.types.TableId;
import org.projectfloodlight.openflow.types.U32;
import org.projectfloodlight.openflow.types.U64;
import org.projectfloodlight.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* OFDescriptionStatistics Vendor (Manufacturer Desc.): Stanford University,
* Ericsson Research and CPqD Research. Make (Hardware Desc.) : OpenFlow 1.3
* Reference Userspace Switch Model (Datapath Desc.) : None Software : Serial :
* None
*/
public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch {
private static Logger log =
LoggerFactory.getLogger(OFSwitchImplCPqD13.class);
private static final int VLAN_ID_OFFSET = 16;
private AtomicBoolean driverHandshakeComplete;
private OFFactory factory;
private static final int OFPCML_NO_BUFFER = 0xffff;
// Configuration of asynch messages to controller. We need different
// asynch messages depending on role-equal or role-master.
// We don't want to get anything if we are slave.
private static final long SET_FLOW_REMOVED_MASK_MASTER = 0xf;
private static final long SET_PACKET_IN_MASK_MASTER = 0x7;
private static final long SET_PORT_STATUS_MASK_MASTER = 0x7;
private static final long SET_FLOW_REMOVED_MASK_EQUAL = 0x0;
private static final long SET_PACKET_IN_MASK_EQUAL = 0x0;
private static final long SET_PORT_STATUS_MASK_EQUAL = 0x7;
private static final long SET_ALL_SLAVE = 0x0;
private static final long TEST_FLOW_REMOVED_MASK = 0xf;
private static final long TEST_PACKET_IN_MASK = 0x7;
private static final long TEST_PORT_STATUS_MASK = 0x7;
private long barrierXidToWaitFor = -1;
private static final int TABLE_VLAN = 0;
private static final int TABLE_TMAC = 1;
private static final int TABLE_IPV4_UNICAST = 2;
private static final int TABLE_MPLS = 3;
private static final int TABLE_META = 4;
private static final int TABLE_ACL = 5;
private static final short MAX_PRIORITY = (short) 0xffff;
private static final short SLASH_24_PRIORITY = (short) 0xfff0;
private static final short MIN_PRIORITY = 0x0;
private static final U64 METADATA_MASK = U64.of(Long.MAX_VALUE << 1 | 0x1);
ConcurrentHashMap<Integer, OFGroup> l2groups;
private final boolean usePipeline13;
public OFSwitchImplCPqD13(Dpid dpid, OFDescStatsReply desc, boolean usePipeline13) {
super(dpid);
driverHandshakeComplete = new AtomicBoolean(false);
l2groups = new ConcurrentHashMap<Integer, OFGroup>();
setSwitchDescription(desc);
this.usePipeline13 = usePipeline13;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "OFSwitchImplCPqD13 [" + ((channel != null)
? channel.getRemoteAddress() : "?")
+ " DPID[" + ((this.getStringId() != null) ? this.getStringId() : "?") + "]]";
}
@Override
public void startDriverHandshake() {
log.debug("Starting driver handshake for sw {}", getStringId());
if (startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeAlreadyStarted();
}
startDriverHandshakeCalled = true;
factory = this.factory();
if (!usePipeline13) {
// Send packet-in to controller if a packet misses the first table
populateTableMissEntry(0, true, false, false, 0);
} //else {
// configureSwitch();
//}
sendBarrier(true);
}
@Override
public boolean isDriverHandshakeComplete() {
if (!startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeNotStarted();
}
return driverHandshakeComplete.get();
}
@Override
public void processDriverHandshakeMessage(OFMessage m) {
if (!startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeNotStarted();
}
if (driverHandshakeComplete.get()) {
throw new SwitchDriverSubHandshakeCompleted(m);
}
if (!startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeNotStarted();
}
if (driverHandshakeComplete.get()) {
throw new SwitchDriverSubHandshakeCompleted(m);
}
switch (m.getType()) {
case BARRIER_REPLY:
if (m.getXid() == barrierXidToWaitFor) {
driverHandshakeComplete.set(true);
}
break;
case ERROR:
log.error("Switch {} Error {}", getStringId(), (OFErrorMsg) m);
break;
case FEATURES_REPLY:
break;
case FLOW_REMOVED:
break;
case GET_ASYNC_REPLY:
OFAsyncGetReply asrep = (OFAsyncGetReply) m;
decodeAsyncGetReply(asrep);
break;
case PACKET_IN:
break;
case PORT_STATUS:
break;
case QUEUE_GET_CONFIG_REPLY:
break;
case ROLE_REPLY:
break;
case STATS_REPLY:
processStatsReply((OFStatsReply) m);
break;
default:
log.debug("Received message {} during switch-driver subhandshake "
+ "from switch {} ... Ignoring message", m, getStringId());
}
}
private void configureSwitch() throws IOException {
// setAsyncConfig();
// getTableFeatures();
sendGroupFeaturesRequest();
setL2Groups();
sendBarrier(false);
setL3Groups();
setL25Groups();
sendGroupDescRequest();
populateTableVlan();
populateTableTMac();
populateIpTable();
populateMplsTable();
populateTableMissEntry(TABLE_ACL, false, false, false, -1);
sendBarrier(true);
}
private void setAsyncConfig() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>(3);
OFMessage setAC = null;
if (role == RoleState.MASTER) {
setAC = factory.buildAsyncSet()
.setFlowRemovedMaskEqualMaster(SET_FLOW_REMOVED_MASK_MASTER)
.setPacketInMaskEqualMaster(SET_PACKET_IN_MASK_MASTER)
.setPortStatusMaskEqualMaster(SET_PORT_STATUS_MASK_MASTER)
.setFlowRemovedMaskSlave(SET_ALL_SLAVE)
.setPacketInMaskSlave(SET_ALL_SLAVE)
.setPortStatusMaskSlave(SET_ALL_SLAVE)
.setXid(getNextTransactionId())
.build();
} else if (role == RoleState.EQUAL) {
setAC = factory.buildAsyncSet()
.setFlowRemovedMaskEqualMaster(SET_FLOW_REMOVED_MASK_EQUAL)
.setPacketInMaskEqualMaster(SET_PACKET_IN_MASK_EQUAL)
.setPortStatusMaskEqualMaster(SET_PORT_STATUS_MASK_EQUAL)
.setFlowRemovedMaskSlave(SET_ALL_SLAVE)
.setPacketInMaskSlave(SET_ALL_SLAVE)
.setPortStatusMaskSlave(SET_ALL_SLAVE)
.setXid(getNextTransactionId())
.build();
}
msglist.add(setAC);
OFMessage br = factory.buildBarrierRequest()
.setXid(getNextTransactionId())
.build();
msglist.add(br);
OFMessage getAC = factory.buildAsyncGetRequest()
.setXid(getNextTransactionId())
.build();
msglist.add(getAC);
write(msglist);
}
private void decodeAsyncGetReply(OFAsyncGetReply rep) {
long frm = rep.getFlowRemovedMaskEqualMaster();
//long frs = rep.getFlowRemovedMaskSlave();
long pim = rep.getPacketInMaskEqualMaster();
//long pis = rep.getPacketInMaskSlave();
long psm = rep.getPortStatusMaskEqualMaster();
//long pss = rep.getPortStatusMaskSlave();
if (role == RoleState.MASTER || role == RoleState.EQUAL) { // should separate
log.info("FRM:{}", HexString.toHexString((frm & TEST_FLOW_REMOVED_MASK)));
log.info("PIM:{}", HexString.toHexString((pim & TEST_PACKET_IN_MASK)));
log.info("PSM:{}", HexString.toHexString((psm & TEST_PORT_STATUS_MASK)));
}
}
private void getTableFeatures() throws IOException {
OFMessage gtf = factory.buildTableFeaturesStatsRequest()
.setXid(getNextTransactionId())
.build();
sendMsg(gtf);
}
private void sendGroupFeaturesRequest() throws IOException {
OFMessage gfr = factory.buildGroupFeaturesStatsRequest()
.setXid(getNextTransactionId())
.build();
sendMsg(gfr);
}
private void sendGroupDescRequest() throws IOException {
OFMessage gdr = factory.buildGroupDescStatsRequest()
.setXid(getNextTransactionId())
.build();
sendMsg(gdr);
}
/*Create L2 interface groups for all physical ports
Naming convention followed is the same as OF-DPA spec
eg. port 1 with allowed vlan 10, is enveloped in group with id,
0x0 00a 0001, where the uppermost 4 bits identify an L2 interface,
the next 12 bits identify the vlan-id, and the lowermost 16 bits
identify the port number.*/
private void setL2Groups() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
for (OFPortDesc p : getPorts()) {
int pnum = p.getPortNo().getPortNumber();
int portVlan = getVlanConfig(pnum);
if (U32.of(pnum).compareTo(U32.of(OFPort.MAX.getPortNumber())) < 1) {
OFGroup gl2 = OFGroup.of(pnum | (portVlan << VLAN_ID_OFFSET));
OFAction out = factory.actions().buildOutput()
.setPort(p.getPortNo()).build();
OFAction popVlan = factory.actions().popVlan();
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(popVlan);
actions.add(out);
OFBucket bucket = factory.buildBucket()
.setActions(actions).build();
List<OFBucket> buckets = Collections.singletonList(bucket);
OFMessage gmAdd = factory.buildGroupAdd()
.setGroup(gl2)
.setBuckets(buckets)
.setGroupType(OFGroupType.INDIRECT)
.setXid(getNextTransactionId())
.build();
msglist.add(gmAdd);
l2groups.put(pnum, gl2);
}
}
log.debug("Creating {} L2 groups in sw {}", msglist.size(), getStringId());
write(msglist);
}
private int getVlanConfig(int portnum) {
int portVlan = 10 * portnum;
if ((getId() == 0x1 && portnum == 6) ||
(getId() == 0x2) ||
(getId() == 0x3 && portnum == 2)) {
portVlan = 192; // 0xc0
}
return portVlan;
}
private MacAddress getRouterMacAddr() {
if (getId() == 0x3) {
return MacAddress.of("00:00:07:07:07:80"); // router mac
}
if (getId() == 0x1) {
return MacAddress.of("00:00:01:01:01:80");
}
// switch 0x2
return MacAddress.of("00:00:02:02:02:80");
}
// only for ports connected to other routers
private OFAction getDestAction(int portnum) {
OFAction setDA = null;
MacAddress dAddr = null;
if (getId() == 0x1 && portnum == 6) { // connected to switch 2
dAddr = MacAddress.of("00:00:02:02:02:80");
}
if (getId() == 0x2) {
if (portnum == 1) { // connected to sw 1
dAddr = MacAddress.of("00:00:01:01:01:80");
} else if (portnum == 2) { // connected to sw 3
dAddr = MacAddress.of("00:00:07:07:07:80");
}
}
if (getId() == 0x3) {
if (portnum == 2) { // connected to switch 2
dAddr = MacAddress.of("00:00:02:02:02:80");
}
}
if (dAddr != null) {
OFOxmEthDst dstAddr = factory.oxms().ethDst(dAddr);
setDA = factory.actions().buildSetField()
.setField(dstAddr).build();
}
return setDA;
}
/*
* L3 groups are created for all router ports and they all point to corresponding
* L2 groups. Only the ports that connect to other routers will have the
* DA set.
*/
private void setL3Groups() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
for (OFGroup gl2 : l2groups.values()) {
int gnum = gl2.getGroupNumber();
int portnum = gnum & 0x0000ffff;
int vlanid = ((gnum & 0x0fff0000) >> VLAN_ID_OFFSET);
MacAddress sAddr = getRouterMacAddr();
OFGroup gl3 = OFGroup.of(0x20000000 | portnum);
OFAction group = factory.actions().buildGroup()
.setGroup(gl2).build();
OFOxmEthSrc srcAddr = factory.oxms().ethSrc(sAddr);
OFAction setSA = factory.actions().buildSetField()
.setField(srcAddr).build();
OFOxmVlanVid vid = factory.oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanid));
OFAction setVlan = factory.actions().buildSetField()
.setField(vid).build();
OFAction decTtl = factory.actions().decNwTtl();
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(decTtl); // decrement the IP TTL/do-checksum/check TTL
// and MTU
actions.add(setVlan); // set the vlan-id of the exit-port (and
// l2group)
actions.add(setSA); // set this routers mac address
// make L3Unicast group setDA for known (configured) ports
// that connect to other routers
OFAction setDA = getDestAction(portnum);
if (setDA != null) {
actions.add(setDA);
}
actions.add(group);
OFBucket bucket = factory.buildBucket()
.setActions(actions).build();
List<OFBucket> buckets = Collections.singletonList(bucket);
OFMessage gmAdd = factory.buildGroupAdd()
.setGroup(gl3)
.setBuckets(buckets)
.setGroupType(OFGroupType.INDIRECT)
.setXid(getNextTransactionId())
.build();
msglist.add(gmAdd);
}
write(msglist);
log.debug("Creating {} L3 groups in sw {}", msglist.size(), getStringId());
}
/*
* L2.5 or mpls-unicast groups are only created for those router ports
* connected to other router ports. They differ from the corresponding
* L3-unicast group only by the fact that they decrement the MPLS TTL
* instead of the IP ttl
*/
private void setL25Groups() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
for (OFGroup gl2 : l2groups.values()) {
int gnum = gl2.getGroupNumber();
int portnum = gnum & 0x0000ffff;
int vlanid = ((gnum & 0x0fff0000) >> VLAN_ID_OFFSET);
MacAddress sAddr = getRouterMacAddr();
OFAction setDA = getDestAction(portnum);
// setDA will only be non-null for ports connected to routers
if (setDA != null) {
OFGroup gl3 = OFGroup.of(0xa0000000 | portnum); // different id
// for mpls
// group
OFAction group = factory.actions().buildGroup()
.setGroup(gl2).build();
OFOxmEthSrc srcAddr = factory.oxms().ethSrc(sAddr);
OFAction setSA = factory.actions().buildSetField()
.setField(srcAddr).build();
OFOxmVlanVid vid = factory.oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanid));
OFAction setVlan = factory.actions().buildSetField()
.setField(vid).build();
OFAction decMplsTtl = factory.actions().decMplsTtl();
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(decMplsTtl); // decrement the MPLS
// TTL/do-checksum/check TTL and MTU
actions.add(setVlan); // set the vlan-id of the exit-port (and
// l2group)
actions.add(setSA); // set this routers mac address
actions.add(setDA);
actions.add(group);
OFBucket bucket = factory.buildBucket()
.setActions(actions).build();
List<OFBucket> buckets = Collections.singletonList(bucket);
OFMessage gmAdd = factory.buildGroupAdd()
.setGroup(gl3)
.setBuckets(buckets)
.setGroupType(OFGroupType.INDIRECT)
.setXid(getNextTransactionId())
.build();
msglist.add(gmAdd);
}
}
write(msglist);
log.debug("Creating {} MPLS groups in sw {}", msglist.size(), getStringId());
}
/* Using ECMP groups
*
* OFGroup group47 = OFGroup.of(47);
OFAction outgroup1 = factory.actions()
.buildGroup()
.setGroup(group61)
.build();
OFBucket buc47_1 = factory.buildBucket()
.setWeight(1)
.setActions(Collections.singletonList(outgroup1))
.build();
OFAction outgroup2 = factory.actions()
.buildGroup()
.setGroup(group62)
.build();
OFBucket buc47_2 = factory.buildBucket()
.setWeight(1)
.setActions(Collections.singletonList(outgroup2))
.build();
List<OFBucket> buckets47 = new ArrayList<OFBucket>();
buckets47.add(buc47_1);
buckets47.add(buc47_2);
OFMessage gmS12 = factory.buildGroupAdd()
.setGroup(group47)
.setBuckets(buckets47)
.setGroupType(OFGroupType.SELECT)
.setXid(getNextTransactionId())
.build();
write(gmS12, null); */
private void processStatsReply(OFStatsReply sr) {
switch (sr.getStatsType()) {
case AGGREGATE:
break;
case DESC:
break;
case EXPERIMENTER:
break;
case FLOW:
break;
case GROUP_DESC:
processGroupDesc((OFGroupDescStatsReply) sr);
break;
case GROUP_FEATURES:
processGroupFeatures((OFGroupFeaturesStatsReply) sr);
break;
case METER_CONFIG:
break;
case METER_FEATURES:
break;
case PORT_DESC:
break;
case TABLE_FEATURES:
break;
default:
break;
}
}
private void processGroupFeatures(OFGroupFeaturesStatsReply gfsr) {
log.info("Sw: {} Group Features {}", getStringId(), gfsr);
}
private void processGroupDesc(OFGroupDescStatsReply gdsr) {
log.info("Sw: {} Group Desc {}", getStringId(), gdsr);
}
private void populateTableVlan() throws IOException {
// for all incoming ports assign configured port-vlans
// currently assign portnum*10 -> vlanid to access ports
// and vlan 192 to router to router ports
List<OFMessage> msglist = new ArrayList<OFMessage>();
for (OFPortDesc p : getPorts()) {
int pnum = p.getPortNo().getPortNumber();
if (U32.of(pnum).compareTo(U32.of(OFPort.MAX.getPortNumber())) < 1) {
int vlanid = getVlanConfig(pnum);
OFOxmInPort oxp = factory.oxms().inPort(p.getPortNo());
OFOxmVlanVid oxv = factory.oxms()
.vlanVid(OFVlanVidMatch.UNTAGGED);
OFOxmList oxmList = OFOxmList.of(oxp, oxv);
OFMatchV3 match = factory.buildMatchV3()
.setOxmList(oxmList).build();
OFOxmVlanVid vidToSet = factory.oxms()
.vlanVid(OFVlanVidMatch.ofVlan(vlanid));
OFAction pushVlan = factory.actions().pushVlan(EthType.VLAN_FRAME);
OFAction setVlan = factory.actions().setField(vidToSet);
List<OFAction> actionlist = new ArrayList<OFAction>();
actionlist.add(pushVlan);
actionlist.add(setVlan);
OFInstruction appAction = factory.instructions().buildApplyActions()
.setActions(actionlist).build();
OFInstruction gotoTbl = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_TMAC)).build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
instructions.add(appAction);
instructions.add(gotoTbl);
OFMessage flowEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_VLAN))
.setMatch(match)
.setInstructions(instructions)
.setPriority(1000) // does not matter - all rules
// exclusive
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
msglist.add(flowEntry);
}
}
// table-vlan has no table-miss entry, and so packets that miss are
// essentially dropped
write(msglist);
log.debug("Adding {} vlan-rules in sw {}", msglist.size(), getStringId());
}
private void populateTableTMac() throws IOException {
// match for ip packets
OFOxmEthType oxe = factory.oxms().ethType(EthType.IPv4);
OFOxmList oxmListIp = OFOxmList.of(oxe);
OFMatchV3 matchIp = factory.buildMatchV3()
.setOxmList(oxmListIp).build();
OFInstruction gotoTblIp = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_IPV4_UNICAST)).build();
List<OFInstruction> instructionsIp = Collections.singletonList(gotoTblIp);
OFMessage ipEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_TMAC))
.setMatch(matchIp)
.setInstructions(instructionsIp)
.setPriority(1000) // strict priority required lower than
// multicastMac
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
// match for mpls packets
OFOxmEthType oxmpls = factory.oxms().ethType(EthType.MPLS_UNICAST);
OFOxmList oxmListMpls = OFOxmList.of(oxmpls);
OFMatchV3 matchMpls = factory.buildMatchV3()
.setOxmList(oxmListMpls).build();
OFInstruction gotoTblMpls = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_MPLS)).build();
List<OFInstruction> instructionsMpls = Collections.singletonList(gotoTblMpls);
OFMessage mplsEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_TMAC))
.setMatch(matchMpls)
.setInstructions(instructionsMpls)
.setPriority(1001) // strict priority required lower than
// multicastMac
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
// match for everything else to send to controller. Essentially
// the table miss flow entry
populateTableMissEntry(TABLE_TMAC, true, false, false, -1);
log.debug("Adding termination-mac-rules in sw {}", getStringId());
List<OFMessage> msglist = new ArrayList<OFMessage>(2);
msglist.add(ipEntry);
msglist.add(mplsEntry);
write(msglist);
}
private List<String> getMyIps() { // send to controller
List<String> myIps = new ArrayList<String>();
if (getId() == 0x1) {
myIps.add("10.0.2.128");
myIps.add("10.0.3.128");
myIps.add("10.0.1.128");
myIps.add("192.168.0.1");
}
if (getId() == 0x2) {
myIps.add("192.168.0.2");
}
if (getId() == 0x3) {
myIps.add("192.168.0.3");
myIps.add("7.7.7.128");
}
return myIps;
}
private List<String> getMySubnetIps() { // send to controller
List<String> subnetIps = new ArrayList<String>();
if (getId() == 0x1) {
subnetIps.add("10.0.2.0");
subnetIps.add("10.0.3.0");
subnetIps.add("10.0.1.0");
}
// TODO needed?
//if (getId() == 0x2) {
//}
if (getId() == 0x3) {
subnetIps.add("7.7.7.0");
}
return subnetIps;
}
private static class RouteEntry {
String prefix;
String mask;
int nextHopPort;
String dstMac;
int label;
public RouteEntry(String prefix, String mask, int nextHopPort, int label) {
this.prefix = prefix;
this.mask = mask;
this.nextHopPort = nextHopPort;
this.label = label;
}
public RouteEntry(String prefix, int nextHopPort, String dstMac) {
this.prefix = prefix;
this.nextHopPort = nextHopPort;
this.dstMac = dstMac;
}
}
// send out of mpls-group where the next-hop mac-da is already set
private List<RouteEntry> getRouterNextHopIps() {
List<RouteEntry> routerNextHopIps = new ArrayList<RouteEntry>();
if (getId() == 0x1) {
routerNextHopIps
.add(new RouteEntry("192.168.0.2", "255.255.255.255", 6, 102));
routerNextHopIps
.add(new RouteEntry("192.168.0.3", "255.255.255.255", 6, 103));
routerNextHopIps.add(new RouteEntry("7.7.7.0", "255.255.255.0", 6, 103));
}
//if (getId() == 0x2) {
/* These are required for normal IP routing without labels.
routerNextHopIps.add(new RouteEntry("192.168.0.1","255.255.255.255",1));
routerNextHopIps.add(new RouteEntry("192.168.0.3","255.255.255.255",2));
routerNextHopIps.add(new RouteEntry("10.0.1.0","255.255.255.0",1));
routerNextHopIps.add(new RouteEntry("10.0.2.0","255.255.255.0",1));
routerNextHopIps.add(new RouteEntry("10.0.3.0","255.255.255.0",1));
routerNextHopIps.add(new RouteEntry("7.7.7.0","255.255.255.0",2));*/
//}
if (getId() == 0x3) {
routerNextHopIps
.add(new RouteEntry("192.168.0.2", "255.255.255.255", 2, 102));
routerNextHopIps
.add(new RouteEntry("192.168.0.1", "255.255.255.255", 2, 101));
routerNextHopIps.add(new RouteEntry("10.0.1.0", "255.255.255.0", 2, 101));
routerNextHopIps.add(new RouteEntry("10.0.2.0", "255.255.255.0", 2, 101));
routerNextHopIps.add(new RouteEntry("10.0.3.0", "255.255.255.0", 2, 101));
}
return routerNextHopIps;
}
// known host mac-addr, setDA/send out of l3group
private List<RouteEntry> getHostNextHopIps() {
List<RouteEntry> hostNextHopIps = new ArrayList<RouteEntry>();
if (getId() == 0x1) {
hostNextHopIps.add(new RouteEntry("10.0.2.1", 4, "00:00:00:00:02:01"));
hostNextHopIps.add(new RouteEntry("10.0.3.1", 5, "00:00:00:00:03:01"));
}
// TODO needed?
//if (getId() == 0x2) {
//}
if (getId() == 0x3) {
hostNextHopIps.add(new RouteEntry("7.7.7.7", 1, "00:00:07:07:07:07"));
}
return hostNextHopIps;
}
private void populateIpTable() throws IOException {
populateMyIps();
populateMySubnets();
populateRoutes();
populateHostRoutes();
// match for everything else to send to ACL table. Essentially
// the table miss flow entry
populateTableMissEntry(TABLE_IPV4_UNICAST, false, true,
true, TABLE_ACL);
}
private void populateMyIps() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
// first all my ip's as exact-matches
// write-action instruction to send to controller
List<String> myIps = getMyIps();
for (int i = 0; i < myIps.size(); i++) {
OFOxmEthType ethTypeIp = factory.oxms()
.ethType(EthType.IPv4);
OFOxmIpv4DstMasked ipPrefix = factory.oxms()
.ipv4DstMasked(IPv4Address.of(myIps.get(i)), IPv4Address.NO_MASK);
OFOxmList oxmListSlash32 = OFOxmList.of(ethTypeIp, ipPrefix);
OFMatchV3 match = factory.buildMatchV3()
.setOxmList(oxmListSlash32).build();
OFAction outc = factory.actions().buildOutput()
.setPort(OFPort.CONTROLLER).setMaxLen(OFPCML_NO_BUFFER)
.build();
OFInstruction writeInstr = factory.instructions().buildWriteActions()
.setActions(Collections.singletonList(outc)).build();
OFInstruction gotoInstr = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_ACL)).build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
instructions.add(writeInstr);
instructions.add(gotoInstr);
OFMessage myIpEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_IPV4_UNICAST))
.setMatch(match)
.setInstructions(instructions)
.setPriority(MAX_PRIORITY) // highest priority for exact
// match
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
msglist.add(myIpEntry);
}
write(msglist);
log.debug("Adding {} my-ip-rules in sw {}", msglist.size(), getStringId());
}
private void populateMySubnets() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
// next prefix-based subnet-IP's configured on my interfaces
// need to ARP for exact-IP, so write-action instruction to send to
// controller
// this has different mask and priority than earlier case
List<String> subnetIps = getMySubnetIps();
for (int i = 0; i < subnetIps.size(); i++) {
OFOxmEthType ethTypeIp = factory.oxms()
.ethType(EthType.IPv4);
OFOxmIpv4DstMasked ipPrefix = factory.oxms().ipv4DstMasked(
IPv4Address.of(subnetIps.get(i)),
IPv4Address.of(0xffffff00)); // '/24' mask
OFOxmList oxmListSlash24 = OFOxmList.of(ethTypeIp, ipPrefix);
OFMatchV3 match = factory.buildMatchV3()
.setOxmList(oxmListSlash24).build();
OFAction outc = factory.actions().buildOutput()
.setPort(OFPort.CONTROLLER).setMaxLen(OFPCML_NO_BUFFER)
.build();
OFInstruction writeInstr = factory.instructions().buildWriteActions()
.setActions(Collections.singletonList(outc)).build();
OFInstruction gotoInstr = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_ACL)).build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
instructions.add(writeInstr);
instructions.add(gotoInstr);
OFMessage myIpEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_IPV4_UNICAST))
.setMatch(match)
.setInstructions(instructions)
.setPriority(SLASH_24_PRIORITY)
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
msglist.add(myIpEntry);
}
write(msglist);
log.debug("Adding {} subnet-ip-rules in sw {}", msglist.size(), getStringId());
msglist.clear();
}
private void populateRoutes() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
// addresses where I know the next-hop's mac-address because it is a
// router port - so I have an L3 interface to it (and an MPLS interface)
List<RouteEntry> routerNextHopIps = getRouterNextHopIps();
for (int i = 0; i < routerNextHopIps.size(); i++) {
OFOxmEthType ethTypeIp = factory.oxms()
.ethType(EthType.IPv4);
OFOxmIpv4DstMasked ipPrefix = factory.oxms()
.ipv4DstMasked(
IPv4Address.of(routerNextHopIps.get(i).prefix),
IPv4Address.of(routerNextHopIps.get(i).mask)
);
OFOxmList oxmListSlash32 = OFOxmList.of(ethTypeIp, ipPrefix);
OFMatchV3 match = factory.buildMatchV3()
.setOxmList(oxmListSlash32).build();
OFAction outg = factory.actions().buildGroup()
.setGroup(OFGroup.of(0xa0000000 | // mpls group id
routerNextHopIps.get(i).nextHopPort))
.build();
// lots of actions before forwarding to mpls group, and
// unfortunately
// they need to be apply-actions
OFAction pushlabel = factory.actions().pushMpls(EthType.MPLS_UNICAST);
OFOxmMplsLabel l = factory.oxms()
.mplsLabel(U32.of(routerNextHopIps.get(i).label));
OFAction setlabelid = factory.actions().buildSetField()
.setField(l).build();
OFAction copyTtlOut = factory.actions().copyTtlOut();
// OFAction setBos =
// factory.actions().buildSetField().setField(bos).build();
/*
writeActions.add(pushlabel); // need to be apply actions so can be
writeActions.add(copyTtlOut); // matched in pseudo-table
//writeActions.add(setlabelid); // bad support in cpqd
//writeActions.add(setBos); no support in loxigen
*/
List<OFAction> applyActions = new ArrayList<OFAction>();
applyActions.add(pushlabel);
applyActions.add(copyTtlOut);
OFInstruction applyInstr = factory.instructions().buildApplyActions()
.setActions(applyActions).build();
List<OFAction> writeActions = new ArrayList<OFAction>();
writeActions.add(outg); // group will decr mpls-ttl, set mac-sa/da,
// vlan
OFInstruction writeInstr = factory.instructions().buildWriteActions()
.setActions(writeActions).build();
// necessary to match in pseudo-table to overcome cpqd 1.3 flaw
OFInstruction writeMeta = factory.instructions().buildWriteMetadata()
.setMetadata(U64.of(routerNextHopIps.get(i).label))
.setMetadataMask(METADATA_MASK).build();
/*OFInstruction gotoInstr = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_ACL)).build();*/
OFInstruction gotoInstr = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_META)).build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
instructions.add(applyInstr);
// instructions.add(writeInstr);// cannot write here - causes switch
// to crash
instructions.add(writeMeta);
instructions.add(gotoInstr);
int priority = -1;
if (routerNextHopIps.get(i).mask.equals("255.255.255.255")) {
priority = MAX_PRIORITY;
} else {
priority = SLASH_24_PRIORITY;
}
OFMessage myIpEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_IPV4_UNICAST))
.setMatch(match)
.setInstructions(instructions)
.setPriority(priority)
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
msglist.add(myIpEntry);
// need to also handle psuedo-table entries to match-metadata and
// set mpls
// label-id
OFOxmEthType ethTypeMpls = factory.oxms()
.ethType(EthType.MPLS_UNICAST);
OFOxmMetadataMasked meta = factory.oxms()
.metadataMasked(
OFMetadata.ofRaw(routerNextHopIps.get(i).label),
OFMetadata.NO_MASK);
OFOxmList oxmListMeta = OFOxmList.of(ethTypeMpls, meta);
OFMatchV3 matchMeta = factory.buildMatchV3()
.setOxmList(oxmListMeta).build();
List<OFAction> writeActions2 = new ArrayList<OFAction>();
writeActions2.add(setlabelid);
OFAction outg2 = factory.actions().buildGroup()
.setGroup(OFGroup.of(routerNextHopIps.get(i).nextHopPort |
(192 << VLAN_ID_OFFSET)))
.build();
writeActions2.add(outg2);
OFInstruction writeInstr2 = factory.instructions().buildWriteActions()
.setActions(writeActions2).build();
OFInstruction gotoInstr2 = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_ACL)).build();
List<OFInstruction> instructions2 = new ArrayList<OFInstruction>();
// unfortunately have to apply this action too
OFInstruction applyInstr2 = factory.instructions().buildApplyActions()
.setActions(writeActions2).build();
instructions2.add(applyInstr2);
// instructions2.add(writeInstr2);
// instructions2.add(gotoInstr2);
/*OFMatchV3 match3 = factory.buildMatchV3()
.setOxmList(OFOxmList.of(meta)).build();
OFInstruction clearInstruction = factory.instructions().clearActions();
List<OFInstruction> instructions3 = new ArrayList<OFInstruction>();
OFAction outc = factory.actions().buildOutput()
.setPort(OFPort.CONTROLLER).setMaxLen(OFPCML_NO_BUFFER)
.build();
OFInstruction writec = factory.instructions()
.writeActions(Collections.singletonList(outc));
instructions3.add(clearInstruction);
instructions3.add(writec);
instructions3.add(gotoInstr2); */
OFMessage myMetaEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_META))
.setMatch(matchMeta)
.setInstructions(instructions2)
.setPriority(MAX_PRIORITY)
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
msglist.add(myMetaEntry);
}
write(msglist);
log.debug("Adding {} next-hop-router-rules in sw {}", msglist.size(),
getStringId());
// add a table-miss entry to table 4 for debugging - leave it out
// unclear packet state - causes switch to crash
// populateTableMissEntry(TABLE_META, false, true,
// true, TABLE_ACL);
}
private void populateHostRoutes() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
// addresses where I know the next hop's mac-address and I can set the
// destination mac in the match-instruction.write-action
// either I sent out arp-request or I got an arp-request from this host
List<RouteEntry> hostNextHopIps = getHostNextHopIps();
for (int i = 0; i < hostNextHopIps.size(); i++) {
OFOxmEthType ethTypeIp = factory.oxms()
.ethType(EthType.IPv4);
OFOxmIpv4DstMasked ipPrefix = factory.oxms()
.ipv4DstMasked(
IPv4Address.of(hostNextHopIps.get(i).prefix),
IPv4Address.NO_MASK); // host addr should be /32
OFOxmList oxmListSlash32 = OFOxmList.of(ethTypeIp, ipPrefix);
OFMatchV3 match = factory.buildMatchV3()
.setOxmList(oxmListSlash32).build();
OFAction setDmac = null, outg = null;
OFOxmEthDst dmac = factory.oxms()
.ethDst(MacAddress.of(hostNextHopIps.get(i).dstMac));
setDmac = factory.actions().buildSetField()
.setField(dmac).build();
outg = factory.actions().buildGroup()
.setGroup(OFGroup.of(0x20000000 | hostNextHopIps.get(i).nextHopPort)) // l3group
// id
.build();
List<OFAction> writeActions = new ArrayList<OFAction>();
writeActions.add(setDmac);
writeActions.add(outg);
OFInstruction writeInstr = factory.instructions().buildWriteActions()
.setActions(writeActions).build();
OFInstruction gotoInstr = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_ACL)).build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
instructions.add(writeInstr);
instructions.add(gotoInstr);
OFMessage myIpEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_IPV4_UNICAST))
.setMatch(match)
.setInstructions(instructions)
.setPriority(MAX_PRIORITY) // highest priority for exact
// match
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
msglist.add(myIpEntry);
}
write(msglist);
log.debug("Adding {} next-hop-host-rules in sw {}", msglist.size(), getStringId());
}
private static class MplsEntry {
int labelid;
int portnum;
public MplsEntry(int labelid, int portnum) {
this.labelid = labelid;
this.portnum = portnum;
}
}
private List<MplsEntry> getMplsEntries() {
List<MplsEntry> myLabels = new ArrayList<MplsEntry>();
if (getId() == 0x1) {
myLabels.add(new MplsEntry(101, OFPort.CONTROLLER.getPortNumber()));
myLabels.add(new MplsEntry(103, 6));
}
if (getId() == 0x2) {
myLabels.add(new MplsEntry(103, 2));
myLabels.add(new MplsEntry(102, OFPort.CONTROLLER.getPortNumber()));
myLabels.add(new MplsEntry(101, 1));
}
if (getId() == 0x3) {
myLabels.add(new MplsEntry(103, OFPort.CONTROLLER.getPortNumber()));
myLabels.add(new MplsEntry(101, 2));
}
return myLabels;
}
private void populateMplsTable() throws IOException {
List<OFMessage> msglist = new ArrayList<OFMessage>();
List<MplsEntry> lfibEntries = getMplsEntries();
for (int i = 0; i < lfibEntries.size(); i++) {
OFOxmEthType ethTypeMpls = factory.oxms()
.ethType(EthType.MPLS_UNICAST);
OFOxmMplsLabel labelid = factory.oxms()
.mplsLabel(U32.of(lfibEntries.get(i).labelid));
OFOxmList oxmList = OFOxmList.of(ethTypeMpls, labelid);
OFMatchV3 matchlabel = factory.buildMatchV3()
.setOxmList(oxmList).build();
OFAction poplabel = factory.actions().popMpls(EthType.IPv4);
OFAction sendTo = null;
if (lfibEntries.get(i).portnum == OFPort.CONTROLLER.getPortNumber()) {
sendTo = factory.actions().output(OFPort.CONTROLLER,
OFPCML_NO_BUFFER);
} else {
sendTo = factory.actions().group(OFGroup.of(
0xa0000000 | lfibEntries.get(i).portnum));
}
List<OFAction> writeActions = new ArrayList<OFAction>();
writeActions.add(poplabel);
writeActions.add(sendTo);
OFInstruction writeInstr = factory.instructions().buildWriteActions()
.setActions(writeActions).build();
OFInstruction gotoInstr = factory.instructions().buildGotoTable()
.setTableId(TableId.of(TABLE_ACL)).build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
instructions.add(writeInstr);
instructions.add(gotoInstr);
OFMessage myMplsEntry = factory.buildFlowAdd()
.setTableId(TableId.of(TABLE_MPLS))
.setMatch(matchlabel)
.setInstructions(instructions)
.setPriority(MAX_PRIORITY) // exact match and exclusive
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
msglist.add(myMplsEntry);
}
write(msglist);
log.debug("Adding {} mpls-forwarding-rules in sw {}", msglist.size(),
getStringId());
// match for everything else to send to ACL table. Essentially
// the table miss flow entry
populateTableMissEntry(TABLE_MPLS, false, true,
true, TABLE_ACL);
}
/**
* By default if none of the booleans in the call are set, then the
* table-miss entry is added with no instructions, which means that pipeline
* execution will stop, and the action set associated with the packet will
* be executed.
*
* @param tableToAdd
* @param toControllerNow as an APPLY_ACTION instruction
* @param toControllerWrite as a WRITE_ACITION instruction
* @param toTable as a GOTO_TABLE instruction
* @param tableToSend
* @throws IOException
*/
@SuppressWarnings("unchecked")
private void populateTableMissEntry(int tableToAdd, boolean toControllerNow,
boolean toControllerWrite,
boolean toTable, int tableToSend) {
OFOxmList oxmList = OFOxmList.EMPTY;
OFMatchV3 match = factory.buildMatchV3()
.setOxmList(oxmList)
.build();
OFAction outc = factory.actions()
.buildOutput()
.setPort(OFPort.CONTROLLER)
.setMaxLen(OFPCML_NO_BUFFER)
.build();
List<OFInstruction> instructions = new ArrayList<OFInstruction>();
if (toControllerNow) {
// table-miss instruction to send to controller immediately
OFInstruction instr = factory.instructions()
.buildApplyActions()
.setActions(Collections.singletonList(outc))
.build();
instructions.add(instr);
}
if (toControllerWrite) {
// table-miss instruction to write-action to send to controller
// this will be executed whenever the action-set gets executed
OFInstruction instr = factory.instructions()
.buildWriteActions()
.setActions(Collections.singletonList(outc))
.build();
instructions.add(instr);
}
if (toTable) {
// table-miss instruction to goto-table x
OFInstruction instr = factory.instructions()
.gotoTable(TableId.of(tableToSend));
instructions.add(instr);
}
if (!toControllerNow && !toControllerWrite && !toTable) {
// table-miss has no instruction - at which point action-set will be
// executed - if there is an action to output/group in the action
// set
// the packet will be sent there, otherwise it will be dropped.
instructions = (List<OFInstruction>) Collections.EMPTY_LIST;
}
OFMessage tableMissEntry = factory.buildFlowAdd()
.setTableId(TableId.of(tableToAdd))
.setMatch(match) // match everything
.setInstructions(instructions)
.setPriority(MIN_PRIORITY)
.setBufferId(OFBufferId.NO_BUFFER)
.setIdleTimeout(0)
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
sendMsg(tableMissEntry);
}
private void sendBarrier(boolean finalBarrier) {
int xid = getNextTransactionId();
if (finalBarrier) {
barrierXidToWaitFor = xid;
}
OFBarrierRequest br = factory
.buildBarrierRequest()
.setXid(xid)
.build();
sendMsg(br);
}
@Override
public void sendMsg(OFMessage m) {
channel.write(m);
}
@Override
public void write(List<OFMessage> msgs) {
for (OFMessage m : msgs) {
channel.write(m);
}
}
@Override
public Boolean supportNxRole() {
return false;
}
}
package org.onlab.onos.of.drivers;
import org.onlab.onos.of.controller.Dpid;
import org.onlab.onos.of.controller.impl.internal.AbstractOpenFlowSwitch;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFMessage;
/**
* OFDescriptionStatistics Vendor (Manufacturer Desc.): Nicira, Inc. Make
* (Hardware Desc.) : Open vSwitch Model (Datapath Desc.) : None Software :
* 1.11.90 (or whatever version + build) Serial : None
*/
public class OFSwitchImplOVS10 extends AbstractOpenFlowSwitch {
public OFSwitchImplOVS10(Dpid dpid, OFDescStatsReply desc) {
super(dpid);
setSwitchDescription(desc);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "OFSwitchImplOVS10 [" + ((channel != null)
? channel.getRemoteAddress() : "?")
+ " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
}
@Override
public void sendMsg(OFMessage m) {
channel.write(m);
}
@Override
public Boolean supportNxRole() {
return true;
}
@Override
public void startDriverHandshake() {}
@Override
public boolean isDriverHandshakeComplete() {
return true;
}
@Override
public void processDriverHandshakeMessage(OFMessage m) {}
}
package org.onlab.onos.of.drivers;
import java.util.concurrent.atomic.AtomicBoolean;
import org.onlab.onos.of.controller.Dpid;
import org.onlab.onos.of.controller.impl.internal.AbstractOpenFlowSwitch;
import org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeAlreadyStarted;
import org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeCompleted;
import org.onlab.onos.of.controller.impl.internal.SwitchDriverSubHandshakeNotStarted;
import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFErrorMsg;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* OFDescriptionStatistics Vendor (Manufacturer Desc.): Nicira, Inc. Make
* (Hardware Desc.) : Open vSwitch Model (Datapath Desc.) : None Software :
* 2.1.0 (or whatever version + build) Serial : None
*/
public class OFSwitchImplOVS13 extends AbstractOpenFlowSwitch {
private static Logger log =
LoggerFactory.getLogger(OFSwitchImplOVS13.class);
private AtomicBoolean driverHandshakeComplete;
private OFFactory factory;
private long barrierXidToWaitFor = -1;
public OFSwitchImplOVS13(Dpid dpid, OFDescStatsReply desc) {
super(dpid);
driverHandshakeComplete = new AtomicBoolean(false);
setSwitchDescription(desc);
}
@Override
public String toString() {
return "OFSwitchImplOVS13 [" + ((channel != null)
? channel.getRemoteAddress() : "?")
+ " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
}
@Override
public void startDriverHandshake() {
log.debug("Starting driver handshake for sw {}", getStringId());
if (startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeAlreadyStarted();
}
startDriverHandshakeCalled = true;
factory = factory();
configureSwitch();
}
@Override
public boolean isDriverHandshakeComplete() {
if (!startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeNotStarted();
}
return driverHandshakeComplete.get();
}
@Override
public void processDriverHandshakeMessage(OFMessage m) {
if (!startDriverHandshakeCalled) {
throw new SwitchDriverSubHandshakeNotStarted();
}
if (driverHandshakeComplete.get()) {
throw new SwitchDriverSubHandshakeCompleted(m);
}
switch (m.getType()) {
case BARRIER_REPLY:
if (m.getXid() == barrierXidToWaitFor) {
driverHandshakeComplete.set(true);
}
break;
case ERROR:
log.error("Switch {} Error {}", getStringId(), (OFErrorMsg) m);
break;
case FEATURES_REPLY:
break;
case FLOW_REMOVED:
break;
case GET_ASYNC_REPLY:
// OFAsyncGetReply asrep = (OFAsyncGetReply)m;
// decodeAsyncGetReply(asrep);
break;
case PACKET_IN:
break;
case PORT_STATUS:
break;
case QUEUE_GET_CONFIG_REPLY:
break;
case ROLE_REPLY:
break;
case STATS_REPLY:
// processStatsReply((OFStatsReply) m);
break;
default:
log.debug("Received message {} during switch-driver subhandshake "
+ "from switch {} ... Ignoring message", m, getStringId());
}
}
private void configureSwitch() {
sendBarrier(true);
}
private void sendBarrier(boolean finalBarrier) {
int xid = getNextTransactionId();
if (finalBarrier) {
barrierXidToWaitFor = xid;
}
OFBarrierRequest br = factory
.buildBarrierRequest()
.setXid(xid)
.build();
sendMsg(br);
}
@Override
public void sendMsg(OFMessage m) {
channel.write(m);
}
@Override
public Boolean supportNxRole() {
return false;
}
}