Class hierarchy for Driver files to support spring open ttp on cpqd and dell switches
diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java b/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java
index e6abf2d..fd78c1f 100644
--- a/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java
+++ b/src/main/java/net/floodlightcontroller/core/web/SwitchResourceBase.java
@@ -25,10 +25,11 @@
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
-import net.onrc.onos.core.drivermanager.OFSwitchImplCPqD13;
+import net.onrc.onos.core.drivermanager.OFSwitchImplSpringOpenTTP;
import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
@@ -36,9 +37,7 @@
import org.projectfloodlight.openflow.protocol.OFMatchV3;
import org.projectfloodlight.openflow.protocol.OFOxmList;
import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
-import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
-import org.projectfloodlight.openflow.protocol.OFPortStatsRequest;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFStatsRequest;
import org.projectfloodlight.openflow.protocol.OFStatsType;
@@ -113,7 +112,7 @@
log.error("Failure retrieving statistics from switch " + sw, e);
}
return flowStats;
- }
+ }
else if (statType == OFStatsType.PORT){
log.debug("Switch Port Stats: req sent for all "
+ "ports in switch {}", sw.getStringId());
@@ -224,7 +223,7 @@
//getting tableId from CPqD driver
TableId tableId;
if (sw != null) {
- if((tableId = ((OFSwitchImplCPqD13) sw).getTableId(tableType)) == null){
+ if ((tableId = ((OFSwitchImplSpringOpenTTP) sw).getTableId(tableType)) == null) {
log.error("Invalid tableType {} " + tableType);
return null;
}
@@ -254,7 +253,7 @@
log.error("Failure retrieving per table statistics from switch " + sw, e);
}
return flowStats;
- }
+ }
}
//should never get to this point
log.error("Failure retrieving {} table statistics from switch {}",tableType, sw);
diff --git a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
index 06ddb37..4391c8b 100644
--- a/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/net/onrc/onos/apps/segmentrouting/SegmentRoutingManager.java
@@ -34,6 +34,7 @@
import net.onrc.onos.api.packet.IPacketListener;
import net.onrc.onos.api.packet.IPacketService;
import net.onrc.onos.apps.segmentrouting.web.SegmentRoutingWebRoutable;
+import net.onrc.onos.core.drivermanager.OFSwitchImplDellOSR;
import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
import net.onrc.onos.core.intent.Path;
import net.onrc.onos.core.main.config.IConfigInfoService;
@@ -118,7 +119,7 @@
private HashMap<Integer, HashMap<Integer, List<Integer>>> adjacencySidTable;
// Flag whether transit router supports ECMP or not
- private boolean supportTransitECMP = true;
+ // private boolean supportTransitECMP = true;
private int testMode = 0;
@@ -1866,7 +1867,7 @@
actions.add(decMplsTtlAction);
}
- if (!supportTransitECMP && isTransitRouter(sw) && !php) {
+ if ((sw instanceof OFSwitchImplDellOSR) && isTransitRouter(sw) && !php) {
PortNumber port = pickOnePort(sw, fwdSws);
if (port == null) {
log.warn("Failed to get a port from NeightborSet");
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/DriverManager.java b/src/main/java/net/onrc/onos/core/drivermanager/DriverManager.java
index 5c967ed..213119f 100644
--- a/src/main/java/net/onrc/onos/core/drivermanager/DriverManager.java
+++ b/src/main/java/net/onrc/onos/core/drivermanager/DriverManager.java
@@ -39,7 +39,13 @@
if (vendor.startsWith("Stanford University, Ericsson Research and CPqD Research")
&&
hw.startsWith("OpenFlow 1.3 Reference Userspace Switch")) {
- return new OFSwitchImplCPqD13(desc, cpqdUsePipeline13);
+ return new OFSwitchImplCpqdOSR(desc, cpqdUsePipeline13);
+ }
+
+ if (vendor.contains("Dell")
+ &&
+ hw.contains("OpenFlow 1.3")) {
+ return new OFSwitchImplDellOSR(desc, cpqdUsePipeline13);
}
if (!disableOvsClassification && vendor.startsWith("Nicira") &&
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCpqdOSR.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCpqdOSR.java
new file mode 100644
index 0000000..6bcdb70
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplCpqdOSR.java
@@ -0,0 +1,16 @@
+package net.onrc.onos.core.drivermanager;
+
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+
+/**
+ * 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 OFSwitchImplCpqdOSR extends OFSwitchImplSpringOpenTTP {
+
+ public OFSwitchImplCpqdOSR(OFDescStatsReply desc, boolean usePipeline13) {
+ super(desc, usePipeline13);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplDellOSR.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplDellOSR.java
new file mode 100644
index 0000000..6c12643
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplDellOSR.java
@@ -0,0 +1,15 @@
+package net.onrc.onos.core.drivermanager;
+
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+
+/**
+ * OFDescriptionStatistics Vendor (Manufacturer Desc.): Dell Make (Hardware
+ * Desc.) : OpenFlow 1.3 Reference Userspace Switch Model (Datapath Desc.) :
+ * None Software : Serial : None
+ */
+public class OFSwitchImplDellOSR extends OFSwitchImplSpringOpenTTP {
+
+ public OFSwitchImplDellOSR(OFDescStatsReply desc, boolean usePipeline13) {
+ super(desc, usePipeline13);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplSpringOpenTTP.java b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplSpringOpenTTP.java
new file mode 100644
index 0000000..bb33591
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/drivermanager/OFSwitchImplSpringOpenTTP.java
@@ -0,0 +1,2055 @@
+package net.onrc.onos.core.drivermanager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import net.floodlightcontroller.core.IFloodlightProviderService.Role;
+import net.floodlightcontroller.core.IOF13Switch;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
+import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
+import net.floodlightcontroller.core.internal.OFSwitchImplBase;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.OrderedCollection;
+import net.onrc.onos.core.configmanager.INetworkConfigService;
+import net.onrc.onos.core.configmanager.INetworkConfigService.NetworkConfigState;
+import net.onrc.onos.core.configmanager.INetworkConfigService.SwitchConfigStatus;
+import net.onrc.onos.core.configmanager.NetworkConfig.LinkConfig;
+import net.onrc.onos.core.configmanager.NetworkConfig.SwitchConfig;
+import net.onrc.onos.core.configmanager.NetworkConfigManager;
+import net.onrc.onos.core.configmanager.PktLinkConfig;
+import net.onrc.onos.core.configmanager.SegmentRouterConfig;
+import net.onrc.onos.core.configmanager.SegmentRouterConfig.AdjacencySid;
+import net.onrc.onos.core.matchaction.MatchAction;
+import net.onrc.onos.core.matchaction.MatchActionOperationEntry;
+import net.onrc.onos.core.matchaction.MatchActionOperations;
+import net.onrc.onos.core.matchaction.MatchActionOperations.Operator;
+import net.onrc.onos.core.matchaction.action.Action;
+import net.onrc.onos.core.matchaction.action.CopyTtlInAction;
+import net.onrc.onos.core.matchaction.action.CopyTtlOutAction;
+import net.onrc.onos.core.matchaction.action.DecMplsTtlAction;
+import net.onrc.onos.core.matchaction.action.DecNwTtlAction;
+import net.onrc.onos.core.matchaction.action.GroupAction;
+import net.onrc.onos.core.matchaction.action.ModifyDstMacAction;
+import net.onrc.onos.core.matchaction.action.ModifySrcMacAction;
+import net.onrc.onos.core.matchaction.action.OutputAction;
+import net.onrc.onos.core.matchaction.action.PopMplsAction;
+import net.onrc.onos.core.matchaction.action.PushMplsAction;
+import net.onrc.onos.core.matchaction.action.SetDAAction;
+import net.onrc.onos.core.matchaction.action.SetMplsBosAction;
+import net.onrc.onos.core.matchaction.action.SetMplsIdAction;
+import net.onrc.onos.core.matchaction.action.SetSAAction;
+import net.onrc.onos.core.matchaction.match.Ipv4Match;
+import net.onrc.onos.core.matchaction.match.Match;
+import net.onrc.onos.core.matchaction.match.MplsMatch;
+import net.onrc.onos.core.matchaction.match.PacketMatch;
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.IPv4Net;
+import net.onrc.onos.core.util.PortNumber;
+
+import org.codehaus.jackson.map.ObjectMapper;
+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.OFFlowMod;
+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.OFPortStatus;
+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.match.Match.Builder;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+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.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;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.util.HexString;
+
+/**
+ * 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 OFSwitchImplSpringOpenTTP extends OFSwitchImplBase implements IOF13Switch {
+ private AtomicBoolean driverHandshakeComplete;
+ private AtomicBoolean haltStateMachine;
+ 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 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_ACL = 5;
+
+ private static final short MAX_PRIORITY = (short) 0xffff;
+ private static final short PRIORITY_MULTIPLIER = (short) 2046;
+ private static final short MIN_PRIORITY = 0x0;
+
+ private long barrierXidToWaitFor = -1;
+ private DriverState driverState;
+ private final boolean usePipeline13;
+ private SegmentRouterConfig srConfig;
+ private ConcurrentMap<Dpid, Set<PortNumber>> neighbors;
+ private ConcurrentMap<PortNumber, Dpid> portToNeighbors;
+ private List<Integer> segmentIds;
+ private boolean isEdgeRouter;
+ private ConcurrentMap<NeighborSet, EcmpInfo> ecmpGroups;
+ private ConcurrentMap<Integer, EcmpInfo> userDefinedGroups;
+ private ConcurrentMap<PortNumber, ArrayList<NeighborSet>> portNeighborSetMap;
+ private AtomicInteger groupid;
+ private Map<String, String> publishAttributes;
+
+ public OFSwitchImplSpringOpenTTP(OFDescStatsReply desc, boolean usePipeline13) {
+ super();
+ haltStateMachine = new AtomicBoolean(false);
+ driverState = DriverState.INIT;
+ driverHandshakeComplete = new AtomicBoolean(false);
+ setSwitchDescription(desc);
+ neighbors = new ConcurrentHashMap<Dpid, Set<PortNumber>>();
+ portToNeighbors = new ConcurrentHashMap<PortNumber, Dpid>();
+ ecmpGroups = new ConcurrentHashMap<NeighborSet, EcmpInfo>();
+ userDefinedGroups = new ConcurrentHashMap<Integer, EcmpInfo>();
+ portNeighborSetMap =
+ new ConcurrentHashMap<PortNumber, ArrayList<NeighborSet>>();
+ segmentIds = new ArrayList<Integer>();
+ isEdgeRouter = false;
+ groupid = new AtomicInteger(0);
+ this.usePipeline13 = usePipeline13;
+ }
+
+ // *****************************
+ // OFSwitchImplBase
+ // *****************************
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "OFSwitchImplCPqD13 [" + ((channel != null)
+ ? channel.getRemoteAddress() : "?")
+ + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
+ }
+
+ @Override
+ public void startDriverHandshake() throws IOException {
+ log.debug("Starting driver handshake for sw {}", getStringId());
+ if (startDriverHandshakeCalled) {
+ throw new SwitchDriverSubHandshakeAlreadyStarted();
+ }
+ startDriverHandshakeCalled = true;
+ factory = getFactory();
+ if (!usePipeline13) {
+ // Send packet-in to controller if a packet misses the first table
+ populateTableMissEntry(0, true, false, false, 0);
+ driverHandshakeComplete.set(true);
+ } else {
+ nextDriverState();
+ }
+ }
+
+ @Override
+ public boolean isDriverHandshakeComplete() {
+ if (!startDriverHandshakeCalled)
+ throw new SwitchDriverSubHandshakeNotStarted();
+ return driverHandshakeComplete.get();
+ }
+
+ @Override
+ public void processDriverHandshakeMessage(OFMessage m) {
+ if (!startDriverHandshakeCalled)
+ throw new SwitchDriverSubHandshakeNotStarted();
+ if (isDriverHandshakeComplete())
+ throw new SwitchDriverSubHandshakeCompleted(m);
+ try {
+ processOFMessage(this, m);
+ } catch (IOException e) {
+ log.error("Error generated when processing OFMessage", e.getCause());
+ }
+ }
+
+ @Override
+ public String getSwitchDriverState() {
+ return driverState.toString();
+ }
+
+ public void removePortFromGroups(PortNumber port) {
+ log.debug("removePortFromGroups: Remove port {} from Switch {}",
+ port, getStringId());
+ ArrayList<NeighborSet> portNSSet = portNeighborSetMap.get(port);
+ if (portNSSet == null)
+ {
+ /* No Groups are created with this port yet */
+ log.warn("removePortFromGroups: No groups exist with Switch {} port {}",
+ getStringId(), port);
+ return;
+ }
+ log.debug("removePortFromGroups: Neighborsets that the port {} is part"
+ + "of on Switch {} are {}",
+ port, getStringId(), portNSSet);
+
+ for (NeighborSet ns : portNSSet) {
+ /* Delete the first matched bucket */
+ EcmpInfo portEcmpInfo = ecmpGroups.get(ns);
+ Iterator<BucketInfo> it = portEcmpInfo.buckets.iterator();
+ log.debug("removePortFromGroups: Group {} on Switch {} has {} buckets",
+ portEcmpInfo.groupId, getStringId(),
+ portEcmpInfo.buckets.size());
+ while (it.hasNext()) {
+ BucketInfo bucket = it.next();
+ if (bucket.outport.equals(port)) {
+ it.remove();
+ }
+ }
+ log.debug("removePortFromGroups: Modifying Group on Switch {} "
+ + "and Neighborset {} with {}",
+ getStringId(), ns, portEcmpInfo);
+ modifyEcmpGroup(portEcmpInfo);
+ }
+ /* Don't delete the entry from portNeighborSetMap because
+ * when the port is up again this info is needed
+ */
+ return;
+ }
+
+ public void addPortToGroups(PortNumber port) {
+ log.debug("addPortToGroups: Add port {} to Switch {}",
+ port, getStringId());
+ ArrayList<NeighborSet> portNSSet = portNeighborSetMap.get(port);
+ if (portNSSet == null) {
+ /* Unknown Port */
+ log.warn("addPortToGroups: Switch {} port {} is unknown",
+ getStringId(), port);
+ return;
+ }
+ log.debug("addPortToGroups: Neighborsets that the port {} is part"
+ + "of on Switch {} are {}",
+ port, getStringId(), portNSSet);
+
+ Dpid neighborDpid = portToNeighbors.get(port);
+ for (NeighborSet ns : portNSSet) {
+ EcmpInfo portEcmpInfo = ecmpGroups.get(ns);
+ /* Find if this port is already part of any bucket
+ * in this group
+ * NOTE: This is needed because in some cases
+ * (such as for configured network nodes), both driver and
+ * application detect the network elements and creates the
+ * buckets in the same group. This check is to avoid
+ * duplicate bucket creation in such scenarios
+ */
+ List<BucketInfo> buckets = portEcmpInfo.buckets;
+ if (buckets == null) {
+ buckets = new ArrayList<BucketInfo>();
+ portEcmpInfo.buckets = buckets;
+ } else {
+ Iterator<BucketInfo> it = buckets.iterator();
+ boolean matchingBucketExist = false;
+ while (it.hasNext()) {
+ BucketInfo bucket = it.next();
+ if (bucket.outport.equals(port)) {
+ matchingBucketExist = true;
+ break;
+ }
+ }
+ if (matchingBucketExist) {
+ log.warn("addPortToGroups: On Switch {} duplicate "
+ + "portAdd is called for port {} with buckets {}",
+ getStringId(), port, buckets);
+ continue;
+ }
+ }
+ BucketInfo b = new BucketInfo(neighborDpid,
+ MacAddress.of(srConfig.getRouterMac()),
+ getNeighborRouterMacAddress(neighborDpid),
+ port,
+ ns.getEdgeLabel(), true, -1);
+ buckets.add(b);
+ log.debug("addPortToGroups: Modifying Group on Switch {} "
+ + "and Neighborset {} with {}",
+ getStringId(), ns, portEcmpInfo);
+ modifyEcmpGroup(portEcmpInfo);
+ }
+ return;
+ }
+
+ @Override
+ public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) {
+ OrderedCollection<PortChangeEvent> events = super.processOFPortStatus(ps);
+ for (PortChangeEvent e : events) {
+ switch (e.type) {
+ case DELETE:
+ case DOWN:
+ log.debug("processOFPortStatus: sw {} Port {} DOWN",
+ getStringId(), e.port.getPortNo().getPortNumber());
+ removePortFromGroups(PortNumber.uint32(
+ e.port.getPortNo().getPortNumber()));
+ break;
+ case UP:
+ log.debug("processOFPortStatus: sw {} Port {} UP",
+ getStringId(), e.port.getPortNo().getPortNumber());
+ addPortToGroups(PortNumber.uint32(
+ e.port.getPortNo().getPortNumber()));
+ }
+ }
+ return events;
+ }
+
+ // *****************************
+ // Driver handshake state-machine
+ // *****************************
+
+ enum DriverState {
+ INIT,
+ SET_TABLE_MISS_ENTRIES,
+ SET_TABLE_VLAN_TMAC,
+ SET_GROUPS,
+ VERIFY_GROUPS,
+ SET_ADJACENCY_LABELS,
+ EXIT
+ }
+
+ protected void nextDriverState() throws IOException {
+ DriverState currentState = driverState;
+ if (haltStateMachine.get()) {
+ return;
+ }
+ switch (currentState) {
+ case INIT:
+ driverState = DriverState.SET_TABLE_MISS_ENTRIES;
+ setTableMissEntries();
+ sendHandshakeBarrier();
+ break;
+ case SET_TABLE_MISS_ENTRIES:
+ driverState = DriverState.SET_TABLE_VLAN_TMAC;
+ getNetworkConfig();
+ populateTableVlan();
+ populateTableTMac();
+ sendHandshakeBarrier();
+ break;
+ case SET_TABLE_VLAN_TMAC:
+ driverState = DriverState.SET_GROUPS;
+ createGroups();
+ sendHandshakeBarrier();
+ break;
+ case SET_GROUPS:
+ driverState = DriverState.VERIFY_GROUPS;
+ verifyGroups();
+ break;
+ case VERIFY_GROUPS:
+ driverState = DriverState.SET_ADJACENCY_LABELS;
+ assignAdjacencyLabels();
+ break;
+ case SET_ADJACENCY_LABELS:
+ driverState = DriverState.EXIT;
+ driverHandshakeComplete.set(true);
+ break;
+ case EXIT:
+ default:
+ driverState = DriverState.EXIT;
+ log.error("Driver handshake has exited for sw: {}", getStringId());
+ }
+ }
+
+ void processOFMessage(IOFSwitch sw, OFMessage m) throws IOException {
+ switch (m.getType()) {
+ case BARRIER_REPLY:
+ processBarrierReply(m);
+ break;
+
+ case ERROR:
+ processErrorMessage(m);
+ break;
+
+ case GET_ASYNC_REPLY:
+ OFAsyncGetReply asrep = (OFAsyncGetReply) m;
+ decodeAsyncGetReply(asrep);
+ break;
+
+ case PACKET_IN:
+ // not ready to handle packet-ins
+ break;
+
+ case QUEUE_GET_CONFIG_REPLY:
+ // not doing queue config yet
+ break;
+
+ case STATS_REPLY:
+ processStatsReply((OFStatsReply) m);
+ break;
+
+ case ROLE_REPLY: // channelHandler should handle this
+ case PORT_STATUS: // channelHandler should handle this
+ case FEATURES_REPLY: // don't care
+ case FLOW_REMOVED: // don't care
+ default:
+ log.debug("Received message {} during switch-driver subhandshake "
+ + "from switch {} ... Ignoring message", m, sw.getStringId());
+ }
+ }
+
+ 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 processErrorMessage(OFMessage m) {
+ log.error("Switch {} Error {} in DriverState", getStringId(),
+ (OFErrorMsg) m, driverState);
+ }
+
+ private void processBarrierReply(OFMessage m) throws IOException {
+ if (m.getXid() == barrierXidToWaitFor) {
+ // Driver state-machine progresses to the next state.
+ // If Barrier messages is not received, then eventually
+ // the ChannelHandler state machine will timeout, and the switch
+ // will be disconnected.
+ nextDriverState();
+ } else {
+ log.error("Received incorrect barrier-message xid {} (expected: {}) in "
+ + "switch-driver state {} for switch {}", m, barrierXidToWaitFor,
+ driverState, getStringId());
+ }
+ }
+
+ private void processGroupDesc(OFGroupDescStatsReply gdsr) {
+ log.info("Sw: {} Group Desc {}", getStringId(), gdsr);
+ // TODO -- actually do verification
+ try {
+ nextDriverState();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ // *****************************
+ // Utility methods
+ // *****************************
+
+ void setTableMissEntries() throws IOException {
+ // set all table-miss-entries
+ populateTableMissEntry(TABLE_VLAN, true, false, false, -1);
+ populateTableMissEntry(TABLE_TMAC, true, false, false, -1);
+ populateTableMissEntry(TABLE_IPv4_UNICAST, false, true, true,
+ TABLE_ACL);
+ populateTableMissEntry(TABLE_MPLS, false, true, true,
+ TABLE_ACL);
+ populateTableMissEntry(TABLE_ACL, false, false, false, -1);
+ }
+
+ private void sendHandshakeBarrier() throws IOException {
+ long xid = getNextTransactionId();
+ barrierXidToWaitFor = xid;
+ OFBarrierRequest br = getFactory()
+ .buildBarrierRequest()
+ .setXid(xid)
+ .build();
+ write(br, null);
+ }
+
+ /**
+ * Adds a table-miss-entry to a pipeline table.
+ * <p>
+ * The table-miss-entry can be added with 'write-actions' or
+ * 'apply-actions'. It can also add a 'goto-table' instruction. 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 if a packet hits the
+ * table-miss-entry, pipeline execution will stop, and the action set
+ * associated with the packet will be executed.
+ *
+ * @param tableToAdd the table to where the table-miss-entry will be added
+ * @param toControllerNow as an APPLY_ACTION instruction
+ * @param toControllerWrite as a WRITE_ACTION instruction
+ * @param toTable as a GOTO_TABLE instruction
+ * @param tableToSend the table to send as per the GOTO_TABLE instruction it
+ * needs to be set if 'toTable' is true. Ignored of 'toTable' is
+ * false.
+ * @throws IOException
+ */
+ @SuppressWarnings("unchecked")
+ private void populateTableMissEntry(int tableToAdd, boolean toControllerNow,
+ boolean toControllerWrite,
+ boolean toTable, int tableToSend) throws IOException {
+ 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();
+ write(tableMissEntry, null);
+ }
+
+ private void getNetworkConfig() {
+ INetworkConfigService ncs = floodlightProvider.getNetworkConfigService();
+ SwitchConfigStatus scs = ncs.checkSwitchConfig(new Dpid(getId()));
+ if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) {
+ srConfig = (SegmentRouterConfig) scs.getSwitchConfig();
+ isEdgeRouter = srConfig.isEdgeRouter();
+ } else {
+ log.error("Switch not configured as Segment-Router");
+ }
+
+ List<LinkConfig> linkConfigList = ncs.getConfiguredAllowedLinks();
+ setNeighbors(linkConfigList);
+
+ if (isEdgeRouter) {
+ List<SwitchConfig> switchList = ncs.getConfiguredAllowedSwitches();
+ getAllNodeSegmentIds(switchList);
+ }
+ }
+
+ private void populateTableVlan() throws IOException {
+ 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) {
+ 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();
+
+ // TODO: match on vlan-tagged packets for vlans configured on
+ // subnet ports and strip-vlan
+
+ // Do not need to add vlans
+ /*int vlanid = getVlanConfig(pnum);
+ 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);
+ }
+ }
+ write(msglist);
+ log.debug("Adding {} port/vlan-rules in sw {}", msglist.size(), getStringId());
+ }
+
+ private void populateTableTMac() throws IOException {
+ // match for router-mac and ip-packets
+ OFOxmEthType oxe = factory.oxms().ethType(EthType.IPv4);
+ OFOxmEthDst dmac = factory.oxms().ethDst(getRouterMacAddr());
+ OFOxmList oxmListIp = OFOxmList.of(dmac, 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 router-mac and mpls packets
+ OFOxmEthType oxmpls = factory.oxms().ethType(EthType.MPLS_UNICAST);
+ OFOxmList oxmListMpls = OFOxmList.of(dmac, 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();
+
+ 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 MacAddress getRouterMacAddr() {
+ if (srConfig != null) {
+ return MacAddress.of(srConfig.getRouterMac());
+ } else {
+ // return a dummy mac address - it will not be used
+ return MacAddress.of("00:00:00:00:00:00");
+ }
+ }
+
+ private boolean isEdgeRouter(Dpid ndpid) {
+ INetworkConfigService ncs = floodlightProvider.getNetworkConfigService();
+ SwitchConfigStatus scs = ncs.checkSwitchConfig(ndpid);
+ if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) {
+ return ((SegmentRouterConfig) scs.getSwitchConfig()).isEdgeRouter();
+ } else {
+ // TODO: return false if router not allowed
+ return false;
+ }
+ }
+
+ private MacAddress getNeighborRouterMacAddress(Dpid ndpid) {
+ INetworkConfigService ncs = floodlightProvider.getNetworkConfigService();
+ SwitchConfigStatus scs = ncs.checkSwitchConfig(ndpid);
+ if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) {
+ return MacAddress.of(((SegmentRouterConfig) scs.getSwitchConfig())
+ .getRouterMac());
+ } else {
+ // return a dummy mac address - it will not be used
+ return MacAddress.of("00:00:00:00:00:00");
+ }
+ }
+
+ private void setNeighbors(List<LinkConfig> linkConfigList) {
+ for (LinkConfig lg : linkConfigList) {
+ if (!lg.getType().equals(NetworkConfigManager.PKT_LINK)) {
+ continue;
+ }
+ PktLinkConfig plg = (PktLinkConfig) lg;
+ if (plg.getDpid1() == getId()) {
+ addNeighborAtPort(new Dpid(plg.getDpid2()),
+ PortNumber.uint32(plg.getPort1()));
+ } else if (plg.getDpid2() == getId()) {
+ addNeighborAtPort(new Dpid(plg.getDpid1()),
+ PortNumber.uint32(plg.getPort2()));
+ }
+ }
+ }
+
+ private void addNeighborAtPort(Dpid neighborDpid, PortNumber portToNeighbor) {
+ /* Update NeighborToPort database */
+ if (neighbors.get(neighborDpid) != null) {
+ neighbors.get(neighborDpid).add(portToNeighbor);
+ } else {
+ Set<PortNumber> ports = new HashSet<PortNumber>();
+ ports.add(portToNeighbor);
+ neighbors.put(neighborDpid, ports);
+ }
+
+ /* Update portToNeighbors database */
+ if (portToNeighbors.get(portToNeighbor) == null)
+ portToNeighbors.put(portToNeighbor, neighborDpid);
+ }
+
+ private void getAllNodeSegmentIds(List<SwitchConfig> switchList) {
+ for (SwitchConfig sc : switchList) {
+ /* TODO: Do we need to check if the SwitchConfig is of
+ * type SegmentRouter?
+ */
+ if (sc.getDpid() == getId()) {
+ continue;
+ }
+ segmentIds.add(((SegmentRouterConfig) sc).getNodeSid());
+ }
+ log.debug("getAllNodeSegmentIds: at sw {} are {}",
+ getStringId(), segmentIds);
+ }
+
+ private boolean isSegmentIdSameAsNodeSegmentId(Dpid dpid, int sId) {
+ INetworkConfigService ncs = floodlightProvider.getNetworkConfigService();
+ SwitchConfigStatus scs = ncs.checkSwitchConfig(dpid);
+ if (scs.getConfigState() == NetworkConfigState.ACCEPT_ADD) {
+ return (((SegmentRouterConfig) scs.getSwitchConfig()).
+ getNodeSid() == sId);
+ } else {
+ // TODO: return false if router not allowed
+ return false;
+ }
+ }
+
+ private Set<Set<Dpid>> getAllNeighborSets(Set<Dpid> neighbors) {
+ List<Dpid> list = new ArrayList<Dpid>(neighbors);
+ Set<Set<Dpid>> sets = new HashSet<Set<Dpid>>();
+ /* get the number of elements in the neighbors */
+ int elements = list.size();
+ /* the number of members of a power set is 2^n
+ * including the empty set
+ */
+ int powerElements = (1 << elements);
+
+ /* run a binary counter for the number of power elements */
+ for (long i = 1; i < powerElements; i++) {
+ Set<Dpid> dpidSubSet = new HashSet<Dpid>();
+ for (int j = 0; j < elements; j++) {
+ if ((i >> j) % 2 == 1) {
+ dpidSubSet.add(list.get(j));
+ }
+ }
+ /* NOTE: Avoid any pairings of edge routers only
+ * at a backbone router */
+ boolean avoidEdgeRouterPairing = true;
+ if ((!isEdgeRouter) && (dpidSubSet.size() > 1)) {
+ for (Dpid dpid : dpidSubSet) {
+ if (!isEdgeRouter(dpid)) {
+ avoidEdgeRouterPairing = false;
+ break;
+ }
+ }
+ }
+ else
+ avoidEdgeRouterPairing = false;
+
+ if (!avoidEdgeRouterPairing)
+ sets.add(dpidSubSet);
+ }
+ return sets;
+ }
+
+ private void createGroupForANeighborSet(NeighborSet ns, int groupId) {
+ List<BucketInfo> buckets = new ArrayList<BucketInfo>();
+ for (Dpid d : ns.getDpids()) {
+ for (PortNumber sp : neighbors.get(d)) {
+ BucketInfo b = new BucketInfo(d,
+ MacAddress.of(srConfig.getRouterMac()),
+ getNeighborRouterMacAddress(d), sp,
+ ns.getEdgeLabel(), true, -1);
+ buckets.add(b);
+
+ /* Update Port Neighborset map */
+ ArrayList<NeighborSet> portNeighborSets =
+ portNeighborSetMap.get(sp);
+ if (portNeighborSets == null) {
+ portNeighborSets = new ArrayList<NeighborSet>();
+ portNeighborSets.add(ns);
+ portNeighborSetMap.put(sp, portNeighborSets);
+ }
+ else
+ portNeighborSets.add(ns);
+ }
+ }
+ EcmpInfo ecmpInfo = new EcmpInfo(groupId, OFGroupType.SELECT, buckets);
+ setEcmpGroup(ecmpInfo);
+ ecmpGroups.put(ns, ecmpInfo);
+ log.debug(
+ "createGroupForANeighborSet: Creating ecmp group {} in sw {} "
+ + "for neighbor set {} with: {}",
+ groupId, getStringId(), ns, ecmpInfo);
+ return;
+ }
+
+ /**
+ * createGroups creates ECMP groups for all ports on this router connected
+ * to other routers (in the OF network). The information for ports is
+ * gleaned from the configured links. If no links are configured no groups
+ * will be created, and it is up to the caller of the IOF13Switch API to
+ * create groups.
+ * <p>
+ * By default all ports connected to the same neighbor router will be part
+ * of the same ECMP group. In addition, groups will be created for all
+ * possible combinations of neighbor routers.
+ * <p>
+ * For example, consider this router (R0) connected to 3 neighbors (R1, R2,
+ * and R3). The following groups will be created in R0:
+ * <li>1) all ports to R1,
+ * <li>2) all ports to R2,
+ * <li>3) all ports to R3,
+ * <li>4) all ports to R1 and R2
+ * <li>5) all ports to R1 and R3
+ * <li>6) all ports to R2 and R3
+ * <li>7) all ports to R1, R2, and R3
+ */
+ private void createGroups() {
+
+ Set<Dpid> dpids = neighbors.keySet();
+ if (dpids == null || dpids.isEmpty()) {
+ return;
+ }
+ /* Create all possible Neighbor sets from this router
+ * NOTE: Avoid any pairings of edge routers only
+ */
+ Set<Set<Dpid>> powerSet = getAllNeighborSets(dpids);
+ log.debug("createGroups: The size of neighbor powerset for sw {} is {}",
+ getStringId(), powerSet.size());
+ Set<NeighborSet> nsSet = new HashSet<NeighborSet>();
+ for (Set<Dpid> combo : powerSet) {
+ if (combo.isEmpty())
+ continue;
+ if (isEdgeRouter && !segmentIds.isEmpty()) {
+ for (Integer sId : segmentIds) {
+ NeighborSet ns = new NeighborSet();
+ ns.addDpids(combo);
+ /* Check if the edge label being set is of the
+ * same node in the Neighbor set
+ */
+ if ((combo.size() != 1) ||
+ (!isSegmentIdSameAsNodeSegmentId(
+ combo.iterator().next(), sId))) {
+ ns.setEdgeLabel(sId);
+ }
+ nsSet.add(ns);
+ }
+ } else {
+ NeighborSet ns = new NeighborSet();
+ ns.addDpids(combo);
+ nsSet.add(ns);
+ }
+ }
+ log.debug("createGroups: The neighborset with label for sw {} is {}",
+ getStringId(), nsSet);
+
+ for (NeighborSet ns : nsSet) {
+ createGroupForANeighborSet(ns, groupid.incrementAndGet());
+ }
+ }
+
+ private class EcmpInfo {
+ int groupId;
+ OFGroupType groupType;
+ List<BucketInfo> buckets;
+
+ EcmpInfo(int gid, OFGroupType gType, List<BucketInfo> bucketInfos) {
+ groupId = gid;
+ groupType = gType;
+ buckets = bucketInfos;
+ }
+
+ @Override
+ public String toString() {
+ return "groupId: " + groupId + ", buckets: " + buckets;
+ }
+ }
+
+ private class BucketInfo {
+ Dpid neighborDpid;
+ MacAddress srcMac;
+ MacAddress dstMac;
+ PortNumber outport;
+ int groupNo;
+ int mplsLabel;
+ boolean bos;
+
+ BucketInfo(Dpid nDpid, MacAddress smac, MacAddress dmac,
+ PortNumber p, int label, boolean bos, int gotoGroupNo) {
+ neighborDpid = nDpid;
+ srcMac = smac;
+ dstMac = dmac;
+ outport = p;
+ mplsLabel = label;
+ this.bos = bos;
+ groupNo = gotoGroupNo;
+ }
+
+
+ @Override
+ public String toString() {
+ return " {neighborDpid: " + neighborDpid + ", dstMac: " + dstMac +
+ ", srcMac: " + srcMac + ", outport: " + outport +
+ ", groupNo: " + groupNo +
+ ", mplsLabel: " + mplsLabel + "}";
+ }
+ }
+
+ private void setEcmpGroup(EcmpInfo ecmpInfo) {
+ List<OFMessage> msglist = new ArrayList<OFMessage>();
+ OFGroup group = OFGroup.of(ecmpInfo.groupId);
+
+ List<OFBucket> buckets = new ArrayList<OFBucket>();
+ for (BucketInfo b : ecmpInfo.buckets) {
+ List<OFAction> actions = new ArrayList<OFAction>();
+ if (b.dstMac != null) {
+ OFOxmEthDst dmac = factory.oxms()
+ .ethDst(b.dstMac);
+ OFAction setDA = factory.actions().buildSetField()
+ .setField(dmac).build();
+ actions.add(setDA);
+ }
+ if (b.srcMac != null) {
+ OFOxmEthSrc smac = factory.oxms()
+ .ethSrc(b.srcMac);
+ OFAction setSA = factory.actions().buildSetField()
+ .setField(smac).build();
+ actions.add(setSA);
+ }
+ if (b.outport != null) {
+ OFAction outp = factory.actions().buildOutput()
+ .setPort(OFPort.of(b.outport.shortValue()))
+ .build();
+ actions.add(outp);
+ }
+ if (b.mplsLabel != -1) {
+ OFAction pushLabel = factory.actions().buildPushMpls()
+ .setEthertype(EthType.MPLS_UNICAST).build();
+ OFBooleanValue bosValue = null;
+ if (b.bos)
+ bosValue = OFBooleanValue.TRUE;
+ else
+ bosValue = OFBooleanValue.FALSE;
+ OFOxmMplsBos bosX = factory.oxms()
+ .mplsBos(bosValue);
+ OFAction setBX = factory.actions().buildSetField()
+ .setField(bosX).build();
+ OFOxmMplsLabel lid = factory.oxms()
+ .mplsLabel(U32.of(b.mplsLabel));
+ OFAction setLabel = factory.actions().buildSetField()
+ .setField(lid).build();
+ OFAction copyTtl = factory.actions().copyTtlOut();
+ OFAction decrTtl = factory.actions().decMplsTtl();
+ actions.add(pushLabel);
+ actions.add(setLabel);
+ actions.add(setBX);
+ actions.add(copyTtl);
+ // decrement TTL only when the first MPLS label is pushed
+ if (b.bos)
+ actions.add(decrTtl);
+ }
+ if (b.groupNo > 0) {
+ OFAction groupTo = factory.actions().buildGroup()
+ .setGroup(OFGroup.of(b.groupNo))
+ .build();
+ actions.add(groupTo);
+ }
+ OFBucket.Builder bldr = factory.buildBucket();
+ bldr.setActions(actions);
+ if (ecmpInfo.groupType == OFGroupType.SELECT)
+ bldr.setWeight(1);
+ OFBucket ofb = bldr.build();
+ buckets.add(ofb);
+ }
+
+ OFMessage gm = factory.buildGroupAdd()
+ .setGroup(group)
+ .setBuckets(buckets)
+ .setGroupType(ecmpInfo.groupType)
+ .setXid(getNextTransactionId())
+ .build();
+ msglist.add(gm);
+ try {
+ write(msglist);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void deleteGroup(EcmpInfo groupInfo) {
+
+ List<OFMessage> msglist = new ArrayList<OFMessage>();
+ OFGroup group = OFGroup.of(groupInfo.groupId);
+
+ OFMessage gm = factory.buildGroupDelete()
+ .setGroup(group)
+ // .setGroupType(groupInfo.groupType) /* Due to a bug in CPqD
+ // switch */
+ .setGroupType(OFGroupType.SELECT)
+ .setXid(getNextTransactionId())
+ .build();
+ msglist.add(gm);
+ try {
+ write(msglist);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void modifyEcmpGroup(EcmpInfo ecmpInfo) {
+ List<OFMessage> msglist = new ArrayList<OFMessage>();
+ OFGroup group = OFGroup.of(ecmpInfo.groupId);
+
+ List<OFBucket> buckets = new ArrayList<OFBucket>();
+ for (BucketInfo b : ecmpInfo.buckets) {
+ OFOxmEthDst dmac = factory.oxms()
+ .ethDst(b.dstMac);
+ OFAction setDA = factory.actions().buildSetField()
+ .setField(dmac).build();
+ OFOxmEthSrc smac = factory.oxms()
+ .ethSrc(b.srcMac);
+ OFAction setSA = factory.actions().buildSetField()
+ .setField(smac).build();
+ OFAction outp = factory.actions().buildOutput()
+ .setPort(OFPort.of(b.outport.shortValue()))
+ .build();
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(setSA);
+ actions.add(setDA);
+ actions.add(outp);
+ if (b.mplsLabel != -1) {
+ OFAction pushLabel = factory.actions().buildPushMpls()
+ .setEthertype(EthType.MPLS_UNICAST).build();
+ OFOxmMplsBos bosX = factory.oxms()
+ .mplsBos(OFBooleanValue.TRUE);
+ OFAction setBX = factory.actions().buildSetField()
+ .setField(bosX).build();
+ OFOxmMplsLabel lid = factory.oxms()
+ .mplsLabel(U32.of(b.mplsLabel));
+ OFAction setLabel = factory.actions().buildSetField()
+ .setField(lid).build();
+ OFAction copyTtl = factory.actions().copyTtlOut();
+ OFAction decrTtl = factory.actions().decMplsTtl();
+ actions.add(pushLabel);
+ actions.add(setLabel);
+ actions.add(setBX);
+ actions.add(copyTtl);
+ actions.add(decrTtl);
+ }
+ OFBucket ofb = factory.buildBucket()
+ .setWeight(1)
+ .setActions(actions)
+ .build();
+ buckets.add(ofb);
+ }
+
+ OFMessage gm = factory.buildGroupModify()
+ .setGroup(group)
+ .setBuckets(buckets)
+ .setGroupType(OFGroupType.SELECT)
+ .setXid(getNextTransactionId())
+ .build();
+ msglist.add(gm);
+ try {
+ write(msglist);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void verifyGroups() throws IOException {
+ sendGroupDescRequest();
+ }
+
+ private void sendGroupDescRequest() throws IOException {
+ OFMessage gdr = factory.buildGroupDescStatsRequest()
+ .setXid(getNextTransactionId())
+ .build();
+ write(gdr, null);
+ }
+
+ private void assignAdjacencyLabels() {
+ List<AdjacencySid> autogenAdjSids = new ArrayList<AdjacencySid>();
+ publishAttributes = new HashMap<String, String>();
+ for (OFPortDesc p : getPorts()) {
+ int pnum = p.getPortNo().getPortNumber();
+
+ if (U32.ofRaw(pnum).compareTo(U32.ofRaw(OFPort.MAX.getPortNumber())) >= 1) {
+ continue;
+ }
+ // create unique adj-sid assuming that operator only
+ // enters adjSids for multiple-ports and only in the range
+ // 1-10k XXX make sure that happens
+ int adjSid = srConfig.getNodeSid() * 1000 + pnum;
+ AdjacencySid as = new AdjacencySid(adjSid,
+ Collections.singletonList(pnum));
+ autogenAdjSids.add(as);
+ }
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ publishAttributes.put("autogenAdjSids",
+ mapper.writeValueAsString(autogenAdjSids));
+ } catch (IOException e1) {
+ log.error("Error while writing adjacency labels: {}", e1.getCause());
+ }
+
+ try {
+ nextDriverState();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private OFAction getOFAction(Action action) {
+ OFAction ofAction = null;
+ if (action instanceof OutputAction) {
+ OutputAction outputAction = (OutputAction) action;
+ OFPort port = OFPort.of((int) outputAction.getPortNumber().value());
+ ofAction = factory.actions().output(port, Short.MAX_VALUE);
+ } else if (action instanceof ModifyDstMacAction) {
+ long dstMac = ((ModifyDstMacAction) action).getDstMac().toLong();
+ OFOxmEthDst dmac = factory.oxms()
+ .ethDst(MacAddress.of(dstMac));
+ ofAction = factory.actions().buildSetField()
+ .setField(dmac).build();
+ } else if (action instanceof ModifySrcMacAction) {
+ long srcMac = ((ModifySrcMacAction) action).getSrcMac().toLong();
+ OFOxmEthSrc smac = factory.oxms()
+ .ethSrc(MacAddress.of(srcMac));
+ ofAction = factory.actions().buildSetField()
+ .setField(smac).build();
+ } else if (action instanceof PushMplsAction) {
+ ofAction = factory.actions().pushMpls(EthType.MPLS_UNICAST);
+ } else if (action instanceof SetMplsIdAction) {
+ int labelid = ((SetMplsIdAction) action).getMplsId();
+ OFOxmMplsLabel lid = factory.oxms()
+ .mplsLabel(U32.of(labelid));
+ ofAction = factory.actions().buildSetField()
+ .setField(lid).build();
+ } else if (action instanceof SetMplsBosAction) {
+ OFBooleanValue val = OFBooleanValue.of(
+ ((SetMplsBosAction) action).isSet());
+ OFOxmMplsBos bos = factory.oxms().mplsBos(val);
+ OFAction setBos = factory.actions().buildSetField()
+ .setField(bos).build();
+ } else if (action instanceof PopMplsAction) {
+ EthType ethertype = ((PopMplsAction) action).getEthType();
+ ofAction = factory.actions().popMpls(ethertype);
+ } else if (action instanceof GroupAction) {
+ int gid = -1;
+ GroupAction ga = (GroupAction)action;
+ if (ga.getGroupId() > 0) {
+ gid = ga.getGroupId();
+ }
+ else {
+ NeighborSet ns = ((GroupAction) action).getDpids();
+ EcmpInfo ei = ecmpGroups.get(ns);
+ if (ei == null) {
+ log.debug("Unable to find ecmp group for neighbors {} at "
+ + "switch {} and hence creating it", ns, getStringId());
+ createGroupForANeighborSet(ns, groupid.incrementAndGet());
+ ei = ecmpGroups.get(ns);
+ }
+ gid = ei.groupId;
+ }
+ ofAction = factory.actions().buildGroup()
+ .setGroup(OFGroup.of(gid))
+ .build();
+ } else if (action instanceof DecNwTtlAction) {
+ ofAction = factory.actions().decNwTtl();
+ } else if (action instanceof DecMplsTtlAction) {
+ ofAction = factory.actions().decMplsTtl();
+ } else if (action instanceof CopyTtlInAction) {
+ ofAction = factory.actions().copyTtlIn();
+ } else if (action instanceof CopyTtlOutAction) {
+ ofAction = factory.actions().copyTtlOut();
+ } else if (action instanceof SetDAAction) {
+ OFOxmEthDst dmac = factory.oxms()
+ .ethDst(((SetDAAction)action).getAddress());
+ ofAction = factory.actions().buildSetField()
+ .setField(dmac).build();
+ } else if (action instanceof SetSAAction) {
+ OFOxmEthSrc smac = factory.oxms()
+ .ethSrc(((SetSAAction)action).getAddress());
+ ofAction = factory.actions().buildSetField()
+ .setField(smac).build();
+ } else {
+ log.warn("Unsupported Action type: {}", action.getClass().getName());
+ return null;
+ }
+
+ return ofAction;
+ }
+
+ private OFMessage getIpEntry(MatchActionOperationEntry mao) {
+ MatchAction ma = mao.getTarget();
+ Operator op = mao.getOperator();
+ Ipv4Match ipm = (Ipv4Match) ma.getMatch();
+
+ // set match
+ IPv4Net ipdst = ipm.getDestination();
+ OFOxmEthType ethTypeIp = factory.oxms()
+ .ethType(EthType.IPv4);
+ OFOxmIpv4DstMasked ipPrefix = factory.oxms()
+ .ipv4DstMasked(
+ IPv4Address.of(ipdst.address().value()),
+ IPv4Address.ofCidrMaskLength(ipdst.prefixLen())
+ );
+ OFOxmList oxmList = OFOxmList.of(ethTypeIp, ipPrefix);
+ OFMatchV3 match = factory.buildMatchV3()
+ .setOxmList(oxmList).build();
+
+ // set actions
+ List<OFAction> writeActions = new ArrayList<OFAction>();
+ for (Action action : ma.getActions()) {
+ OFAction ofAction = getOFAction(action);
+ if (ofAction != null) {
+ writeActions.add(ofAction);
+ }
+ }
+
+ // set instructions
+ 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);
+
+ // set flow priority to emulate longest prefix match
+ int priority = ipdst.prefixLen() * PRIORITY_MULTIPLIER;
+ if (ipdst.prefixLen() == (short) 32) {
+ priority = MAX_PRIORITY;
+ }
+
+ // set flow-mod
+ OFFlowMod.Builder fmBuilder = null;
+ switch (op) {
+ case ADD:
+ fmBuilder = factory.buildFlowAdd();
+ break;
+ case REMOVE:
+ fmBuilder = factory.buildFlowDeleteStrict();
+ break;
+ case MODIFY: // TODO
+ fmBuilder = factory.buildFlowModifyStrict();
+ break;
+ default:
+ log.warn("Unsupported MatchAction Operator: {}", op);
+ return null;
+ }
+ OFMessage ipFlow = fmBuilder
+ .setTableId(TableId.of(TABLE_IPv4_UNICAST))
+ .setMatch(match)
+ .setInstructions(instructions)
+ .setPriority(priority)
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setIdleTimeout(0)
+ .setHardTimeout(0)
+ .setXid(getNextTransactionId())
+ .build();
+ log.debug("{} ip-rule {}-{} in sw {}",
+ (op == MatchActionOperations.Operator.ADD) ? "Adding" : "Deleting",
+ match, writeActions,
+ getStringId());
+ return ipFlow;
+ }
+
+ private OFMessage getMplsEntry(MatchActionOperationEntry mao) {
+ MatchAction ma = mao.getTarget();
+ Operator op = mao.getOperator();
+ MplsMatch mplsm = (MplsMatch) ma.getMatch();
+
+ // set match
+ OFOxmEthType ethTypeMpls = factory.oxms()
+ .ethType(EthType.MPLS_UNICAST);
+ OFOxmMplsLabel labelid = factory.oxms()
+ .mplsLabel(U32.of(mplsm.getMplsLabel()));
+ OFOxmMplsBos bos = factory.oxms()
+ .mplsBos(OFBooleanValue.of(mplsm.isBos()));
+ OFOxmList oxmList = OFOxmList.of(ethTypeMpls, labelid, bos);
+ OFMatchV3 matchlabel = factory.buildMatchV3()
+ .setOxmList(oxmList).build();
+
+ // set actions
+ List<OFAction> writeActions = new ArrayList<OFAction>();
+ for (Action action : ma.getActions()) {
+ OFAction ofAction = getOFAction(action);
+ if (ofAction != null) {
+ writeActions.add(ofAction);
+ }
+ }
+
+ // set instructions
+ 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);
+
+ // set flow-mod
+ OFFlowMod.Builder fmBuilder = null;
+ switch (op) {
+ case ADD:
+ fmBuilder = factory.buildFlowAdd();
+ break;
+ case REMOVE:
+ fmBuilder = factory.buildFlowDeleteStrict();
+ break;
+ case MODIFY: // TODO
+ fmBuilder = factory.buildFlowModifyStrict();
+ break;
+ default:
+ log.warn("Unsupported MatchAction Operator: {}", op);
+ return null;
+ }
+
+ OFMessage mplsFlow = fmBuilder
+ .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();
+ log.debug("{} mpls-rule {}-{} in sw {}",
+ (op == MatchActionOperations.Operator.ADD) ? "Adding" : "Deleting",
+ matchlabel, writeActions,
+ getStringId());
+ return mplsFlow;
+ }
+
+ private OFMessage getAclEntry(MatchActionOperationEntry mao) {
+ MatchAction ma = mao.getTarget();
+ Operator op = mao.getOperator();
+ PacketMatch packetMatch = (PacketMatch) ma.getMatch();
+ Builder matchBuilder = factory.buildMatch();
+
+ // set match
+ int inport = 0;
+ if (ma.getSwitchPort() != null) {
+ inport = (int) ma.getSwitchPort().getPortNumber().value();
+ }
+ final MACAddress srcMac = packetMatch.getSrcMacAddress();
+ final MACAddress dstMac = packetMatch.getDstMacAddress();
+ final Short etherType = packetMatch.getEtherType();
+ final IPv4Net srcIp = packetMatch.getSrcIpAddress();
+ final IPv4Net dstIp = packetMatch.getDstIpAddress();
+ final Byte ipProto = packetMatch.getIpProtocolNumber();
+ final Short srcTcpPort = packetMatch.getSrcTcpPortNumber();
+ final Short dstTcpPort = packetMatch.getDstTcpPortNumber();
+ if (inport > 0) {
+ matchBuilder.setExact(MatchField.IN_PORT,
+ OFPort.of(inport));
+ }
+ if (srcMac != null) {
+ matchBuilder.setExact(MatchField.ETH_SRC, MacAddress.of(srcMac.toLong()));
+ }
+ if (dstMac != null) {
+ matchBuilder.setExact(MatchField.ETH_DST, MacAddress.of(dstMac.toLong()));
+ }
+ if (etherType != null) {
+ matchBuilder.setExact(MatchField.ETH_TYPE, EthType.of(etherType));
+ }
+ if (srcIp != null) {
+ matchBuilder.setMasked(MatchField.IPV4_SRC,
+ IPv4Address.of(srcIp.address().value())
+ .withMaskOfLength(srcIp.prefixLen()));
+ }
+ if (dstIp != null) {
+ matchBuilder.setMasked(MatchField.IPV4_DST,
+ IPv4Address.of(dstIp.address().value())
+ .withMaskOfLength(dstIp.prefixLen()));
+ }
+ if (ipProto != null) {
+ matchBuilder.setExact(MatchField.IP_PROTO, IpProtocol.of(ipProto));
+ }
+ if (srcTcpPort != null) {
+ matchBuilder.setExact(MatchField.TCP_SRC, TransportPort.of(srcTcpPort));
+ }
+ if (dstTcpPort != null) {
+ matchBuilder.setExact(MatchField.TCP_DST, TransportPort.of(dstTcpPort));
+ }
+
+ // set actions
+ List<OFAction> writeActions = new ArrayList<OFAction>();
+ for (Action action : ma.getActions()) {
+ OFAction ofAction = getOFAction(action);
+ if (ofAction != null) {
+ writeActions.add(ofAction);
+ }
+ }
+
+ // set instructions
+ 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);
+
+ // set flow-mod
+ OFFlowMod.Builder fmBuilder = null;
+ switch (op) {
+ case ADD:
+ fmBuilder = factory.buildFlowAdd();
+ break;
+ case REMOVE:
+ fmBuilder = factory.buildFlowDeleteStrict();
+ break;
+ case MODIFY: // TODO
+ fmBuilder = factory.buildFlowModifyStrict();
+ break;
+ default:
+ log.warn("Unsupported MatchAction Operator: {}", op);
+ return null;
+ }
+
+ OFMessage aclFlow = fmBuilder
+ .setTableId(TableId.of(TABLE_ACL))
+ .setMatch(matchBuilder.build())
+ .setInstructions(instructions)
+ .setPriority(ma.getPriority()) // exact match and exclusive
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setIdleTimeout(0)
+ .setHardTimeout(0)
+ .setXid(getNextTransactionId())
+ .build();
+
+ return aclFlow;
+ }
+
+ // *****************************
+ // IOF13Switch
+ // *****************************
+
+ @Override
+ public void pushFlow(MatchActionOperationEntry matchActionOp) throws IOException {
+ OFMessage ofm = getFlow(matchActionOp);
+ if (ofm != null) {
+ write(Collections.singletonList(ofm));
+ }
+ }
+
+ private OFMessage getFlow(MatchActionOperationEntry matchActionOp) {
+ final MatchAction matchAction = matchActionOp.getTarget();
+ final Match match = matchAction.getMatch();
+ if (match instanceof Ipv4Match) {
+ return getIpEntry(matchActionOp);
+ } else if (match instanceof MplsMatch) {
+ return getMplsEntry(matchActionOp);
+ } else if (match instanceof PacketMatch) {
+ return getAclEntry(matchActionOp);
+ } else {
+ log.error("Unknown match type {} pushed to switch {}", match,
+ getStringId());
+ }
+ return null;
+ }
+
+ @Override
+ public void pushFlows(Collection<MatchActionOperationEntry> matchActionOps)
+ throws IOException {
+ List<OFMessage> flowMods = new ArrayList<OFMessage>();
+ for (MatchActionOperationEntry matchActionOp : matchActionOps) {
+ OFMessage ofm = getFlow(matchActionOp);
+ if (ofm != null) {
+ flowMods.add(ofm);
+ }
+ }
+ write(flowMods);
+ }
+
+ @Override
+ public int getEcmpGroupId(NeighborSet ns) {
+ EcmpInfo ei = ecmpGroups.get(ns);
+ if (ei == null) {
+ return -1;
+ } else {
+ return ei.groupId;
+ }
+ }
+
+ @Override
+ public TableId getTableId(String tableType) {
+ tableType = tableType.toLowerCase();
+ if (tableType.contentEquals("ip")) {
+ return TableId.of(OFSwitchImplSpringOpenTTP.TABLE_IPv4_UNICAST);
+ }
+ else if (tableType.contentEquals("mpls")) {
+ return TableId.of(OFSwitchImplSpringOpenTTP.TABLE_MPLS);
+ }
+ else if (tableType.contentEquals("acl")) {
+ return TableId.of(OFSwitchImplSpringOpenTTP.TABLE_ACL);
+ }
+ else {
+ log.warn("Invalid tableType: {}", tableType);
+ return null;
+ }
+ }
+
+ private EcmpInfo createIndirectGroup(int groupId, MacAddress srcMac,
+ MacAddress dstMac, PortNumber outPort, int gotoGroupNo,
+ int mplsLabel, boolean bos) {
+ List<BucketInfo> buckets = new ArrayList<BucketInfo>();
+ BucketInfo b = new BucketInfo(null, srcMac, dstMac, outPort,
+ mplsLabel, bos, gotoGroupNo);
+ buckets.add(b);
+
+ EcmpInfo ecmpInfo = new EcmpInfo(groupId, OFGroupType.INDIRECT, buckets);
+ setEcmpGroup(ecmpInfo);
+ log.debug(
+ "createIndirectGroup: Creating indirect group {} in sw {} "
+ + "with: {}", groupId, getStringId(), ecmpInfo);
+ return ecmpInfo;
+ }
+
+ private EcmpInfo createInnermostLabelGroup(int innermostGroupId,
+ List<PortNumber> ports, int mplsLabel, boolean bos,
+ HashMap<PortNumber, Integer> lastSetOfGroupIds) {
+ List<BucketInfo> buckets = new ArrayList<BucketInfo>();
+ for (PortNumber sp : ports) {
+ Dpid neighborDpid = portToNeighbors.get(sp);
+ BucketInfo b = new BucketInfo(neighborDpid,
+ MacAddress.of(srConfig.getRouterMac()),
+ getNeighborRouterMacAddress(neighborDpid), null,
+ mplsLabel, bos,
+ lastSetOfGroupIds.get(sp));
+ buckets.add(b);
+ }
+ EcmpInfo ecmpInfo = new EcmpInfo(innermostGroupId,
+ OFGroupType.SELECT, buckets);
+ setEcmpGroup(ecmpInfo);
+ log.debug(
+ "createInnermostLabelGroup: Creating select group {} in sw {} "
+ + "with: {}", innermostGroupId, getStringId(), ecmpInfo);
+ return ecmpInfo;
+ }
+ @Override
+ /**
+ * Create a group chain with the specified label stack for a given set of
+ * ports. This API can be used by user to create groups for a tunnel based
+ * policy routing scenario. NOTE: This API can not be used if a group to be
+ * created with different label stacks for each port in the given set of
+ * ports. Use XXX API for this purpose
+ *
+ * @param labelStack list of router segment Ids to be pushed. Can be empty.
+ * labelStack is processed from left to right with leftmost
+ * representing the outermost label and rightmost representing
+ * innermost label to be pushed
+ * @param ports List of ports on this switch to get to the first router in
+ * the labelStack
+ * @return group identifier
+ */
+ public int createGroup(List<Integer> labelStack, List<PortNumber> ports) {
+
+ if ((ports == null) ||
+ ((labelStack != null) && (labelStack.size() > 3))) {
+ log.warn("createGroup in sw {} with wrong input parameters", getStringId());
+ }
+ log.debug("createGroup in sw {} with labelStack {} and ports {}",
+ getStringId(), labelStack, ports);
+
+ HashMap<PortNumber, Integer> lastSetOfGroupIds =
+ new HashMap<PortNumber, Integer>();
+ int innermostGroupId = -1;
+ /* If it is empty label stack or label stack with only one label,
+ * Create a single select group with buckets for each port in the list
+ * of specified ports and specified label if any and return the
+ * created group id
+ */
+ if (labelStack.size() < 2) {
+ int curLabel = -1;
+ boolean bos = false;
+ if (labelStack.size()==1) {
+ curLabel = labelStack.get(0).intValue();
+ bos = true;
+ }
+
+ List<BucketInfo> buckets = new ArrayList<BucketInfo>();
+ for (PortNumber sp : ports) {
+ Dpid neighborDpid = portToNeighbors.get(sp);
+ BucketInfo b = new BucketInfo(neighborDpid,
+ MacAddress.of(srConfig.getRouterMac()),
+ getNeighborRouterMacAddress(neighborDpid),
+ sp, curLabel, bos, -1);
+ buckets.add(b);
+ }
+ innermostGroupId = groupid.incrementAndGet();
+ EcmpInfo ecmpInfo = new EcmpInfo(innermostGroupId,
+ OFGroupType.SELECT, buckets);
+ setEcmpGroup(ecmpInfo);
+ userDefinedGroups.put(innermostGroupId, ecmpInfo);
+ return innermostGroupId;
+ }
+
+ /* If the label stack has two or more labels, then a chain of groups
+ * to be created.
+ * Step1: Create for each port in the list of specified ports,
+ * an indirect group with the outermost label. These groups are the
+ * end of the chain and hence don't reference to any other groups
+ * Step2: Create for each port in the list of specified ports, an
+ * indirect group with middle labels (if any). These groups will
+ * have references to group ids that are created in the previous
+ * iteration for the same ports
+ * Step3: Create a select group with all ports and innermost label.
+ * This group will have references to indirect group ids that are
+ * created in the previous iteration for the same ports
+ */
+ for (int i = 0; i < labelStack.size(); i++) {
+ for (PortNumber sp : ports) {
+ if (i == 0) {
+ /* Outermost label processing */
+ int currGroupId = groupid.incrementAndGet();
+ EcmpInfo indirectGroup = createIndirectGroup(currGroupId,
+ null, null, sp, -1,
+ labelStack.get(i).intValue(), false);
+ lastSetOfGroupIds.put(sp, currGroupId);
+ userDefinedGroups.put(currGroupId, indirectGroup);
+ }
+ else if (i == (labelStack.size() - 1)) {
+ /* Innermost label processing */
+ innermostGroupId = groupid.incrementAndGet();
+ EcmpInfo topLevelGroup = createInnermostLabelGroup(
+ innermostGroupId,
+ ports,
+ labelStack.get(i).intValue(), true,
+ lastSetOfGroupIds);
+ userDefinedGroups.put(
+ innermostGroupId, topLevelGroup);
+ break;
+ }
+ else {
+ /* Middle label processing */
+ int currGroupId = groupid.incrementAndGet();
+ EcmpInfo indirectGroup = createIndirectGroup(currGroupId,
+ null, null, null,
+ lastSetOfGroupIds.get(sp),
+ labelStack.get(i).intValue(), false);
+ /* Overwrite with this iteration's group IDs */
+ lastSetOfGroupIds.put(sp, currGroupId);
+ userDefinedGroups.put(currGroupId, indirectGroup);
+ }
+ }
+ }
+ log.debug("createGroup in sw{}: group created with innermost group id {}",
+ getStringId(), innermostGroupId);
+ return innermostGroupId;
+ }
+
+ /**
+ * Remove the specified group
+ *
+ * @param groupId group identifier
+ * @return success/fail
+ */
+ public boolean removeGroup(int groupId) {
+ EcmpInfo group = userDefinedGroups.get(groupId);
+ if (group == null) {
+ log.warn("removeGroup in sw {}: with invalid group id", getStringId());
+ return false;
+ }
+ deleteGroup(group);
+ for (BucketInfo bucket : group.buckets) {
+ int currGroupIdToBeDeleted = bucket.groupNo;
+ while (currGroupIdToBeDeleted != -1) {
+ /* Assuming indirect groups with single buckets */
+ int nextGroupIdToBeDeleted =
+ userDefinedGroups.get(currGroupIdToBeDeleted).
+ buckets.get(0).groupNo;
+ EcmpInfo groupToBeDeleted =
+ userDefinedGroups.get(currGroupIdToBeDeleted);
+ deleteGroup(groupToBeDeleted);
+ userDefinedGroups.remove(currGroupIdToBeDeleted);
+ currGroupIdToBeDeleted = nextGroupIdToBeDeleted;
+ }
+ }
+
+ userDefinedGroups.remove(groupId);
+ log.debug("removeGroup in sw {}: removed group with group id {}",
+ getStringId(), groupId);
+ return true;
+ }
+
+ @Override
+ public Map<String, String> getPublishAttributes() {
+ return publishAttributes;
+ }
+
+ // *****************************
+ // Unused
+ // *****************************
+
+ @SuppressWarnings("unused")
+ private void setAsyncConfig() throws IOException {
+ List<OFMessage> msglist = new ArrayList<OFMessage>(3);
+ OFMessage setAC = null;
+
+ if (role == Role.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 == Role.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);
+ }
+
+ @SuppressWarnings("unused")
+ 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 == Role.MASTER || role == Role.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)));
+ }
+
+ }
+
+ @SuppressWarnings("unused")
+ private void getTableFeatures() throws IOException {
+ OFMessage gtf = factory.buildTableFeaturesStatsRequest()
+ .setXid(getNextTransactionId())
+ .build();
+ write(gtf, null);
+ }
+
+ @SuppressWarnings("unused")
+ private void sendGroupFeaturesRequest() throws IOException {
+ OFMessage gfr = factory.buildGroupFeaturesStatsRequest()
+ .setXid(getNextTransactionId())
+ .build();
+ write(gfr, null);
+ }
+
+ private void processGroupFeatures(OFGroupFeaturesStatsReply gfsr) {
+ log.info("Sw: {} Group Features {}", getStringId(), gfsr);
+ }
+
+ @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();
+ }
+ }
+ }
+
+
+
+}