Renamed devicemanager, flowprogrammer, linkdiscovery and util packages
net.onrc.onos.ofcontroller.devicemanager.* => net.onrc.onos.core.devicemanager.*
net.onrc.onos.ofcontroller.flowprogrammer.* => net.onrc.onos.core.flowprogrammer.*
net.onrc.onos.ofcontroller.linkdiscovery.* => net.onrc.onos.core.linkdiscovery.*
net.onrc.onos.ofcontroller.util.* => net.onrc.onos.core.util.*
Change-Id: Iaa865af552e8fb3a589e73d006569ac79f5a0f08
diff --git a/src/main/java/net/onrc/onos/core/datagrid/HazelcastEventChannel.java b/src/main/java/net/onrc/onos/core/datagrid/HazelcastEventChannel.java
index 8797dba..fab6943 100644
--- a/src/main/java/net/onrc/onos/core/datagrid/HazelcastEventChannel.java
+++ b/src/main/java/net/onrc/onos/core/datagrid/HazelcastEventChannel.java
@@ -14,7 +14,7 @@
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
-import net.onrc.onos.ofcontroller.util.serializers.KryoFactory;
+import net.onrc.onos.core.util.serializers.KryoFactory;
/**
* A datagrid event channel that uses Hazelcast as a datagrid.
diff --git a/src/main/java/net/onrc/onos/core/datagrid/web/GetNGFlowsSummaryResource.java b/src/main/java/net/onrc/onos/core/datagrid/web/GetNGFlowsSummaryResource.java
index 33b9a5f..22b74ae 100644
--- a/src/main/java/net/onrc/onos/core/datagrid/web/GetNGFlowsSummaryResource.java
+++ b/src/main/java/net/onrc/onos/core/datagrid/web/GetNGFlowsSummaryResource.java
@@ -10,17 +10,17 @@
import net.onrc.onos.core.intent.ShortestPathIntent;
import net.onrc.onos.core.intent.Intent.IntentState;
import net.onrc.onos.core.intent.runtime.IPathCalcRuntimeService;
+import net.onrc.onos.core.util.CallerId;
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.FlowEntry;
+import net.onrc.onos.core.util.FlowId;
+import net.onrc.onos.core.util.FlowPath;
+import net.onrc.onos.core.util.FlowPathType;
+import net.onrc.onos.core.util.FlowPathUserState;
+import net.onrc.onos.core.util.Port;
+import net.onrc.onos.core.util.SwitchPort;
import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
import net.onrc.onos.ofcontroller.networkgraph.Path;
-import net.onrc.onos.ofcontroller.util.CallerId;
-import net.onrc.onos.ofcontroller.util.Dpid;
-import net.onrc.onos.ofcontroller.util.FlowEntry;
-import net.onrc.onos.ofcontroller.util.FlowId;
-import net.onrc.onos.ofcontroller.util.FlowPath;
-import net.onrc.onos.ofcontroller.util.FlowPathType;
-import net.onrc.onos.ofcontroller.util.FlowPathUserState;
-import net.onrc.onos.ofcontroller.util.Port;
-import net.onrc.onos.ofcontroller.util.SwitchPort;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
diff --git a/src/main/java/net/onrc/onos/core/devicemanager/IOnosDeviceListener.java b/src/main/java/net/onrc/onos/core/devicemanager/IOnosDeviceListener.java
new file mode 100644
index 0000000..69b340b
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/devicemanager/IOnosDeviceListener.java
@@ -0,0 +1,7 @@
+package net.onrc.onos.core.devicemanager;
+
+public interface IOnosDeviceListener {
+
+ public void onosDeviceAdded(OnosDevice device);
+ public void onosDeviceRemoved(OnosDevice device);
+}
diff --git a/src/main/java/net/onrc/onos/core/devicemanager/IOnosDeviceService.java b/src/main/java/net/onrc/onos/core/devicemanager/IOnosDeviceService.java
new file mode 100644
index 0000000..f343308
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/devicemanager/IOnosDeviceService.java
@@ -0,0 +1,24 @@
+package net.onrc.onos.core.devicemanager;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * {@link OnosDeviceManager} doesn't yet provide any API to fellow modules,
+ * however making it export a dummy service means we can specify it as
+ * a dependency of Forwarding
+ * @author jono
+ *
+ */
+public interface IOnosDeviceService extends IFloodlightService {
+
+ public void addOnosDeviceListener(IOnosDeviceListener listener);
+
+ public void deleteOnosDeviceListener(IOnosDeviceListener listener);
+
+ public void deleteOnosDevice(OnosDevice dev);
+
+ public void deleteOnosDeviceByMac(MACAddress mac);
+
+ public void addOnosDevice(Long mac, OnosDevice dev);
+}
diff --git a/src/main/java/net/onrc/onos/core/devicemanager/OnosDevice.java b/src/main/java/net/onrc/onos/core/devicemanager/OnosDevice.java
new file mode 100644
index 0000000..4e6a8dc
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/devicemanager/OnosDevice.java
@@ -0,0 +1,264 @@
+/**
+l* Copyright 2011,2012, Big Switch Networks, Inc.
+* Originally created by David Erickson, Stanford University
+*
+* Licensed under the Apache License, Version 2.0 (the "License"); you may
+* not use this file except in compliance with the License. You may obtain
+* a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations
+* under the License.
+**/
+
+package net.onrc.onos.core.devicemanager;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.packet.IPv4;
+
+/**
+ * An entity on the network is a visible trace of a device that corresponds
+ * to a packet received from a particular interface on the edge of a network,
+ * with a particular VLAN tag, and a particular MAC address, along with any
+ * other packet characteristics we might want to consider as helpful for
+ * disambiguating devices.
+ *
+ * Entities are the most basic element of devices; devices consist of one or
+ * more entities. Entities are immutable once created, except for the last
+ * seen timestamp.
+ *
+ * @author readams
+ *
+ */
+public class OnosDevice implements Serializable { //implements Comparable<OnosDevice> {
+
+ private static int ACTIVITY_TIMEOUT = 30000;
+
+ /**
+ * The MAC address associated with this entity
+ */
+ private MACAddress macAddress;
+
+ /**
+ * The IP address associated with this entity, or null if no IP learned
+ * from the network observation associated with this entity
+ */
+ private Integer ipv4Address;
+
+ /**
+ * The VLAN tag on this entity, or null if untagged
+ */
+ private Short vlan;
+
+ /**
+ * The DPID of the switch for the ingress point for this entity,
+ * or null if not present
+ */
+ private long switchDPID;
+
+ /**
+ * The port number of the switch for the ingress point for this entity,
+ * or null if not present
+ */
+ private short switchPort;
+
+ /**
+ * The last time we observed this entity on the network
+ */
+ private Date lastSeenTimestamp;
+
+ private Date activeSince;
+
+ private int hashCode = 0;
+
+ // ************
+ // Constructors
+ // ************
+ protected OnosDevice() {}
+
+ /**
+ * Create a new entity
+ *
+ * @param macAddress
+ * @param vlan
+ * @param ipv4Address
+ * @param switchDPID
+ * @param switchPort
+ * @param lastSeenTimestamp
+ */
+ public OnosDevice(MACAddress macAddress, Short vlan,
+ Integer ipv4Address, Long switchDPID, short switchPort,
+ Date lastSeenTimestamp) {
+ this.macAddress = macAddress;
+ this.ipv4Address = ipv4Address;
+ this.vlan = vlan;
+ this.switchDPID = switchDPID;
+ this.switchPort = switchPort;
+ this.lastSeenTimestamp = lastSeenTimestamp;
+ this.activeSince = lastSeenTimestamp;
+ }
+
+ // ***************
+ // Getters/Setters
+ // ***************
+
+ public MACAddress getMacAddress() {
+ return macAddress;
+ }
+
+ public Integer getIpv4Address() {
+ return ipv4Address;
+ }
+
+ public void setIpv4Address(Integer ipv4Address) {
+ this.ipv4Address = ipv4Address;
+ }
+
+ public Short getVlan() {
+ return vlan;
+ }
+
+ public Long getSwitchDPID() {
+ return switchDPID;
+ }
+
+ public void setSwitchDPID(long dpid) {
+ this.switchDPID = dpid;
+ }
+
+ public short getSwitchPort() {
+ return switchPort;
+ }
+
+ public void setSwitchPort(short port) {
+ this.switchPort = port;
+ }
+
+ public Date getLastSeenTimestamp() {
+ return lastSeenTimestamp;
+ }
+
+
+ public void setLastSeenTimestamp(Date lastSeenTimestamp) {
+ if (activeSince == null ||
+ (activeSince.getTime() + ACTIVITY_TIMEOUT) <
+ lastSeenTimestamp.getTime())
+ this.activeSince = lastSeenTimestamp;
+ this.lastSeenTimestamp = lastSeenTimestamp;
+ }
+
+ public Date getActiveSince() {
+ return activeSince;
+ }
+
+ public void setActiveSince(Date activeSince) {
+ this.activeSince = activeSince;
+ }
+
+ @Override
+ public int hashCode() {
+ if (hashCode != 0) return hashCode;
+ final int prime = 31;
+ hashCode = 1;
+ hashCode = prime * hashCode
+ + ((ipv4Address == null) ? 0 : ipv4Address.hashCode());
+ hashCode = prime * hashCode + (int) (macAddress.toLong() ^ (macAddress.toLong() >>> 32));
+ hashCode = prime * hashCode + (int)switchDPID;
+ hashCode = prime * hashCode + (int)switchPort;
+ hashCode = prime * hashCode + ((vlan == null) ? 0 : vlan.hashCode());
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ OnosDevice other = (OnosDevice) obj;
+ if (hashCode() != other.hashCode()) return false;
+ if (ipv4Address == null) {
+ if (other.ipv4Address != null) return false;
+ } else if (!ipv4Address.equals(other.ipv4Address)) return false;
+ if (macAddress == null) {
+ if (other.macAddress != null) return false;
+ } else if (!macAddress.equals(other.macAddress)) return false;
+ if(switchDPID != other.switchDPID) return false;
+ if (switchPort != other.switchPort) return false;
+ if (vlan == null) {
+ if (other.vlan != null) return false;
+ } else if (!vlan.equals(other.vlan)) return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Entity [macAddress=");
+ builder.append(macAddress.toString());
+ builder.append(", ipv4Address=");
+ builder.append(IPv4.fromIPv4Address(ipv4Address==null ?
+ 0 : ipv4Address.intValue()));
+ builder.append(", vlan=");
+ builder.append(vlan);
+ builder.append(", switchDPID=");
+ builder.append(switchDPID);
+ builder.append(", switchPort=");
+ builder.append(switchPort);
+ builder.append(", lastSeenTimestamp=");
+ builder.append(lastSeenTimestamp == null? "null" : lastSeenTimestamp.getTime());
+ builder.append(", activeSince=");
+ builder.append(activeSince == null? "null" : activeSince.getTime());
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /*
+ @Override
+ public int compareTo(OnosDevice o) {
+ if (macAddress < o.macAddress) return -1;
+ if (macAddress > o.macAddress) return 1;
+
+ int r;
+ if (switchDPID == null)
+ r = o.switchDPID == null ? 0 : -1;
+ else if (o.switchDPID == null)
+ r = 1;
+ else
+ r = switchDPID.compareTo(o.switchDPID);
+ if (r != 0) return r;
+
+ if (switchPort == null)
+ r = o.switchPort == null ? 0 : -1;
+ else if (o.switchPort == null)
+ r = 1;
+ else
+ r = switchPort.compareTo(o.switchPort);
+ if (r != 0) return r;
+
+ if (ipv4Address == null)
+ r = o.ipv4Address == null ? 0 : -1;
+ else if (o.ipv4Address == null)
+ r = 1;
+ else
+ r = ipv4Address.compareTo(o.ipv4Address);
+ if (r != 0) return r;
+
+ if (vlan == null)
+ r = o.vlan == null ? 0 : -1;
+ else if (o.vlan == null)
+ r = 1;
+ else
+ r = vlan.compareTo(o.vlan);
+ if (r != 0) return r;
+
+ return 0;
+ }*/
+
+}
diff --git a/src/main/java/net/onrc/onos/core/devicemanager/OnosDeviceManager.java b/src/main/java/net/onrc/onos/core/devicemanager/OnosDeviceManager.java
new file mode 100644
index 0000000..e673674
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/devicemanager/OnosDeviceManager.java
@@ -0,0 +1,360 @@
+package net.onrc.onos.core.devicemanager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.IUpdate;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.core.datagrid.IDatagridService;
+import net.onrc.onos.core.datagrid.IEventChannel;
+import net.onrc.onos.core.datagrid.IEventChannelListener;
+import net.onrc.onos.packet.ARP;
+import net.onrc.onos.packet.DHCP;
+import net.onrc.onos.packet.Ethernet;
+import net.onrc.onos.packet.IPv4;
+import net.onrc.onos.packet.UDP;
+import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
+import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
+
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OnosDeviceManager implements IFloodlightModule,
+ IOFMessageListener,
+ IOnosDeviceService,
+ IEventChannelListener<Long, OnosDevice> {
+ protected final static Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
+ private static final int CLEANUP_SECOND = 60*60;
+ private static final int AGEING_MILLSEC = 60*60*1000;
+
+ private CopyOnWriteArrayList<IOnosDeviceListener> deviceListeners;
+ private IFloodlightProviderService floodlightProvider;
+ private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+
+ private IDatagridService datagrid;
+ private IEventChannel<Long, OnosDevice> eventChannel;
+ private static final String DEVICE_CHANNEL_NAME = "onos.device";
+ private Map<Long, OnosDevice> mapDevice = new ConcurrentHashMap<Long, OnosDevice>();
+ private INetworkGraphService networkGraphService;
+ private NetworkGraph networkGraph;
+
+ public enum OnosDeviceUpdateType {
+ ADD, DELETE, UPDATE;
+ }
+
+ private class OnosDeviceUpdate implements IUpdate {
+ private OnosDevice device;
+ private OnosDeviceUpdateType type;
+
+ public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
+ this.device = device;
+ this.type = type;
+ }
+
+ @Override
+ public void dispatch() {
+ if(type == OnosDeviceUpdateType.ADD) {
+ for(IOnosDeviceListener listener: deviceListeners) {
+ listener.onosDeviceAdded(device);
+ }
+ } else if (type == OnosDeviceUpdateType.DELETE){
+ for(IOnosDeviceListener listener: deviceListeners) {
+ listener.onosDeviceRemoved(device);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "onosdevicemanager";
+ }
+
+ @Override
+ public boolean isCallbackOrderingPrereq(OFType type, String name) {
+ // We want link discovery to consume LLDP first otherwise we'll
+ // end up reading bad device info from LLDP packets
+ return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
+ }
+
+ @Override
+ public boolean isCallbackOrderingPostreq(OFType type, String name) {
+ return type == OFType.PACKET_IN &&
+ ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
+ }
+
+ @Override
+ public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+ if (msg.getType().equals(OFType.PACKET_IN)) {
+ OFPacketIn pi = (OFPacketIn) msg;
+
+ Ethernet eth = IFloodlightProviderService.bcStore.
+ get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+
+ return processPacketIn(sw, pi, eth);
+ }
+
+ return Command.CONTINUE;
+ }
+
+ private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
+ long dpid =sw.getId();
+ short portId = pi.getInPort();
+ Long mac = eth.getSourceMAC().toLong();
+
+ OnosDevice srcDevice =
+ getSourceDeviceFromPacket(eth, dpid, portId);
+
+ if (srcDevice == null){
+ return Command.STOP;
+ }
+
+ //We check if it is the same device in datagrid to suppress the device update
+ OnosDevice exDev = null;
+ if((exDev = mapDevice.get(mac)) != null ){
+ if(exDev.equals(srcDevice)) {
+ //There is the same existing device. Update only ActiveSince time.
+ exDev.setLastSeenTimestamp(new Date());
+ if(log.isTraceEnabled()) {
+ log.debug("In the datagrid, there is the same device."
+ + "Only update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
+ dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+ }
+ return Command.CONTINUE;
+ } else if (srcDevice.getIpv4Address() == null &&
+ exDev.getSwitchDPID().equals(srcDevice.getSwitchDPID()) &&
+ exDev.getSwitchPort() == srcDevice.getSwitchPort()) {
+ //Vlan should be handled based on the Onos spec. Until then, don't handle it.
+ //Device attachment point and mac address are the same
+ //but the packet does not have an ip address.
+ exDev.setLastSeenTimestamp(new Date());
+ if(log.isTraceEnabled()) {
+ log.debug("In the datagrid, there is the same device with no ip."
+ + "Keep ip and update last seen time. dpid {}, port {}, mac {}, ip {} lastSeenTime {}",
+ dpid, portId, srcDevice.getMacAddress(), exDev.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+ }
+ return Command.CONTINUE;
+ }
+ }
+
+ //If the switch port we try to attach a new device already has a link, then stop adding device
+ if(networkGraph.getLink(dpid, (long)portId) != null) {
+ if(log.isTraceEnabled()) {
+ log.debug("Stop adding OnosDevice {} due to there is a link to: dpid {} port {}",
+ srcDevice.getMacAddress(), dpid, portId);
+ }
+ return Command.CONTINUE;
+ }
+
+ addOnosDevice(mac, srcDevice);
+
+ if(log.isTraceEnabled()) {
+ log.debug("Add device info in the set. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
+ dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+ }
+ return Command.CONTINUE;
+ }
+
+ //Thread to delete devices periodically.
+ //Remove all devices from the map first and then finally delete devices from the DB.
+ private class CleanDevice implements Runnable {
+ @Override
+ public void run() {
+ log.debug("called CleanDevice");
+ try{
+ Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
+ for (OnosDevice dev : mapDevice.values() ) {
+ long now = new Date().getTime();
+ if((now - dev.getLastSeenTimestamp().getTime() > AGEING_MILLSEC)) {
+ if(log.isTraceEnabled()) {
+ log.debug("Remove device info in the datagrid. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}, diff {}",
+ dev.getSwitchDPID(), dev.getSwitchPort(), dev.getMacAddress(), dev.getIpv4Address(),
+ dev.getLastSeenTimestamp().getTime(), now - dev.getLastSeenTimestamp().getTime());
+ }
+ deleteSet.add(dev);
+ }
+ }
+
+ for(OnosDevice dev : deleteSet) {
+ deleteOnosDevice(dev);
+ }
+ } catch(Exception e) {
+ log.error("Error:", e);
+ }
+ }
+ }
+
+ /**
+ * Get IP address from packet if the packet is either an ARP
+ * or a DHCP packet
+ * @param eth
+ * @param dlAddr
+ * @return
+ */
+ private int getSrcNwAddr(Ethernet eth, long dlAddr) {
+ if (eth.getPayload() instanceof ARP) {
+ ARP arp = (ARP) eth.getPayload();
+ if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) &&
+ (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
+ return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
+ }
+ } else if (eth.getPayload() instanceof IPv4) {
+ IPv4 ipv4 = (IPv4) eth.getPayload();
+ if (ipv4.getPayload() instanceof UDP) {
+ UDP udp = (UDP)ipv4.getPayload();
+ if (udp.getPayload() instanceof DHCP) {
+ DHCP dhcp = (DHCP)udp.getPayload();
+ if (dhcp.getOpCode() == DHCP.OPCODE_REPLY) {
+ return ipv4.getSourceAddress();
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Parse an entity from an {@link Ethernet} packet.
+ * @param eth the packet to parse
+ * @param sw the switch on which the packet arrived
+ * @param pi the original packetin
+ * @return the entity from the packet
+ */
+ private OnosDevice getSourceDeviceFromPacket(Ethernet eth,
+ long swdpid,
+ short port) {
+ byte[] dlAddrArr = eth.getSourceMACAddress();
+ long dlAddr = Ethernet.toLong(dlAddrArr);
+
+ // Ignore broadcast/multicast source
+ if ((dlAddrArr[0] & 0x1) != 0)
+ return null;
+
+ short vlan = eth.getVlanID();
+ int nwSrc = getSrcNwAddr(eth, dlAddr);
+ return new OnosDevice(MACAddress.valueOf(dlAddr),
+ ((vlan >= 0) ? vlan : null),
+ ((nwSrc != 0) ? nwSrc : null),
+ swdpid,
+ port,
+ new Date());
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ List<Class<? extends IFloodlightService>> services =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ services.add(IOnosDeviceService.class);
+ return services;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+ Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
+ new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+ impls.put(IOnosDeviceService.class, this);
+ return impls;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ List<Class<? extends IFloodlightService>> dependencies =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ dependencies.add(IFloodlightProviderService.class);
+ dependencies.add(INetworkGraphService.class);
+ dependencies.add(IDatagridService.class);
+ return dependencies;
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ executor.scheduleAtFixedRate(new CleanDevice(), 30 ,CLEANUP_SECOND, TimeUnit.SECONDS);
+
+ deviceListeners = new CopyOnWriteArrayList<IOnosDeviceListener>();
+ datagrid = context.getServiceImpl(IDatagridService.class);
+ networkGraphService = context.getServiceImpl(INetworkGraphService.class);
+ networkGraph = networkGraphService.getNetworkGraph();
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+ eventChannel = datagrid.addListener(DEVICE_CHANNEL_NAME, this,
+ Long.class,
+ OnosDevice.class);
+ }
+
+ @Override
+ public void deleteOnosDevice(OnosDevice dev) {
+ Long mac = dev.getMacAddress().toLong();
+ eventChannel.removeEntry(mac);
+ floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
+ }
+
+ @Override
+ public void deleteOnosDeviceByMac(MACAddress mac) {
+ OnosDevice deleteDevice = mapDevice.get(mac);
+ deleteOnosDevice(deleteDevice);
+ }
+
+ @Override
+ public void addOnosDevice(Long mac, OnosDevice dev) {
+ eventChannel.addEntry(mac, dev);
+ floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
+ }
+
+ @Override
+ public void entryAdded(OnosDevice dev) {
+ Long mac = dev.getMacAddress().toLong();
+ mapDevice.put(mac, dev);
+ log.debug("Device added: device mac {}", mac);
+ }
+
+ @Override
+ public void entryRemoved(OnosDevice dev) {
+ Long mac = dev.getMacAddress().toLong();
+ mapDevice.remove(mac);
+ log.debug("Device removed: device mac {}", mac);
+ }
+
+ @Override
+ public void entryUpdated(OnosDevice dev) {
+ Long mac = dev.getMacAddress().toLong();
+ mapDevice.put(mac, dev);
+ log.debug("Device updated: device mac {}", mac);
+ }
+
+ @Override
+ public void addOnosDeviceListener(IOnosDeviceListener listener) {
+ deviceListeners.add(listener);
+ }
+
+ @Override
+ public void deleteOnosDeviceListener(IOnosDeviceListener listener) {
+ deviceListeners.remove(listener);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/FlowProgrammer.java b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowProgrammer.java
new file mode 100644
index 0000000..d30c10f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowProgrammer.java
@@ -0,0 +1,175 @@
+package net.onrc.onos.core.flowprogrammer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openflow.protocol.OFFlowRemoved;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.IOFSwitchListener;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.onrc.onos.core.flowprogrammer.web.FlowProgrammerWebRoutable;
+import net.onrc.onos.core.util.FlowEntryId;
+import net.onrc.onos.registry.controller.IControllerRegistryService;
+
+/**
+ * FlowProgrammer is a module responsible to maintain flows installed to switches.
+ * FlowProgrammer consists of FlowPusher and FlowSynchronizer.
+ * FlowPusher manages the rate of installation, and FlowSynchronizer synchronizes
+ * flows between GraphDB and switches.
+ * FlowProgrammer also watch the event of addition/deletion of switches to
+ * start/stop synchronization. When a switch is added to network, FlowProgrammer
+ * immediately kicks synchronization to keep switch's flow table latest state.
+ * Adversely, when a switch is removed from network, FlowProgrammer immediately
+ * stops synchronization.
+ *
+ * @author Brian
+ */
+public class FlowProgrammer implements IFloodlightModule,
+ IOFMessageListener,
+ IOFSwitchListener {
+ // flag to enable FlowSynchronizer
+ private static final boolean enableFlowSync = false;
+ protected static final Logger log = LoggerFactory.getLogger(FlowProgrammer.class);
+ protected volatile IFloodlightProviderService floodlightProvider;
+ protected volatile IControllerRegistryService registryService;
+ protected volatile IRestApiService restApi;
+
+ protected FlowPusher pusher;
+ private static final int NUM_PUSHER_THREAD = 1;
+
+ protected FlowSynchronizer synchronizer;
+
+ public FlowProgrammer() {
+ pusher = new FlowPusher(NUM_PUSHER_THREAD);
+ if (enableFlowSync) {
+ synchronizer = new FlowSynchronizer();
+ }
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ registryService = context.getServiceImpl(IControllerRegistryService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
+ pusher.init(null, context, floodlightProvider.getOFMessageFactory(), null);
+ if (enableFlowSync) {
+ synchronizer.init(pusher);
+ }
+ }
+
+ @Override
+ public void startUp(FloodlightModuleContext context) {
+ restApi.addRestletRoutable(new FlowProgrammerWebRoutable());
+ pusher.start();
+ floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this);
+ floodlightProvider.addOFSwitchListener(this);
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ Collection<Class<? extends IFloodlightService>> l =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IFlowPusherService.class);
+ if (enableFlowSync) {
+ l.add(IFlowSyncService.class);
+ }
+ return l;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+ Map<Class<? extends IFloodlightService>,
+ IFloodlightService> m =
+ new HashMap<Class<? extends IFloodlightService>,
+ IFloodlightService>();
+ m.put(IFlowPusherService.class, pusher);
+ if (enableFlowSync) {
+ m.put(IFlowSyncService.class, synchronizer);
+ }
+ return m;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ Collection<Class<? extends IFloodlightService>> l =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IFloodlightProviderService.class);
+ l.add(IRestApiService.class);
+ return l;
+ }
+
+ @Override
+ public String getName() {
+ // TODO Auto-generated method stub
+ return "FlowProgrammer";
+ }
+
+ @Override
+ public boolean isCallbackOrderingPrereq(OFType type, String name) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isCallbackOrderingPostreq(OFType type, String name) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+ switch (msg.getType()) {
+ case FLOW_REMOVED:
+ OFFlowRemoved flowMsg = (OFFlowRemoved) msg;
+ FlowEntryId id = new FlowEntryId(flowMsg.getCookie());
+ log.debug("Got flow entry removed from {}: {}", sw.getId(), id);
+ // TODO: Inform the Forwarding module that a flow has expired
+ break;
+ default:
+ break;
+ }
+
+ return Command.CONTINUE;
+ }
+
+ @Override
+ public void addedSwitch(IOFSwitch sw) {
+ log.debug("Switch added: {}", sw.getId());
+
+ if (enableFlowSync) {
+ if (registryService.hasControl(sw.getId())) {
+ synchronizer.synchronize(sw);
+ }
+ }
+ }
+
+ @Override
+ public void removedSwitch(IOFSwitch sw) {
+ log.debug("Switch removed: {}", sw.getId());
+
+ if (enableFlowSync) {
+ synchronizer.interrupt(sw);
+ }
+ }
+
+ @Override
+ public void switchPortChanged(Long switchId) {
+ // TODO Auto-generated method stub
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/FlowPusher.java b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowPusher.java
new file mode 100644
index 0000000..bdb1f43
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowPusher.java
@@ -0,0 +1,1068 @@
+package net.onrc.onos.core.flowprogrammer;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.openflow.protocol.*;
+import org.openflow.protocol.action.*;
+import org.openflow.protocol.factory.BasicFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.internal.OFMessageFuture;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.util.OFMessageDamper;
+import net.onrc.onos.core.util.FlowEntry;
+import net.onrc.onos.core.util.FlowEntryAction;
+import net.onrc.onos.core.util.FlowEntryActions;
+import net.onrc.onos.core.util.FlowEntryMatch;
+import net.onrc.onos.core.util.FlowEntryUserState;
+import net.onrc.onos.core.util.IPv4Net;
+import net.onrc.onos.core.util.Pair;
+import net.onrc.onos.core.util.Port;
+import net.onrc.onos.core.util.FlowEntryAction.*;
+
+/**
+ * FlowPusher is a implementation of FlowPusherService.
+ * FlowPusher assigns one message queue instance for each one switch.
+ * Number of message processing threads is configurable by constructor, and
+ * one thread can handle multiple message queues. Each queue will be assigned to
+ * a thread according to hash function defined by getHash().
+ * Each processing thread reads messages from queues and sends it to switches
+ * in round-robin. Processing thread also calculates rate of sending to suppress
+ * excessive message sending.
+ *
+ * @author Naoki Shiota
+ */
+public class FlowPusher implements IFlowPusherService, IOFMessageListener {
+ private final static Logger log = LoggerFactory.getLogger(FlowPusher.class);
+ protected static final int DEFAULT_NUMBER_THREAD = 1;
+
+ // TODO: Values copied from elsewhere (class LearningSwitch).
+ // The local copy should go away!
+ //
+ protected static final int OFMESSAGE_DAMPER_CAPACITY = 50000; // TODO: find sweet spot
+ protected static final int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
+
+ // Number of messages sent to switch at once
+ protected static final int MAX_MESSAGE_SEND = 100;
+
+ private static class SwitchQueueEntry {
+ OFMessage msg;
+
+ public SwitchQueueEntry(OFMessage msg) {
+ this.msg = msg;
+ }
+
+ public OFMessage getOFMessage() {
+ return msg;
+ }
+ }
+
+ /**
+ * SwitchQueue represents message queue attached to a switch.
+ * This consists of queue itself and variables used for limiting sending rate.
+ *
+ * @author Naoki Shiota
+ */
+ private class SwitchQueue {
+ List<Queue<SwitchQueueEntry>> raw_queues;
+ QueueState state;
+
+ // Max rate of sending message (bytes/ms). 0 implies no limitation.
+ long max_rate = 0; // 0 indicates no limitation
+ long last_sent_time = 0;
+ long last_sent_size = 0;
+
+ // "To be deleted" flag
+ boolean toBeDeleted = false;
+
+ SwitchQueue() {
+ raw_queues = new ArrayList<Queue<SwitchQueueEntry>>(
+ MsgPriority.values().length);
+ for (int i = 0; i < MsgPriority.values().length; ++i) {
+ raw_queues.add(i, new ArrayDeque<SwitchQueueEntry>());
+ }
+
+ state = QueueState.READY;
+ }
+
+ /**
+ * Check if sending rate is within the rate
+ *
+ * @param current Current time
+ * @return true if within the rate
+ */
+ boolean isSendable(long current) {
+ if (max_rate == 0) {
+ // no limitation
+ return true;
+ }
+
+ if (current == last_sent_time) {
+ return false;
+ }
+
+ // Check if sufficient time (from aspect of rate) elapsed or not.
+ long rate = last_sent_size / (current - last_sent_time);
+ return (rate < max_rate);
+ }
+
+ /**
+ * Log time and size of last sent data.
+ *
+ * @param current Time to be sent.
+ * @param size Size of sent data (in bytes).
+ */
+ void logSentData(long current, long size) {
+ last_sent_time = current;
+ last_sent_size = size;
+ }
+
+ boolean add(SwitchQueueEntry entry, MsgPriority priority) {
+ Queue<SwitchQueueEntry> queue = getQueue(priority);
+ if (queue == null) {
+ log.error("Unexpected priority : ", priority);
+ return false;
+ }
+ return queue.add(entry);
+ }
+
+ /**
+ * Poll single appropriate entry object according to QueueState.
+ *
+ * @return Entry object.
+ */
+ SwitchQueueEntry poll() {
+ switch (state) {
+ case READY: {
+ for (int i = 0; i < raw_queues.size(); ++i) {
+ SwitchQueueEntry entry = raw_queues.get(i).poll();
+ if (entry != null) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+ case SUSPENDED: {
+ // Only polling from high priority queue
+ SwitchQueueEntry entry = getQueue(MsgPriority.HIGH).poll();
+ return entry;
+ }
+ default:
+ log.error("Unexpected QueueState : ", state);
+ return null;
+ }
+ }
+
+ /**
+ * Check if this object has any messages in the queues to be sent
+ *
+ * @return True if there are some messages to be sent.
+ */
+ boolean hasMessageToSend() {
+ switch (state) {
+ case READY:
+ for (Queue<SwitchQueueEntry> queue : raw_queues) {
+ if (!queue.isEmpty()) {
+ return true;
+ }
+ }
+ break;
+ case SUSPENDED:
+ // Only checking high priority queue
+ return (!getQueue(MsgPriority.HIGH).isEmpty());
+ default:
+ log.error("Unexpected QueueState : ", state);
+ return false;
+ }
+
+ return false;
+ }
+
+ Queue<SwitchQueueEntry> getQueue(MsgPriority priority) {
+ return raw_queues.get(priority.ordinal());
+ }
+ }
+
+ /**
+ * BarrierInfo holds information to specify barrier message sent to switch.
+ *
+ * @author Naoki
+ */
+ private static class BarrierInfo {
+ final long dpid;
+ final int xid;
+
+ static BarrierInfo create(IOFSwitch sw, OFBarrierRequest req) {
+ return new BarrierInfo(sw.getId(), req.getXid());
+ }
+
+ static BarrierInfo create(IOFSwitch sw, OFBarrierReply rpy) {
+ return new BarrierInfo(sw.getId(), rpy.getXid());
+ }
+
+ private BarrierInfo(long dpid, int xid) {
+ this.dpid = dpid;
+ this.xid = xid;
+ }
+
+ // Auto generated code by Eclipse
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (dpid ^ (dpid >>> 32));
+ result = prime * result + xid;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+
+ BarrierInfo other = (BarrierInfo) obj;
+ return (this.dpid == other.dpid) && (this.xid == other.xid);
+ }
+
+
+ }
+
+ private OFMessageDamper messageDamper = null;
+ private IThreadPoolService threadPool = null;
+
+ private FloodlightContext context = null;
+ private BasicFactory factory = null;
+
+ // Map of threads versus dpid
+ private Map<Long, FlowPusherThread> threadMap = null;
+ // Map from (DPID and transaction ID) to Future objects.
+ private Map<BarrierInfo, OFBarrierReplyFuture> barrierFutures
+ = new ConcurrentHashMap<BarrierInfo, OFBarrierReplyFuture>();
+
+ private int number_thread;
+
+ /**
+ * Main thread that reads messages from queues and sends them to switches.
+ *
+ * @author Naoki Shiota
+ */
+ private class FlowPusherThread extends Thread {
+ private Map<IOFSwitch, SwitchQueue> assignedQueues
+ = new ConcurrentHashMap<IOFSwitch, SwitchQueue>();
+
+ final Lock queuingLock = new ReentrantLock();
+ final Condition messagePushed = queuingLock.newCondition();
+
+ @Override
+ public void run() {
+ this.setName("FlowPusherThread " + this.getId());
+ while (true) {
+ while (!queuesHasMessageToSend()) {
+ queuingLock.lock();
+
+ try {
+ // wait for message pushed to queue
+ messagePushed.await();
+ } catch (InterruptedException e) {
+ // Interrupted to be shut down (not an error)
+ log.debug("FlowPusherThread is interrupted");
+ return;
+ } finally {
+ queuingLock.unlock();
+ }
+ }
+
+ // for safety of concurrent access, copy set of key objects
+ Set<IOFSwitch> keys = new HashSet<IOFSwitch>(assignedQueues.size());
+ for (IOFSwitch sw : assignedQueues.keySet()) {
+ keys.add(sw);
+ }
+
+ for (IOFSwitch sw : keys) {
+ SwitchQueue queue = assignedQueues.get(sw);
+
+ if (sw == null || queue == null) {
+ continue;
+ }
+
+ synchronized (queue) {
+ processQueue(sw, queue, MAX_MESSAGE_SEND);
+ if (queue.toBeDeleted && !queue.hasMessageToSend()) {
+ // remove queue if flagged to be.
+ assignedQueues.remove(sw);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Read messages from queue and send them to the switch.
+ * If number of messages excess the limit, stop sending messages.
+ *
+ * @param sw Switch to which messages will be sent.
+ * @param queue Queue of messages.
+ * @param max_msg Limitation of number of messages to be sent. If set to 0,
+ * all messages in queue will be sent.
+ */
+ private void processQueue(IOFSwitch sw, SwitchQueue queue, int max_msg) {
+ // check sending rate and determine it to be sent or not
+ long current_time = System.currentTimeMillis();
+ long size = 0;
+
+ if (queue.isSendable(current_time)) {
+ int i = 0;
+ while (queue.hasMessageToSend()) {
+ // Number of messages excess the limit
+ if (0 < max_msg && max_msg <= i) {
+ break;
+ }
+ ++i;
+
+ SwitchQueueEntry queueEntry;
+ synchronized (queue) {
+ queueEntry = queue.poll();
+ }
+
+ OFMessage msg = queueEntry.getOFMessage();
+ try {
+ messageDamper.write(sw, msg, context);
+ if (log.isTraceEnabled()) {
+ log.trace("Pusher sends message : {}", msg);
+ }
+ size += msg.getLength();
+ } catch (IOException e) {
+ e.printStackTrace();
+ log.error("Exception in sending message ({}) : {}", msg, e);
+ }
+ }
+
+ sw.flush();
+ queue.logSentData(current_time, size);
+ }
+ }
+
+ private boolean queuesHasMessageToSend() {
+ for (SwitchQueue queue : assignedQueues.values()) {
+ if (queue.hasMessageToSend()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void notifyMessagePushed() {
+ queuingLock.lock();
+ try {
+ messagePushed.signal();
+ } finally {
+ queuingLock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Initialize object with one thread.
+ */
+ public FlowPusher() {
+ number_thread = DEFAULT_NUMBER_THREAD;
+ }
+
+ /**
+ * Initialize object with threads of given number.
+ *
+ * @param number_thread Number of threads to handle messages.
+ */
+ public FlowPusher(int number_thread) {
+ if (number_thread > 0) {
+ this.number_thread = number_thread;
+ } else {
+ this.number_thread = DEFAULT_NUMBER_THREAD;
+ }
+ }
+
+ /**
+ * Set parameters needed for sending messages.
+ *
+ * @param context FloodlightContext used for sending messages.
+ * If null, FlowPusher uses default context.
+ * @param modContext FloodlightModuleContext used for acquiring
+ * ThreadPoolService and registering MessageListener.
+ * @param factory Factory object to create OFMessage objects.
+ * @param damper Message damper used for sending messages.
+ * If null, FlowPusher creates its own damper object.
+ */
+ public void init(FloodlightContext context,
+ FloodlightModuleContext modContext,
+ BasicFactory factory,
+ OFMessageDamper damper) {
+ this.context = context;
+ this.factory = factory;
+ this.threadPool = modContext.getServiceImpl(IThreadPoolService.class);
+ IFloodlightProviderService flservice
+ = modContext.getServiceImpl(IFloodlightProviderService.class);
+ flservice.addOFMessageListener(OFType.BARRIER_REPLY, this);
+
+ if (damper != null) {
+ messageDamper = damper;
+ } else {
+ // use default values
+ messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+ EnumSet.of(OFType.FLOW_MOD),
+ OFMESSAGE_DAMPER_TIMEOUT);
+ }
+ }
+
+ /**
+ * Begin processing queue.
+ */
+ public void start() {
+ if (factory == null) {
+ log.error("FlowPusher not yet initialized.");
+ return;
+ }
+
+ threadMap = new HashMap<Long, FlowPusherThread>();
+ for (long i = 0; i < number_thread; ++i) {
+ FlowPusherThread thread = new FlowPusherThread();
+
+ threadMap.put(i, thread);
+ thread.start();
+ }
+ }
+
+ @Override
+ public boolean suspend(IOFSwitch sw) {
+ SwitchQueue queue = getQueue(sw);
+
+ if (queue == null) {
+ // create queue in case suspend is called before first message addition
+ queue = createQueueImpl(sw);
+ }
+
+ synchronized (queue) {
+ if (queue.state == QueueState.READY) {
+ queue.state = QueueState.SUSPENDED;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public boolean resume(IOFSwitch sw) {
+ SwitchQueue queue = getQueue(sw);
+
+ if (queue == null) {
+ log.error("No queue is attached to DPID : {}", sw.getId());
+ return false;
+ }
+
+ synchronized (queue) {
+ if (queue.state == QueueState.SUSPENDED) {
+ queue.state = QueueState.READY;
+
+ // Free the latch if queue has any messages
+ FlowPusherThread thread = getProcessingThread(sw);
+ if (queue.hasMessageToSend()) {
+ thread.notifyMessagePushed();
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public QueueState getState(IOFSwitch sw) {
+ SwitchQueue queue = getQueue(sw);
+
+ if (queue == null) {
+ return QueueState.UNKNOWN;
+ }
+
+ return queue.state;
+ }
+
+ /**
+ * Stop processing queue and exit thread.
+ */
+ public void stop() {
+ if (threadMap == null) {
+ return;
+ }
+
+ for (FlowPusherThread t : threadMap.values()) {
+ t.interrupt();
+ }
+ }
+
+ @Override
+ public void setRate(IOFSwitch sw, long rate) {
+ SwitchQueue queue = getQueue(sw);
+ if (queue == null) {
+ queue = createQueueImpl(sw);
+ }
+
+ if (rate > 0) {
+ log.debug("rate for {} is set to {}", sw.getId(), rate);
+ synchronized (queue) {
+ queue.max_rate = rate;
+ }
+ }
+ }
+
+ @Override
+ public boolean createQueue(IOFSwitch sw) {
+ SwitchQueue queue = createQueueImpl(sw);
+
+ return (queue != null);
+ }
+
+ protected SwitchQueue createQueueImpl(IOFSwitch sw) {
+ SwitchQueue queue = getQueue(sw);
+ if (queue != null) {
+ return queue;
+ }
+
+ FlowPusherThread proc = getProcessingThread(sw);
+ queue = new SwitchQueue();
+ queue.state = QueueState.READY;
+ proc.assignedQueues.put(sw, queue);
+
+ return queue;
+ }
+
+ @Override
+ public boolean deleteQueue(IOFSwitch sw) {
+ return deleteQueue(sw, false);
+ }
+
+ @Override
+ public boolean deleteQueue(IOFSwitch sw, boolean forceStop) {
+ FlowPusherThread proc = getProcessingThread(sw);
+
+ if (forceStop) {
+ SwitchQueue queue = proc.assignedQueues.remove(sw);
+ if (queue == null) {
+ return false;
+ }
+ return true;
+ } else {
+ SwitchQueue queue = getQueue(sw);
+ if (queue == null) {
+ return false;
+ }
+ synchronized (queue) {
+ queue.toBeDeleted = true;
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public boolean add(IOFSwitch sw, OFMessage msg) {
+ return add(sw, msg, MsgPriority.NORMAL);
+ }
+
+ @Override
+ public boolean add(IOFSwitch sw, OFMessage msg, MsgPriority priority) {
+ return addMessageImpl(sw, msg, priority);
+ }
+
+ @Override
+ public void pushFlowEntries(
+ Collection<Pair<IOFSwitch, FlowEntry>> entries) {
+ pushFlowEntries(entries, MsgPriority.NORMAL);
+ }
+
+ @Override
+ public void pushFlowEntries(
+ Collection<Pair<IOFSwitch, FlowEntry>> entries, MsgPriority priority) {
+
+ for (Pair<IOFSwitch, FlowEntry> entry : entries) {
+ add(entry.first, entry.second, priority);
+ }
+ }
+
+ @Override
+ public void pushFlowEntry(IOFSwitch sw, FlowEntry flowEntry) {
+ pushFlowEntry(sw, flowEntry, MsgPriority.NORMAL);
+ }
+
+ @Override
+ public void pushFlowEntry(IOFSwitch sw, FlowEntry flowEntry, MsgPriority priority) {
+ Collection<Pair<IOFSwitch, FlowEntry>> entries =
+ new LinkedList<Pair<IOFSwitch, FlowEntry>>();
+
+ entries.add(new Pair<IOFSwitch, FlowEntry>(sw, flowEntry));
+ pushFlowEntries(entries, priority);
+ }
+
+ /**
+ * Create a message from FlowEntry and add it to the queue of the switch.
+ *
+ * @param sw Switch to which message is pushed.
+ * @param flowEntry FlowEntry object used for creating message.
+ * @return true if message is successfully added to a queue.
+ */
+ private boolean add(IOFSwitch sw, FlowEntry flowEntry, MsgPriority priority) {
+ //
+ // Create the OpenFlow Flow Modification Entry to push
+ //
+ OFFlowMod fm = (OFFlowMod) factory.getMessage(OFType.FLOW_MOD);
+ long cookie = flowEntry.flowEntryId().value();
+
+ short flowModCommand = OFFlowMod.OFPFC_ADD;
+ if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_ADD) {
+ flowModCommand = OFFlowMod.OFPFC_ADD;
+ } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_MODIFY) {
+ flowModCommand = OFFlowMod.OFPFC_MODIFY_STRICT;
+ } else if (flowEntry.flowEntryUserState() == FlowEntryUserState.FE_USER_DELETE) {
+ flowModCommand = OFFlowMod.OFPFC_DELETE_STRICT;
+ } else {
+ // Unknown user state. Ignore the entry
+ log.debug(
+ "Flow Entry ignored (FlowEntryId = {}): unknown user state {}",
+ flowEntry.flowEntryId(),
+ flowEntry.flowEntryUserState());
+ return false;
+ }
+
+ //
+ // Fetch the match conditions.
+ //
+ // NOTE: The Flow matching conditions common for all Flow Entries are
+ // used ONLY if a Flow Entry does NOT have the corresponding matching
+ // condition set.
+ //
+ OFMatch match = new OFMatch();
+ match.setWildcards(OFMatch.OFPFW_ALL);
+ FlowEntryMatch flowEntryMatch = flowEntry.flowEntryMatch();
+
+ // Match the Incoming Port
+ Port matchInPort = flowEntryMatch.inPort();
+ if (matchInPort != null) {
+ match.setInputPort(matchInPort.value());
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_IN_PORT);
+ }
+
+ // Match the Source MAC address
+ MACAddress matchSrcMac = flowEntryMatch.srcMac();
+ if (matchSrcMac != null) {
+ match.setDataLayerSource(matchSrcMac.toString());
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_SRC);
+ }
+
+ // Match the Destination MAC address
+ MACAddress matchDstMac = flowEntryMatch.dstMac();
+ if (matchDstMac != null) {
+ match.setDataLayerDestination(matchDstMac.toString());
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
+ }
+
+ // Match the Ethernet Frame Type
+ Short matchEthernetFrameType = flowEntryMatch.ethernetFrameType();
+ if (matchEthernetFrameType != null) {
+ match.setDataLayerType(matchEthernetFrameType);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+ }
+
+ // Match the VLAN ID
+ Short matchVlanId = flowEntryMatch.vlanId();
+ if (matchVlanId != null) {
+ match.setDataLayerVirtualLan(matchVlanId);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_VLAN);
+ }
+
+ // Match the VLAN priority
+ Byte matchVlanPriority = flowEntryMatch.vlanPriority();
+ if (matchVlanPriority != null) {
+ match.setDataLayerVirtualLanPriorityCodePoint(matchVlanPriority);
+ match.setWildcards(match.getWildcards()
+ & ~OFMatch.OFPFW_DL_VLAN_PCP);
+ }
+
+ // Match the Source IPv4 Network prefix
+ IPv4Net matchSrcIPv4Net = flowEntryMatch.srcIPv4Net();
+ if (matchSrcIPv4Net != null) {
+ match.setFromCIDR(matchSrcIPv4Net.toString(), OFMatch.STR_NW_SRC);
+ }
+
+ // Natch the Destination IPv4 Network prefix
+ IPv4Net matchDstIPv4Net = flowEntryMatch.dstIPv4Net();
+ if (matchDstIPv4Net != null) {
+ match.setFromCIDR(matchDstIPv4Net.toString(), OFMatch.STR_NW_DST);
+ }
+
+ // Match the IP protocol
+ Byte matchIpProto = flowEntryMatch.ipProto();
+ if (matchIpProto != null) {
+ match.setNetworkProtocol(matchIpProto);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_NW_PROTO);
+ }
+
+ // Match the IP ToS (DSCP field, 6 bits)
+ Byte matchIpToS = flowEntryMatch.ipToS();
+ if (matchIpToS != null) {
+ match.setNetworkTypeOfService(matchIpToS);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_NW_TOS);
+ }
+
+ // Match the Source TCP/UDP port
+ Short matchSrcTcpUdpPort = flowEntryMatch.srcTcpUdpPort();
+ if (matchSrcTcpUdpPort != null) {
+ match.setTransportSource(matchSrcTcpUdpPort);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_TP_SRC);
+ }
+
+ // Match the Destination TCP/UDP port
+ Short matchDstTcpUdpPort = flowEntryMatch.dstTcpUdpPort();
+ if (matchDstTcpUdpPort != null) {
+ match.setTransportDestination(matchDstTcpUdpPort);
+ match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_TP_DST);
+ }
+
+ //
+ // Fetch the actions
+ //
+ Short actionOutputPort = null;
+ List<OFAction> openFlowActions = new ArrayList<OFAction>();
+ int actionsLen = 0;
+ FlowEntryActions flowEntryActions = flowEntry.flowEntryActions();
+ //
+ for (FlowEntryAction action : flowEntryActions.actions()) {
+ ActionOutput actionOutput = action.actionOutput();
+ ActionSetVlanId actionSetVlanId = action.actionSetVlanId();
+ ActionSetVlanPriority actionSetVlanPriority = action
+ .actionSetVlanPriority();
+ ActionStripVlan actionStripVlan = action.actionStripVlan();
+ ActionSetEthernetAddr actionSetEthernetSrcAddr = action
+ .actionSetEthernetSrcAddr();
+ ActionSetEthernetAddr actionSetEthernetDstAddr = action
+ .actionSetEthernetDstAddr();
+ ActionSetIPv4Addr actionSetIPv4SrcAddr = action
+ .actionSetIPv4SrcAddr();
+ ActionSetIPv4Addr actionSetIPv4DstAddr = action
+ .actionSetIPv4DstAddr();
+ ActionSetIpToS actionSetIpToS = action.actionSetIpToS();
+ ActionSetTcpUdpPort actionSetTcpUdpSrcPort = action
+ .actionSetTcpUdpSrcPort();
+ ActionSetTcpUdpPort actionSetTcpUdpDstPort = action
+ .actionSetTcpUdpDstPort();
+ ActionEnqueue actionEnqueue = action.actionEnqueue();
+
+ if (actionOutput != null) {
+ actionOutputPort = actionOutput.port().value();
+ // XXX: The max length is hard-coded for now
+ OFActionOutput ofa = new OFActionOutput(actionOutput.port()
+ .value(), (short) 0xffff);
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionSetVlanId != null) {
+ OFActionVirtualLanIdentifier ofa = new OFActionVirtualLanIdentifier(
+ actionSetVlanId.vlanId());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionSetVlanPriority != null) {
+ OFActionVirtualLanPriorityCodePoint ofa = new OFActionVirtualLanPriorityCodePoint(
+ actionSetVlanPriority.vlanPriority());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionStripVlan != null) {
+ if (actionStripVlan.stripVlan() == true) {
+ OFActionStripVirtualLan ofa = new OFActionStripVirtualLan();
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+ }
+
+ if (actionSetEthernetSrcAddr != null) {
+ OFActionDataLayerSource ofa = new OFActionDataLayerSource(
+ actionSetEthernetSrcAddr.addr().toBytes());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionSetEthernetDstAddr != null) {
+ OFActionDataLayerDestination ofa = new OFActionDataLayerDestination(
+ actionSetEthernetDstAddr.addr().toBytes());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionSetIPv4SrcAddr != null) {
+ OFActionNetworkLayerSource ofa = new OFActionNetworkLayerSource(
+ actionSetIPv4SrcAddr.addr().value());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionSetIPv4DstAddr != null) {
+ OFActionNetworkLayerDestination ofa = new OFActionNetworkLayerDestination(
+ actionSetIPv4DstAddr.addr().value());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionSetIpToS != null) {
+ OFActionNetworkTypeOfService ofa = new OFActionNetworkTypeOfService(
+ actionSetIpToS.ipToS());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionSetTcpUdpSrcPort != null) {
+ OFActionTransportLayerSource ofa = new OFActionTransportLayerSource(
+ actionSetTcpUdpSrcPort.port());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionSetTcpUdpDstPort != null) {
+ OFActionTransportLayerDestination ofa = new OFActionTransportLayerDestination(
+ actionSetTcpUdpDstPort.port());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+
+ if (actionEnqueue != null) {
+ OFActionEnqueue ofa = new OFActionEnqueue(actionEnqueue.port()
+ .value(), actionEnqueue.queueId());
+ openFlowActions.add(ofa);
+ actionsLen += ofa.getLength();
+ }
+ }
+
+ fm.setIdleTimeout((short) flowEntry.idleTimeout())
+ .setHardTimeout((short) flowEntry.hardTimeout())
+ .setPriority((short) flowEntry.priority())
+ .setBufferId(OFPacketOut.BUFFER_ID_NONE).setCookie(cookie)
+ .setCommand(flowModCommand).setMatch(match)
+ .setActions(openFlowActions)
+ .setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLen);
+ fm.setOutPort(OFPort.OFPP_NONE.getValue());
+ if ((flowModCommand == OFFlowMod.OFPFC_DELETE)
+ || (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) {
+ if (actionOutputPort != null)
+ fm.setOutPort(actionOutputPort);
+ }
+
+ //
+ // Set the OFPFF_SEND_FLOW_REM flag if the Flow Entry is not
+ // permanent.
+ //
+ if ((flowEntry.idleTimeout() != 0) ||
+ (flowEntry.hardTimeout() != 0)) {
+ fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Installing flow entry {} into switch DPID: {} flowEntryId: {} srcMac: {} dstMac: {} inPort: {} outPort: {}"
+ , flowEntry.flowEntryUserState()
+ , sw.getStringId()
+ , flowEntry.flowEntryId()
+ , matchSrcMac
+ , matchDstMac
+ , matchInPort
+ , actionOutputPort
+ );
+ }
+
+ return addMessageImpl(sw, fm, priority);
+ }
+
+ /**
+ * Add message to queue
+ *
+ * @param sw
+ * @param msg
+ * @param flowEntryId
+ * @return
+ */
+ protected boolean addMessageImpl(IOFSwitch sw, OFMessage msg, MsgPriority priority) {
+ FlowPusherThread thread = getProcessingThread(sw);
+
+ SwitchQueue queue = getQueue(sw);
+
+ // create queue at first addition of message
+ if (queue == null) {
+ queue = createQueueImpl(sw);
+ }
+
+ SwitchQueueEntry entry = new SwitchQueueEntry(msg);
+
+ synchronized (queue) {
+ queue.add(entry, priority);
+ if (log.isTraceEnabled()) {
+ log.trace("Message is pushed : {}", entry.getOFMessage());
+ }
+ }
+
+ thread.notifyMessagePushed();
+
+ return true;
+ }
+
+ @Override
+ public OFBarrierReply barrier(IOFSwitch sw) {
+ OFMessageFuture<OFBarrierReply> future = barrierAsync(sw);
+ if (future == null) {
+ return null;
+ }
+
+ try {
+ return future.get();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ log.error("InterruptedException: {}", e);
+ return null;
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ log.error("ExecutionException: {}", e);
+ return null;
+ }
+ }
+
+ @Override
+ public OFBarrierReplyFuture barrierAsync(IOFSwitch sw) {
+ // TODO creation of message and future should be moved to OFSwitchImpl
+
+ if (sw == null) {
+ return null;
+ }
+
+ OFBarrierRequest msg = createBarrierRequest(sw);
+
+ OFBarrierReplyFuture future = new OFBarrierReplyFuture(threadPool, sw, msg.getXid());
+ barrierFutures.put(BarrierInfo.create(sw, msg), future);
+
+ addMessageImpl(sw, msg, MsgPriority.NORMAL);
+
+ return future;
+ }
+
+ protected OFBarrierRequest createBarrierRequest(IOFSwitch sw) {
+ OFBarrierRequest msg = (OFBarrierRequest) factory.getMessage(OFType.BARRIER_REQUEST);
+ msg.setXid(sw.getNextTransactionId());
+
+ return msg;
+ }
+
+ /**
+ * Get a queue attached to a switch.
+ *
+ * @param sw Switch object
+ * @return Queue object
+ */
+ protected SwitchQueue getQueue(IOFSwitch sw) {
+ if (sw == null) {
+ return null;
+ }
+
+ FlowPusherThread th = getProcessingThread(sw);
+ if (th == null) {
+ return null;
+ }
+
+ return th.assignedQueues.get(sw);
+ }
+
+ /**
+ * Get a hash value correspondent to a switch.
+ *
+ * @param sw Switch object
+ * @return Hash value
+ */
+ protected long getHash(IOFSwitch sw) {
+ // This code assumes DPID is sequentially assigned.
+ // TODO consider equalization algorithm
+ return sw.getId() % number_thread;
+ }
+
+ /**
+ * Get a Thread object which processes the queue attached to a switch.
+ *
+ * @param sw Switch object
+ * @return Thread object
+ */
+ protected FlowPusherThread getProcessingThread(IOFSwitch sw) {
+ long hash = getHash(sw);
+
+ return threadMap.get(hash);
+ }
+
+ @Override
+ public String getName() {
+ return "flowpusher";
+ }
+
+ @Override
+ public boolean isCallbackOrderingPrereq(OFType type, String name) {
+ return false;
+ }
+
+ @Override
+ public boolean isCallbackOrderingPostreq(OFType type, String name) {
+ return false;
+ }
+
+ @Override
+ public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+ if (log.isTraceEnabled()) {
+ log.trace("Received BARRIER_REPLY from : {}", sw.getId());
+ }
+
+ if (msg.getType() != OFType.BARRIER_REPLY) {
+ log.error("Unexpected reply message : {}", msg.getType());
+ return Command.CONTINUE;
+ }
+
+ OFBarrierReply reply = (OFBarrierReply) msg;
+ BarrierInfo info = BarrierInfo.create(sw, reply);
+
+ // Deliver future if exists
+ OFBarrierReplyFuture future = barrierFutures.get(info);
+ if (future != null) {
+ future.deliverFuture(sw, msg);
+ barrierFutures.remove(info);
+ }
+
+ return Command.CONTINUE;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/FlowSynchronizer.java b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowSynchronizer.java
new file mode 100644
index 0000000..52eeff2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/FlowSynchronizer.java
@@ -0,0 +1,387 @@
+package net.onrc.onos.core.flowprogrammer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFStatisticsRequest;
+import org.openflow.protocol.statistics.OFFlowStatisticsReply;
+import org.openflow.protocol.statistics.OFFlowStatisticsRequest;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.openflow.protocol.statistics.OFStatisticsType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.floodlightcontroller.core.IOFSwitch;
+import net.onrc.onos.core.flowprogrammer.IFlowPusherService.MsgPriority;
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.FlowEntry;
+import net.onrc.onos.core.util.FlowEntryId;
+
+/**
+ * FlowSynchronizer is an implementation of FlowSyncService.
+ * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow
+ * tables from switches and compare them with GraphDB to drop unnecessary
+ * flows and/or to install missing flows.
+ *
+ * @author Brian
+ */
+public class FlowSynchronizer implements IFlowSyncService {
+
+ private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class);
+
+ // TODO: fix when FlowSynchronizer is refactored
+ // private DBOperation dbHandler;
+ protected IFlowPusherService pusher;
+ private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads;
+
+ public FlowSynchronizer() {
+ // TODO: fix when FlowSynchronizer is refactored
+ // dbHandler = GraphDBManager.getDBOperation();
+ switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>();
+ }
+
+ @Override
+ public Future<SyncResult> synchronize(IOFSwitch sw) {
+ Synchronizer sync = new Synchronizer(sw);
+ FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync);
+ switchThreads.put(sw, task);
+ task.run();
+ return task;
+ }
+
+ @Override
+ public void interrupt(IOFSwitch sw) {
+ FutureTask<SyncResult> t = switchThreads.remove(sw);
+ if (t != null) {
+ t.cancel(true);
+ }
+ }
+
+ /**
+ * Initialize Synchronizer.
+ *
+ * @param pusherService FlowPusherService used for sending messages.
+ */
+ public void init(IFlowPusherService pusherService) {
+ pusher = pusherService;
+ }
+
+ /**
+ * Synchronizer represents main thread of synchronization.
+ *
+ * @author Brian
+ */
+ protected class Synchronizer implements Callable<SyncResult> {
+ IOFSwitch sw;
+ // TODO: fix when FlowSynchronizer is refactored
+ // ISwitchObject swObj;
+
+ public Synchronizer(IOFSwitch sw) {
+ this.sw = sw;
+ Dpid dpid = new Dpid(sw.getId());
+ // TODO: fix when FlowSynchronizer is refactored
+ // this.swObj = dbHandler.searchSwitch(dpid.toString());
+ }
+
+ double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime;
+
+ @Override
+ public SyncResult call() {
+ pusher.suspend(sw);
+ try {
+ long start = System.nanoTime();
+ Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph();
+ long step1 = System.nanoTime();
+ Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch();
+ if (switchEntries == null) {
+ log.debug("getFlowEntriesFromSwitch() failed");
+ return null;
+ }
+ long step2 = System.nanoTime();
+ SyncResult result = compare(graphEntries, switchEntries);
+ long step3 = System.nanoTime();
+ graphIDTime = (step1 - start);
+ switchTime = (step2 - step1);
+ compareTime = (step3 - step2);
+ totalTime = (step3 - start);
+ outputTime();
+
+ return result;
+ } finally {
+ pusher.resume(sw);
+ }
+ }
+
+ private void outputTime() {
+ double div = Math.pow(10, 6); //convert nanoseconds to ms
+ graphIDTime /= div;
+ switchTime /= div;
+ compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div;
+ graphEntryTime /= div;
+ extractTime /= div;
+ pushTime /= div;
+ totalTime /= div;
+ log.debug("Sync time (ms):{},{},{},{},{},{},{}"
+ , graphIDTime
+ , switchTime
+ , compareTime
+ , graphEntryTime
+ , extractTime
+ , pushTime
+ , totalTime);
+ }
+
+ /**
+ * Compare flows entries in GraphDB and switch to pick up necessary
+ * messages.
+ * After picking up, picked messages are added to FlowPusher.
+ *
+ * @param graphEntries Flow entries in GraphDB.
+ * @param switchEntries Flow entries in switch.
+ */
+ private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) {
+ int added = 0, removed = 0, skipped = 0;
+ for (FlowEntryWrapper entry : switchEntries) {
+ if (graphEntries.contains(entry)) {
+ graphEntries.remove(entry);
+ skipped++;
+ } else {
+ // remove flow entry from the switch
+ entry.removeFromSwitch(sw);
+ removed++;
+ }
+ }
+ for (FlowEntryWrapper entry : graphEntries) {
+ // add flow entry to switch
+ entry.addToSwitch(sw);
+ graphEntryTime += entry.dbTime;
+ extractTime += entry.extractTime;
+ pushTime += entry.pushTime;
+ added++;
+ }
+ log.debug("Flow entries added {}, " +
+ "Flow entries removed {}, " +
+ "Flow entries skipped {}"
+ , added
+ , removed
+ , skipped);
+
+ return new SyncResult(added, removed, skipped);
+ }
+
+ /**
+ * Read GraphDB to get FlowEntries associated with a switch.
+ *
+ * @return set of FlowEntries
+ */
+ private Set<FlowEntryWrapper> getFlowEntriesFromGraph() {
+ Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>();
+
+ // TODO: fix when FlowSynchronizer is refactored
+ /*
+ for(IFlowEntry entry : swObj.getFlowEntries()) {
+ FlowEntryWrapper fe = new FlowEntryWrapper(entry);
+ entries.add(fe);
+ }
+ */
+ return entries;
+ }
+
+ /**
+ * Read flow table from switch and derive FlowEntries from table.
+ *
+ * @return set of FlowEntries
+ */
+ private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() {
+
+ int lengthU = 0;
+ OFMatch match = new OFMatch();
+ match.setWildcards(OFMatch.OFPFW_ALL);
+
+ OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest();
+ stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE
+ stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES)
+ stat.setMatch(match);
+ List<OFStatistics> stats = new ArrayList<OFStatistics>();
+ stats.add(stat);
+ lengthU += stat.getLength();
+
+ OFStatisticsRequest req = new OFStatisticsRequest();
+ req.setStatisticType(OFStatisticsType.FLOW);
+ req.setStatistics(stats);
+ lengthU += req.getLengthU();
+ req.setLengthU(lengthU);
+
+ List<OFStatistics> entries = null;
+ try {
+ Future<List<OFStatistics>> dfuture = sw.getStatistics(req);
+ entries = dfuture.get();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ } catch (ExecutionException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return null;
+ }
+
+ Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>();
+ for (OFStatistics result : entries) {
+ OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result;
+ FlowEntryWrapper fe = new FlowEntryWrapper(entry);
+ results.add(fe);
+ }
+ return results;
+ }
+
+ }
+
+ /**
+ * FlowEntryWrapper represents abstract FlowEntry which is embodied
+ * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch).
+ *
+ * @author Brian
+ */
+ class FlowEntryWrapper {
+ FlowEntryId flowEntryId;
+ // TODO: fix when FlowSynchronizer is refactored
+ // IFlowEntry iFlowEntry;
+ OFFlowStatisticsReply statisticsReply;
+
+
+ // TODO: fix when FlowSynchronizer is refactored
+ /*
+ public FlowEntryWrapper(IFlowEntry entry) {
+ flowEntryId = new FlowEntryId(entry.getFlowEntryId());
+ iFlowEntry = entry;
+ }
+ */
+
+ public FlowEntryWrapper(OFFlowStatisticsReply entry) {
+ flowEntryId = new FlowEntryId(entry.getCookie());
+ statisticsReply = entry;
+ }
+
+ /**
+ * Install this FlowEntry to a switch via FlowPusher.
+ *
+ * @param sw Switch to which flow will be installed.
+ */
+ double dbTime, extractTime, pushTime;
+
+ public void addToSwitch(IOFSwitch sw) {
+ if (statisticsReply != null) {
+ log.error("Error adding existing flow entry {} to sw {}",
+ statisticsReply.getCookie(), sw.getId());
+ return;
+ }
+
+ double startDB = System.nanoTime();
+ // Get the Flow Entry state from the Network Graph
+ // TODO: fix when FlowSynchronizer is refactored
+ /*
+ if (iFlowEntry == null) {
+ try {
+ // TODO: fix when FlowSynchronizer is refactored
+ iFlowEntry = dbHandler.searchFlowEntry(flowEntryId);
+ } catch (Exception e) {
+ log.error("Error finding flow entry {} in Network Graph",
+ flowEntryId);
+ return;
+ }
+ }
+ */
+ dbTime = System.nanoTime() - startDB;
+
+ //
+ // TODO: The old FlowDatabaseOperation class is gone, so the code
+ //
+ /*
+ double startExtract = System.nanoTime();
+ FlowEntry flowEntry =
+ FlowDatabaseOperation.extractFlowEntry(iFlowEntry);
+ if (flowEntry == null) {
+ log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted",
+ flowEntryId, sw.getId());
+ return;
+ }
+ extractTime = System.nanoTime() - startExtract;
+
+ double startPush = System.nanoTime();
+ pusher.pushFlowEntry(sw, flowEntry, MsgPriority.HIGH);
+ pushTime = System.nanoTime() - startPush;
+ */
+ }
+
+ /**
+ * Remove this FlowEntry from a switch via FlowPusher.
+ *
+ * @param sw Switch from which flow will be removed.
+ */
+ public void removeFromSwitch(IOFSwitch sw) {
+ if (statisticsReply == null) {
+ log.error("Error removing non-existent flow entry {} from sw {}",
+ flowEntryId, sw.getId());
+ return;
+ }
+
+ // Convert Statistics Reply to Flow Mod, then write it
+ OFFlowMod fm = new OFFlowMod();
+ fm.setCookie(statisticsReply.getCookie());
+ fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
+ fm.setLengthU(OFFlowMod.MINIMUM_LENGTH);
+ fm.setMatch(statisticsReply.getMatch());
+ fm.setPriority(statisticsReply.getPriority());
+ fm.setOutPort(OFPort.OFPP_NONE);
+
+ pusher.add(sw, fm, MsgPriority.HIGH);
+ }
+
+ /**
+ * Return the hash code of the Flow Entry ID
+ */
+ @Override
+ public int hashCode() {
+ return flowEntryId.hashCode();
+ }
+
+ /**
+ * Returns true of the object is another Flow Entry ID with
+ * the same value; otherwise, returns false.
+ *
+ * @param Object to compare
+ * @return true if the object has the same Flow Entry ID.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj != null && obj.getClass() == this.getClass()) {
+ FlowEntryWrapper entry = (FlowEntryWrapper) obj;
+ // TODO: we need to actually compare the match + actions
+ return this.flowEntryId.equals(entry.flowEntryId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return flowEntryId.toString();
+ }
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/IFlowPusherService.java b/src/main/java/net/onrc/onos/core/flowprogrammer/IFlowPusherService.java
new file mode 100644
index 0000000..c0c7ae7
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/IFlowPusherService.java
@@ -0,0 +1,187 @@
+package net.onrc.onos.core.flowprogrammer;
+
+import java.util.Collection;
+
+import org.openflow.protocol.OFBarrierReply;
+import org.openflow.protocol.OFMessage;
+
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.internal.OFMessageFuture;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.onrc.onos.core.util.FlowEntry;
+import net.onrc.onos.core.util.Pair;
+
+/**
+ * FlowPusherService is a service to send message to switches in proper rate.
+ * Conceptually a queue is attached to each switch, and FlowPusherService
+ * read a message from queue and send it to switch in order.
+ * To guarantee message has been installed, FlowPusherService can add barrier
+ * message to queue and can notify when barrier message is sent to switch.
+ *
+ * @author Naoki Shiota
+ */
+public interface IFlowPusherService extends IFloodlightService {
+ public static enum MsgPriority {
+ HIGH, // High priority: e.g. flow synchronization
+ NORMAL, // Normal priority
+// LOW, // Low priority, not needed for now
+ }
+
+ public static enum QueueState {
+ READY, // Queues with all priority are at work
+ SUSPENDED, // Only prior queue is at work
+ UNKNOWN
+ }
+
+ /**
+ * Create a queue correspondent to the switch.
+ *
+ * @param sw Switch to which new queue is attached.
+ * @return true if new queue is successfully created.
+ */
+ boolean createQueue(IOFSwitch sw);
+
+ /**
+ * Delete a queue correspondent to the switch.
+ * Messages remains in queue will be all sent before queue is deleted.
+ *
+ * @param sw Switch of which queue is deleted.
+ * @return true if queue is successfully deleted.
+ */
+ boolean deleteQueue(IOFSwitch sw);
+
+ /**
+ * Delete a queue correspondent to the switch.
+ * By setting force flag on, queue will be deleted immediately.
+ *
+ * @param sw Switch of which queue is deleted.
+ * @param forceStop If this flag is set to true, queue will be deleted
+ * immediately regardless of any messages in the queue.
+ * If false, all messages will be sent to switch and queue will
+ * be deleted after that.
+ * @return true if queue is successfully deleted or flagged to be deleted.
+ */
+ boolean deleteQueue(IOFSwitch sw, boolean forceStop);
+
+ /**
+ * Add a message to the queue of the switch with normal priority.
+ * <p/>
+ * Note: Notification is NOT delivered for the pushed message.
+ *
+ * @param sw Switch to which message is pushed.
+ * @param msg Message object to be added.
+ * @return true if message is successfully added to a queue.
+ */
+ boolean add(IOFSwitch sw, OFMessage msg);
+
+ /**
+ * Add a message to the queue of the switch with specific priority.
+ *
+ * @param sw Switch to which message is pushed.
+ * @param msg Message object to be added.
+ * @param priority Sending priority of the message.
+ * @return true if message is successfully added to a queue.
+ */
+ boolean add(IOFSwitch sw, OFMessage msg, MsgPriority priority);
+
+ /**
+ * Push a collection of Flow Entries to the corresponding switches
+ * with normal priority.
+ * <p/>
+ * Note: Notification is delivered for the Flow Entries that
+ * are pushed successfully.
+ *
+ * @param entries the collection of <IOFSwitch, FlowEntry> pairs
+ * to push.
+ */
+ void pushFlowEntries(Collection<Pair<IOFSwitch, FlowEntry>> entries);
+
+ /**
+ * Push a collection of Flow Entries to the corresponding switches
+ * with specific priority.
+ * <p/>
+ * Note: Notification is delivered for the Flow Entries that
+ * are pushed successfully.
+ *
+ * @param entries the collection of <IOFSwitch, FlowEntry> pairs
+ * to push.
+ * @param priority Sending priority of flow entries.
+ */
+ void pushFlowEntries(Collection<Pair<IOFSwitch, FlowEntry>> entries,
+ MsgPriority priority);
+
+ /**
+ * Create a message from FlowEntry and add it to the queue of the
+ * switch with normal priority.
+ * <p/>
+ * Note: Notification is delivered for the Flow Entries that
+ * are pushed successfully.
+ *
+ * @param sw Switch to which message is pushed.
+ * @param flowEntry FlowEntry object used for creating message.
+ * @return true if message is successfully added to a queue.
+ */
+ void pushFlowEntry(IOFSwitch sw, FlowEntry flowEntry);
+
+ /**
+ * Create a message from FlowEntry and add it to the queue of the
+ * switch with specific priority.
+ * <p/>
+ * Note: Notification is delivered for the Flow Entries that
+ * are pushed successfully.
+ *
+ * @param sw Switch to which message is pushed.
+ * @param flowEntry FlowEntry object used for creating message.
+ * @return true if message is successfully added to a queue.
+ */
+ void pushFlowEntry(IOFSwitch sw, FlowEntry flowEntry,
+ MsgPriority priority);
+
+ /**
+ * Set sending rate to a switch.
+ *
+ * @param sw Switch.
+ * @param rate Rate in bytes/ms.
+ */
+ public void setRate(IOFSwitch sw, long rate);
+
+ /**
+ * Add BARRIER message to queue and wait for reply.
+ *
+ * @param sw Switch to which barrier message is pushed.
+ * @return BARRIER_REPLY message sent from switch.
+ */
+ OFBarrierReply barrier(IOFSwitch sw);
+
+ /**
+ * Add BARRIER message to queue asynchronously.
+ *
+ * @param sw Switch to which barrier message is pushed.
+ * @return Future object of BARRIER_REPLY message which will be sent from switch.
+ */
+ OFMessageFuture<OFBarrierReply> barrierAsync(IOFSwitch sw);
+
+ /**
+ * Suspend pushing message to a switch.
+ *
+ * @param sw Switch to be suspended pushing message.
+ * @return true if success
+ */
+ boolean suspend(IOFSwitch sw);
+
+ /**
+ * Resume pushing message to a switch.
+ *
+ * @param sw Switch to be resumed pushing message.
+ * @return true if success
+ */
+ boolean resume(IOFSwitch sw);
+
+ /**
+ * Get state of queue attached to a switch.
+ *
+ * @param sw Switch to be checked.
+ * @return State of queue.
+ */
+ QueueState getState(IOFSwitch sw);
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/IFlowSyncService.java b/src/main/java/net/onrc/onos/core/flowprogrammer/IFlowSyncService.java
new file mode 100644
index 0000000..f2d5989
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/IFlowSyncService.java
@@ -0,0 +1,30 @@
+package net.onrc.onos.core.flowprogrammer;
+
+import java.util.concurrent.Future;
+
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.module.IFloodlightService;
+
+/**
+ * FlowSyncService is a service to synchronize GraphDB and switch's flow table.
+ * FlowSyncService offers APIs to trigger and interrupt synchronization explicitly.
+ *
+ * @author Brian
+ */
+public interface IFlowSyncService extends IFloodlightService {
+ public Future<SyncResult> synchronize(IOFSwitch sw);
+
+ public void interrupt(IOFSwitch sw);
+
+ public class SyncResult {
+ public final int flowAdded;
+ public final int flowRemoved;
+ public final int flowSkipped;
+
+ public SyncResult(int added, int removed, int skipped) {
+ flowAdded = added;
+ flowRemoved = removed;
+ flowSkipped = skipped;
+ }
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/OFBarrierReplyFuture.java b/src/main/java/net/onrc/onos/core/flowprogrammer/OFBarrierReplyFuture.java
new file mode 100644
index 0000000..5815137
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/OFBarrierReplyFuture.java
@@ -0,0 +1,49 @@
+package net.onrc.onos.core.flowprogrammer;
+
+import java.util.concurrent.TimeUnit;
+
+import org.openflow.protocol.OFBarrierReply;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFType;
+
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.internal.OFMessageFuture;
+import net.floodlightcontroller.threadpool.IThreadPoolService;
+
+public class OFBarrierReplyFuture extends OFMessageFuture<OFBarrierReply> {
+
+ protected volatile boolean finished;
+
+ public OFBarrierReplyFuture(IThreadPoolService tp,
+ IOFSwitch sw, int transactionId) {
+ super(tp, sw, OFType.FEATURES_REPLY, transactionId);
+ init();
+ }
+
+ public OFBarrierReplyFuture(IThreadPoolService tp,
+ IOFSwitch sw, int transactionId, long timeout, TimeUnit unit) {
+ super(tp, sw, OFType.FEATURES_REPLY, transactionId, timeout, unit);
+ init();
+ }
+
+ private void init() {
+ this.finished = false;
+ this.result = null;
+ }
+
+ @Override
+ protected void handleReply(IOFSwitch sw, OFMessage msg) {
+ this.result = (OFBarrierReply) msg;
+ this.finished = true;
+ }
+
+ @Override
+ protected boolean isFinished() {
+ return finished;
+ }
+
+ @Override
+ protected void unRegister() {
+ super.unRegister();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/DoInterruptResource.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/DoInterruptResource.java
new file mode 100644
index 0000000..f2b4631
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/DoInterruptResource.java
@@ -0,0 +1,44 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import net.floodlightcontroller.core.IOFSwitch;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+
+/**
+ * FlowProgrammer REST API implementation: Interrupt synchronization to a switch.
+ * <p/>
+ * GET /wm/fprog/synchronizer/interrupt/{dpid}/json"
+ */
+public class DoInterruptResource extends SynchronizerResource {
+
+ /**
+ * Implement the API.
+ *
+ * @return true if succeeded, false if failed.
+ */
+ @Get("json")
+ public boolean retrieve() {
+ if (!init()) {
+ return false;
+ }
+
+ long dpid;
+ try {
+ dpid = HexString.toLong((String) getRequestAttributes().get("dpid"));
+ } catch (NumberFormatException e) {
+ log.error("Invalid number format");
+ return false;
+ }
+
+ IOFSwitch sw = provider.getSwitches().get(dpid);
+ if (sw == null) {
+ log.error("Invalid dpid");
+ return false;
+ }
+
+ synchronizer.interrupt(sw);
+
+ return true;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/DoSynchronizeResource.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/DoSynchronizeResource.java
new file mode 100644
index 0000000..51d39d8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/DoSynchronizeResource.java
@@ -0,0 +1,44 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import net.floodlightcontroller.core.IOFSwitch;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+
+/**
+ * FlowProgrammer REST API implementation: Begin synchronization to a switch.
+ * <p/>
+ * GET /wm/fprog/synchronizer/sync/{dpid}/json"
+ */
+public class DoSynchronizeResource extends SynchronizerResource {
+ /**
+ * Implement the API.
+ *
+ * @return true if succeeded, false if failed.
+ */
+ @Get("json")
+ public boolean retrieve() {
+ if (!init()) {
+ return false;
+ }
+
+ long dpid;
+ try {
+ dpid = HexString.toLong((String) getRequestAttributes().get("dpid"));
+ } catch (NumberFormatException e) {
+ log.error("Invalid number format");
+ return false;
+ }
+
+ IOFSwitch sw = provider.getSwitches().get(dpid);
+ if (sw == null) {
+ log.error("Invalid dpid");
+ return false;
+ }
+
+ synchronizer.synchronize(sw);
+
+ return true;
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/FlowProgrammerWebRoutable.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/FlowProgrammerWebRoutable.java
new file mode 100644
index 0000000..ab3f641
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/FlowProgrammerWebRoutable.java
@@ -0,0 +1,28 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+public class FlowProgrammerWebRoutable implements RestletRoutable {
+
+ @Override
+ public Restlet getRestlet(Context context) {
+ Router router = new Router(context);
+ router.attach("/pusher/setrate/{dpid}/{rate}/json", SetPushRateResource.class);
+ router.attach("/pusher/suspend/{dpid}/json", SuspendPusherResource.class);
+ router.attach("/pusher/resume/{dpid}/json", ResumePusherResource.class);
+ router.attach("/pusher/barrier/{dpid}/json", SendBarrierResource.class);
+ router.attach("/synchronizer/sync/{dpid}/json", DoSynchronizeResource.class);
+ router.attach("/synchronizer/interrupt/{dpid}/json", DoInterruptResource.class);
+ return router;
+ }
+
+ @Override
+ public String basePath() {
+ return "/wm/onos/flowprogrammer";
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/PusherResource.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/PusherResource.java
new file mode 100644
index 0000000..2f7af29
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/PusherResource.java
@@ -0,0 +1,33 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
+
+public class PusherResource extends ServerResource {
+ protected final static Logger log = LoggerFactory.getLogger(PusherResource.class);
+
+ protected IFloodlightProviderService provider;
+ protected IFlowPusherService pusher;
+
+ protected boolean init() {
+ provider = (IFloodlightProviderService)
+ getContext().getAttributes().
+ get(IFloodlightProviderService.class.getCanonicalName());
+ if (provider == null) {
+ log.debug("ONOS FloodlightProvider not found");
+ return false;
+ }
+
+ pusher = (IFlowPusherService) getContext().getAttributes().
+ get(IFlowPusherService.class.getCanonicalName());
+ if (pusher == null) {
+ log.debug("ONOS FlowPusherService not found");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/ResumePusherResource.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/ResumePusherResource.java
new file mode 100644
index 0000000..a9e6c81
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/ResumePusherResource.java
@@ -0,0 +1,41 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import net.floodlightcontroller.core.IOFSwitch;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+
+/**
+ * FlowProgrammer REST API implementation: Resume sending message to switch.
+ * <p/>
+ * GET /wm/fprog/pusher/resume/{dpid}/json"
+ */
+public class ResumePusherResource extends PusherResource {
+ /**
+ * Implement the API.
+ *
+ * @return true if succeeded, false if failed.
+ */
+ @Get("json")
+ public boolean retrieve() {
+ if (!init()) {
+ return false;
+ }
+
+ long dpid;
+ try {
+ dpid = HexString.toLong((String) getRequestAttributes().get("dpid"));
+ } catch (NumberFormatException e) {
+ log.error("Invalid number format");
+ return false;
+ }
+
+ IOFSwitch sw = provider.getSwitches().get(dpid);
+ if (sw == null) {
+ log.error("Invalid dpid");
+ return false;
+ }
+
+ return pusher.resume(sw);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/SendBarrierResource.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/SendBarrierResource.java
new file mode 100644
index 0000000..236cc85
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/SendBarrierResource.java
@@ -0,0 +1,41 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import net.floodlightcontroller.core.IOFSwitch;
+
+import org.openflow.protocol.OFBarrierReply;
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+
+/**
+ * FlowProgrammer REST API implementation: Send barrier message to switch.
+ * <p/>
+ * GET /wm/fprog/pusher/barrier/{dpid}/json"
+ */
+public class SendBarrierResource extends PusherResource {
+ /**
+ * Implement the API.
+ *
+ * @return true if succeeded, false if failed.
+ */
+ @Get("json")
+ public OFBarrierReply retrieve() {
+ if (!init()) {
+ return null;
+ }
+ long dpid;
+ try {
+ dpid = HexString.toLong((String) getRequestAttributes().get("dpid"));
+ } catch (NumberFormatException e) {
+ log.error("Invalid number format");
+ return null;
+ }
+
+ IOFSwitch sw = provider.getSwitches().get(dpid);
+ if (sw == null) {
+ log.error("Invalid dpid");
+ return null;
+ }
+
+ return pusher.barrier(sw);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/SetPushRateResource.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/SetPushRateResource.java
new file mode 100644
index 0000000..11bbd28
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/SetPushRateResource.java
@@ -0,0 +1,47 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import net.floodlightcontroller.core.IOFSwitch;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+
+/**
+ * FlowProgrammer REST API implementation: Set sending rate to the switch.
+ * <p/>
+ * GET /wm/fprog/pusher/setrate/{dpid}/{rate}/json"
+ */
+public class SetPushRateResource extends PusherResource {
+
+ /**
+ * Implement the API.
+ *
+ * @return true if succeeded, false if failed.
+ */
+ @Get("json")
+ public boolean retrieve() {
+ if (!init()) {
+ return false;
+ }
+
+ long dpid;
+ long rate;
+
+ try {
+ dpid = HexString.toLong((String) getRequestAttributes().get("dpid"));
+ rate = Long.valueOf((String) getRequestAttributes().get("rate"));
+ } catch (NumberFormatException e) {
+ log.error("Invalid number format");
+ return false;
+ }
+
+ IOFSwitch sw = provider.getSwitches().get(dpid);
+ if (sw == null) {
+ log.error("Invalid dpid");
+ return false;
+ }
+
+ pusher.setRate(sw, rate);
+
+ return true;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/SuspendPusherResource.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/SuspendPusherResource.java
new file mode 100644
index 0000000..4d651d3
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/SuspendPusherResource.java
@@ -0,0 +1,46 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import net.floodlightcontroller.core.IOFSwitch;
+
+import org.openflow.util.HexString;
+import org.restlet.resource.Get;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * FlowProgrammer REST API implementation: Suspend sending message to switch.
+ * <p/>
+ * GET /wm/fprog/pusher/suspend/{dpid}/json"
+ */
+public class SuspendPusherResource extends PusherResource {
+
+ protected final static Logger log = LoggerFactory.getLogger(SetPushRateResource.class);
+
+ /**
+ * Implement the API.
+ *
+ * @return true if succeeded, false if failed.
+ */
+ @Get("json")
+ public boolean retrieve() {
+ if (!init()) {
+ return false;
+ }
+
+ long dpid;
+ try {
+ dpid = HexString.toLong((String) getRequestAttributes().get("dpid"));
+ } catch (NumberFormatException e) {
+ log.error("Invalid number format");
+ return false;
+ }
+
+ IOFSwitch sw = provider.getSwitches().get(dpid);
+ if (sw == null) {
+ log.error("Invalid dpid");
+ return false;
+ }
+
+ return pusher.suspend(sw);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/flowprogrammer/web/SynchronizerResource.java b/src/main/java/net/onrc/onos/core/flowprogrammer/web/SynchronizerResource.java
new file mode 100644
index 0000000..429d52f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/flowprogrammer/web/SynchronizerResource.java
@@ -0,0 +1,35 @@
+package net.onrc.onos.core.flowprogrammer.web;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.onrc.onos.core.flowprogrammer.IFlowSyncService;
+
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SynchronizerResource extends ServerResource {
+ protected final static Logger log = LoggerFactory.getLogger(SynchronizerResource.class);
+
+ protected IFloodlightProviderService provider;
+ protected IFlowSyncService synchronizer;
+
+ protected boolean init() {
+ provider = (IFloodlightProviderService)
+ getContext().getAttributes().
+ get(IFloodlightProviderService.class.getCanonicalName());
+ if (provider == null) {
+ log.debug("ONOS FloodlightProvider not found");
+ return false;
+ }
+
+ synchronizer = (IFlowSyncService)
+ getContext().getAttributes().
+ get(IFlowSyncService.class.getCanonicalName());
+ if (synchronizer == null) {
+ log.debug("ONOS FlowSyncService not found");
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/intent/Action.java b/src/main/java/net/onrc/onos/core/intent/Action.java
index feea8e4..71c7183 100644
--- a/src/main/java/net/onrc/onos/core/intent/Action.java
+++ b/src/main/java/net/onrc/onos/core/intent/Action.java
@@ -1,6 +1,6 @@
package net.onrc.onos.core.intent;
-import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.core.util.FlowEntryAction;
/**
*
diff --git a/src/main/java/net/onrc/onos/core/intent/FlowEntry.java b/src/main/java/net/onrc/onos/core/intent/FlowEntry.java
index 7c9c696..570f4ef 100644
--- a/src/main/java/net/onrc/onos/core/intent/FlowEntry.java
+++ b/src/main/java/net/onrc/onos/core/intent/FlowEntry.java
@@ -5,10 +5,10 @@
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.core.intent.IntentOperation.Operator;
-import net.onrc.onos.ofcontroller.util.Dpid;
-import net.onrc.onos.ofcontroller.util.FlowEntryActions;
-import net.onrc.onos.ofcontroller.util.FlowEntryId;
-import net.onrc.onos.ofcontroller.util.FlowEntryUserState;
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.FlowEntryActions;
+import net.onrc.onos.core.util.FlowEntryId;
+import net.onrc.onos.core.util.FlowEntryUserState;
/**
*
@@ -48,8 +48,8 @@
operator = op;
}
- public net.onrc.onos.ofcontroller.util.FlowEntry getFlowEntry() {
- net.onrc.onos.ofcontroller.util.FlowEntry entry = new net.onrc.onos.ofcontroller.util.FlowEntry();
+ public net.onrc.onos.core.util.FlowEntry getFlowEntry() {
+ net.onrc.onos.core.util.FlowEntry entry = new net.onrc.onos.core.util.FlowEntry();
entry.setDpid(new Dpid(sw));
entry.setFlowEntryId(new FlowEntryId(hashCode())); // naive, but useful for now
entry.setFlowEntryMatch(match.getFlowEntryMatch());
diff --git a/src/main/java/net/onrc/onos/core/intent/ForwardAction.java b/src/main/java/net/onrc/onos/core/intent/ForwardAction.java
index 2294d65..482d3f1 100644
--- a/src/main/java/net/onrc/onos/core/intent/ForwardAction.java
+++ b/src/main/java/net/onrc/onos/core/intent/ForwardAction.java
@@ -1,6 +1,6 @@
package net.onrc.onos.core.intent;
-import net.onrc.onos.ofcontroller.util.FlowEntryAction;
+import net.onrc.onos.core.util.FlowEntryAction;
/**
*
@@ -22,7 +22,7 @@
@Override
public FlowEntryAction getFlowEntryAction() {
FlowEntryAction action = new FlowEntryAction();
- action.setActionOutput(new net.onrc.onos.ofcontroller.util.Port((short) dstPort));
+ action.setActionOutput(new net.onrc.onos.core.util.Port((short) dstPort));
return action;
}
diff --git a/src/main/java/net/onrc/onos/core/intent/Match.java b/src/main/java/net/onrc/onos/core/intent/Match.java
index 0d5d38a..7ddf429 100644
--- a/src/main/java/net/onrc/onos/core/intent/Match.java
+++ b/src/main/java/net/onrc/onos/core/intent/Match.java
@@ -5,7 +5,7 @@
import net.floodlightcontroller.util.MACAddress;
//import net.onrc.onos.ofcontroller.networkgraph.Port;
//import net.onrc.onos.ofcontroller.networkgraph.Switch;
-import net.onrc.onos.ofcontroller.util.FlowEntryMatch;
+import net.onrc.onos.core.util.FlowEntryMatch;
/**
*
@@ -45,7 +45,7 @@
FlowEntryMatch match = new FlowEntryMatch();
match.enableSrcMac(srcMac);
match.enableDstMac(dstMac);
- match.enableInPort(new net.onrc.onos.ofcontroller.util.Port((short) srcPort));
+ match.enableInPort(new net.onrc.onos.core.util.Port((short) srcPort));
return match;
}
diff --git a/src/main/java/net/onrc/onos/core/intent/ShortestPathIntent.java b/src/main/java/net/onrc/onos/core/intent/ShortestPathIntent.java
index 9a39627..f4bb18c 100644
--- a/src/main/java/net/onrc/onos/core/intent/ShortestPathIntent.java
+++ b/src/main/java/net/onrc/onos/core/intent/ShortestPathIntent.java
@@ -1,7 +1,7 @@
package net.onrc.onos.core.intent;
import net.floodlightcontroller.util.MACAddress;
-import net.onrc.onos.ofcontroller.util.Dpid;
+import net.onrc.onos.core.util.Dpid;
/**
* @author Toshio Koide (t-koide@onlab.us)
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PersistIntent.java b/src/main/java/net/onrc/onos/core/intent/runtime/PersistIntent.java
index 134f437..103d131 100755
--- a/src/main/java/net/onrc/onos/core/intent/runtime/PersistIntent.java
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PersistIntent.java
@@ -18,9 +18,9 @@
import net.onrc.onos.core.datastore.IKVTable;
import net.onrc.onos.core.datastore.ObjectExistsException;
import net.onrc.onos.core.intent.IntentOperationList;
+import net.onrc.onos.core.util.serializers.KryoFactory;
import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
-import net.onrc.onos.ofcontroller.util.serializers.KryoFactory;
import net.onrc.onos.registry.controller.IControllerRegistryService;
import net.onrc.onos.registry.controller.IdBlock;
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallModule.java b/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallModule.java
index bbc303d..eded26a 100644
--- a/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallModule.java
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallModule.java
@@ -17,16 +17,17 @@
import net.onrc.onos.core.datagrid.IDatagridService;
import net.onrc.onos.core.datagrid.IEventChannel;
import net.onrc.onos.core.datagrid.IEventChannelListener;
+import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
import net.onrc.onos.core.intent.FlowEntry;
import net.onrc.onos.core.intent.IntentOperation;
import net.onrc.onos.core.intent.IntentOperationList;
import net.onrc.onos.core.intent.Intent.IntentState;
-import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
import net.onrc.onos.ofcontroller.networkgraph.INetworkGraphService;
//import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallRuntime.java b/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallRuntime.java
index e3bdc41..9f4a4d5 100644
--- a/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallRuntime.java
+++ b/src/main/java/net/onrc/onos/core/intent/runtime/PlanInstallRuntime.java
@@ -11,10 +11,10 @@
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.internal.OFMessageFuture;
+import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
import net.onrc.onos.core.intent.FlowEntry;
-import net.onrc.onos.ofcontroller.flowprogrammer.IFlowPusherService;
//import net.onrc.onos.ofcontroller.networkgraph.NetworkGraph;
-import net.onrc.onos.ofcontroller.util.Pair;
+import net.onrc.onos.core.util.Pair;
import org.openflow.protocol.OFBarrierReply;
import org.slf4j.Logger;
@@ -102,7 +102,7 @@
FlowModCount.startCount();
for(Set<FlowEntry> phase : plan) {
- Set<Pair<IOFSwitch, net.onrc.onos.ofcontroller.util.FlowEntry>> entries = new HashSet<>();
+ Set<Pair<IOFSwitch, net.onrc.onos.core.util.FlowEntry>> entries = new HashSet<>();
Set<IOFSwitch> modifiedSwitches = new HashSet<>();
long step1 = System.nanoTime();
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscovery.java b/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscovery.java
new file mode 100644
index 0000000..ba35850
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscovery.java
@@ -0,0 +1,172 @@
+package net.onrc.onos.core.linkdiscovery;
+
+import net.floodlightcontroller.core.IUpdate;
+
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.codehaus.jackson.map.ser.std.ToStringSerializer;
+import org.openflow.util.HexString;
+
+public interface ILinkDiscovery {
+
+ @JsonSerialize(using=ToStringSerializer.class)
+ public enum UpdateOperation {
+ LINK_ADDED("Link Added"), // Operation Added by ONOS
+ LINK_UPDATED("Link Updated"),
+ LINK_REMOVED("Link Removed"),
+ SWITCH_UPDATED("Switch Updated"),
+ SWITCH_REMOVED("Switch Removed"),
+ PORT_UP("Port Up"),
+ PORT_DOWN("Port Down");
+
+ private String value;
+ UpdateOperation(String v) {
+ value = v;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+
+ public class LDUpdate implements IUpdate{
+ protected long src;
+ protected short srcPort;
+ protected long dst;
+ protected short dstPort;
+ protected SwitchType srcType;
+ protected LinkType type;
+ protected UpdateOperation operation;
+
+ public LDUpdate(long src, short srcPort,
+ long dst, short dstPort,
+ ILinkDiscovery.LinkType type,
+ UpdateOperation operation) {
+ this.src = src;
+ this.srcPort = srcPort;
+ this.dst = dst;
+ this.dstPort = dstPort;
+ this.type = type;
+ this.operation = operation;
+ }
+
+ public LDUpdate(LDUpdate old) {
+ this.src = old.src;
+ this.srcPort = old.srcPort;
+ this.dst = old.dst;
+ this.dstPort = old.dstPort;
+ this.srcType = old.srcType;
+ this.type = old.type;
+ this.operation = old.operation;
+ }
+
+ // For updtedSwitch(sw)
+ public LDUpdate(long switchId, SwitchType stype, UpdateOperation oper ){
+ this.operation = oper;
+ this.src = switchId;
+ this.srcType = stype;
+ }
+
+ // For port up or port down.
+ public LDUpdate(long sw, short port, UpdateOperation operation) {
+ this.src = sw;
+ this.srcPort = port;
+ this.operation = operation;
+ }
+
+ public long getSrc() {
+ return src;
+ }
+
+ public short getSrcPort() {
+ return srcPort;
+ }
+
+ public long getDst() {
+ return dst;
+ }
+
+ public short getDstPort() {
+ return dstPort;
+ }
+
+ public SwitchType getSrcType() {
+ return srcType;
+ }
+
+ public LinkType getType() {
+ return type;
+ }
+
+ public UpdateOperation getOperation() {
+ return operation;
+ }
+
+ public void setOperation(UpdateOperation operation) {
+ this.operation = operation;
+ }
+
+ @Override
+ public String toString() {
+ switch (operation) {
+ case LINK_ADDED:
+ case LINK_REMOVED:
+ case LINK_UPDATED:
+ return "LDUpdate [operation=" + operation +
+ ", src=" + HexString.toHexString(src)
+ + ", srcPort=" + srcPort
+ + ", dst=" + HexString.toHexString(dst)
+ + ", dstPort=" + dstPort
+ + ", type=" + type + "]";
+ case PORT_DOWN:
+ case PORT_UP:
+ return "LDUpdate [operation=" + operation +
+ ", src=" + HexString.toHexString(src)
+ + ", srcPort=" + srcPort + "]";
+ case SWITCH_REMOVED:
+ case SWITCH_UPDATED:
+ return "LDUpdate [operation=" + operation +
+ ", src=" + HexString.toHexString(src) + "]";
+ default:
+ return "LDUpdate: Unknown update.";
+ }
+ }
+
+ @Override
+ public void dispatch() {
+ // TODO Auto-generated method stub
+
+ }
+ }
+
+ public enum SwitchType {
+ BASIC_SWITCH, CORE_SWITCH
+ };
+
+ public enum LinkType {
+ INVALID_LINK {
+ @Override
+ public String toString() {
+ return "invalid";
+ }
+ },
+ DIRECT_LINK{
+ @Override
+ public String toString() {
+ return "internal";
+ }
+ },
+ MULTIHOP_LINK {
+ @Override
+ public String toString() {
+ return "external";
+ }
+ },
+ TUNNEL {
+ @Override
+ public String toString() {
+ return "tunnel";
+ }
+ }
+ };
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscoveryListener.java b/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscoveryListener.java
new file mode 100644
index 0000000..53721d8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscoveryListener.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright 2011, Big Switch Networks, Inc.
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ **/
+
+package net.onrc.onos.core.linkdiscovery;
+
+public interface ILinkDiscoveryListener extends ILinkDiscovery{
+
+ public void linkDiscoveryUpdate(LDUpdate update);
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscoveryService.java
new file mode 100644
index 0000000..6d41532
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/ILinkDiscoveryService.java
@@ -0,0 +1,82 @@
+/**
+* Copyright 2011, Big Switch Networks, Inc.
+* Originally created by David Erickson, Stanford University
+*
+* Licensed under the Apache License, Version 2.0 (the "License"); you may
+* not use this file except in compliance with the License. You may obtain
+* a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations
+* under the License.
+**/
+
+package net.onrc.onos.core.linkdiscovery;
+
+import java.util.Map;
+import java.util.Set;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+
+
+public interface ILinkDiscoveryService extends IFloodlightService {
+ /**
+ * Retrieves a map of all known link connections between OpenFlow switches
+ * and the associated info (valid time, port states) for the link.
+ */
+ public Map<Link, LinkInfo> getLinks();
+
+ /**
+ * Returns link type of a given link
+ * @param info
+ * @return
+ */
+ public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info);
+
+ /**
+ * Returns an unmodifiable map from switch id to a set of all links with it
+ * as an endpoint.
+ */
+ public Map<Long, Set<Link>> getSwitchLinks();
+
+ /**
+ * Adds a listener to listen for ILinkDiscoveryService messages
+ * @param listener The listener that wants the notifications
+ */
+ public void addListener(ILinkDiscoveryListener listener);
+
+ /**
+ * Retrieves a set of all switch ports on which lldps are suppressed.
+ */
+ public Set<NodePortTuple> getSuppressLLDPsInfo();
+
+ /**
+ * Adds a switch port to suppress lldp set
+ */
+ public void AddToSuppressLLDPs(long sw, short port);
+
+ /**
+ * Removes a switch port from suppress lldp set
+ */
+ public void RemoveFromSuppressLLDPs(long sw, short port);
+
+ /**
+ * Get the set of quarantined ports on a switch
+ */
+ public Set<Short> getQuarantinedPorts(long sw);
+
+ /**
+ * Get the status of auto port fast feature.
+ */
+ public boolean isAutoPortFastFeature();
+
+ /**
+ * Set the state for auto port fast feature.
+ * @param autoPortFastFeature
+ */
+ public void setAutoPortFastFeature(boolean autoPortFastFeature);
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/Link.java b/src/main/java/net/onrc/onos/core/linkdiscovery/Link.java
new file mode 100755
index 0000000..eb43304
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/Link.java
@@ -0,0 +1,122 @@
+/**
+* Copyright 2011, Big Switch Networks, Inc.
+* Originally created by David Erickson, Stanford University
+*
+* Licensed under the Apache License, Version 2.0 (the "License"); you may
+* not use this file except in compliance with the License. You may obtain
+* a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations
+* under the License.
+**/
+
+package net.onrc.onos.core.linkdiscovery;
+
+import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
+import net.floodlightcontroller.core.web.serializers.UShortSerializer;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.openflow.util.HexString;
+
+public class Link {
+ private long src;
+ private short srcPort;
+ private long dst;
+ private short dstPort;
+
+
+ public Link(long srcId, short srcPort, long dstId, short dstPort) {
+ this.src = srcId;
+ this.srcPort = srcPort;
+ this.dst = dstId;
+ this.dstPort = dstPort;
+ }
+
+ // Convenience method
+ public Link(long srcId, int srcPort, long dstId, int dstPort) {
+ this.src = srcId;
+ this.srcPort = (short) srcPort;
+ this.dst = dstId;
+ this.dstPort = (short) dstPort;
+ }
+
+ @JsonProperty("src-switch")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getSrc() {
+ return src;
+ }
+
+ @JsonProperty("src-port")
+ @JsonSerialize(using=UShortSerializer.class)
+ public short getSrcPort() {
+ return srcPort;
+ }
+
+ @JsonProperty("dst-switch")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getDst() {
+ return dst;
+ }
+ @JsonProperty("dst-port")
+ @JsonSerialize(using=UShortSerializer.class)
+ public short getDstPort() {
+ return dstPort;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (dst ^ (dst >>> 32));
+ result = prime * result + dstPort;
+ result = prime * result + (int) (src ^ (src >>> 32));
+ result = prime * result + srcPort;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Link other = (Link) obj;
+ if (dst != other.dst)
+ return false;
+ if (dstPort != other.dstPort)
+ return false;
+ if (src != other.src)
+ return false;
+ if (srcPort != other.srcPort)
+ return false;
+ return true;
+ }
+
+
+ @Override
+ public String toString() {
+ return "Link [src=" + HexString.toHexString(this.src)
+ + " outPort="
+ + (srcPort & 0xffff)
+ + ", dst=" + HexString.toHexString(this.dst)
+ + ", inPort="
+ + (dstPort & 0xffff)
+ + "]";
+ }
+
+ public String toKeyString() {
+ return (HexString.toHexString(this.src) + "|" +
+ (this.srcPort & 0xffff) + "|" +
+ HexString.toHexString(this.dst) + "|" +
+ (this.dstPort & 0xffff) );
+ }
+}
+
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/LinkInfo.java b/src/main/java/net/onrc/onos/core/linkdiscovery/LinkInfo.java
new file mode 100644
index 0000000..831c63b
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/LinkInfo.java
@@ -0,0 +1,182 @@
+/**
+* Copyright 2011, Big Switch Networks, Inc.* Originally created by David Erickson, Stanford University
+** Licensed under the Apache License, Version 2.0 (the "License"); you may
+* not use this file except in compliance with the License. You may obtain
+* a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+* License for the specific language governing permissions and limitations
+* under the License.
+**/
+
+package net.onrc.onos.core.linkdiscovery;
+
+import net.onrc.onos.core.linkdiscovery.ILinkDiscovery.LinkType;
+
+import org.openflow.protocol.OFPhysicalPort.OFPortState;
+
+public class LinkInfo {
+
+ public LinkInfo(Long firstSeenTime,
+ Long lastLldpReceivedTime,
+ Long lastBddpReceivedTime,
+ int srcPortState,
+ int dstPortState) {
+ super();
+ this.srcPortState = srcPortState;
+ this.dstPortState = dstPortState;
+ this.firstSeenTime = firstSeenTime;
+ this.lastLldpReceivedTime = lastLldpReceivedTime;
+ this.lastBddpReceivedTime = lastBddpReceivedTime;
+ }
+
+ protected Integer srcPortState;
+ protected Integer dstPortState;
+ protected Long firstSeenTime;
+ protected Long lastLldpReceivedTime; /* Standard LLLDP received time */
+ protected Long lastBddpReceivedTime; /* Modified LLDP received time */
+
+ /** The port states stored here are topology's last knowledge of
+ * the state of the port. This mostly mirrors the state
+ * maintained in the port list in IOFSwitch (i.e. the one returned
+ * from getPort), except that during a port status message the
+ * IOFSwitch port state will already have been updated with the
+ * new port state, so topology needs to keep its own copy so that
+ * it can determine if the port state has changed and therefore
+ * requires the new state to be written to storage.
+ */
+
+
+
+ public boolean linkStpBlocked() {
+ return ((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue()) ||
+ ((dstPortState & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue());
+ }
+
+ public Long getFirstSeenTime() {
+ return firstSeenTime;
+ }
+
+ public void setFirstSeenTime(Long firstSeenTime) {
+ this.firstSeenTime = firstSeenTime;
+ }
+
+ public Long getUnicastValidTime() {
+ return lastLldpReceivedTime;
+ }
+
+ public void setUnicastValidTime(Long unicastValidTime) {
+ this.lastLldpReceivedTime = unicastValidTime;
+ }
+
+ public Long getMulticastValidTime() {
+ return lastBddpReceivedTime;
+ }
+
+ public void setMulticastValidTime(Long multicastValidTime) {
+ this.lastBddpReceivedTime = multicastValidTime;
+ }
+
+ public Integer getSrcPortState() {
+ return srcPortState;
+ }
+
+ public void setSrcPortState(Integer srcPortState) {
+ this.srcPortState = srcPortState;
+ }
+
+ public Integer getDstPortState() {
+ return dstPortState;
+ }
+
+ public void setDstPortState(int dstPortState) {
+ this.dstPortState = dstPortState;
+ }
+
+ public LinkType getLinkType() {
+ if (lastLldpReceivedTime != null) {
+ return LinkType.DIRECT_LINK;
+ } else if (lastBddpReceivedTime != null) {
+ return LinkType.MULTIHOP_LINK;
+ }
+ return LinkType.INVALID_LINK;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 5557;
+ int result = 1;
+ result = prime * result + ((firstSeenTime == null) ? 0 : firstSeenTime.hashCode());
+ result = prime * result + ((lastLldpReceivedTime == null) ? 0 : lastLldpReceivedTime.hashCode());
+ result = prime * result + ((lastBddpReceivedTime == null) ? 0 : lastBddpReceivedTime.hashCode());
+ result = prime * result + ((srcPortState == null) ? 0 : srcPortState.hashCode());
+ result = prime * result + ((dstPortState == null) ? 0 : dstPortState.hashCode());
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (!(obj instanceof LinkInfo))
+ return false;
+ LinkInfo other = (LinkInfo) obj;
+
+ if (firstSeenTime == null) {
+ if (other.firstSeenTime != null)
+ return false;
+ } else if (!firstSeenTime.equals(other.firstSeenTime))
+ return false;
+
+ if (lastLldpReceivedTime == null) {
+ if (other.lastLldpReceivedTime != null)
+ return false;
+ } else if (!lastLldpReceivedTime.equals(other.lastLldpReceivedTime))
+ return false;
+
+ if (lastBddpReceivedTime == null) {
+ if (other.lastBddpReceivedTime != null)
+ return false;
+ } else if (!lastBddpReceivedTime.equals(other.lastBddpReceivedTime))
+ return false;
+
+ if (srcPortState == null) {
+ if (other.srcPortState != null)
+ return false;
+ } else if (!srcPortState.equals(other.srcPortState))
+ return false;
+
+ if (dstPortState == null) {
+ if (other.dstPortState != null)
+ return false;
+ } else if (!dstPortState.equals(other.dstPortState))
+ return false;
+
+ return true;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "LinkInfo [unicastValidTime=" + ((lastLldpReceivedTime == null) ? "null" : lastLldpReceivedTime)
+ + ", multicastValidTime=" + ((lastBddpReceivedTime == null) ? "null" : lastBddpReceivedTime)
+ + ", srcPortState=" + ((srcPortState == null) ? "null" : srcPortState)
+ + ", dstPortState=" + ((dstPortState == null) ? "null" : srcPortState)
+ + "]";
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/NodePortTuple.java b/src/main/java/net/onrc/onos/core/linkdiscovery/NodePortTuple.java
new file mode 100644
index 0000000..8d7ba19
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/NodePortTuple.java
@@ -0,0 +1,90 @@
+package net.onrc.onos.core.linkdiscovery;
+
+import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
+import net.floodlightcontroller.core.web.serializers.UShortSerializer;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.openflow.util.HexString;
+
+/**
+ * A NodePortTuple is similar to a SwitchPortTuple
+ * but it only stores IDs instead of references
+ * to the actual objects.
+ * @author srini
+ */
+public class NodePortTuple {
+ protected long nodeId; // switch DPID
+ protected short portId; // switch port id
+
+ /**
+ * Creates a NodePortTuple
+ * @param nodeId The DPID of the switch
+ * @param portId The port of the switch
+ */
+ public NodePortTuple(long nodeId, short portId) {
+ this.nodeId = nodeId;
+ this.portId = portId;
+ }
+
+ public NodePortTuple(long nodeId, int portId) {
+ this.nodeId = nodeId;
+ this.portId = (short) portId;
+ }
+
+ @JsonProperty("switch")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getNodeId() {
+ return nodeId;
+ }
+ public void setNodeId(long nodeId) {
+ this.nodeId = nodeId;
+ }
+ @JsonProperty("port")
+ @JsonSerialize(using=UShortSerializer.class)
+ public short getPortId() {
+ return portId;
+ }
+ public void setPortId(short portId) {
+ this.portId = portId;
+ }
+
+ public String toString() {
+ return "[id=" + HexString.toHexString(nodeId) + ", port=" + new Short(portId) + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (nodeId ^ (nodeId >>> 32));
+ result = prime * result + portId;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ NodePortTuple other = (NodePortTuple) obj;
+ if (nodeId != other.nodeId)
+ return false;
+ if (portId != other.portId)
+ return false;
+ return true;
+ }
+
+ /**
+ * API to return a String value formed wtih NodeID and PortID
+ * The portID is a 16-bit field, so mask it as an integer to get full
+ * positive value
+ * @return
+ */
+ public String toKeyString() {
+ return (HexString.toHexString(nodeId)+ "|" + (portId & 0xffff));
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/README b/src/main/java/net/onrc/onos/core/linkdiscovery/README
new file mode 100644
index 0000000..3cec58d
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/README
@@ -0,0 +1,8 @@
+Note about this directory
+=========================
+
+This directory contains link discovery manager and it's related module derived from Flood Light v0.9.0.
+Many of the code is unmodified from it's original state, but they had to be moved due to package visibility etc.
+
+Compare with floodlight's linkdiscovery directory to see what was modified from it's original code.
+
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologyCluster.java b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologyCluster.java
new file mode 100644
index 0000000..172714c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologyCluster.java
@@ -0,0 +1,43 @@
+package net.onrc.onos.core.linkdiscovery.internal;
+
+import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/***
+ * Topology Cluster merge/split event history related classes and members
+ * @author subrata
+ *
+ */
+public class EventHistoryTopologyCluster {
+ // The following fields are not stored as String to save memory
+ // They should be converted to appropriate human-readable strings by
+ // the front end (e.g. in cli in Python)
+ public long dpid;
+ public long clusterIdOld; // Switch with dpid moved from cluster x to y
+ public long clusterIdNew;
+ public String reason;
+
+ @JsonProperty("Switch")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getDpid() {
+ return dpid;
+ }
+ @JsonProperty("OldClusterId")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getClusterIdOld() {
+ return clusterIdOld;
+ }
+ @JsonProperty("NewClusterId")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getClusterIdNew() {
+ return clusterIdNew;
+ }
+ @JsonProperty("Reason")
+ public String getReason() {
+ return reason;
+ }
+
+
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologyLink.java b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologyLink.java
new file mode 100644
index 0000000..378859f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologyLink.java
@@ -0,0 +1,62 @@
+package net.onrc.onos.core.linkdiscovery.internal;
+
+import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/***
+ * Topology link up/down event history related classes and members
+ * @author subrata
+ *
+ */
+public class EventHistoryTopologyLink {
+ // The following fields are not stored as String to save memory
+ // They should be converted to appropriate human-readable strings by
+ // the front end (e.g. in cli in Python)
+ public long srcSwDpid;
+ public long dstSwDpid;
+ public int srcPortState;
+ public int dstPortState;
+ public int srcSwport;
+ public int dstSwport;
+ public String linkType;
+ public String reason;
+
+ @JsonProperty("Source-Switch")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getSrcSwDpid() {
+ return srcSwDpid;
+ }
+ @JsonProperty("Dest-Switch")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getDstSwDpid() {
+ return dstSwDpid;
+ }
+ @JsonProperty("SrcPortState")
+ public int getSrcPortState() {
+ return srcPortState;
+ }
+ @JsonProperty("DstPortState")
+ public int getDstPortState() {
+ return dstPortState;
+ }
+ @JsonProperty("SrcPort")
+ public int getSrcSwport() {
+ return srcSwport;
+ }
+ @JsonProperty("DstPort")
+ public int getDstSwport() {
+ return dstSwport;
+ }
+ @JsonProperty("LinkType")
+ public String getLinkType() {
+ return linkType;
+ }
+ @JsonProperty("Reason")
+ public String getReason() {
+ return reason;
+ }
+
+
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologySwitch.java b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologySwitch.java
new file mode 100644
index 0000000..b659be7
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/EventHistoryTopologySwitch.java
@@ -0,0 +1,43 @@
+package net.onrc.onos.core.linkdiscovery.internal;
+
+import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
+import net.floodlightcontroller.core.web.serializers.IPv4Serializer;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/***
+ * Topology Switch event history related classes and members
+ * @author subrata
+ *
+ */
+public class EventHistoryTopologySwitch {
+ // The following fields are not stored as String to save memory
+ // They should be converted to appropriate human-readable strings by
+ // the front end (e.g. in cli in Python)
+ public long dpid;
+ public int ipv4Addr;
+ public int l4Port;
+ public String reason;
+
+ @JsonProperty("Switch")
+ @JsonSerialize(using=DPIDSerializer.class)
+ public long getDpid() {
+ return dpid;
+ }
+ @JsonProperty("IpAddr")
+ @JsonSerialize(using=IPv4Serializer.class)
+ public int getIpv4Addr() {
+ return ipv4Addr;
+ }
+ @JsonProperty("Port")
+ public int getL4Port() {
+ return l4Port;
+ }
+ @JsonProperty("Reason")
+ public String getReason() {
+ return reason;
+ }
+
+
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/LinkDiscoveryManager.java
new file mode 100644
index 0000000..a9879f2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -0,0 +1,1816 @@
+/**
+ * Copyright 2011, Big Switch Networks, Inc.
+ * Originally created by David Erickson, Stanford University
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ **/
+
+package net.onrc.onos.core.linkdiscovery.internal;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+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.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.IOFSwitchListener;
+import net.floodlightcontroller.core.annotations.LogMessageCategory;
+import net.floodlightcontroller.core.annotations.LogMessageDoc;
+import net.floodlightcontroller.core.annotations.LogMessageDocs;
+import net.floodlightcontroller.core.internal.OFSwitchImpl;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.core.util.SingletonTask;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.floodlightcontroller.util.EventHistory;
+import net.floodlightcontroller.util.EventHistory.EvAction;
+import net.onrc.onos.core.linkdiscovery.ILinkDiscovery;
+import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryListener;
+import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
+import net.onrc.onos.core.linkdiscovery.Link;
+import net.onrc.onos.core.linkdiscovery.LinkInfo;
+import net.onrc.onos.core.linkdiscovery.NodePortTuple;
+import net.onrc.onos.core.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.onrc.onos.core.linkdiscovery.ILinkDiscovery.UpdateOperation;
+import net.onrc.onos.core.linkdiscovery.web.LinkDiscoveryWebRoutable;
+import net.onrc.onos.core.main.IOnosRemoteSwitch;
+import net.onrc.onos.packet.BSN;
+import net.onrc.onos.packet.Ethernet;
+import net.onrc.onos.packet.IPv4;
+import net.onrc.onos.packet.LLDP;
+import net.onrc.onos.packet.LLDPTLV;
+import net.onrc.onos.registry.controller.IControllerRegistryService;
+
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFPhysicalPort;
+import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
+import org.openflow.protocol.OFPhysicalPort.OFPortState;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFPortStatus;
+import org.openflow.protocol.OFPortStatus.OFPortReason;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class sends out LLDP messages containing the sending switch's datapath
+ * id as well as the outgoing port number. Received LLrescDP messages that
+ * match a known switch cause a new LinkTuple to be created according to the
+ * invariant rules listed below. This new LinkTuple is also passed to routing
+ * if it exists to trigger updates.
+ *
+ * This class also handles removing links that are associated to switch ports
+ * that go down, and switches that are disconnected.
+ *
+ * Invariants:
+ * -portLinks and switchLinks will not contain empty Sets outside of
+ * critical sections
+ * -portLinks contains LinkTuples where one of the src or dst
+ * SwitchPortTuple matches the map key
+ * -switchLinks contains LinkTuples where one of the src or dst
+ * SwitchPortTuple's id matches the switch id
+ * -Each LinkTuple will be indexed into switchLinks for both
+ * src.id and dst.id, and portLinks for each src and dst
+ * -The updates queue is only added to from within a held write lock
+ */
+@LogMessageCategory("Network Topology")
+public class LinkDiscoveryManager
+implements IOFMessageListener, IOFSwitchListener,
+ILinkDiscoveryService, IFloodlightModule {
+ protected IFloodlightProviderService controller;
+ protected final static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class);
+
+ protected IFloodlightProviderService floodlightProvider;
+ protected IThreadPoolService threadPool;
+ protected IRestApiService restApi;
+ // Registry Service for ONOS
+ protected IControllerRegistryService registryService;
+
+
+ // LLDP and BDDP fields
+ private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
+ HexString.fromHexString("01:80:c2:00:00:0e");
+ private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
+ private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
+
+ // BigSwitch OUI is 5C:16:C7, so 5D:16:C7 is the multicast version
+ // private static final String LLDP_BSN_DST_MAC_STRING = "5d:16:c7:00:00:01";
+ private static final String LLDP_BSN_DST_MAC_STRING = "ff:ff:ff:ff:ff:ff";
+
+
+ // Direction TLVs are used to indicate if the LLDPs were sent
+ // periodically or in response to a recieved LLDP
+ private static final byte TLV_DIRECTION_TYPE = 0x73;
+ private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte
+ private static final byte TLV_DIRECTION_VALUE_FORWARD[] = {0x01};
+ private static final byte TLV_DIRECTION_VALUE_REVERSE[] = {0x02};
+ private static final LLDPTLV forwardTLV
+ = new LLDPTLV().
+ setType(TLV_DIRECTION_TYPE).
+ setLength(TLV_DIRECTION_LENGTH).
+ setValue(TLV_DIRECTION_VALUE_FORWARD);
+
+ private static final LLDPTLV reverseTLV
+ = new LLDPTLV().
+ setType(TLV_DIRECTION_TYPE).
+ setLength(TLV_DIRECTION_LENGTH).
+ setValue(TLV_DIRECTION_VALUE_REVERSE);
+
+ // Link discovery task details.
+ protected SingletonTask discoveryTask;
+ protected final int DISCOVERY_TASK_INTERVAL = 1;
+ protected final int LINK_TIMEOUT = 35; // original 35 secs, aggressive 5 secs
+ protected final int LLDP_TO_ALL_INTERVAL = 15 ; //original 15 seconds, aggressive 2 secs.
+ protected long lldpClock = 0;
+ // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL.
+ // If we want to identify link failures faster, we could decrease this
+ // value to a small number, say 1 or 2 sec.
+ protected final int LLDP_TO_KNOWN_INTERVAL= 20; // LLDP frequency for known links
+
+ protected LLDPTLV controllerTLV;
+ protected ReentrantReadWriteLock lock;
+ int lldpTimeCount = 0;
+
+ /**
+ * Flag to indicate if automatic port fast is enabled or not.
+ * Default is set to false -- Initialized in the init method as well.
+ */
+ boolean autoPortFastFeature = false;
+
+ /**
+ * Map of remote switches that are not connected to this controller. This
+ * is used to learn remote switches in a distributed controller ONOS.
+ */
+ protected Map<Long, IOnosRemoteSwitch> remoteSwitches;
+
+ /**
+ * Map from link to the most recent time it was verified functioning
+ */
+ protected Map<Link, LinkInfo> links;
+
+ /**
+ * Map from switch id to a set of all links with it as an endpoint
+ */
+ protected Map<Long, Set<Link>> switchLinks;
+
+ /**
+ * Map from a id:port to the set of links containing it as an endpoint
+ */
+ protected Map<NodePortTuple, Set<Link>> portLinks;
+
+ /**
+ * Set of link tuples over which multicast LLDPs are received
+ * and unicast LLDPs are not received.
+ */
+ protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks;
+
+ protected volatile boolean shuttingDown = false;
+
+ /* topology aware components are called in the order they were added to the
+ * the array */
+ protected ArrayList<ILinkDiscoveryListener> linkDiscoveryAware;
+
+ protected class LinkUpdate extends LDUpdate {
+
+ public LinkUpdate(LDUpdate old) {
+ super(old);
+ }
+ @LogMessageDoc(level="ERROR",
+ message="Error in link discovery updates loop",
+ explanation="An unknown error occured while dispatching " +
+ "link update notifications",
+ recommendation=LogMessageDoc.GENERIC_ACTION)
+ @Override
+ public void dispatch() {
+ if (linkDiscoveryAware != null) {
+ if (log.isTraceEnabled()) {
+ log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
+ new Object[]{this.getOperation(),
+ HexString.toHexString(this.getSrc()), this.getSrcPort(),
+ HexString.toHexString(this.getDst()), this.getDstPort(),
+ linkDiscoveryAware});
+ }
+ try {
+ for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained
+ lda.linkDiscoveryUpdate(this);
+ }
+ }
+ catch (Exception e) {
+ log.error("Error in link discovery updates loop", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * List of ports through which LLDP/BDDPs are not sent.
+ */
+ protected Set<NodePortTuple> suppressLinkDiscovery;
+
+ /** A list of ports that are quarantined for discovering links through
+ * them. Data traffic from these ports are not allowed until the ports
+ * are released from quarantine.
+ */
+ protected LinkedBlockingQueue<NodePortTuple> quarantineQueue;
+ protected LinkedBlockingQueue<NodePortTuple> maintenanceQueue;
+ /**
+ * Quarantine task
+ */
+ protected SingletonTask bddpTask;
+ protected final int BDDP_TASK_INTERVAL = 100; // 100 ms.
+ protected final int BDDP_TASK_SIZE = 5; // # of ports per iteration
+
+ /**
+ * Map of broadcast domain ports and the last time a BDDP was either
+ * sent or received on that port.
+ */
+ protected Map<NodePortTuple, Long> broadcastDomainPortTimeMap;
+
+ /**
+ * Get the LLDP sending period in seconds.
+ * @return LLDP sending period in seconds.
+ */
+ public int getLldpFrequency() {
+ return LLDP_TO_KNOWN_INTERVAL;
+ }
+
+ /**
+ * Get the LLDP timeout value in seconds
+ * @return LLDP timeout value in seconds
+ */
+ public int getLldpTimeout() {
+ return LINK_TIMEOUT;
+ }
+
+ public Map<NodePortTuple, Set<Link>> getPortLinks() {
+ return portLinks;
+ }
+
+ @Override
+ public Set<NodePortTuple> getSuppressLLDPsInfo() {
+ return suppressLinkDiscovery;
+ }
+
+ /**
+ * Add a switch port to the suppressed LLDP list.
+ * Remove any known links on the switch port.
+ */
+ @Override
+ public void AddToSuppressLLDPs(long sw, short port)
+ {
+ NodePortTuple npt = new NodePortTuple(sw, port);
+ this.suppressLinkDiscovery.add(npt);
+ deleteLinksOnPort(npt, "LLDP suppressed.");
+ }
+
+ /**
+ * Remove a switch port from the suppressed LLDP list.
+ * Discover links on that switchport.
+ */
+ @Override
+ public void RemoveFromSuppressLLDPs(long sw, short port)
+ {
+ NodePortTuple npt = new NodePortTuple(sw, port);
+ this.suppressLinkDiscovery.remove(npt);
+ discover(npt);
+ }
+
+ public boolean isShuttingDown() {
+ return shuttingDown;
+ }
+
+ public boolean isFastPort(long sw, short port) {
+ return false;
+ }
+
+ @Override
+ public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
+ if (info.getUnicastValidTime() != null) {
+ return ILinkDiscovery.LinkType.DIRECT_LINK;
+ } else if (info.getMulticastValidTime() != null) {
+ return ILinkDiscovery.LinkType.MULTIHOP_LINK;
+ }
+ return ILinkDiscovery.LinkType.INVALID_LINK;
+ }
+
+
+ private boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
+ return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, portNumber));
+ }
+
+ protected void discoverLinks() {
+
+ // timeout known links.
+ timeoutLinks();
+
+ //increment LLDP clock
+ lldpClock = (lldpClock + 1)% LLDP_TO_ALL_INTERVAL;
+
+ if (lldpClock == 0) {
+ log.debug("Sending LLDP out on all ports.");
+ discoverOnAllPorts();
+ }
+ }
+
+
+ /**
+ * Quarantine Ports.
+ */
+ protected class QuarantineWorker implements Runnable {
+ @Override
+ public void run() {
+ try {
+ processBDDPLists();
+ }
+ catch (Exception e) {
+ log.error("Error in quarantine worker thread", e);
+ } finally {
+ bddpTask.reschedule(BDDP_TASK_INTERVAL,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+
+ /**
+ * Add a switch port to the quarantine queue. Schedule the
+ * quarantine task if the quarantine queue was empty before adding
+ * this switch port.
+ * @param npt
+ */
+ protected void addToQuarantineQueue(NodePortTuple npt) {
+ if (quarantineQueue.contains(npt) == false)
+ quarantineQueue.add(npt);
+ }
+
+ /**
+ * Remove a switch port from the quarantine queue.
+ */
+ protected void removeFromQuarantineQueue(NodePortTuple npt) {
+ // Remove all occurrences of the node port tuple from the list.
+ while (quarantineQueue.remove(npt));
+ }
+
+ /**
+ * Add a switch port to maintenance queue.
+ * @param npt
+ */
+ protected void addToMaintenanceQueue(NodePortTuple npt) {
+ // TODO We are not checking if the switch port tuple is already
+ // in the maintenance list or not. This will be an issue for
+ // really large number of switch ports in the network.
+ if (maintenanceQueue.contains(npt) == false)
+ maintenanceQueue.add(npt);
+ }
+
+ /**
+ * Remove a switch port from maintenance queue.
+ * @param npt
+ */
+ protected void removeFromMaintenanceQueue(NodePortTuple npt) {
+ // Remove all occurrences of the node port tuple from the queue.
+ while (maintenanceQueue.remove(npt));
+ }
+
+ /**
+ * This method processes the quarantine list in bursts. The task is
+ * at most once per BDDP_TASK_INTERVAL.
+ * One each call, BDDP_TASK_SIZE number of switch ports are processed.
+ * Once the BDDP packets are sent out through the switch ports, the ports
+ * are removed from the quarantine list.
+ */
+
+ protected void processBDDPLists() {
+ int count = 0;
+ Set<NodePortTuple> nptList = new HashSet<NodePortTuple>();
+
+ while(count < BDDP_TASK_SIZE && quarantineQueue.peek() !=null) {
+ NodePortTuple npt;
+ npt = quarantineQueue.remove();
+ sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
+ nptList.add(npt);
+ count++;
+ }
+
+ count = 0;
+ while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) {
+ NodePortTuple npt;
+ npt = maintenanceQueue.remove();
+ sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, false);
+ count++;
+ }
+
+ for(NodePortTuple npt:nptList) {
+ generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId());
+ }
+ }
+
+ @Override
+ public Set<Short> getQuarantinedPorts(long sw) {
+ Set<Short> qPorts = new HashSet<Short>();
+
+ Iterator<NodePortTuple> iter = quarantineQueue.iterator();
+ while (iter.hasNext()) {
+ NodePortTuple npt = iter.next();
+ if (npt.getNodeId() == sw) {
+ qPorts.add(npt.getPortId());
+ }
+ }
+ return qPorts;
+ }
+
+ private void generateSwitchPortStatusUpdate(long sw, short port) {
+ UpdateOperation operation;
+
+ IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+ if (iofSwitch == null) return;
+
+ OFPhysicalPort ofp = iofSwitch.getPort(port);
+ if (ofp == null) return;
+
+ int srcPortState = ofp.getState();
+ boolean portUp = ((srcPortState &
+ OFPortState.OFPPS_STP_MASK.getValue()) !=
+ OFPortState.OFPPS_STP_BLOCK.getValue());
+
+ if (portUp) operation = UpdateOperation.PORT_UP;
+ else operation = UpdateOperation.PORT_DOWN;
+
+ LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
+
+
+ controller.publishUpdate(update);
+ }
+
+ /**
+ * Send LLDP on known ports
+ */
+ protected void discoverOnKnownLinkPorts() {
+ // Copy the port set.
+ Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
+ nptSet.addAll(portLinks.keySet());
+
+ // Send LLDP from each of them.
+ for(NodePortTuple npt: nptSet) {
+ discover(npt);
+ }
+ }
+
+ protected void discover(NodePortTuple npt) {
+ discover(npt.getNodeId(), npt.getPortId());
+ }
+
+ protected void discover(long sw, short port) {
+ sendDiscoveryMessage(sw, port, true, false);
+ }
+
+ /**
+ * Learn remote switches when running as a distributed controller ONOS
+ */
+ protected IOFSwitch addRemoteSwitch(long sw, short port) {
+ IOnosRemoteSwitch remotesw = null;
+
+ // add a switch if we have not seen it before
+ remotesw = remoteSwitches.get(sw);
+
+ if (remotesw == null) {
+ remotesw = new OFSwitchImpl();
+ remotesw.setupRemoteSwitch(sw);
+ remoteSwitches.put(remotesw.getId(), remotesw);
+ log.debug("addRemoteSwitch(): added fake remote sw {}", remotesw);
+ }
+
+ // add the port if we have not seen it before
+ if (remotesw.getPort(port) == null) {
+ OFPhysicalPort remoteport = new OFPhysicalPort();
+ remoteport.setPortNumber(port);
+ remoteport.setName("fake_" + port);
+ remoteport.setConfig(0);
+ remoteport.setState(0);
+ remotesw.setPort(remoteport);
+ log.debug("addRemoteSwitch(): added fake remote port {} to sw {}", remoteport, remotesw.getId());
+ }
+
+ return remotesw;
+ }
+
+ /**
+ * Send link discovery message out of a given switch port.
+ * The discovery message may be a standard LLDP or a modified
+ * LLDP, where the dst mac address is set to :ff.
+ *
+ * TODO: The modified LLDP will updated in the future and may
+ * use a different eth-type.
+ * @param sw
+ * @param port
+ * @param isStandard indicates standard or modified LLDP
+ * @param isReverse indicates whether the LLDP was sent as a response
+ */
+ @LogMessageDoc(level="ERROR",
+ message="Failure sending LLDP out port {port} on switch {switch}",
+ explanation="An I/O error occured while sending LLDP message " +
+ "to the switch.",
+ recommendation=LogMessageDoc.CHECK_SWITCH)
+ protected void sendDiscoveryMessage(long sw, short port,
+ boolean isStandard,
+ boolean isReverse) {
+
+ IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+ if (iofSwitch == null) {
+ return;
+ }
+
+ if (port == OFPort.OFPP_LOCAL.getValue())
+ return;
+
+ OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+
+ if (ofpPort == null) {
+ if (log.isTraceEnabled()) {
+ log.trace("Null physical port. sw={}, port={}", sw, port);
+ }
+ return;
+ }
+
+ if (isLinkDiscoverySuppressed(sw, port)) {
+ /* Dont send LLDPs out of this port as suppressLLDPs set
+ *
+ */
+ return;
+ }
+
+ // For fast ports, do not send forward LLDPs or BDDPs.
+ if (!isReverse && autoPortFastFeature && isFastPort(sw, port))
+ return;
+
+ if (log.isTraceEnabled()) {
+ log.trace("Sending LLDP packet out of swich: {}, port: {}",
+ sw, port);
+ }
+
+ // using "nearest customer bridge" MAC address for broadest possible propagation
+ // through provider and TPMR bridges (see IEEE 802.1AB-2009 and 802.1Q-2011),
+ // in particular the Linux bridge which behaves mostly like a provider bridge
+ byte[] chassisId = new byte[] {4, 0, 0, 0, 0, 0, 0}; // filled in later
+ byte[] portId = new byte[] {2, 0, 0}; // filled in later
+ byte[] ttlValue = new byte[] {0, 0x78};
+ // OpenFlow OUI - 00-26-E1
+ byte[] dpidTLVValue = new byte[] {0x0, 0x26, (byte) 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127).setLength((short) dpidTLVValue.length).setValue(dpidTLVValue);
+
+ byte[] dpidArray = new byte[8];
+ ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
+ ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
+
+ Long dpid = sw;
+ dpidBB.putLong(dpid);
+ // set the ethernet source mac to last 6 bytes of dpid
+ System.arraycopy(dpidArray, 2, ofpPort.getHardwareAddress(), 0, 6);
+ // set the chassis id's value to last 6 bytes of dpid
+ System.arraycopy(dpidArray, 2, chassisId, 1, 6);
+ // set the optional tlv to the full dpid
+ System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
+
+
+ // set the portId to the outgoing port
+ portBB.putShort(port);
+ if (log.isTraceEnabled()) {
+ log.trace("Sending LLDP out of interface: {}/{}",
+ HexString.toHexString(sw), port);
+ }
+
+ LLDP lldp = new LLDP();
+ lldp.setChassisId(new LLDPTLV().setType((byte) 1).setLength((short) chassisId.length).setValue(chassisId));
+ lldp.setPortId(new LLDPTLV().setType((byte) 2).setLength((short) portId.length).setValue(portId));
+ lldp.setTtl(new LLDPTLV().setType((byte) 3).setLength((short) ttlValue.length).setValue(ttlValue));
+ lldp.getOptionalTLVList().add(dpidTLV);
+
+ // Add the controller identifier to the TLV value.
+ lldp.getOptionalTLVList().add(controllerTLV);
+ if (isReverse) {
+ lldp.getOptionalTLVList().add(reverseTLV);
+ }else {
+ lldp.getOptionalTLVList().add(forwardTLV);
+ }
+
+ Ethernet ethernet;
+ if (isStandard) {
+ ethernet = new Ethernet()
+ .setSourceMACAddress(ofpPort.getHardwareAddress())
+ .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
+ .setEtherType(Ethernet.TYPE_LLDP);
+ ethernet.setPayload(lldp);
+ } else {
+ BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
+ bsn.setPayload(lldp);
+
+ ethernet = new Ethernet()
+ .setSourceMACAddress(ofpPort.getHardwareAddress())
+ .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
+ .setEtherType(Ethernet.TYPE_BSN);
+ ethernet.setPayload(bsn);
+ }
+
+
+ // serialize and wrap in a packet out
+ byte[] data = ethernet.serialize();
+ OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
+ po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
+ po.setInPort(OFPort.OFPP_NONE);
+
+ // set actions
+ List<OFAction> actions = new ArrayList<OFAction>();
+ actions.add(new OFActionOutput(port, (short) 0));
+ po.setActions(actions);
+ po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
+
+ // set data
+ po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + data.length);
+ po.setPacketData(data);
+
+ // send
+ try {
+ iofSwitch.write(po, null);
+ iofSwitch.flush();
+ } catch (IOException e) {
+ log.error("Failure sending LLDP out port "+port+" on switch "+iofSwitch.getStringId(), e);
+ }
+
+ }
+
+ /**
+ * Send LLDPs to all switch-ports
+ */
+ protected void discoverOnAllPorts() {
+ if (log.isTraceEnabled()) {
+ log.trace("Sending LLDP packets out of all the enabled ports on switch");
+ }
+ Set<Long> switches = floodlightProvider.getSwitches().keySet();
+ // Send standard LLDPs
+ for (long sw: switches) {
+ IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+ if (iofSwitch == null) continue;
+ if (iofSwitch.getEnabledPorts() != null) {
+ for (OFPhysicalPort ofp: iofSwitch.getEnabledPorts()) {
+ if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber()))
+ continue;
+ if (autoPortFastFeature && isFastPort(sw, ofp.getPortNumber()))
+ continue;
+
+ // sends forward LLDP only non-fastports.
+ sendDiscoveryMessage(sw, ofp.getPortNumber(), true, false);
+
+ // If the switch port is not alreayd in the maintenance
+ // queue, add it.
+ NodePortTuple npt = new NodePortTuple(sw, ofp.getPortNumber());
+ addToMaintenanceQueue(npt);
+ }
+ }
+ }
+ }
+
+ protected void setControllerTLV() {
+ //Setting the controllerTLVValue based on current nano time,
+ //controller's IP address, and the network interface object hash
+ //the corresponding IP address.
+
+ final int prime = 7867;
+ InetAddress localIPAddress = null;
+ NetworkInterface localInterface = null;
+
+ byte[] controllerTLVValue = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; // 8 byte value.
+ ByteBuffer bb = ByteBuffer.allocate(10);
+
+ try{
+ localIPAddress = java.net.InetAddress.getLocalHost();
+ localInterface = NetworkInterface.getByInetAddress(localIPAddress);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ long result = System.nanoTime();
+ if (localIPAddress != null)
+ result = result * prime + IPv4.toIPv4Address(localIPAddress.getHostAddress());
+ if (localInterface != null)
+ result = result * prime + localInterface.hashCode();
+ // set the first 4 bits to 0.
+ result = result & (0x0fffffffffffffffL);
+
+ bb.putLong(result);
+
+ bb.rewind();
+ bb.get(controllerTLVValue, 0, 8);
+
+ this.controllerTLV = new LLDPTLV().setType((byte) 0x0c).setLength((short) controllerTLVValue.length).setValue(controllerTLVValue);
+ }
+
+ @Override
+ public String getName() {
+ return "linkdiscovery";
+ }
+
+ @Override
+ public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+ switch (msg.getType()) {
+ case PACKET_IN:
+ return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx);
+ case PORT_STATUS:
+ return this.handlePortStatus(sw.getId(), (OFPortStatus) msg);
+ default:
+ break;
+ }
+ return Command.CONTINUE;
+ }
+
+ private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, boolean isStandard, FloodlightContext cntx) {
+ // If LLDP is suppressed on this port, ignore received packet as well
+ IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+ if (iofSwitch == null) {
+ return Command.STOP;
+ }
+
+ if (isLinkDiscoverySuppressed(sw, pi.getInPort()))
+ return Command.STOP;
+
+ // If this is a malformed LLDP, or not from us, exit
+ if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3)
+ return Command.CONTINUE;
+
+ long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
+ long otherId = 0;
+ boolean myLLDP = false;
+ Boolean isReverse = null;
+
+ ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
+ portBB.position(1);
+
+ Short remotePort = portBB.getShort();
+ IOFSwitch remoteSwitch = null;
+
+ // Verify this LLDP packet matches what we're looking for
+ for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
+ if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 &&
+ lldptlv.getValue()[0] == 0x0 && lldptlv.getValue()[1] == 0x26 &&
+ lldptlv.getValue()[2] == (byte)0xe1 && lldptlv.getValue()[3] == 0x0) {
+ ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue());
+ remoteSwitch = floodlightProvider.getSwitches().get(dpidBB.getLong(4));
+ if (remoteSwitch == null) {
+ // Added by ONOS
+ // floodlight LLDP coming from a remote switch connected to a different controller
+ // add it to our cache of unconnected remote switches
+ remoteSwitch = addRemoteSwitch(dpidBB.getLong(4), remotePort);
+ }
+ } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8){
+ otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
+ if (myId == otherId)
+ myLLDP = true;
+ } else if (lldptlv.getType() == TLV_DIRECTION_TYPE &&
+ lldptlv.getLength() == TLV_DIRECTION_LENGTH) {
+ if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0])
+ isReverse = false;
+ else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0])
+ isReverse = true;
+ }
+ }
+
+ if (myLLDP == false) {
+ // This is not the LLDP sent by this controller.
+ // If the LLDP message has multicast bit set, then we need to broadcast
+ // the packet as a regular packet.
+ if (isStandard) {
+ if (log.isTraceEnabled()) {
+ log.trace("Getting standard LLDP from a different controller and quelching it.");
+ }
+ return Command.STOP;
+ }
+ else if(sw <= remoteSwitch.getId()){
+ if (log.isTraceEnabled()) {
+ log.trace("Getting BBDP from a different controller. myId {}: remoteId {}", myId, otherId);
+ log.trace("and my controller id is smaller than the other, so quelching it. myPort {}: rPort {}", pi.getInPort(), remotePort);
+ }
+ //XXX ONOS: Fix the BDDP broadcast issue
+ //return Command.CONTINUE;
+ return Command.STOP;
+ }
+ /*
+ else if (myId < otherId) {
+ if (log.isTraceEnabled()) {
+ log.trace("Getting BDDP packets from a different controller" +
+ "and letting it go through normal processing chain.");
+ }
+ //XXX ONOS: Fix the BDDP broadcast issue
+ //return Command.CONTINUE;
+ return Command.STOP;
+ }
+ */
+ }
+
+
+ if (remoteSwitch == null) {
+ // Ignore LLDPs not generated by Floodlight, or from a switch that has recently
+ // disconnected, or from a switch connected to another Floodlight instance
+ if (log.isTraceEnabled()) {
+ log.trace("Received LLDP from remote switch not connected to the controller");
+ }
+ return Command.STOP;
+ }
+
+ if (!remoteSwitch.portEnabled(remotePort)) {
+ if (log.isTraceEnabled()) {
+ log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
+ }
+ return Command.STOP;
+ }
+ if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
+ remotePort))) {
+ if (log.isTraceEnabled()) {
+ log.trace("Ignoring link with suppressed src port: switch {} port {}",
+ remoteSwitch, remotePort);
+ }
+ return Command.STOP;
+ }
+ if (!iofSwitch.portEnabled(pi.getInPort())) {
+ if (log.isTraceEnabled()) {
+ log.trace("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort());
+ }
+ return Command.STOP;
+ }
+
+ OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
+ int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
+ physicalPort = iofSwitch.getPort(pi.getInPort());
+ int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0;
+
+ // Store the time of update to this link, and push it out to routingEngine
+ Link lt = new Link(remoteSwitch.getId(), remotePort, iofSwitch.getId(), pi.getInPort());
+
+
+ Long lastLldpTime = null;
+ Long lastBddpTime = null;
+
+ Long firstSeenTime = System.currentTimeMillis();
+
+ if (isStandard)
+ lastLldpTime = System.currentTimeMillis();
+ else
+ lastBddpTime = System.currentTimeMillis();
+
+ LinkInfo newLinkInfo =
+ new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
+ srcPortState, dstPortState);
+
+ addOrUpdateLink(lt, newLinkInfo);
+
+ // Check if reverse link exists.
+ // If it doesn't exist and if the forward link was seen
+ // first seen within a small interval, send probe on the
+ // reverse link.
+
+ newLinkInfo = links.get(lt);
+ if (newLinkInfo != null && isStandard && isReverse == false) {
+ Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
+ lt.getSrc(), lt.getSrcPort());
+ LinkInfo reverseInfo = links.get(reverseLink);
+ if (reverseInfo == null) {
+ // the reverse link does not exist.
+ if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - LINK_TIMEOUT) {
+ this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), isStandard, true);
+ }
+ }
+ }
+
+ // If the received packet is a BDDP packet, then create a reverse BDDP
+ // link as well.
+ if (!isStandard) {
+ Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
+ lt.getSrc(), lt.getSrcPort());
+
+ // srcPortState and dstPort state are reversed.
+ LinkInfo reverseInfo =
+ new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
+ dstPortState, srcPortState);
+
+ addOrUpdateLink(reverseLink, reverseInfo);
+ }
+
+ // Remove the node ports from the quarantine and maintenance queues.
+ NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
+ NodePortTuple nptDst = new NodePortTuple(lt.getDst(), lt.getDstPort());
+ removeFromQuarantineQueue(nptSrc);
+ removeFromMaintenanceQueue(nptSrc);
+ removeFromQuarantineQueue(nptDst);
+ removeFromMaintenanceQueue(nptDst);
+
+ // Consume this message
+ return Command.STOP;
+ }
+
+ protected Command handlePacketIn(long sw, OFPacketIn pi,
+ FloodlightContext cntx) {
+ Ethernet eth =
+ IFloodlightProviderService.bcStore.get(cntx,
+ IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+
+ if(eth.getEtherType() == Ethernet.TYPE_BSN) {
+ BSN bsn = (BSN) eth.getPayload();
+ if (bsn == null) return Command.STOP;
+ if (bsn.getPayload() == null) return Command.STOP;
+ // It could be a packet other than BSN LLDP, therefore
+ // continue with the regular processing.
+ if (bsn.getPayload() instanceof LLDP == false)
+ return Command.CONTINUE;
+ return handleLldp((LLDP) bsn.getPayload(), sw, pi, false, cntx);
+ } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
+ return handleLldp((LLDP) eth.getPayload(), sw, pi, true, cntx);
+ } else if (eth.getEtherType() < 1500) {
+ long destMac = eth.getDestinationMAC().toLong();
+ if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE){
+ if (log.isTraceEnabled()) {
+ log.trace("Ignoring packet addressed to 802.1D/Q " +
+ "reserved address.");
+ }
+ return Command.STOP;
+ }
+ }
+
+ // If packet-in is from a quarantine port, stop processing.
+ NodePortTuple npt = new NodePortTuple(sw, pi.getInPort());
+ if (quarantineQueue.contains(npt)) return Command.STOP;
+
+ return Command.CONTINUE;
+ }
+
+ protected UpdateOperation getUpdateOperation(int srcPortState,
+ int dstPortState) {
+ boolean added =
+ (((srcPortState &
+ OFPortState.OFPPS_STP_MASK.getValue()) !=
+ OFPortState.OFPPS_STP_BLOCK.getValue()) &&
+ ((dstPortState &
+ OFPortState.OFPPS_STP_MASK.getValue()) !=
+ OFPortState.OFPPS_STP_BLOCK.getValue()));
+
+ if (added) return UpdateOperation.LINK_UPDATED;
+ return UpdateOperation.LINK_REMOVED;
+ }
+
+
+
+ protected UpdateOperation getUpdateOperation(int srcPortState) {
+ boolean portUp = ((srcPortState &
+ OFPortState.OFPPS_STP_MASK.getValue()) !=
+ OFPortState.OFPPS_STP_BLOCK.getValue());
+
+ if (portUp) return UpdateOperation.PORT_UP;
+ else return UpdateOperation.PORT_DOWN;
+ }
+
+ protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {
+
+ NodePortTuple srcNpt, dstNpt;
+ boolean linkChanged = false;
+
+ lock.writeLock().lock();
+ try {
+ // put the new info. if an old info exists, it will be returned.
+ LinkInfo oldInfo = links.put(lt, newInfo);
+ if (oldInfo != null &&
+ oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
+ newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
+
+ if (log.isTraceEnabled()) {
+ log.trace("addOrUpdateLink: {} {}",
+ lt,
+ (newInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast");
+ }
+
+ UpdateOperation updateOperation = null;
+ linkChanged = false;
+
+ srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
+ dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
+
+ if (oldInfo == null) {
+ // index it by switch source
+ if (!switchLinks.containsKey(lt.getSrc()))
+ switchLinks.put(lt.getSrc(), new HashSet<Link>());
+ switchLinks.get(lt.getSrc()).add(lt);
+
+ // index it by switch dest
+ if (!switchLinks.containsKey(lt.getDst()))
+ switchLinks.put(lt.getDst(), new HashSet<Link>());
+ switchLinks.get(lt.getDst()).add(lt);
+
+ // index both ends by switch:port
+ if (!portLinks.containsKey(srcNpt))
+ portLinks.put(srcNpt, new HashSet<Link>());
+ portLinks.get(srcNpt).add(lt);
+
+ if (!portLinks.containsKey(dstNpt))
+ portLinks.put(dstNpt, new HashSet<Link>());
+ portLinks.get(dstNpt).add(lt);
+
+ // Add to portNOFLinks if the unicast valid time is null
+ if (newInfo.getUnicastValidTime() == null)
+ addLinkToBroadcastDomain(lt);
+
+ // ONOS: Distinguish added event separately from updated event
+ updateOperation = UpdateOperation.LINK_ADDED;
+ linkChanged = true;
+
+ // Add to event history
+ evHistTopoLink(lt.getSrc(),
+ lt.getDst(),
+ lt.getSrcPort(),
+ lt.getDstPort(),
+ newInfo.getSrcPortState(), newInfo.getDstPortState(),
+ getLinkType(lt, newInfo),
+ EvAction.LINK_ADDED, "LLDP Recvd");
+ } else {
+ // Since the link info is already there, we need to
+ // update the right fields.
+ if (newInfo.getUnicastValidTime() == null) {
+ // This is due to a multicast LLDP, so copy the old unicast
+ // value.
+ if (oldInfo.getUnicastValidTime() != null) {
+ newInfo.setUnicastValidTime(oldInfo.getUnicastValidTime());
+ }
+ } else if (newInfo.getMulticastValidTime() == null) {
+ // This is due to a unicast LLDP, so copy the old multicast
+ // value.
+ if (oldInfo.getMulticastValidTime() != null) {
+ newInfo.setMulticastValidTime(oldInfo.getMulticastValidTime());
+ }
+ }
+
+ Long oldTime = oldInfo.getUnicastValidTime();
+ Long newTime = newInfo.getUnicastValidTime();
+ // the link has changed its state between openflow and non-openflow
+ // if the unicastValidTimes are null or not null
+ if (oldTime != null & newTime == null) {
+ // openflow -> non-openflow transition
+ // we need to add the link tuple to the portNOFLinks
+ addLinkToBroadcastDomain(lt);
+ linkChanged = true;
+ } else if (oldTime == null & newTime != null) {
+ // non-openflow -> openflow transition
+ // we need to remove the link from the portNOFLinks
+ removeLinkFromBroadcastDomain(lt);
+ linkChanged = true;
+ }
+
+ // Only update the port states if they've changed
+ if (newInfo.getSrcPortState().intValue() !=
+ oldInfo.getSrcPortState().intValue() ||
+ newInfo.getDstPortState().intValue() !=
+ oldInfo.getDstPortState().intValue())
+ linkChanged = true;
+
+ if (linkChanged) {
+ updateOperation = getUpdateOperation(newInfo.getSrcPortState(),
+ newInfo.getDstPortState());
+ if (log.isTraceEnabled()) {
+ log.trace("Updated link {}", lt);
+ }
+ // Add to event history
+ evHistTopoLink(lt.getSrc(),
+ lt.getDst(),
+ lt.getSrcPort(),
+ lt.getDstPort(),
+ newInfo.getSrcPortState(), newInfo.getDstPortState(),
+ getLinkType(lt, newInfo),
+ EvAction.LINK_PORT_STATE_UPDATED,
+ "LLDP Recvd");
+ }
+ }
+
+ if (linkChanged) {
+ // find out if the link was added or removed here.
+ LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
+ lt.getDst(), lt.getDstPort(),
+ getLinkType(lt, newInfo),
+ updateOperation));
+ controller.publishUpdate(update);
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+
+ return linkChanged;
+ }
+
+ @Override
+ public Map<Long, Set<Link>> getSwitchLinks() {
+ return this.switchLinks;
+ }
+
+ /**
+ * Removes links from memory and storage.
+ * @param links The List of @LinkTuple to delete.
+ */
+ protected void deleteLinks(List<Link> links, String reason) {
+ NodePortTuple srcNpt, dstNpt;
+
+ lock.writeLock().lock();
+ try {
+ for (Link lt : links) {
+ srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
+ dstNpt =new NodePortTuple(lt.getDst(), lt.getDstPort());
+
+ switchLinks.get(lt.getSrc()).remove(lt);
+ switchLinks.get(lt.getDst()).remove(lt);
+ if (switchLinks.containsKey(lt.getSrc()) &&
+ switchLinks.get(lt.getSrc()).isEmpty())
+ this.switchLinks.remove(lt.getSrc());
+ if (this.switchLinks.containsKey(lt.getDst()) &&
+ this.switchLinks.get(lt.getDst()).isEmpty())
+ this.switchLinks.remove(lt.getDst());
+
+ if (this.portLinks.get(srcNpt) != null) {
+ this.portLinks.get(srcNpt).remove(lt);
+ if (this.portLinks.get(srcNpt).isEmpty())
+ this.portLinks.remove(srcNpt);
+ }
+ if (this.portLinks.get(dstNpt) != null) {
+ this.portLinks.get(dstNpt).remove(lt);
+ if (this.portLinks.get(dstNpt).isEmpty())
+ this.portLinks.remove(dstNpt);
+ }
+
+ LinkInfo info = this.links.remove(lt);
+ LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
+ lt.getDst(), lt.getDstPort(),
+ getLinkType(lt, info),
+ UpdateOperation.LINK_REMOVED));
+ controller.publishUpdate(update);
+
+ // Update Event History
+ evHistTopoLink(lt.getSrc(),
+ lt.getDst(),
+ lt.getSrcPort(),
+ lt.getDstPort(),
+ 0, 0, // Port states
+ ILinkDiscovery.LinkType.INVALID_LINK,
+ EvAction.LINK_DELETED, reason);
+
+ // TODO Whenever link is removed, it has to checked if
+ // the switchports must be added to quarantine.
+
+ if (log.isTraceEnabled()) {
+ log.trace("Deleted link {}", lt);
+ }
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Handles an OFPortStatus message from a switch. We will add or
+ * delete LinkTupes as well re-compute the topology if needed.
+ * @param sw The IOFSwitch that sent the port status message
+ * @param ps The OFPortStatus message
+ * @return The Command to continue or stop after we process this message
+ */
+ protected Command handlePortStatus(long sw, OFPortStatus ps) {
+
+ IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+ if (iofSwitch == null) return Command.CONTINUE;
+
+ // ONOS: If we do not control this switch, then we should not process its port status messages
+ if (!registryService.hasControl(iofSwitch.getId())) return Command.CONTINUE;
+
+ if (log.isTraceEnabled()) {
+ log.trace("handlePortStatus: Switch {} port #{} reason {}; " +
+ "config is {} state is {}",
+ new Object[] {iofSwitch.getStringId(),
+ ps.getDesc().getPortNumber(),
+ ps.getReason(),
+ ps.getDesc().getConfig(),
+ ps.getDesc().getState()});
+ }
+
+ short port = ps.getDesc().getPortNumber();
+ NodePortTuple npt = new NodePortTuple(sw, port);
+ boolean linkDeleted = false;
+ boolean linkInfoChanged = false;
+
+ lock.writeLock().lock();
+ try {
+ // if ps is a delete, or a modify where the port is down or
+ // configured down
+ if ((byte)OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() ||
+ ((byte)OFPortReason.OFPPR_MODIFY.ordinal() ==
+ ps.getReason() && !portEnabled(ps.getDesc()))) {
+ deleteLinksOnPort(npt, "Port Status Changed");
+ LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, UpdateOperation.PORT_DOWN));
+ controller.publishUpdate(update);
+ linkDeleted = true;
+ }
+ else if (ps.getReason() ==
+ (byte)OFPortReason.OFPPR_MODIFY.ordinal()) {
+ // If ps is a port modification and the port state has changed
+ // that affects links in the topology
+
+ if (this.portLinks.containsKey(npt)) {
+ for (Link lt: this.portLinks.get(npt)) {
+ LinkInfo linkInfo = links.get(lt);
+ assert(linkInfo != null);
+ Integer updatedSrcPortState = null;
+ Integer updatedDstPortState = null;
+ if (lt.getSrc() == npt.getNodeId() &&
+ lt.getSrcPort() == npt.getPortId() &&
+ (linkInfo.getSrcPortState() !=
+ ps.getDesc().getState())) {
+ updatedSrcPortState = ps.getDesc().getState();
+ linkInfo.setSrcPortState(updatedSrcPortState);
+ }
+ if (lt.getDst() == npt.getNodeId() &&
+ lt.getDstPort() == npt.getPortId() &&
+ (linkInfo.getDstPortState() !=
+ ps.getDesc().getState())) {
+ updatedDstPortState = ps.getDesc().getState();
+ linkInfo.setDstPortState(updatedDstPortState);
+ }
+ if ((updatedSrcPortState != null) ||
+ (updatedDstPortState != null)) {
+ // The link is already known to link discovery
+ // manager and the status has changed, therefore
+ // send an LinkUpdate.
+ UpdateOperation operation =
+ getUpdateOperation(linkInfo.getSrcPortState(),
+ linkInfo.getDstPortState());
+ LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
+ lt.getDst(), lt.getDstPort(),
+ getLinkType(lt, linkInfo),
+ operation));
+ controller.publishUpdate(update);
+
+ linkInfoChanged = true;
+ }
+ }
+ }
+
+ UpdateOperation operation =
+ getUpdateOperation(ps.getDesc().getState());
+ LinkUpdate update = new LinkUpdate(new LDUpdate(sw, port, operation));
+ controller.publishUpdate(update);
+ }
+
+ if (!linkDeleted && !linkInfoChanged){
+ if (log.isTraceEnabled()) {
+ log.trace("handlePortStatus: Switch {} port #{} reason {};"+
+ " no links to update/remove",
+ new Object[] {HexString.toHexString(sw),
+ ps.getDesc().getPortNumber(),
+ ps.getReason()});
+ }
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+
+ if (!linkDeleted) {
+ // Send LLDP right away when port state is changed for faster
+ // cluster-merge. If it is a link delete then there is not need
+ // to send the LLDPs right away and instead we wait for the LLDPs
+ // to be sent on the timer as it is normally done
+ // do it outside the write-lock
+ // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
+ processNewPort(npt.getNodeId(), npt.getPortId());
+ }
+ return Command.CONTINUE;
+ }
+
+ /**
+ * Process a new port.
+ * If link discovery is disabled on the port, then do nothing.
+ * If autoportfast feature is enabled and the port is a fast port, then
+ * do nothing.
+ * Otherwise, send LLDP message. Add the port to quarantine.
+ * @param sw
+ * @param p
+ */
+ private void processNewPort(long sw, short p) {
+ if (isLinkDiscoverySuppressed(sw, p)) {
+ // Do nothing as link discovery is suppressed.
+ }
+ else if (autoPortFastFeature && isFastPort(sw, p)) {
+ // Do nothing as the port is a fast port.
+ }
+ else {
+ NodePortTuple npt = new NodePortTuple(sw, p);
+ discover(sw, p);
+ // if it is not a fast port, add it to quarantine.
+ if (!isFastPort(sw, p)) {
+ addToQuarantineQueue(npt);
+ } else {
+ // Add to maintenance queue to ensure that BDDP packets
+ // are sent out.
+ addToMaintenanceQueue(npt);
+ }
+ }
+ }
+
+ /**
+ * We send out LLDP messages when a switch is added to discover the topology
+ * @param sw The IOFSwitch that connected to the controller
+ */
+ @Override
+ public void addedSwitch(IOFSwitch sw) {
+
+ if (sw.getEnabledPorts() != null) {
+ for (Short p : sw.getEnabledPortNumbers()) {
+ processNewPort(sw.getId(), p);
+ }
+ }
+ // Update event history
+ evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
+ LinkUpdate update = new LinkUpdate(new LDUpdate(sw.getId(), null,
+ UpdateOperation.SWITCH_UPDATED));
+ controller.publishUpdate(update);
+ }
+
+ /**
+ * When a switch disconnects we remove any links from our map and notify.
+ */
+ @Override
+ public void removedSwitch(IOFSwitch iofSwitch) {
+ // Update event history
+ long sw = iofSwitch.getId();
+ evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None");
+ List<Link> eraseList = new ArrayList<Link>();
+ lock.writeLock().lock();
+ try {
+ if (switchLinks.containsKey(sw)) {
+ if (log.isTraceEnabled()) {
+ log.trace("Handle switchRemoved. Switch {}; removing links {}",
+ HexString.toHexString(sw), switchLinks.get(sw));
+ }
+ // add all tuples with an endpoint on this switch to erase list
+ eraseList.addAll(switchLinks.get(sw));
+ deleteLinks(eraseList, "Switch Removed");
+
+ // Send a switch removed update
+ LinkUpdate update = new LinkUpdate(new LDUpdate(sw, null, UpdateOperation.SWITCH_REMOVED));
+ controller.publishUpdate(update);
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * We don't react the port changed notifications here. we listen for
+ * OFPortStatus messages directly. Might consider using this notifier
+ * instead
+ */
+ @Override
+ public void switchPortChanged(Long switchId) {
+ // no-op
+ }
+
+ /**
+ * Delete links incident on a given switch port.
+ * @param npt
+ * @param reason
+ */
+ protected void deleteLinksOnPort(NodePortTuple npt, String reason) {
+ List<Link> eraseList = new ArrayList<Link>();
+ if (this.portLinks.containsKey(npt)) {
+ if (log.isTraceEnabled()) {
+ log.trace("handlePortStatus: Switch {} port #{} " +
+ "removing links {}",
+ new Object[] {HexString.toHexString(npt.getNodeId()),
+ npt.getPortId(),
+ this.portLinks.get(npt)});
+ }
+ eraseList.addAll(this.portLinks.get(npt));
+ deleteLinks(eraseList, reason);
+ }
+ }
+
+ /**
+ * Iterates through the list of links and deletes if the
+ * last discovery message reception time exceeds timeout values.
+ */
+ protected void timeoutLinks() {
+ List<Link> eraseList = new ArrayList<Link>();
+ Long curTime = System.currentTimeMillis();
+ boolean linkChanged = false;
+
+ // reentrant required here because deleteLink also write locks
+ lock.writeLock().lock();
+ try {
+ Iterator<Entry<Link, LinkInfo>> it =
+ this.links.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<Link, LinkInfo> entry = it.next();
+ Link lt = entry.getKey();
+ LinkInfo info = entry.getValue();
+
+ // Timeout the unicast and multicast LLDP valid times
+ // independently.
+ if ((info.getUnicastValidTime() != null) &&
+ (info.getUnicastValidTime() + (this.LINK_TIMEOUT * 1000) < curTime)){
+ info.setUnicastValidTime(null);
+
+ if (info.getMulticastValidTime() != null)
+ addLinkToBroadcastDomain(lt);
+ // Note that even if mTime becomes null later on,
+ // the link would be deleted, which would trigger updateClusters().
+ linkChanged = true;
+ }
+ if ((info.getMulticastValidTime()!= null) &&
+ (info.getMulticastValidTime()+ (this.LINK_TIMEOUT * 1000) < curTime)) {
+ info.setMulticastValidTime(null);
+ // if uTime is not null, then link will remain as openflow
+ // link. If uTime is null, it will be deleted. So, we
+ // don't care about linkChanged flag here.
+ removeLinkFromBroadcastDomain(lt);
+ linkChanged = true;
+ }
+ // Add to the erase list only if the unicast
+ // time is null.
+ if (info.getUnicastValidTime() == null &&
+ info.getMulticastValidTime() == null){
+ eraseList.add(entry.getKey());
+ } else if (linkChanged) {
+ UpdateOperation operation;
+ operation = getUpdateOperation(info.getSrcPortState(),
+ info.getDstPortState());
+ LinkUpdate update = new LinkUpdate(new LDUpdate(lt.getSrc(), lt.getSrcPort(),
+ lt.getDst(), lt.getDstPort(),
+ getLinkType(lt, info),
+ operation));
+ controller.publishUpdate(update);
+ }
+ }
+
+ // if any link was deleted or any link was changed.
+ if ((eraseList.size() > 0) || linkChanged) {
+ deleteLinks(eraseList, "LLDP timeout");
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ private boolean portEnabled(OFPhysicalPort port) {
+ if (port == null)
+ return false;
+ if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0)
+ return false;
+ if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0)
+ return false;
+ // Port STP state doesn't work with multiple VLANs, so ignore it for now
+ // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
+ // return false;
+ return true;
+ }
+
+ public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
+ return portBroadcastDomainLinks;
+ }
+
+ @Override
+ public Map<Link, LinkInfo> getLinks() {
+ lock.readLock().lock();
+ Map<Link, LinkInfo> result;
+ try {
+ result = new HashMap<Link, LinkInfo>(links);
+ } finally {
+ lock.readLock().unlock();
+ }
+ return result;
+ }
+
+ protected void addLinkToBroadcastDomain(Link lt) {
+
+ NodePortTuple srcNpt, dstNpt;
+ srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
+ dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
+
+ if (!portBroadcastDomainLinks.containsKey(srcNpt))
+ portBroadcastDomainLinks.put(srcNpt, new HashSet<Link>());
+ portBroadcastDomainLinks.get(srcNpt).add(lt);
+
+ if (!portBroadcastDomainLinks.containsKey(dstNpt))
+ portBroadcastDomainLinks.put(dstNpt, new HashSet<Link>());
+ portBroadcastDomainLinks.get(dstNpt).add(lt);
+ }
+
+ protected void removeLinkFromBroadcastDomain(Link lt) {
+
+ NodePortTuple srcNpt, dstNpt;
+ srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
+ dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
+
+ if (portBroadcastDomainLinks.containsKey(srcNpt)) {
+ portBroadcastDomainLinks.get(srcNpt).remove(lt);
+ if (portBroadcastDomainLinks.get(srcNpt).isEmpty())
+ portBroadcastDomainLinks.remove(srcNpt);
+ }
+
+ if (portBroadcastDomainLinks.containsKey(dstNpt)) {
+ portBroadcastDomainLinks.get(dstNpt).remove(lt);
+ if (portBroadcastDomainLinks.get(dstNpt).isEmpty())
+ portBroadcastDomainLinks.remove(dstNpt);
+ }
+ }
+
+ @Override
+ public void addListener(ILinkDiscoveryListener listener) {
+ linkDiscoveryAware.add(listener);
+ }
+
+ /**
+ * Register a link discovery aware component
+ * @param linkDiscoveryAwareComponent
+ */
+ public void addLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
+ // TODO make this a copy on write set or lock it somehow
+ this.linkDiscoveryAware.add(linkDiscoveryAwareComponent);
+ }
+
+ /**
+ * Deregister a link discovery aware component
+ * @param linkDiscoveryAwareComponent
+ */
+ public void removeLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
+ // TODO make this a copy on write set or lock it somehow
+ this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent);
+ }
+
+ @Override
+ public boolean isCallbackOrderingPrereq(OFType type, String name) {
+ return false;
+ }
+
+ @Override
+ public boolean isCallbackOrderingPostreq(OFType type, String name) {
+ return false;
+ }
+
+ // IFloodlightModule classes
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+ Collection<Class<? extends IFloodlightService>> l =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(ILinkDiscoveryService.class);
+ //l.add(ITopologyService.class);
+ return l;
+ }
+
+ @Override
+ public Map<Class<? extends IFloodlightService>, IFloodlightService>
+ getServiceImpls() {
+ Map<Class<? extends IFloodlightService>,
+ IFloodlightService> m =
+ new HashMap<Class<? extends IFloodlightService>,
+ IFloodlightService>();
+ // We are the class that implements the service
+ m.put(ILinkDiscoveryService.class, this);
+ return m;
+ }
+
+ @Override
+ public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+ Collection<Class<? extends IFloodlightService>> l =
+ new ArrayList<Class<? extends IFloodlightService>>();
+ l.add(IFloodlightProviderService.class);
+ l.add(IThreadPoolService.class);
+ l.add(IRestApiService.class);
+ // Added by ONOS
+ l.add(IControllerRegistryService.class);
+ return l;
+ }
+
+ @Override
+ public void init(FloodlightModuleContext context)
+ throws FloodlightModuleException {
+ floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+ threadPool = context.getServiceImpl(IThreadPoolService.class);
+ restApi = context.getServiceImpl(IRestApiService.class);
+ // Added by ONOS
+ registryService = context.getServiceImpl(IControllerRegistryService.class);
+
+ // Set the autoportfast feature to false.
+ this.autoPortFastFeature = false;
+
+ // We create this here because there is no ordering guarantee
+ this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>();
+ this.lock = new ReentrantReadWriteLock();
+ this.links = new HashMap<Link, LinkInfo>();
+ this.portLinks = new HashMap<NodePortTuple, Set<Link>>();
+ this.suppressLinkDiscovery =
+ Collections.synchronizedSet(new HashSet<NodePortTuple>());
+ this.portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>();
+ this.switchLinks = new HashMap<Long, Set<Link>>();
+ this.quarantineQueue = new LinkedBlockingQueue<NodePortTuple>();
+ this.maintenanceQueue = new LinkedBlockingQueue<NodePortTuple>();
+ // Added by ONOS
+ this.remoteSwitches = new HashMap<Long, IOnosRemoteSwitch>();
+
+ this.evHistTopologySwitch =
+ new EventHistory<EventHistoryTopologySwitch>("Topology: Switch");
+ this.evHistTopologyLink =
+ new EventHistory<EventHistoryTopologyLink>("Topology: Link");
+ this.evHistTopologyCluster =
+ new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster");
+ }
+
+ @Override
+ @LogMessageDocs({
+ @LogMessageDoc(level="ERROR",
+ message="No storage source found.",
+ explanation="Storage source was not initialized; cannot initialize " +
+ "link discovery.",
+ recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
+ @LogMessageDoc(level="ERROR",
+ message="Error in installing listener for " +
+ "switch config table {table}",
+ explanation="Failed to install storage notification for the " +
+ "switch config table",
+ recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
+ @LogMessageDoc(level="ERROR",
+ message="No storage source found.",
+ explanation="Storage source was not initialized; cannot initialize " +
+ "link discovery.",
+ recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG),
+ @LogMessageDoc(level="ERROR",
+ message="Exception in LLDP send timer.",
+ explanation="An unknown error occured while sending LLDP " +
+ "messages to switches.",
+ recommendation=LogMessageDoc.CHECK_SWITCH)
+ })
+ public void startUp(FloodlightModuleContext context) {
+ ScheduledExecutorService ses = threadPool.getScheduledExecutor();
+ controller =
+ context.getServiceImpl(IFloodlightProviderService.class);
+
+ // To be started by the first switch connection
+ discoveryTask = new SingletonTask(ses, new Runnable() {
+ @Override
+ public void run() {
+ try {
+ discoverLinks();
+ } catch (Exception e) {
+ log.error("Exception in LLDP send timer.", e);
+ } finally {
+ if (!shuttingDown) {
+ // Always reschedule link discovery if we're not
+ // shutting down (no chance of SLAVE role now)
+ log.trace("Rescheduling discovery task");
+ discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL,
+ TimeUnit.SECONDS);
+ }
+ }
+ }
+ });
+
+ // Always reschedule link discovery as we are never in SLAVE role now
+ discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, TimeUnit.SECONDS);
+
+ // Setup the BDDP task. It is invoked whenever switch port tuples
+ // are added to the quarantine list.
+ bddpTask = new SingletonTask(ses, new QuarantineWorker());
+ bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
+
+
+ // Register for the OpenFlow messages we want to receive
+ floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+ floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this);
+ // Register for switch updates
+ floodlightProvider.addOFSwitchListener(this);
+ if (restApi != null)
+ restApi.addRestletRoutable(new LinkDiscoveryWebRoutable());
+ setControllerTLV();
+ }
+
+ // ****************************************************
+ // Topology Manager's Event History members and methods
+ // ****************************************************
+
+ // Topology Manager event history
+ public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch;
+ public EventHistory<EventHistoryTopologyLink> evHistTopologyLink;
+ public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster;
+ public EventHistoryTopologySwitch evTopoSwitch;
+ public EventHistoryTopologyLink evTopoLink;
+ public EventHistoryTopologyCluster evTopoCluster;
+
+ // Switch Added/Deleted
+ private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
+ if (evTopoSwitch == null) {
+ evTopoSwitch = new EventHistoryTopologySwitch();
+ }
+ evTopoSwitch.dpid = sw.getId();
+ if ((sw.getChannel() != null) &&
+ (SocketAddress.class.isInstance(
+ sw.getChannel().getRemoteAddress()))) {
+ evTopoSwitch.ipv4Addr =
+ IPv4.toIPv4Address(((InetSocketAddress)(sw.getChannel().
+ getRemoteAddress())).getAddress().getAddress());
+ evTopoSwitch.l4Port =
+ ((InetSocketAddress)(sw.getChannel().
+ getRemoteAddress())).getPort();
+ } else {
+ evTopoSwitch.ipv4Addr = 0;
+ evTopoSwitch.l4Port = 0;
+ }
+ evTopoSwitch.reason = reason;
+ evTopoSwitch = evHistTopologySwitch.put(evTopoSwitch, actn);
+ }
+
+ private void evHistTopoLink(long srcDpid, long dstDpid, short srcPort,
+ short dstPort, int srcPortState, int dstPortState,
+ ILinkDiscovery.LinkType linkType,
+ EvAction actn, String reason) {
+ if (evTopoLink == null) {
+ evTopoLink = new EventHistoryTopologyLink();
+ }
+ evTopoLink.srcSwDpid = srcDpid;
+ evTopoLink.dstSwDpid = dstDpid;
+ evTopoLink.srcSwport = srcPort & 0xffff;
+ evTopoLink.dstSwport = dstPort & 0xffff;
+ evTopoLink.srcPortState = srcPortState;
+ evTopoLink.dstPortState = dstPortState;
+ evTopoLink.reason = reason;
+ switch (linkType) {
+ case DIRECT_LINK:
+ evTopoLink.linkType = "DIRECT_LINK";
+ break;
+ case MULTIHOP_LINK:
+ evTopoLink.linkType = "MULTIHOP_LINK";
+ break;
+ case TUNNEL:
+ evTopoLink.linkType = "TUNNEL";
+ break;
+ case INVALID_LINK:
+ default:
+ evTopoLink.linkType = "Unknown";
+ break;
+ }
+ evTopoLink = evHistTopologyLink.put(evTopoLink, actn);
+ }
+
+ public void evHistTopoCluster(long dpid, long clusterIdOld,
+ long clusterIdNew, EvAction action, String reason) {
+ if (evTopoCluster == null) {
+ evTopoCluster = new EventHistoryTopologyCluster();
+ }
+ evTopoCluster.dpid = dpid;
+ evTopoCluster.clusterIdOld = clusterIdOld;
+ evTopoCluster.clusterIdNew = clusterIdNew;
+ evTopoCluster.reason = reason;
+ evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action);
+ }
+
+ @Override
+ public boolean isAutoPortFastFeature() {
+ return autoPortFastFeature;
+ }
+
+ @Override
+ public void setAutoPortFastFeature(boolean autoPortFastFeature) {
+ this.autoPortFastFeature = autoPortFastFeature;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/web/AutoPortFast.java b/src/main/java/net/onrc/onos/core/linkdiscovery/web/AutoPortFast.java
new file mode 100644
index 0000000..028776b
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/web/AutoPortFast.java
@@ -0,0 +1,31 @@
+package net.onrc.onos.core.linkdiscovery.web;
+
+import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
+
+import org.restlet.data.Status;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AutoPortFast extends ServerResource {
+ protected final static Logger log = LoggerFactory.getLogger(AutoPortFast.class);
+
+ @Get("json")
+ public String retrieve() {
+ ILinkDiscoveryService linkDiscovery;
+ linkDiscovery = (ILinkDiscoveryService)getContext().getAttributes().
+ get(ILinkDiscoveryService.class.getCanonicalName());
+
+ String param = ((String)getRequestAttributes().get("state")).toLowerCase();
+ if (param.equals("enable") || param.equals("true")) {
+ linkDiscovery.setAutoPortFastFeature(true);
+ } else if (param.equals("disable") || param.equals("false")) {
+ linkDiscovery.setAutoPortFastFeature(false);
+ }
+ setStatus(Status.SUCCESS_OK, "OK");
+ if (linkDiscovery.isAutoPortFastFeature())
+ return "enabled";
+ else return "disabled";
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinkDiscoveryWebRoutable.java b/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinkDiscoveryWebRoutable.java
new file mode 100644
index 0000000..8d3831c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinkDiscoveryWebRoutable.java
@@ -0,0 +1,27 @@
+package net.onrc.onos.core.linkdiscovery.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.routing.Router;
+
+public class LinkDiscoveryWebRoutable implements RestletRoutable {
+ /**
+ * Create the Restlet router and bind to the proper resources.
+ */
+ @Override
+ public Router getRestlet(Context context) {
+ Router router = new Router(context);
+ router.attach("/links/json", LinksResource.class);
+ router.attach("/autoportfast/{state}/json", AutoPortFast.class); // enable/true or disable/false
+ return router;
+ }
+
+ /**
+ * Set the base path for the Topology
+ */
+ @Override
+ public String basePath() {
+ return "/wm/onos/linkdiscovery";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinkWithType.java b/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinkWithType.java
new file mode 100644
index 0000000..2997261
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinkWithType.java
@@ -0,0 +1,65 @@
+package net.onrc.onos.core.linkdiscovery.web;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.openflow.util.HexString;
+
+import net.onrc.onos.core.linkdiscovery.Link;
+import net.onrc.onos.core.linkdiscovery.ILinkDiscovery.LinkType;
+
+/**
+ * This class is both the datastructure and the serializer
+ * for a link with the corresponding type of link.
+ * @author alexreimers
+ */
+@JsonSerialize(using=LinkWithType.class)
+public class LinkWithType extends JsonSerializer<LinkWithType> {
+ public long srcSwDpid;
+ public short srcPort;
+ public int srcPortState;
+ public long dstSwDpid;
+ public short dstPort;
+ public int dstPortState;
+ public LinkType type;
+
+ // Do NOT delete this, it's required for the serializer
+ public LinkWithType() {}
+
+ public LinkWithType(Link link,
+ int srcPortState,
+ int dstPortState,
+ LinkType type) {
+ this.srcSwDpid = link.getSrc();
+ this.srcPort = link.getSrcPort();
+ this.srcPortState = srcPortState;
+ this.dstSwDpid = link.getDst();
+ this.dstPort = link.getDstPort();
+ this.dstPortState = dstPortState;
+ this.type = type;
+ }
+
+ @Override
+ public void serialize(LinkWithType lwt, JsonGenerator jgen, SerializerProvider arg2)
+ throws IOException, JsonProcessingException {
+ // You ****MUST*** use lwt for the fields as it's actually a different object.
+ jgen.writeStartObject();
+ jgen.writeStringField("src-switch", HexString.toHexString(lwt.srcSwDpid));
+ jgen.writeNumberField("src-port", lwt.srcPort);
+ jgen.writeNumberField("src-port-state", lwt.srcPortState);
+ jgen.writeStringField("dst-switch", HexString.toHexString(lwt.dstSwDpid));
+ jgen.writeNumberField("dst-port", lwt.dstPort);
+ jgen.writeNumberField("dst-port-state", lwt.dstPortState);
+ jgen.writeStringField("type", lwt.type.toString());
+ jgen.writeEndObject();
+ }
+
+ @Override
+ public Class<LinkWithType> handledType() {
+ return LinkWithType.class;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinksResource.java b/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinksResource.java
new file mode 100644
index 0000000..2714f66
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/web/LinksResource.java
@@ -0,0 +1,39 @@
+package net.onrc.onos.core.linkdiscovery.web;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
+import net.onrc.onos.core.linkdiscovery.Link;
+import net.onrc.onos.core.linkdiscovery.LinkInfo;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+
+public class LinksResource extends ServerResource {
+
+ @Get("json")
+ public Set<LinkWithType> retrieve() {
+ ILinkDiscoveryService ld = (ILinkDiscoveryService)getContext().getAttributes().
+ get(ILinkDiscoveryService.class.getCanonicalName());
+ Map<Link, LinkInfo> links = new HashMap<Link, LinkInfo>();
+ Set<LinkWithType> returnLinkSet = new HashSet<LinkWithType>();
+
+ if (ld != null) {
+ links.putAll(ld.getLinks());
+ for(Entry<Link, LinkInfo> e : links.entrySet()) {
+ Link link = e.getKey();
+ LinkInfo info = e.getValue();
+ LinkWithType lwt = new LinkWithType(link,
+ info.getSrcPortState(),
+ info.getDstPortState(),
+ ld.getLinkType(link, info));
+ returnLinkSet.add(lwt);
+ }
+ }
+ return returnLinkSet;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/CallerId.java b/src/main/java/net/onrc/onos/core/util/CallerId.java
new file mode 100644
index 0000000..bcd9704
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/CallerId.java
@@ -0,0 +1,77 @@
+package net.onrc.onos.core.util;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing a Caller ID for an ONOS component.
+ */
+public class CallerId {
+ private String value;
+
+ /**
+ * Default constructor.
+ */
+ public CallerId() {}
+
+ /**
+ * Copy constructor
+ * @param otherCallerId
+ */
+ public CallerId(CallerId otherCallerId) {
+ // Note: make a full copy if we change value to a mutable type
+ value = otherCallerId.value;
+ }
+
+ /**
+ * Constructor from a string value.
+ *
+ * @param value the value to use.
+ */
+ public CallerId(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the value of the Caller ID.
+ *
+ * @return the value of the Caller ID.
+ */
+ @JsonProperty("value")
+ public String value() { return value; }
+
+ /**
+ * Set the value of the Caller ID.
+ *
+ * @param value the value to set.
+ */
+ @JsonProperty("value")
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Convert the Caller ID value to a string.
+ *
+ * @return the Caller ID value to a string.
+ */
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof CallerId)) {
+ return false;
+ }
+
+ CallerId otherCallerId = (CallerId) other;
+
+ return value.equals(otherCallerId.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/DataPath.java b/src/main/java/net/onrc/onos/core/util/DataPath.java
new file mode 100644
index 0000000..7dd1f51
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/DataPath.java
@@ -0,0 +1,157 @@
+package net.onrc.onos.core.util;
+
+import java.util.ArrayList;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The data forwarding path state from a source to a destination.
+ */
+public class DataPath {
+ private SwitchPort srcPort; // The source port
+ private SwitchPort dstPort; // The destination port
+ private ArrayList<FlowEntry> flowEntries; // The Flow Entries
+
+ /**
+ * Default constructor.
+ */
+ public DataPath() {
+ srcPort = new SwitchPort();
+ dstPort = new SwitchPort();
+ flowEntries = new ArrayList<FlowEntry>();
+ }
+
+ /**
+ * Get the data path source port.
+ *
+ * @return the data path source port.
+ */
+ @JsonProperty("srcPort")
+ public SwitchPort srcPort() { return srcPort; }
+
+ /**
+ * Set the data path source port.
+ *
+ * @param srcPort the data path source port to set.
+ */
+ @JsonProperty("srcPort")
+ public void setSrcPort(SwitchPort srcPort) {
+ this.srcPort = srcPort;
+ }
+
+ /**
+ * Get the data path destination port.
+ *
+ * @return the data path destination port.
+ */
+ @JsonProperty("dstPort")
+ public SwitchPort dstPort() { return dstPort; }
+
+ /**
+ * Set the data path destination port.
+ *
+ * @param dstPort the data path destination port to set.
+ */
+ @JsonProperty("dstPort")
+ public void setDstPort(SwitchPort dstPort) {
+ this.dstPort = dstPort;
+ }
+
+ /**
+ * Get the data path flow entries.
+ *
+ * @return the data path flow entries.
+ */
+ @JsonProperty("flowEntries")
+ public ArrayList<FlowEntry> flowEntries() { return flowEntries; }
+
+ /**
+ * Set the data path flow entries.
+ *
+ * @param flowEntries the data path flow entries to set.
+ */
+ @JsonProperty("flowEntries")
+ public void setFlowEntries(ArrayList<FlowEntry> flowEntries) {
+ this.flowEntries = flowEntries;
+ }
+
+ /**
+ * Apply Flow Path Flags to the pre-computed Data Path.
+ *
+ * @param flowPathFlags the Flow Path Flags to apply.
+ */
+ public void applyFlowPathFlags(FlowPathFlags flowPathFlags) {
+ if (flowPathFlags == null)
+ return; // Nothing to do
+
+ // Discard the first Flow Entry
+ if (flowPathFlags.isDiscardFirstHopEntry()) {
+ if (flowEntries.size() > 0)
+ flowEntries.remove(0);
+ }
+
+ // Keep only the first Flow Entry
+ if (flowPathFlags.isKeepOnlyFirstHopEntry()) {
+ if (flowEntries.size() > 1) {
+ FlowEntry flowEntry = flowEntries.get(0);
+ flowEntries.clear();
+ flowEntries.add(flowEntry);
+ }
+ }
+ }
+
+ /**
+ * Remove Flow Entries that were deleted.
+ */
+ public void removeDeletedFlowEntries() {
+ //
+ // NOTE: We create a new ArrayList, and add only the Flow Entries
+ // that are NOT FE_USER_DELETE.
+ // This is sub-optimal: if it adds notable processing cost,
+ // the Flow Entries container should be changed to LinkedList
+ // or some other container that has O(1) cost of removing an entry.
+ //
+
+ // Test first whether any Flow Entry was deleted
+ boolean foundDeletedFlowEntry = false;
+ for (FlowEntry flowEntry : this.flowEntries) {
+ if (flowEntry.flowEntryUserState() ==
+ FlowEntryUserState.FE_USER_DELETE) {
+ foundDeletedFlowEntry = true;
+ break;
+ }
+ }
+ if (! foundDeletedFlowEntry)
+ return; // Nothing to do
+
+ // Create a new collection and exclude the deleted flow entries
+ ArrayList<FlowEntry> newFlowEntries = new ArrayList<FlowEntry>();
+ for (FlowEntry flowEntry : this.flowEntries()) {
+ if (flowEntry.flowEntryUserState() !=
+ FlowEntryUserState.FE_USER_DELETE) {
+ newFlowEntries.add(flowEntry);
+ }
+ }
+ setFlowEntries(newFlowEntries);
+ }
+
+ /**
+ * Convert the data path to a string.
+ *
+ * The string has the following form:
+ * [src=01:01:01:01:01:01:01:01/1111 flowEntry=<entry1> flowEntry=<entry2> flowEntry=<entry3> dst=02:02:02:02:02:02:02:02/2222]
+ *
+ * @return the data path as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[src=" + this.srcPort.toString();
+
+ for (FlowEntry fe : flowEntries) {
+ ret += " flowEntry=" + fe.toString();
+ }
+ ret += " dst=" + this.dstPort.toString() + "]";
+
+ return ret;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/DataPathEndpoints.java b/src/main/java/net/onrc/onos/core/util/DataPathEndpoints.java
new file mode 100644
index 0000000..eeb5aaa
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/DataPathEndpoints.java
@@ -0,0 +1,80 @@
+package net.onrc.onos.core.util;
+
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing the Data Path Endpoints.
+ */
+public class DataPathEndpoints {
+ private SwitchPort srcPort; // The source port
+ private SwitchPort dstPort; // The destination port
+
+ /**
+ * Default constructor.
+ */
+ public DataPathEndpoints() {
+ }
+
+ /**
+ * Constructor for given source and destination ports.
+ *
+ * @param srcPort the source port to use.
+ * @param dstPort the destination port to use.
+ */
+ public DataPathEndpoints(SwitchPort srcPort, SwitchPort dstPort) {
+ this.srcPort = srcPort;
+ this.dstPort = dstPort;
+ }
+
+ /**
+ * Get the data path source port.
+ *
+ * @return the data path source port.
+ */
+ @JsonProperty("srcPort")
+ public SwitchPort srcPort() { return srcPort; }
+
+ /**
+ * Set the data path source port.
+ *
+ * @param srcPort the data path source port to set.
+ */
+ @JsonProperty("srcPort")
+ public void setSrcPort(SwitchPort srcPort) {
+ this.srcPort = srcPort;
+ }
+
+ /**
+ * Get the data path destination port.
+ *
+ * @return the data path destination port.
+ */
+ @JsonProperty("dstPort")
+ public SwitchPort dstPort() { return dstPort; }
+
+ /**
+ * Set the data path destination port.
+ *
+ * @param dstPort the data path destination port to set.
+ */
+ @JsonProperty("dstPort")
+ public void setDstPort(SwitchPort dstPort) {
+ this.dstPort = dstPort;
+ }
+
+ /**
+ * Convert the data path endpoints to a string.
+ *
+ * The string has the following form:
+ * [src=01:01:01:01:01:01:01:01/1111 dst=02:02:02:02:02:02:02:02/2222]
+ *
+ * @return the data path endpoints as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[src=" + this.srcPort.toString() +
+ " dst=" + this.dstPort.toString() + "]";
+ return ret;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/Dpid.java b/src/main/java/net/onrc/onos/core/util/Dpid.java
new file mode 100644
index 0000000..dcc3a7c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/Dpid.java
@@ -0,0 +1,88 @@
+package net.onrc.onos.core.util;
+
+import net.onrc.onos.core.util.serializers.DpidDeserializer;
+import net.onrc.onos.core.util.serializers.DpidSerializer;
+
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.openflow.util.HexString;
+
+/**
+ * The class representing a network switch DPID.
+ */
+@JsonDeserialize(using=DpidDeserializer.class)
+@JsonSerialize(using=DpidSerializer.class)
+public class Dpid {
+ static public final long UNKNOWN = 0;
+
+ private long value;
+
+ /**
+ * Default constructor.
+ */
+ public Dpid() {
+ this.value = Dpid.UNKNOWN;
+ }
+
+ /**
+ * Constructor from a long value.
+ *
+ * @param value the value to use.
+ */
+ public Dpid(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public Dpid(String value) {
+ this.value = HexString.toLong(value);
+ }
+
+ /**
+ * Get the value of the DPID.
+ *
+ * @return the value of the DPID.
+ */
+ public long value() { return value; }
+
+ /**
+ * Set the value of the DPID.
+ *
+ * @param value the value to set.
+ */
+ public void setValue(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Convert the DPID value to a ':' separated hexadecimal string.
+ *
+ * @return the DPID value as a ':' separated hexadecimal string.
+ */
+ @Override
+ public String toString() {
+ return HexString.toHexString(this.value);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Dpid)) {
+ return false;
+ }
+
+ Dpid otherDpid = (Dpid) other;
+
+ return value == otherDpid.value;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 17;
+ hash += 31 * hash + (int)(value ^ value >>> 32);
+ return hash;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/EventEntry.java b/src/main/java/net/onrc/onos/core/util/EventEntry.java
new file mode 100644
index 0000000..fe4c3ac
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/EventEntry.java
@@ -0,0 +1,64 @@
+package net.onrc.onos.core.util;
+
+/**
+ * Class for encapsulating events with event-related data entry.
+ */
+public class EventEntry<T> {
+ /**
+ * The event types.
+ */
+ public enum Type {
+ ENTRY_ADD, // Add or update an entry
+ ENTRY_REMOVE // Remove an entry
+ }
+
+ private Type eventType; // The event type
+ private T eventData; // The relevant event data entry
+
+ /**
+ * Constructor for a given event type and event-related data entry.
+ *
+ * @param eventType the event type.
+ * @param eventData the event data entry.
+ */
+ public EventEntry(EventEntry.Type eventType, T eventData) {
+ this.eventType = eventType;
+ this.eventData = eventData;
+ }
+
+ /**
+ * Test whether the event type is ENTRY_ADD.
+ *
+ * @return true if the event type is ENTRY_ADD, otherwise false.
+ */
+ public boolean isAdd() {
+ return (this.eventType == Type.ENTRY_ADD);
+ }
+
+ /**
+ * Test whether the event type is ENTRY_REMOVE.
+ *
+ * @return true if the event type is ENTRY_REMOVE, otherwise false.
+ */
+ public boolean isRemove() {
+ return (this.eventType == Type.ENTRY_REMOVE);
+ }
+
+ /**
+ * Get the event type.
+ *
+ * @return the event type.
+ */
+ public EventEntry.Type eventType() {
+ return this.eventType;
+ }
+
+ /**
+ * Get the event-related data entry.
+ *
+ * @return the event-related data entry.
+ */
+ public T eventData() {
+ return this.eventData;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowEntry.java b/src/main/java/net/onrc/onos/core/util/FlowEntry.java
new file mode 100644
index 0000000..fb5e3a3
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowEntry.java
@@ -0,0 +1,462 @@
+package net.onrc.onos.core.util;
+
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing the Flow Entry.
+ *
+ * NOTE: The specification is incomplete. E.g., the entry needs to
+ * support multiple in-ports and multiple out-ports.
+ */
+public class FlowEntry {
+ private FlowId flowId; // FlowID of the Flow Entry
+ private FlowEntryId flowEntryId; // The Flow Entry ID
+ private int idleTimeout; // The Flow idle timeout
+ private int hardTimeout; // The Flow hard timeout
+ private int priority; // The Flow priority
+ private FlowEntryMatch flowEntryMatch; // The Flow Entry Match
+ private FlowEntryActions flowEntryActions; // The Flow Entry Actions
+ private Dpid dpid; // The Switch DPID
+ private Port inPort; // The Switch incoming port. Used only
+ // when the entry is used to return
+ // Shortest Path computation.
+ private Port outPort; // The Switch outgoing port. Used only
+ // when the entry is used to return
+ // Shortest Path computation.
+ private FlowEntryUserState flowEntryUserState; // The Flow Entry User state
+ private FlowEntrySwitchState flowEntrySwitchState; // The Flow Entry Switch state
+ // The Flow Entry Error state (if FlowEntrySwitchState is FE_SWITCH_FAILED)
+ private FlowEntryErrorState flowEntryErrorState;
+
+ /**
+ * Default constructor.
+ */
+ public FlowEntry() {
+ // TODO: Test code
+ /*
+ MACAddress mac = MACAddress.valueOf("01:02:03:04:05:06");
+ IPv4 ipv4 = new IPv4("1.2.3.4");
+ IPv4Net ipv4net = new IPv4Net("5.6.7.0/24");
+
+ flowEntryMatch = new FlowEntryMatch();
+ flowEntryMatch.enableInPort(new Port((short)10));
+ flowEntryMatch.enableSrcMac(mac);
+ flowEntryMatch.enableDstMac(mac);
+ flowEntryMatch.enableVlanId((short)20);
+ flowEntryMatch.enableVlanPriority((byte)30);
+ flowEntryMatch.enableEthernetFrameType((short)40);
+ flowEntryMatch.enableIpToS((byte)50);
+ flowEntryMatch.enableIpProto((byte)60);
+ flowEntryMatch.enableSrcIPv4Net(ipv4net);
+ flowEntryMatch.enableDstIPv4Net(ipv4net);
+ flowEntryMatch.enableSrcTcpUdpPort((short)70);
+ flowEntryMatch.enableDstTcpUdpPort((short)80);
+
+ FlowEntryAction action = null;
+ FlowEntryActions actions = new FlowEntryActions();
+
+ action = new FlowEntryAction();
+ action.setActionOutput(new Port((short)12));
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionOutputToController((short)13);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetVlanId((short)14);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetVlanPriority((byte)15);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionStripVlan(true);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetEthernetSrcAddr(mac);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetEthernetDstAddr(mac);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetIPv4SrcAddr(ipv4);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetIPv4DstAddr(ipv4);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetIpToS((byte)16);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetTcpUdpSrcPort((short)17);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionSetTcpUdpDstPort((short)18);
+ actions.addAction(action);
+
+ action = new FlowEntryAction();
+ action.setActionEnqueue(new Port((short)19), 20);
+ actions.addAction(action);
+
+ setFlowEntryActions(actions);
+ */
+
+ priority = FlowPath.PRIORITY_DEFAULT;
+ flowEntryActions = new FlowEntryActions();
+ flowEntryUserState = FlowEntryUserState.FE_USER_UNKNOWN;
+ flowEntrySwitchState = FlowEntrySwitchState.FE_SWITCH_UNKNOWN;
+ }
+
+ /**
+ * Get the Flow ID.
+ *
+ * @return the Flow ID.
+ */
+ @JsonIgnore
+ public FlowId flowId() { return flowId; }
+
+ /**
+ * Set the Flow ID.
+ *
+ * @param flowId the Flow ID to set.
+ */
+ public void setFlowId(FlowId flowId) {
+ this.flowId = flowId;
+ }
+
+ /**
+ * Test whether the Flow ID is valid.
+ *
+ * @return true if the Flow ID is valid, otherwise false.
+ */
+ @JsonIgnore
+ public boolean isValidFlowId() {
+ if (this.flowId == null)
+ return false;
+ return (this.flowId.isValid());
+ }
+
+ /**
+ * Get the Flow Entry ID.
+ *
+ * @return the Flow Entry ID.
+ */
+ @JsonProperty("flowEntryId")
+ public FlowEntryId flowEntryId() { return flowEntryId; }
+
+ /**
+ * Set the Flow Entry ID.
+ *
+ * @param flowEntryId the Flow Entry ID to set.
+ */
+ @JsonProperty("flowEntryId")
+ public void setFlowEntryId(FlowEntryId flowEntryId) {
+ this.flowEntryId = flowEntryId;
+ }
+
+ /**
+ * Test whether the Flow Entry ID is valid.
+ *
+ * @return true if the Flow Entry ID is valid, otherwise false.
+ */
+ @JsonIgnore
+ public boolean isValidFlowEntryId() {
+ if (this.flowEntryId == null)
+ return false;
+ return (this.flowEntryId.isValid());
+ }
+
+ /**
+ * Get the flow idle timeout in seconds.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ * If zero, the timeout is not set.
+ *
+ * @return the flow idle timeout.
+ */
+ @JsonProperty("idleTimeout")
+ public int idleTimeout() { return idleTimeout; }
+
+ /**
+ * Set the flow idle timeout in seconds.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ * If zero, the timeout is not set.
+ *
+ * @param idleTimeout the flow idle timeout to set.
+ */
+ @JsonProperty("idleTimeout")
+ public void setIdleTimeout(int idleTimeout) {
+ this.idleTimeout = 0xffff & idleTimeout;
+ }
+
+ /**
+ * Get the flow hard timeout in seconds.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ * If zero, the timeout is not set.
+ *
+ * @return the flow hard timeout.
+ */
+ @JsonProperty("hardTimeout")
+ public int hardTimeout() { return hardTimeout; }
+
+ /**
+ * Set the flow hard timeout in seconds.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ * If zero, the timeout is not set.
+ *
+ * @param hardTimeout the flow hard timeout to set.
+ */
+ @JsonProperty("hardTimeout")
+ public void setHardTimeout(int hardTimeout) {
+ this.hardTimeout = 0xffff & hardTimeout;
+ }
+
+ /**
+ * Get the flow priority.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ *
+ * @return the flow priority.
+ */
+ @JsonProperty("priority")
+ public int priority() { return priority; }
+
+ /**
+ * Set the flow priority.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ *
+ * @param priority the flow priority to set.
+ */
+ @JsonProperty("priority")
+ public void setPriority(int priority) {
+ this.priority = 0xffff & priority;
+ }
+
+ /**
+ * Get the Flow Entry Match.
+ *
+ * @return the Flow Entry Match.
+ */
+ @JsonProperty("flowEntryMatch")
+ public FlowEntryMatch flowEntryMatch() { return flowEntryMatch; }
+
+ /**
+ * Set the Flow Entry Match.
+ *
+ * @param flowEntryMatch the Flow Entry Match to set.
+ */
+ @JsonProperty("flowEntryMatch")
+ public void setFlowEntryMatch(FlowEntryMatch flowEntryMatch) {
+ this.flowEntryMatch = flowEntryMatch;
+ }
+
+ /**
+ * Get the Flow Entry Actions.
+ *
+ * @return the Flow Entry Actions.
+ */
+ @JsonProperty("flowEntryActions")
+ public FlowEntryActions flowEntryActions() {
+ return flowEntryActions;
+ }
+
+ /**
+ * Set the Flow Entry Actions.
+ *
+ * @param flowEntryActions the Flow Entry Actions to set.
+ */
+ @JsonProperty("flowEntryActions")
+ public void setFlowEntryActions(FlowEntryActions flowEntryActions) {
+ this.flowEntryActions = flowEntryActions;
+ }
+
+ /**
+ * Get the Switch DPID.
+ *
+ * @return the Switch DPID.
+ */
+ @JsonProperty("dpid")
+ public Dpid dpid() { return dpid; }
+
+ /**
+ * Set the Switch DPID.
+ *
+ * @param dpid the Switch DPID to set.
+ */
+ @JsonProperty("dpid")
+ public void setDpid(Dpid dpid) {
+ this.dpid = dpid;
+ }
+
+ /**
+ * Get the Switch incoming port.
+ *
+ * Used only when the entry is used to return Shortest Path computation.
+ *
+ * @return the Switch incoming port.
+ */
+ @JsonProperty("inPort")
+ public Port inPort() { return inPort; }
+
+ /**
+ * Set the Switch incoming port.
+ *
+ * Used only when the entry is used to return Shortest Path computation.
+ *
+ * @param inPort the Switch incoming port to set.
+ */
+ @JsonProperty("inPort")
+ public void setInPort(Port inPort) {
+ this.inPort = inPort;
+ }
+
+ /**
+ * Get the Switch outgoing port.
+ *
+ * Used only when the entry is used to return Shortest Path computation.
+ *
+ * @return the Switch outgoing port.
+ */
+ @JsonProperty("outPort")
+ public Port outPort() { return outPort; }
+
+ /**
+ * Set the Switch outgoing port.
+ *
+ * Used only when the entry is used to return Shortest Path computation.
+ *
+ * @param outPort the Switch outgoing port to set.
+ */
+ @JsonProperty("outPort")
+ public void setOutPort(Port outPort) {
+ this.outPort = outPort;
+ }
+
+ /**
+ * Get the Flow Entry User state.
+ *
+ * @return the Flow Entry User state.
+ */
+ @JsonProperty("flowEntryUserState")
+ public FlowEntryUserState flowEntryUserState() {
+ return flowEntryUserState;
+ }
+
+ /**
+ * Set the Flow Entry User state.
+ *
+ * @param flowEntryUserState the Flow Entry User state to set.
+ */
+ @JsonProperty("flowEntryUserState")
+ public void setFlowEntryUserState(FlowEntryUserState flowEntryUserState) {
+ this.flowEntryUserState = flowEntryUserState;
+ }
+
+ /**
+ * Get the Flow Entry Switch state.
+ *
+ * The Flow Entry Error state is used if FlowEntrySwitchState is
+ * FE_SWITCH_FAILED.
+ *
+ * @return the Flow Entry Switch state.
+ */
+ @JsonProperty("flowEntrySwitchState")
+ public FlowEntrySwitchState flowEntrySwitchState() {
+ return flowEntrySwitchState;
+ }
+
+ /**
+ * Set the Flow Entry Switch state.
+ *
+ * The Flow Entry Error state is used if FlowEntrySwitchState is
+ * FE_SWITCH_FAILED.
+ *
+ * @param flowEntrySwitchState the Flow Entry Switch state to set.
+ */
+ @JsonProperty("flowEntrySwitchState")
+ public void setFlowEntrySwitchState(FlowEntrySwitchState flowEntrySwitchState) {
+ this.flowEntrySwitchState = flowEntrySwitchState;
+ }
+
+ /**
+ * Get the Flow Entry Error state.
+ *
+ * @return the Flow Entry Error state.
+ */
+ @JsonProperty("flowEntryErrorState")
+ public FlowEntryErrorState flowEntryErrorState() {
+ return flowEntryErrorState;
+ }
+
+ /**
+ * Set the Flow Entry Error state.
+ *
+ * @param flowEntryErrorState the Flow Entry Error state to set.
+ */
+ @JsonProperty("flowEntryErrorState")
+ public void setFlowEntryErrorState(FlowEntryErrorState flowEntryErrorState) {
+ this.flowEntryErrorState = flowEntryErrorState;
+ }
+
+ /**
+ * Convert the flow entry to a string.
+ *
+ * The string has the following form:
+ * [flowEntryId=XXX idleTimeout=XXX hardTimeout=XXX priority=XXX
+ * flowEntryMatch=XXX flowEntryActions=XXX dpid=XXX
+ * inPort=XXX outPort=XXX flowEntryUserState=XXX flowEntrySwitchState=XXX
+ * flowEntryErrorState=XXX]
+ * @return the flow entry as a string.
+ */
+ @Override
+ public String toString() {
+ StringBuilder ret = new StringBuilder();
+ if ( flowEntryId != null ) {
+ ret.append("[flowEntryId=" + this.flowEntryId.toString());
+ } else {
+ ret.append("[");
+ }
+ if ( flowId != null ) {
+ ret.append(" flowId=" + this.flowId.toString());
+ }
+ ret.append(" idleTimeout=" + this.idleTimeout);
+ ret.append(" hardTimeout=" + this.hardTimeout);
+ ret.append(" priority=" + this.priority);
+ if ( flowEntryMatch != null ) {
+ ret.append(" flowEntryMatch=" + this.flowEntryMatch.toString());
+ }
+ ret.append(" flowEntryActions=" + this.flowEntryActions.toString() );
+ if ( dpid != null ) {
+ ret.append(" dpid=" + this.dpid.toString());
+ }
+ if ( inPort != null ) {
+ ret.append(" inPort=" + this.inPort.toString());
+ }
+ if ( outPort != null ) {
+ ret.append(" outPort=" + this.outPort.toString());
+ }
+ ret.append(" flowEntryUserState=" + this.flowEntryUserState);
+ ret.append(" flowEntrySwitchState=" + this.flowEntrySwitchState);
+ if ( flowEntryErrorState != null ) {
+ ret.append(" flowEntryErrorState=" + this.flowEntryErrorState.toString());
+ }
+ ret.append("]");
+
+ return ret.toString();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowEntryAction.java b/src/main/java/net/onrc/onos/core/util/FlowEntryAction.java
new file mode 100644
index 0000000..da86ed3
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowEntryAction.java
@@ -0,0 +1,1677 @@
+package net.onrc.onos.core.util;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing a single Flow Entry action.
+ *
+ * A Flow Entry action that needs to be applied to each packet.
+ * Note that it contains only a single action. Multiple actions are
+ * listed in a list inside @ref FlowEntryActions.
+ */
+public class FlowEntryAction {
+ /**
+ * Special action values.
+ *
+ * Those values are taken as-is from the OpenFlow-v1.0.0 specification
+ * (pp 21-22).
+ */
+ public enum ActionValues {
+ ACTION_OUTPUT ((short)0x0), // Output to switch port
+ ACTION_SET_VLAN_VID ((short)0x1), // Set the 802.1q VLAN id
+ ACTION_SET_VLAN_PCP ((short)0x2), // Set the 802.1q priority
+ ACTION_STRIP_VLAN ((short)0x3), // Strip the 802.1q header
+ ACTION_SET_DL_SRC ((short)0x4), // Ethernet source address
+ ACTION_SET_DL_DST ((short)0x5), // Ethernet destination address
+ ACTION_SET_NW_SRC ((short)0x6), // IP source address
+ ACTION_SET_NW_DST ((short)0x7), // IP destination address
+ ACTION_SET_NW_TOS ((short)0x8), // IP ToS (DSCP field, 6 bits)
+ ACTION_SET_TP_SRC ((short)0x9), // TCP/UDP source port
+ ACTION_SET_TP_DST ((short)0xa), // TCP/UDP destination port
+ ACTION_ENQUEUE ((short)0xb), // Output to queue on port
+ ACTION_VENDOR ((short)0xffff); // Vendor-specific
+
+ private final short value; // The value
+
+ /**
+ * Constructor for a given value.
+ *
+ * @param value the value to use for the initialization.
+ */
+ private ActionValues(short value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the value.
+ *
+ * @return the value.
+ */
+ public short getValue() { return value; }
+ }
+
+ /**
+ * Action structure for ACTION_OUTPUT: Output to switch port.
+ */
+ public static class ActionOutput {
+ private Port port; // Output port
+ private short maxLen; // Max. length (in bytes) to send to controller
+ // if the port is set to PORT_CONTROLLER
+
+ /**
+ * Default constructor.
+ */
+ public ActionOutput() {
+ this.port = null;
+ this.maxLen = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionOutput(ActionOutput other) {
+ if (other.port != null)
+ this.port = new Port(other.port);
+ this.maxLen = other.maxLen;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [port=XXX maxLen=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionOutput(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given output port and maximum length.
+ *
+ * @param port the output port to set.
+ * @param maxLen the maximum length (in bytes) to send to controller
+ * if the port is set to PORT_CONTROLLER.
+ */
+ public ActionOutput(Port port, short maxLen) {
+ this.port = port;
+ this.maxLen = maxLen;
+ }
+
+ /**
+ * Constructor for a given output port.
+ *
+ * @param port the output port to set.
+ */
+ public ActionOutput(Port port) {
+ this.port = port;
+ this.maxLen = 0;
+ }
+
+ /**
+ * Get the output port.
+ *
+ * @return the output port.
+ */
+ @JsonProperty("port")
+ public Port port() {
+ return this.port;
+ }
+
+ /**
+ * Get the maximum length (in bytes) to send to controller if the
+ * port is set to PORT_CONTROLLER.
+ *
+ * @return the maximum length (in bytes) to send to controller if the
+ * port is set to PORT_CONTROLLER.
+ */
+ @JsonProperty("maxLen")
+ public short maxLen() {
+ return this.maxLen;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [port=XXX maxLen=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "port=" + port.toString();
+ ret += " maxLen=" + maxLen;
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [port=XXX maxLen=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split(" ");
+ String decode = null;
+
+ // Decode the "port=XXX" part
+ if (parts.length > 0)
+ decode = parts[0];
+ if (decode != null) {
+ String[] tokens = decode.split("port=");
+ if (tokens.length > 1 && tokens[1] != null) {
+ try {
+ Short valueShort = Short.valueOf(tokens[1]);
+ port = new Port(valueShort);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+
+ // Decode the "maxLen=XXX" part
+ decode = null;
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ String[] tokens = decode.split("maxLen=");
+ if (tokens.length > 1 && tokens[1] != null) {
+ try {
+ maxLen = Short.valueOf(tokens[1]);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_VLAN_VID: Set the 802.1q VLAN id
+ */
+ public static class ActionSetVlanId {
+ private short vlanId; // The VLAN ID to set
+
+ /**
+ * Default constructor.
+ */
+ public ActionSetVlanId() {
+ this.vlanId = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionSetVlanId(ActionSetVlanId other) {
+ this.vlanId = other.vlanId;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [vlanId=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionSetVlanId(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given VLAN ID.
+ *
+ * @param vlanId the VLAN ID to set.
+ */
+ public ActionSetVlanId(short vlanId) {
+ this.vlanId = vlanId;
+ }
+
+ /**
+ * Get the VLAN ID.
+ *
+ * @return the VLAN ID.
+ */
+ @JsonProperty("vlanId")
+ public short vlanId() {
+ return this.vlanId;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [vlanId=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "vlanId=" + this.vlanId;
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [vlanId=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split("vlanId=");
+ String decode = null;
+
+ // Decode the value
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ try {
+ vlanId = Short.valueOf(decode);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_VLAN_PCP: Set the 802.1q priority
+ */
+ public static class ActionSetVlanPriority {
+ private byte vlanPriority; // The VLAN priority to set
+
+ /**
+ * Default constructor.
+ */
+ public ActionSetVlanPriority() {
+ this.vlanPriority = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionSetVlanPriority(ActionSetVlanPriority other) {
+ this.vlanPriority = other.vlanPriority;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [vlanPriority=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionSetVlanPriority(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given VLAN priority.
+ *
+ * @param vlanPriority the VLAN priority to set.
+ */
+ public ActionSetVlanPriority(byte vlanPriority) {
+ this.vlanPriority = vlanPriority;
+ }
+
+ /**
+ * Get the VLAN priority.
+ *
+ * @return the VLAN priority.
+ */
+ @JsonProperty("vlanPriority")
+ public byte vlanPriority() {
+ return this.vlanPriority;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [vlanPriority=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "vlanPriority=" + this.vlanPriority;
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [vlanPriority=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split("vlanPriority=");
+ String decode = null;
+
+ // Decode the value
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ try {
+ vlanPriority = Byte.valueOf(decode);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ /**
+ * Action structure for ACTION_STRIP_VLAN: Strip the 802.1q header
+ */
+ public static class ActionStripVlan {
+ private boolean stripVlan; // If true, strip the VLAN header
+
+ /**
+ * Default constructor.
+ */
+ public ActionStripVlan() {
+ this.stripVlan = false;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionStripVlan(ActionStripVlan other) {
+ this.stripVlan = other.stripVlan;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [stripVlan=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionStripVlan(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given boolean flag.
+ *
+ * @param stripVlan if true, strip the VLAN header.
+ */
+ public ActionStripVlan(boolean stripVlan) {
+ this.stripVlan = stripVlan;
+ }
+
+ /**
+ * Get the boolean flag whether the VLAN header should be stripped.
+ *
+ * @return the boolean flag whether the VLAN header should be stripped.
+ */
+ @JsonProperty("stripVlan")
+ public boolean stripVlan() {
+ return this.stripVlan;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [stripVlan=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "stripVlan=" + this.stripVlan;
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [stripVlan=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split("stripVlan=");
+ String decode = null;
+
+ // Decode the value
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ stripVlan = Boolean.valueOf(decode);
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_DL_SRC and ACTION_SET_DL_DST:
+ * Set the Ethernet source/destination address.
+ */
+ public static class ActionSetEthernetAddr {
+ private MACAddress addr; // The MAC address to set
+
+ /**
+ * Default constructor.
+ */
+ public ActionSetEthernetAddr() {
+ this.addr = null;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionSetEthernetAddr(ActionSetEthernetAddr other) {
+ if (other.addr != null)
+ this.addr = MACAddress.valueOf(other.addr.toLong());
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [addr=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionSetEthernetAddr(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given MAC address.
+ *
+ * @param addr the MAC address to set.
+ */
+ public ActionSetEthernetAddr(MACAddress addr) {
+ this.addr = addr;
+ }
+
+ /**
+ * Get the MAC address.
+ *
+ * @return the MAC address.
+ */
+ @JsonProperty("addr")
+ public MACAddress addr() {
+ return this.addr;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [addr=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "addr=" + addr.toString();
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [addr=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split("addr=");
+ String decode = null;
+
+ // Decode the value
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ try {
+ addr = MACAddress.valueOf(decode);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_NW_SRC and ACTION_SET_NW_DST:
+ * Set the IPv4 source/destination address.
+ */
+ public static class ActionSetIPv4Addr {
+ private IPv4 addr; // The IPv4 address to set
+
+ /**
+ * Default constructor.
+ */
+ public ActionSetIPv4Addr() {
+ this.addr = null;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionSetIPv4Addr(ActionSetIPv4Addr other) {
+ if (other.addr != null)
+ this.addr = new IPv4(other.addr);
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [addr=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionSetIPv4Addr(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given IPv4 address.
+ *
+ * @param addr the IPv4 address to set.
+ */
+ public ActionSetIPv4Addr(IPv4 addr) {
+ this.addr = addr;
+ }
+
+ /**
+ * Get the IPv4 address.
+ *
+ * @return the IPv4 address.
+ */
+ @JsonProperty("addr")
+ public IPv4 addr() {
+ return this.addr;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [addr=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "addr=" + addr.toString();
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [addr=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split("addr=");
+ String decode = null;
+
+ // Decode the value
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ try {
+ addr = new IPv4(decode);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_NW_TOS:
+ * Set the IP ToS (DSCP field, 6 bits).
+ */
+ public static class ActionSetIpToS {
+ private byte ipToS; // The IP ToS to set DSCP field, 6 bits)
+
+ /**
+ * Default constructor.
+ */
+ public ActionSetIpToS() {
+ this.ipToS = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionSetIpToS(ActionSetIpToS other) {
+ this.ipToS = other.ipToS;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [ipToS=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionSetIpToS(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given IP ToS (DSCP field, 6 bits).
+ *
+ * @param ipToS the IP ToS (DSCP field, 6 bits) to set.
+ */
+ public ActionSetIpToS(byte ipToS) {
+ this.ipToS = ipToS;
+ }
+
+ /**
+ * Get the IP ToS (DSCP field, 6 bits).
+ *
+ * @return the IP ToS (DSCP field, 6 bits).
+ */
+ @JsonProperty("ipToS")
+ public byte ipToS() {
+ return this.ipToS;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [ipToS=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "ipToS=" + ipToS;
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [ipToS=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split("ipToS=");
+ String decode = null;
+
+ // Decode the value
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ try {
+ ipToS = Byte.valueOf(decode);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ /**
+ * Action structure for ACTION_SET_TP_SRC and ACTION_SET_TP_DST:
+ * Set the TCP/UDP source/destination port.
+ */
+ public static class ActionSetTcpUdpPort {
+ private short port; // The TCP/UDP port to set
+
+ /**
+ * Default constructor.
+ */
+ public ActionSetTcpUdpPort() {
+ this.port = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionSetTcpUdpPort(ActionSetTcpUdpPort other) {
+ this.port = other.port;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [port=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionSetTcpUdpPort(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given TCP/UDP port.
+ *
+ * @param port the TCP/UDP port to set.
+ */
+ public ActionSetTcpUdpPort(short port) {
+ this.port = port;
+ }
+
+ /**
+ * Get the TCP/UDP port.
+ *
+ * @return the TCP/UDP port.
+ */
+ @JsonProperty("port")
+ public short port() {
+ return this.port;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [port=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "port=" + port;
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [port=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split("port=");
+ String decode = null;
+
+ // Decode the value
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ try {
+ port = Short.valueOf(decode);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ /**
+ * Action structure for ACTION_ENQUEUE: Output to queue on port.
+ */
+ public static class ActionEnqueue {
+ private Port port; // Port that queue belongs. Should
+ // refer to a valid physical port
+ // (i.e. < PORT_MAX) or PORT_IN_PORT
+ private int queueId; // Where to enqueue the packets
+
+ /**
+ * Default constructor.
+ */
+ public ActionEnqueue() {
+ this.port = null;
+ this.queueId = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public ActionEnqueue(ActionEnqueue other) {
+ if (other.port != null)
+ this.port = new Port(other.port);
+ this.queueId = other.queueId;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [port=XXX queueId=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public ActionEnqueue(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Constructor for a given port and queue ID.
+ *
+ * @param port the port to set.
+ * @param queueId the queue ID on the port.
+ */
+ public ActionEnqueue(Port port, int queueId) {
+ this.port = port;
+ this.queueId = queueId;
+ }
+
+ /**
+ * Get the port.
+ *
+ * @return the port.
+ */
+ @JsonProperty("port")
+ public Port port() {
+ return this.port;
+ }
+
+ /**
+ * Get the queue ID.
+ *
+ * @return the queue ID.
+ */
+ @JsonProperty("queueId")
+ public int queueId() {
+ return this.queueId;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [port=XXX queueId=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "port=" + port.toString();
+ ret += " queueId=" + queueId;
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [port=XXX queueId=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split(" ");
+ String decode = null;
+
+ // Decode the "port=XXX" part
+ if (parts.length > 0)
+ decode = parts[0];
+ if (decode != null) {
+ String[] tokens = decode.split("port=");
+ if (tokens.length > 1 && tokens[1] != null) {
+ try {
+ Short valueShort = Short.valueOf(tokens[1]);
+ port = new Port(valueShort);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+
+ // Decode the "queueId=XXX" part
+ decode = null;
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode != null) {
+ decode = decode.replace("]", "");
+ String[] tokens = decode.split("queueId=");
+ if (tokens.length > 1 && tokens[1] != null) {
+ try {
+ queueId = Short.valueOf(tokens[1]);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+ }
+
+ private ActionValues actionType; // The action type
+
+ //
+ // The actions.
+ // NOTE: Only one action should be set.
+ //
+ private ActionOutput actionOutput;
+ private ActionSetVlanId actionSetVlanId;
+ private ActionSetVlanPriority actionSetVlanPriority;
+ private ActionStripVlan actionStripVlan;
+ private ActionSetEthernetAddr actionSetEthernetSrcAddr;
+ private ActionSetEthernetAddr actionSetEthernetDstAddr;
+ private ActionSetIPv4Addr actionSetIPv4SrcAddr;
+ private ActionSetIPv4Addr actionSetIPv4DstAddr;
+ private ActionSetIpToS actionSetIpToS;
+ private ActionSetTcpUdpPort actionSetTcpUdpSrcPort;
+ private ActionSetTcpUdpPort actionSetTcpUdpDstPort;
+ private ActionEnqueue actionEnqueue;
+
+ /**
+ * Default constructor.
+ */
+ public FlowEntryAction() {
+ actionType = ActionValues.ACTION_VENDOR; // XXX: Initial value
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public FlowEntryAction(FlowEntryAction other) {
+ this.actionType = other.actionType;
+
+ //
+ if (other.actionOutput != null)
+ this.actionOutput = new ActionOutput(other.actionOutput);
+ else
+ this.actionOutput = null;
+ //
+ if (other.actionSetVlanId != null)
+ this.actionSetVlanId = new ActionSetVlanId(other.actionSetVlanId);
+ else
+ this.actionSetVlanId = null;
+ //
+ if (other.actionSetVlanPriority != null)
+ this.actionSetVlanPriority = new ActionSetVlanPriority(other.actionSetVlanPriority);
+ else
+ this.actionSetVlanPriority = null;
+ //
+ if (other.actionStripVlan != null)
+ this.actionStripVlan = new ActionStripVlan(other.actionStripVlan);
+ else
+ this.actionStripVlan = null;
+ //
+ if (other.actionSetEthernetSrcAddr != null)
+ this.actionSetEthernetSrcAddr = new ActionSetEthernetAddr(other.actionSetEthernetSrcAddr);
+ else
+ this.actionSetEthernetSrcAddr = null;
+ //
+ if (other.actionSetEthernetDstAddr != null)
+ this.actionSetEthernetDstAddr = new ActionSetEthernetAddr(other.actionSetEthernetDstAddr);
+ else
+ this.actionSetEthernetDstAddr = null;
+ //
+ if (other.actionSetIPv4SrcAddr != null)
+ this.actionSetIPv4SrcAddr = new ActionSetIPv4Addr(other.actionSetIPv4SrcAddr);
+ else
+ this.actionSetIPv4SrcAddr = null;
+ //
+ if (other.actionSetIPv4DstAddr != null)
+ this.actionSetIPv4DstAddr = new ActionSetIPv4Addr(other.actionSetIPv4DstAddr);
+ else
+ this.actionSetIPv4DstAddr = null;
+ //
+ if (other.actionSetIpToS != null)
+ this.actionSetIpToS = new ActionSetIpToS(other.actionSetIpToS);
+ else
+ this.actionSetIpToS = null;
+ //
+ if (other.actionSetTcpUdpSrcPort != null)
+ this.actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(other.actionSetTcpUdpSrcPort);
+ else
+ this.actionSetTcpUdpSrcPort = null;
+ //
+ if (other.actionSetTcpUdpDstPort != null)
+ this.actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(other.actionSetTcpUdpDstPort);
+ else
+ this.actionSetTcpUdpDstPort = null;
+ //
+ if (other.actionEnqueue != null)
+ this.actionEnqueue = new ActionEnqueue(other.actionEnqueue);
+ else
+ this.actionEnqueue = null;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [type=XXX action=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public FlowEntryAction(String actionStr) {
+ this.fromString(actionStr);
+ }
+
+ /**
+ * Get the action type.
+ *
+ * @return the action type.
+ */
+ @JsonProperty("actionType")
+ public ActionValues actionType() { return actionType; }
+
+ /**
+ * Get the output action.
+ *
+ * @return the output action.
+ */
+ @JsonProperty("actionOutput")
+ public ActionOutput actionOutput() { return actionOutput; }
+
+ /**
+ * Set the output action on a port.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionOutput")
+ public void setActionOutput(ActionOutput action) {
+ actionOutput = action;
+ actionType = ActionValues.ACTION_OUTPUT;
+ }
+
+ /**
+ * Set the output action on a port.
+ *
+ * @param port the output port to set.
+ */
+ public void setActionOutput(Port port) {
+ actionOutput = new ActionOutput(port);
+ actionType = ActionValues.ACTION_OUTPUT;
+ }
+
+ /**
+ * Set the output action to controller.
+ *
+ * @param maxLen the maximum length (in bytes) to send to controller.
+ */
+ public void setActionOutputToController(short maxLen) {
+ Port port = new Port(Port.PortValues.PORT_CONTROLLER);
+ actionOutput = new ActionOutput(port, maxLen);
+ actionType = ActionValues.ACTION_OUTPUT;
+ }
+
+ /**
+ * Get the action to set the VLAN ID.
+ *
+ * @return the action to set the VLAN ID.
+ */
+ @JsonProperty("actionSetVlanId")
+ public ActionSetVlanId actionSetVlanId() { return actionSetVlanId; }
+
+ /**
+ * Set the action to set the VLAN ID.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetVlanId")
+ public void setActionSetVlanId(ActionSetVlanId action) {
+ actionSetVlanId = action;
+ actionType = ActionValues.ACTION_SET_VLAN_VID;
+ }
+
+ /**
+ * Set the action to set the VLAN ID.
+ *
+ * @param vlanId the VLAN ID to set.
+ */
+ public void setActionSetVlanId(short vlanId) {
+ actionSetVlanId = new ActionSetVlanId(vlanId);
+ actionType = ActionValues.ACTION_SET_VLAN_VID;
+ }
+
+ /**
+ * Get the action to set the VLAN priority.
+ *
+ * @return the action to set the VLAN priority.
+ */
+ @JsonProperty("actionSetVlanPriority")
+ public ActionSetVlanPriority actionSetVlanPriority() {
+ return actionSetVlanPriority;
+ }
+
+ /**
+ * Set the action to set the VLAN priority.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetVlanPriority")
+ public void setActionSetVlanPriority(ActionSetVlanPriority action) {
+ actionSetVlanPriority = action;
+ actionType = ActionValues.ACTION_SET_VLAN_PCP;
+ }
+
+ /**
+ * Set the action to set the VLAN priority.
+ *
+ * @param vlanPriority the VLAN priority to set.
+ */
+ public void setActionSetVlanPriority(byte vlanPriority) {
+ actionSetVlanPriority = new ActionSetVlanPriority(vlanPriority);
+ actionType = ActionValues.ACTION_SET_VLAN_PCP;
+ }
+
+ /**
+ * Get the action to strip the VLAN header.
+ *
+ * @return the action to strip the VLAN header.
+ */
+ @JsonProperty("actionStripVlan")
+ public ActionStripVlan actionStripVlan() {
+ return actionStripVlan;
+ }
+
+ /**
+ * Set the action to strip the VLAN header.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionStripVlan")
+ public void setActionStripVlan(ActionStripVlan action) {
+ actionStripVlan = action;
+ actionType = ActionValues.ACTION_STRIP_VLAN;
+ }
+
+ /**
+ * Set the action to strip the VLAN header.
+ *
+ * @param stripVlan if true, strip the VLAN header.
+ */
+ public void setActionStripVlan(boolean stripVlan) {
+ actionStripVlan = new ActionStripVlan(stripVlan);
+ actionType = ActionValues.ACTION_STRIP_VLAN;
+ }
+
+ /**
+ * Get the action to set the Ethernet source address.
+ *
+ * @return the action to set the Ethernet source address.
+ */
+ @JsonProperty("actionSetEthernetSrcAddr")
+ public ActionSetEthernetAddr actionSetEthernetSrcAddr() {
+ return actionSetEthernetSrcAddr;
+ }
+
+ /**
+ * Set the action to set the Ethernet source address.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetEthernetSrcAddr")
+ public void setActionSetEthernetSrcAddr(ActionSetEthernetAddr action) {
+ actionSetEthernetSrcAddr = action;
+ actionType = ActionValues.ACTION_SET_DL_SRC;
+ }
+
+ /**
+ * Set the action to set the Ethernet source address.
+ *
+ * @param addr the MAC address to set as the Ethernet source address.
+ */
+ public void setActionSetEthernetSrcAddr(MACAddress addr) {
+ actionSetEthernetSrcAddr = new ActionSetEthernetAddr(addr);
+ actionType = ActionValues.ACTION_SET_DL_SRC;
+ }
+
+ /**
+ * Get the action to set the Ethernet destination address.
+ *
+ * @return the action to set the Ethernet destination address.
+ */
+ @JsonProperty("actionSetEthernetDstAddr")
+ public ActionSetEthernetAddr actionSetEthernetDstAddr() {
+ return actionSetEthernetDstAddr;
+ }
+
+ /**
+ * Set the action to set the Ethernet destination address.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetEthernetDstAddr")
+ public void setActionSetEthernetDstAddr(ActionSetEthernetAddr action) {
+ actionSetEthernetDstAddr = action;
+ actionType = ActionValues.ACTION_SET_DL_DST;
+ }
+
+ /**
+ * Set the action to set the Ethernet destination address.
+ *
+ * @param addr the MAC address to set as the Ethernet destination address.
+ */
+ public void setActionSetEthernetDstAddr(MACAddress addr) {
+ actionSetEthernetDstAddr = new ActionSetEthernetAddr(addr);
+ actionType = ActionValues.ACTION_SET_DL_DST;
+ }
+
+ /**
+ * Get the action to set the IPv4 source address.
+ *
+ * @return the action to set the IPv4 source address.
+ */
+ @JsonProperty("actionSetIPv4SrcAddr")
+ public ActionSetIPv4Addr actionSetIPv4SrcAddr() {
+ return actionSetIPv4SrcAddr;
+ }
+
+ /**
+ * Set the action to set the IPv4 source address.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetIPv4SrcAddr")
+ public void setActionSetIPv4SrcAddr(ActionSetIPv4Addr action) {
+ actionSetIPv4SrcAddr = action;
+ actionType = ActionValues.ACTION_SET_NW_SRC;
+ }
+
+ /**
+ * Set the action to set the IPv4 source address.
+ *
+ * @param addr the IPv4 address to set as the IPv4 source address.
+ */
+ public void setActionSetIPv4SrcAddr(IPv4 addr) {
+ actionSetIPv4SrcAddr = new ActionSetIPv4Addr(addr);
+ actionType = ActionValues.ACTION_SET_NW_SRC;
+ }
+
+ /**
+ * Get the action to set the IPv4 destination address.
+ *
+ * @return the action to set the IPv4 destination address.
+ */
+ @JsonProperty("actionSetIPv4DstAddr")
+ public ActionSetIPv4Addr actionSetIPv4DstAddr() {
+ return actionSetIPv4DstAddr;
+ }
+
+ /**
+ * Set the action to set the IPv4 destination address.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetIPv4DstAddr")
+ public void setActionSetIPv4DstAddr(ActionSetIPv4Addr action) {
+ actionSetIPv4DstAddr = action;
+ actionType = ActionValues.ACTION_SET_NW_DST;
+ }
+
+ /**
+ * Set the action to set the IPv4 destination address.
+ *
+ * @param addr the IPv4 address to set as the IPv4 destination address.
+ */
+ public void setActionSetIPv4DstAddr(IPv4 addr) {
+ actionSetIPv4DstAddr = new ActionSetIPv4Addr(addr);
+ actionType = ActionValues.ACTION_SET_NW_DST;
+ }
+
+ /**
+ * Get the action to set the IP ToS (DSCP field, 6 bits).
+ *
+ * @return the action to set the IP ToS (DSCP field, 6 bits).
+ */
+ @JsonProperty("actionSetIpToS")
+ public ActionSetIpToS actionSetIpToS() {
+ return actionSetIpToS;
+ }
+
+ /**
+ * Set the action to set the IP ToS (DSCP field, 6 bits).
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetIpToS")
+ public void setActionSetIpToS(ActionSetIpToS action) {
+ actionSetIpToS = action;
+ actionType = ActionValues.ACTION_SET_NW_TOS;
+ }
+
+ /**
+ * Set the action to set the IP ToS (DSCP field, 6 bits).
+ *
+ * @param ipToS the IP ToS (DSCP field, 6 bits) to set.
+ */
+ public void setActionSetIpToS(byte ipToS) {
+ actionSetIpToS = new ActionSetIpToS(ipToS);
+ actionType = ActionValues.ACTION_SET_NW_TOS;
+ }
+
+ /**
+ * Get the action to set the TCP/UDP source port.
+ *
+ * @return the action to set the TCP/UDP source port.
+ */
+ @JsonProperty("actionSetTcpUdpSrcPort")
+ public ActionSetTcpUdpPort actionSetTcpUdpSrcPort() {
+ return actionSetTcpUdpSrcPort;
+ }
+
+ /**
+ * Set the action to set the TCP/UDP source port.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetTcpUdpSrcPort")
+ public void setActionSetTcpUdpSrcPort(ActionSetTcpUdpPort action) {
+ actionSetTcpUdpSrcPort = action;
+ actionType = ActionValues.ACTION_SET_TP_SRC;
+ }
+
+ /**
+ * Set the action to set the TCP/UDP source port.
+ *
+ * @param port the TCP/UDP port to set as the TCP/UDP source port.
+ */
+ public void setActionSetTcpUdpSrcPort(short port) {
+ actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(port);
+ actionType = ActionValues.ACTION_SET_TP_SRC;
+ }
+
+ /**
+ * Get the action to set the TCP/UDP destination port.
+ *
+ * @return the action to set the TCP/UDP destination port.
+ */
+ @JsonProperty("actionSetTcpUdpDstPort")
+ public ActionSetTcpUdpPort actionSetTcpUdpDstPort() {
+ return actionSetTcpUdpDstPort;
+ }
+
+ /**
+ * Set the action to set the TCP/UDP destination port.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionSetTcpUdpDstPort")
+ public void setActionSetTcpUdpDstPort(ActionSetTcpUdpPort action) {
+ actionSetTcpUdpDstPort = action;
+ actionType = ActionValues.ACTION_SET_TP_DST;
+ }
+
+ /**
+ * Set the action to set the TCP/UDP destination port.
+ *
+ * @param port the TCP/UDP port to set as the TCP/UDP destination port.
+ */
+ public void setActionSetTcpUdpDstPort(short port) {
+ actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(port);
+ actionType = ActionValues.ACTION_SET_TP_DST;
+ }
+
+ /**
+ * Get the action to output to queue on a port.
+ *
+ * @return the action to output to queue on a port.
+ */
+ @JsonProperty("actionEnqueue")
+ public ActionEnqueue actionEnqueue() { return actionEnqueue; }
+
+ /**
+ * Set the action to output to queue on a port.
+ *
+ * @param action the action to set.
+ */
+ @JsonProperty("actionEnqueue")
+ public void setActionEnqueue(ActionEnqueue action) {
+ actionEnqueue = action;
+ actionType = ActionValues.ACTION_ENQUEUE;
+ }
+
+ /**
+ * Set the action to output to queue on a port.
+ *
+ * @param port the port to set.
+ * @param queueId the queue ID to set.
+ */
+ public void setActionEnqueue(Port port, int queueId) {
+ actionEnqueue = new ActionEnqueue(port, queueId);
+ actionType = ActionValues.ACTION_ENQUEUE;
+ }
+
+ /**
+ * Convert the action to a string.
+ *
+ * The string has the following form:
+ * [type=XXX action=XXX]
+ *
+ * @return the action as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ ret += "type=" + actionType;
+ switch (actionType) {
+ case ACTION_OUTPUT:
+ ret += " action=" + actionOutput.toString();
+ break;
+ case ACTION_SET_VLAN_VID:
+ ret += " action=" + actionSetVlanId.toString();
+ break;
+ case ACTION_SET_VLAN_PCP:
+ ret += " action=" + actionSetVlanPriority.toString();
+ break;
+ case ACTION_STRIP_VLAN:
+ ret += " action=" + actionStripVlan.toString();
+ break;
+ case ACTION_SET_DL_SRC:
+ ret += " action=" + actionSetEthernetSrcAddr.toString();
+ break;
+ case ACTION_SET_DL_DST:
+ ret += " action=" + actionSetEthernetDstAddr.toString();
+ break;
+ case ACTION_SET_NW_SRC:
+ ret += " action=" + actionSetIPv4SrcAddr.toString();
+ break;
+ case ACTION_SET_NW_DST:
+ ret += " action=" + actionSetIPv4DstAddr.toString();
+ break;
+ case ACTION_SET_NW_TOS:
+ ret += " action=" + actionSetIpToS.toString();
+ break;
+ case ACTION_SET_TP_SRC:
+ ret += " action=" + actionSetTcpUdpSrcPort.toString();
+ break;
+ case ACTION_SET_TP_DST:
+ ret += " action=" + actionSetTcpUdpDstPort.toString();
+ break;
+ case ACTION_ENQUEUE:
+ ret += " action=" + actionEnqueue.toString();
+ break;
+ case ACTION_VENDOR:
+ ret += " action=VENDOR";
+ break;
+ }
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to an action.
+ *
+ * The string has the following form:
+ * [type=XXX action=XXX]
+ *
+ * @param actionStr the action as a string.
+ */
+ public void fromString(String actionStr) {
+ String[] parts = actionStr.split("type=");
+ String decode = null;
+
+ // Extract the string after the "type="
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode == null)
+ throw new IllegalArgumentException("Invalid action string");
+
+ // Remove the trailing ']'
+ if ((decode.length() > 0) && (decode.charAt(decode.length() - 1) == ']')) {
+ decode = decode.substring(0, decode.length() - 1);
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+
+ // Extract the type value and the action value
+ parts = decode.split(" action=");
+
+ // Decode the "type=XXX" payload
+ if (parts.length > 0)
+ decode = parts[0];
+ if (decode != null) {
+ try {
+ actionType = Enum.valueOf(ActionValues.class, decode);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+
+ // Decode the "action=XXX" payload
+ decode = null;
+ if (parts.length > 1)
+ decode = parts[1];
+ if (decode == null)
+ throw new IllegalArgumentException("Invalid action string");
+ //
+ try {
+ switch (actionType) {
+ case ACTION_OUTPUT:
+ actionOutput = new ActionOutput(decode);
+ break;
+ case ACTION_SET_VLAN_VID:
+ actionSetVlanId = new ActionSetVlanId(decode);
+ break;
+ case ACTION_SET_VLAN_PCP:
+ actionSetVlanPriority = new ActionSetVlanPriority(decode);
+ break;
+ case ACTION_STRIP_VLAN:
+ actionStripVlan = new ActionStripVlan(decode);
+ break;
+ case ACTION_SET_DL_SRC:
+ actionSetEthernetSrcAddr = new ActionSetEthernetAddr(decode);
+ break;
+ case ACTION_SET_DL_DST:
+ actionSetEthernetDstAddr = new ActionSetEthernetAddr(decode);
+ break;
+ case ACTION_SET_NW_SRC:
+ actionSetIPv4SrcAddr = new ActionSetIPv4Addr(decode);
+ break;
+ case ACTION_SET_NW_DST:
+ actionSetIPv4DstAddr = new ActionSetIPv4Addr(decode);
+ break;
+ case ACTION_SET_NW_TOS:
+ actionSetIpToS = new ActionSetIpToS(decode);
+ break;
+ case ACTION_SET_TP_SRC:
+ actionSetTcpUdpSrcPort = new ActionSetTcpUdpPort(decode);
+ break;
+ case ACTION_SET_TP_DST:
+ actionSetTcpUdpDstPort = new ActionSetTcpUdpPort(decode);
+ break;
+ case ACTION_ENQUEUE:
+ actionEnqueue = new ActionEnqueue(decode);
+ break;
+ case ACTION_VENDOR:
+ // TODO: Handle it as appropriate
+ break;
+ }
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowEntryActions.java b/src/main/java/net/onrc/onos/core/util/FlowEntryActions.java
new file mode 100644
index 0000000..8839ceb
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowEntryActions.java
@@ -0,0 +1,148 @@
+package net.onrc.onos.core.util;
+
+import java.util.ArrayList;
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing multiple Flow Entry actions.
+ *
+ * A set of Flow Entry actions need to be applied to each packet.
+ */
+public class FlowEntryActions {
+ private ArrayList<FlowEntryAction> actions; // The Flow Entry Actions
+
+ /**
+ * Default constructor.
+ */
+ public FlowEntryActions() {
+ actions = new ArrayList<FlowEntryAction>();
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * The string has the following form:
+ * [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+ *
+ * @param actionsStr the set of actions as a string.
+ */
+ public FlowEntryActions(String actionsStr) {
+ this.fromString(actionsStr);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public FlowEntryActions(FlowEntryActions other) {
+ actions = new ArrayList<FlowEntryAction>();
+
+ for (FlowEntryAction action : other.actions) {
+ FlowEntryAction newAction = new FlowEntryAction(action);
+ actions.add(newAction);
+ }
+ }
+
+ /**
+ * Get the Flow Entry Actions.
+ *
+ * @return the Flow Entry Actions.
+ */
+ @JsonProperty("actions")
+ public ArrayList<FlowEntryAction> actions() {
+ return actions;
+ }
+
+ /**
+ * Set the Flow Entry Actions.
+ *
+ * @param actions the Flow Entry Actions to set.
+ */
+ @JsonProperty("actions")
+ public void setActions(ArrayList<FlowEntryAction> actions) {
+ this.actions = actions;
+ }
+
+ /**
+ * Add a Flow Entry Action.
+ *
+ * @param flowEntryAction the Flow Entry Action to add.
+ */
+ public void addAction(FlowEntryAction flowEntryAction) {
+ actions.add(flowEntryAction);
+ }
+
+ /**
+ * Test whether the set of actions is empty.
+ *
+ * @return true if the set of actions is empty, otherwise false.
+ */
+ @JsonIgnore
+ public Boolean isEmpty() {
+ return actions.isEmpty();
+ }
+
+ /**
+ * Convert the set of actions to a string.
+ *
+ * The string has the following form:
+ * [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+ *
+ * @return the set of actions as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ for (FlowEntryAction action : actions) {
+ ret += action.toString() + ";";
+ }
+ ret += "]";
+
+ return ret;
+ }
+
+ /**
+ * Convert a string to a set of actions.
+ *
+ * The string has the following form:
+ * [[type=XXX action=XXX];[type=XXX action=XXX];...;]
+ *
+ * @param actionsStr the set of actions as a string.
+ */
+ public void fromString(String actionsStr) {
+ String decode = actionsStr;
+
+ actions = new ArrayList<FlowEntryAction>();
+
+ if (decode.isEmpty())
+ return; // Nothing to do
+
+ // Remove the '[' and ']' in the beginning and the end of the string
+ if ((decode.length() > 1) && (decode.charAt(0) == '[') &&
+ (decode.charAt(decode.length() - 1) == ']')) {
+ decode = decode.substring(1, decode.length() - 1);
+ } else {
+ throw new IllegalArgumentException("Invalid action string");
+ }
+
+ // Split the string, and decode each action
+ String[] parts = decode.split(";");
+ for (int i = 0; i < parts.length; i++) {
+ decode = parts[i];
+ if ((decode == null) || decode.isEmpty())
+ continue;
+ FlowEntryAction flowEntryAction = null;
+ try {
+ flowEntryAction = new FlowEntryAction(decode);
+ } catch (IllegalArgumentException e) {
+ // TODO: Ignore invalid actions for now
+ continue;
+ }
+ if (flowEntryAction != null)
+ actions.add(flowEntryAction);
+ }
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowEntryErrorState.java b/src/main/java/net/onrc/onos/core/util/FlowEntryErrorState.java
new file mode 100644
index 0000000..e1c5731
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowEntryErrorState.java
@@ -0,0 +1,91 @@
+package net.onrc.onos.core.util;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing the Flow Entry error state.
+ */
+public class FlowEntryErrorState {
+ private short type; // The error type (e.g., see OF-1.3.1 spec, pp. 95)
+ private short code; // The error code (e.g., see OF-1.3.1 spec, pp. 95)
+
+ /**
+ * Default constructor.
+ */
+ public FlowEntryErrorState() {
+ this.type = 0;
+ this.code = 0;
+ }
+
+ /**
+ * Constructor for a given error type and code.
+ *
+ * @param type the error type to use.
+ * @param code the error code to use.
+ */
+ public FlowEntryErrorState(short type, short code) {
+ this.type = type;
+ this.code = code;
+ }
+
+ /**
+ * Get the error type.
+ *
+ * @return the error type.
+ */
+ @JsonProperty("type")
+ public short type() { return type; }
+
+ /**
+ * Set the error type.
+ *
+ * @param type the error type to use.
+ */
+ @JsonProperty("type")
+ public void setType(short type) {
+ this.type = type;
+ }
+
+ /**
+ * Get the error code.
+ *
+ * @return the error code.
+ */
+ @JsonProperty("code")
+ public short code() { return code; }
+
+ /**
+ * Set the error code.
+ *
+ * @param code the error code to use.
+ */
+ @JsonProperty("code")
+ public void setCode(short code) {
+ this.code = code;
+ }
+
+ /**
+ * Set the values of the error type and code.
+ *
+ * @param type the error type to use.
+ * @param code the error code to use.
+ */
+ public void setValue(short type, short code) {
+ this.type = type;
+ this.code = code;
+ }
+
+ /**
+ * Convert the error type and code to a string.
+ *
+ * The string has the following form:
+ * [type=1 code=2]
+ *
+ * @return the error type and code as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[type=" + this.type + " code=" + code + "]";
+ return ret;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowEntryId.java b/src/main/java/net/onrc/onos/core/util/FlowEntryId.java
new file mode 100644
index 0000000..e4dd32c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowEntryId.java
@@ -0,0 +1,113 @@
+package net.onrc.onos.core.util;
+
+import java.math.BigInteger;
+
+import net.onrc.onos.core.util.serializers.FlowEntryIdDeserializer;
+import net.onrc.onos.core.util.serializers.FlowEntryIdSerializer;
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * The class representing a Flow Entry ID.
+ */
+@JsonDeserialize(using=FlowEntryIdDeserializer.class)
+@JsonSerialize(using=FlowEntryIdSerializer.class)
+public class FlowEntryId {
+ private long value;
+
+ /**
+ * Default constructor.
+ */
+ public FlowEntryId() {
+ this.value = -1;
+ }
+
+ /**
+ * Constructor from an integer value.
+ *
+ * @param value the value to use.
+ */
+ public FlowEntryId(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public FlowEntryId(String value) {
+ //
+ // Use the help of BigInteger to parse strings representing
+ // large unsigned hex long values.
+ //
+ char c = 0;
+ if (value.length() > 2)
+ c = value.charAt(1);
+ if ((c == 'x') || (c == 'X'))
+ this.value = new BigInteger(value.substring(2), 16).longValue();
+ else
+ this.value = Long.decode(value);
+ }
+
+ /**
+ * Get the value of the Flow Entry ID.
+ *
+ * @return the value of the Flow Entry ID.
+ */
+ public long value() { return value; }
+
+ /**
+ * Set the value of the Flow Entry ID.
+ *
+ * @param value the value to set.
+ */
+ public void setValue(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Test whether the Flow Entry ID is valid.
+ *
+ * @return true if the Flow Entry ID is valid, otherwise false.
+ */
+ @JsonIgnore
+ public boolean isValid() {
+ return (this.value() != -1);
+ }
+
+ /**
+ * Returns true of the object is another Flow Entry ID with
+ * the same value; otherwise, returns false.
+ *
+ * @param Object to compare
+ */
+ @Override
+ public boolean equals(Object obj){
+ if(obj != null && obj.getClass() == this.getClass()) {
+ FlowEntryId entry = (FlowEntryId) obj;
+ return this.value() == entry.value();
+ }
+ return false;
+ }
+
+ /**
+ * Return the hash code of the Flow Entry ID
+ */
+ @Override
+ public int hashCode() {
+ return Long.valueOf(value).hashCode();
+ }
+
+ /**
+ * Convert the Flow Entry ID value to a hexadecimal string.
+ *
+ * @return the Flow Entry ID value to a hexadecimal string.
+ */
+ @Override
+ public String toString() {
+ return "0x" + Long.toHexString(this.value);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowEntryMatch.java b/src/main/java/net/onrc/onos/core/util/FlowEntryMatch.java
new file mode 100644
index 0000000..cc18071
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowEntryMatch.java
@@ -0,0 +1,711 @@
+package net.onrc.onos.core.util;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing the Flow Entry Matching filter.
+ *
+ * The Flow Entry matching filter that is used to specify
+ * the network data that would be forwarded on the data path from
+ * the source to the destination. Examples: source or destination MAC address,
+ * IP prefix that includes the destination's IP address, etc.
+ */
+public class FlowEntryMatch {
+ /**
+ * A class for storing a value to match.
+ */
+ public static class Field<T> {
+ /**
+ * Default constructor.
+ */
+ public Field() {
+ this.enabled = false;
+ }
+
+ /**
+ * Constructor for a given value to match.
+ *
+ * @param value the value to match.
+ */
+ public Field(T value) {
+ this.value = value;
+ this.enabled = true;
+ }
+
+ /**
+ * Get the value.
+ *
+ * @return the value.
+ */
+ public T value() { return this.value; }
+
+ /**
+ * Enable the matching for a given value.
+ *
+ * @param value the value to set.
+ */
+ public void enableMatch(T value) {
+ this.value = value;
+ this.enabled = true;
+ }
+
+ /**
+ * Disable the matching.
+ */
+ public void disableMatch() {
+ this.enabled = false;
+ }
+
+ /**
+ * Test whether matching is enabled.
+ *
+ * @return true if matching is enabled, otherwise false.
+ */
+ public boolean enabled() { return this.enabled; }
+
+ private T value; // The value to match
+ private boolean enabled; // Set to true, if matching is enabled
+ }
+
+ private Field<Port> inPort; // Matching input switch port
+ private Field<MACAddress> srcMac; // Matching source MAC address
+ private Field<MACAddress> dstMac; // Matching destination MAC address
+ private Field<Short> ethernetFrameType; // Matching Ethernet frame type
+ private Field<Short> vlanId; // Matching VLAN ID
+ private Field<Byte> vlanPriority; // Matching VLAN priority
+ private Field<IPv4Net> srcIPv4Net; // Matching source IPv4 prefix
+ private Field<IPv4Net> dstIPv4Net; // Matching destination IPv4 prefix
+ private Field<Byte> ipProto; // Matching IP protocol
+ private Field<Byte> ipToS; // Matching IP ToS (DSCP field, 6 bits)
+ private Field<Short> srcTcpUdpPort; // Matching source TCP/UDP port
+ private Field<Short> dstTcpUdpPort; // Matching destination TCP/UDP port
+
+ /**
+ * Default constructor.
+ */
+ public FlowEntryMatch() {
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public FlowEntryMatch(FlowEntryMatch other) {
+ if ((other.inPort != null) && other.inPort.enabled())
+ this.enableInPort(other.inPort.value());
+ if ((other.srcMac != null) && other.srcMac.enabled())
+ this.enableSrcMac(other.srcMac.value());
+ if ((other.dstMac != null) && other.dstMac.enabled())
+ this.enableDstMac(other.dstMac.value());
+ if ((other.ethernetFrameType != null) && other.ethernetFrameType.enabled())
+ this.enableEthernetFrameType(other.ethernetFrameType.value());
+ if ((other.vlanId != null) && other.vlanId.enabled())
+ this.enableVlanId(other.vlanId.value());
+ if ((other.vlanPriority != null) && other.vlanPriority.enabled())
+ this.enableVlanPriority(other.vlanPriority.value());
+ if ((other.srcIPv4Net != null) && other.srcIPv4Net.enabled())
+ this.enableSrcIPv4Net(other.srcIPv4Net.value());
+ if ((other.dstIPv4Net != null) && other.dstIPv4Net.enabled())
+ this.enableDstIPv4Net(other.dstIPv4Net.value());
+ if ((other.ipProto != null) && other.ipProto.enabled())
+ this.enableIpProto(other.ipProto.value());
+ if ((other.ipToS != null) && other.ipToS.enabled())
+ this.enableIpToS(other.ipToS.value());
+ if ((other.srcTcpUdpPort != null) && other.srcTcpUdpPort.enabled())
+ this.enableSrcTcpUdpPort(other.srcTcpUdpPort.value());
+ if ((other.dstTcpUdpPort != null) && other.dstTcpUdpPort.enabled())
+ this.enableDstTcpUdpPort(other.dstTcpUdpPort.value());
+ }
+
+ /**
+ * Get the matching input switch port.
+ *
+ * @return the matching input switch port.
+ */
+ @JsonProperty("inPort")
+ public Port inPort() {
+ if (inPort != null)
+ return inPort.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on input switch port.
+ *
+ * @param inPort the input switch port value to enable for matching.
+ */
+ @JsonProperty("inPort")
+ public void enableInPort(Port inPort) {
+ this.inPort = new Field<Port>(inPort);
+ }
+
+ /**
+ * Disable the matching on input switch port.
+ */
+ public void disableInPort() {
+ this.inPort = null;
+ }
+
+ /**
+ * Test if matching on input switch port is enabled.
+ *
+ * @return true if matching on input switch port is enabled.
+ */
+ @JsonProperty("matchInPort")
+ public boolean matchInPort() {
+ if (inPort != null)
+ return inPort.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching source MAC address.
+ *
+ * @return the matching source MAC address.
+ */
+ @JsonProperty("srcMac")
+ public MACAddress srcMac() {
+ if (srcMac != null)
+ return srcMac.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on source MAC address.
+ *
+ * @param srcMac the source MAC address value to enable for matching.
+ */
+ @JsonProperty("srcMac")
+ public void enableSrcMac(MACAddress srcMac) {
+ this.srcMac = new Field<MACAddress>(srcMac);
+ }
+
+ /**
+ * Disable the matching on source MAC address.
+ */
+ public void disableSrcMac() {
+ this.srcMac = null;
+ }
+
+ /**
+ * Test if matching on source MAC address is enabled.
+ *
+ * @return true if matching on source MAC address is enabled.
+ */
+ @JsonProperty("matchSrcMac")
+ public boolean matchSrcMac() {
+ if (srcMac != null)
+ return srcMac.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching destination MAC address.
+ *
+ * @return the matching destination MAC address.
+ */
+ @JsonProperty("dstMac")
+ public MACAddress dstMac() {
+ if (dstMac != null)
+ return dstMac.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on destination MAC address.
+ *
+ * @param dstMac the destination MAC address value to enable for matching.
+ */
+ @JsonProperty("dstMac")
+ public void enableDstMac(MACAddress dstMac) {
+ this.dstMac = new Field<MACAddress>(dstMac);
+ }
+
+ /**
+ * Disable the matching on destination MAC address.
+ */
+ public void disableDstMac() {
+ this.dstMac = null;
+ }
+
+ /**
+ * Test if matching on destination MAC address is enabled.
+ *
+ * @return true if matching on destination MAC address is enabled.
+ */
+ @JsonProperty("matchDstMac")
+ public boolean matchDstMac() {
+ if (dstMac != null)
+ return dstMac.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching Ethernet frame type.
+ *
+ * @return the matching Ethernet frame type.
+ */
+ @JsonProperty("ethernetFrameType")
+ public Short ethernetFrameType() {
+ if (ethernetFrameType != null)
+ return ethernetFrameType.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on Ethernet frame type.
+ *
+ * @param ethernetFrameType the Ethernet frame type value to enable for
+ * matching.
+ */
+ @JsonProperty("ethernetFrameType")
+ public void enableEthernetFrameType(Short ethernetFrameType) {
+ this.ethernetFrameType = new Field<Short>(ethernetFrameType);
+ }
+
+ /**
+ * Disable the matching on Ethernet frame type.
+ */
+ public void disableEthernetFrameType() {
+ this.ethernetFrameType = null;
+ }
+
+ /**
+ * Test if matching on Ethernet frame type is enabled.
+ *
+ * @return true if matching on Ethernet frame type is enabled.
+ */
+ @JsonProperty("matchEthernetFrameType")
+ public boolean matchEthernetFrameType() {
+ if (ethernetFrameType != null)
+ return ethernetFrameType.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching VLAN ID.
+ *
+ * @return the matching VLAN ID.
+ */
+ @JsonProperty("vlanId")
+ public Short vlanId() {
+ if (vlanId != null)
+ return vlanId.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on VLAN ID.
+ *
+ * @param vlanId the VLAN ID value to enable for matching.
+ */
+ @JsonProperty("vlanId")
+ public void enableVlanId(Short vlanId) {
+ this.vlanId = new Field<Short>(vlanId);
+ }
+
+ /**
+ * Disable the matching on VLAN ID.
+ */
+ public void disableVlanId() {
+ this.vlanId = null;
+ }
+
+ /**
+ * Test if matching on VLAN ID is enabled.
+ *
+ * @return true if matching on VLAN ID is enabled.
+ */
+ @JsonProperty("matchVlanId")
+ public boolean matchVlanId() {
+ if (vlanId != null)
+ return vlanId.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching VLAN priority.
+ *
+ * @return the matching VLAN priority.
+ */
+ @JsonProperty("vlanPriority")
+ public Byte vlanPriority() {
+ if (vlanPriority != null)
+ return vlanPriority.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on VLAN priority.
+ *
+ * @param vlanPriority the VLAN priority value to enable for matching.
+ */
+ @JsonProperty("vlanPriority")
+ public void enableVlanPriority(Byte vlanPriority) {
+ this.vlanPriority = new Field<Byte>(vlanPriority);
+ }
+
+ /**
+ * Disable the matching on VLAN priority.
+ */
+ public void disableVlanPriority() {
+ this.vlanPriority = null;
+ }
+
+ /**
+ * Test if matching on VLAN priority is enabled.
+ *
+ * @return true if matching on VLAN priority is enabled.
+ */
+ @JsonProperty("matchVlanPriority")
+ public boolean matchVlanPriority() {
+ if (vlanPriority != null)
+ return vlanPriority.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching source IPv4 prefix.
+ *
+ * @return the matching source IPv4 prefix.
+ */
+ @JsonProperty("srcIPv4Net")
+ public IPv4Net srcIPv4Net() {
+ if (srcIPv4Net != null)
+ return srcIPv4Net.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on source IPv4 prefix.
+ *
+ * @param srcIPv4Net the source IPv4 prefix value to enable for matching.
+ */
+ @JsonProperty("srcIPv4Net")
+ public void enableSrcIPv4Net(IPv4Net srcIPv4Net) {
+ this.srcIPv4Net = new Field<IPv4Net>(srcIPv4Net);
+ }
+
+ /**
+ * Disable the matching on source IPv4 prefix.
+ */
+ public void disableSrcIPv4Net() {
+ this.srcIPv4Net = null;
+ }
+
+ /**
+ * Test if matching on source IPv4 prefix is enabled.
+ *
+ * @return true if matching on source IPv4 prefix is enabled.
+ */
+ @JsonProperty("matchSrcIPv4Net")
+ public boolean matchSrcIPv4Net() {
+ if (srcIPv4Net != null)
+ return srcIPv4Net.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching destination IPv4 prefix.
+ *
+ * @return the matching destination IPv4 prefix.
+ */
+ @JsonProperty("dstIPv4Net")
+ public IPv4Net dstIPv4Net() {
+ if (dstIPv4Net != null)
+ return dstIPv4Net.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on destination IPv4 prefix.
+ *
+ * @param dstIPv4Net the destination IPv4 prefix value to enable for
+ * matching.
+ */
+ @JsonProperty("dstIPv4Net")
+ public void enableDstIPv4Net(IPv4Net dstIPv4Net) {
+ this.dstIPv4Net = new Field<IPv4Net>(dstIPv4Net);
+ }
+
+ /**
+ * Disable the matching on destination IPv4 prefix.
+ */
+ public void disableDstIPv4Net() {
+ this.dstIPv4Net = null;
+ }
+
+ /**
+ * Test if matching on destination IPv4 prefix is enabled.
+ *
+ * @return true if matching on destination IPv4 prefix is enabled.
+ */
+ @JsonProperty("matchDstIPv4Net")
+ public boolean matchDstIPv4Net() {
+ if (dstIPv4Net != null)
+ return dstIPv4Net.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching IP protocol.
+ *
+ * @return the matching IP protocol.
+ */
+ @JsonProperty("ipProto")
+ public Byte ipProto() {
+ if (ipProto != null)
+ return ipProto.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on IP protocol.
+ *
+ * @param ipProto the IP protocol value to enable for matching.
+ */
+ @JsonProperty("ipProto")
+ public void enableIpProto(Byte ipProto) {
+ this.ipProto = new Field<Byte>(ipProto);
+ }
+
+ /**
+ * Disable the matching on IP protocol.
+ */
+ public void disableIpProto() {
+ this.ipProto = null;
+ }
+
+ /**
+ * Test if matching on IP protocol is enabled.
+ *
+ * @return true if matching on IP protocol is enabled.
+ */
+ @JsonProperty("matchIpProto")
+ public boolean matchIpProto() {
+ if (ipProto != null)
+ return ipProto.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching IP ToS (DSCP field, 6 bits)
+ *
+ * @return the matching IP ToS.
+ */
+ @JsonProperty("ipToS")
+ public Byte ipToS() {
+ if (ipToS != null)
+ return ipToS.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on IP ToS (DSCP field, 6 bits).
+ *
+ * @param ipToS the IP ToS value to enable for matching.
+ */
+ @JsonProperty("ipToS")
+ public void enableIpToS(Byte ipToS) {
+ this.ipToS = new Field<Byte>(ipToS);
+ }
+
+ /**
+ * Disable the matching on IP ToS (DSCP field, 6 bits).
+ */
+ public void disableIpToS() {
+ this.ipToS = null;
+ }
+
+ /**
+ * Test if matching on IP ToS (DSCP field, 6 bits) is enabled.
+ *
+ * @return true if matching on IP ToS is enabled.
+ */
+ @JsonProperty("matchIpToS")
+ public boolean matchIpToS() {
+ if (ipToS != null)
+ return ipToS.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching source TCP/UDP port.
+ *
+ * @return the matching source TCP/UDP port.
+ */
+ @JsonProperty("srcTcpUdpPort")
+ public Short srcTcpUdpPort() {
+ if (srcTcpUdpPort != null)
+ return srcTcpUdpPort.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on source TCP/UDP port.
+ *
+ * @param srcTcpUdpPort the source TCP/UDP port to enable for matching.
+ */
+ @JsonProperty("srcTcpUdpPort")
+ public void enableSrcTcpUdpPort(Short srcTcpUdpPort) {
+ this.srcTcpUdpPort = new Field<Short>(srcTcpUdpPort);
+ }
+
+ /**
+ * Disable the matching on source TCP/UDP port.
+ */
+ public void disableSrcTcpUdpPort() {
+ this.srcTcpUdpPort = null;
+ }
+
+ /**
+ * Test if matching on source TCP/UDP port is enabled.
+ *
+ * @return true if matching on source TCP/UDP port is enabled.
+ */
+ @JsonProperty("matchSrcTcpUdpPort")
+ public boolean matchSrcTcpUdpPort() {
+ if (srcTcpUdpPort != null)
+ return srcTcpUdpPort.enabled();
+ return false;
+ }
+
+ /**
+ * Get the matching destination TCP/UDP port.
+ *
+ * @return the matching destination TCP/UDP port.
+ */
+ @JsonProperty("dstTcpUdpPort")
+ public Short dstTcpUdpPort() {
+ if (dstTcpUdpPort != null)
+ return dstTcpUdpPort.value();
+ return null;
+ }
+
+ /**
+ * Enable the matching on destination TCP/UDP port.
+ *
+ * @param dstTcpUdpPort the destination TCP/UDP port to enable for
+ * matching.
+ */
+ @JsonProperty("dstTcpUdpPort")
+ public void enableDstTcpUdpPort(Short dstTcpUdpPort) {
+ this.dstTcpUdpPort = new Field<Short>(dstTcpUdpPort);
+ }
+
+ /**
+ * Disable the matching on destination TCP/UDP port.
+ */
+ public void disableDstTcpUdpPort() {
+ this.dstTcpUdpPort = null;
+ }
+
+ /**
+ * Test if matching on destination TCP/UDP port is enabled.
+ *
+ * @return true if matching on destination TCP/UDP port is enabled.
+ */
+ @JsonProperty("matchDstTcpUdpPort")
+ public boolean matchDstTcpUdpPort() {
+ if (dstTcpUdpPort != null)
+ return dstTcpUdpPort.enabled();
+ return false;
+ }
+
+ /**
+ * Convert the matching filter to a string.
+ *
+ * The string has the following form:
+ * [srcMac=XXX dstMac=XXX srcIPv4Net=XXX dstIPv4Net=XXX]
+ *
+ * @return the matching filter as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[";
+ boolean addSpace = false;
+
+ //
+ // Conditionally add only those matching fields that are enabled
+ //
+ if (matchInPort()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "inPort=" + this.inPort().toString();
+ }
+ if (matchSrcMac()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "srcMac=" + this.srcMac().toString();
+ }
+ if (matchDstMac()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "dstMac=" + this.dstMac().toString();
+ }
+ if (matchEthernetFrameType()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "ethernetFrameType=" + this.ethernetFrameType().toString();
+ }
+ if (matchVlanId()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "vlanId=" + this.vlanId().toString();
+ }
+ if (matchVlanPriority()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "vlanPriority=" + this.vlanPriority().toString();
+ }
+ if (matchSrcIPv4Net()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "srcIPv4Net=" + this.srcIPv4Net().toString();
+ }
+ if (matchDstIPv4Net()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "dstIPv4Net=" + this.dstIPv4Net().toString();
+ }
+ if (matchIpProto()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "ipProto=" + this.ipProto().toString();
+ }
+ if (matchIpToS()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "ipToS=" + this.ipToS().toString();
+ }
+ if (matchSrcTcpUdpPort()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "srcTcpUdpPort=" + this.srcTcpUdpPort().toString();
+ }
+ if (matchDstTcpUdpPort()) {
+ if (addSpace)
+ ret += " ";
+ addSpace = true;
+ ret += "dstTcpUdpPort=" + this.dstTcpUdpPort().toString();
+ }
+
+ ret += "]";
+
+ return ret;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowEntrySwitchState.java b/src/main/java/net/onrc/onos/core/util/FlowEntrySwitchState.java
new file mode 100644
index 0000000..c5a79de
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowEntrySwitchState.java
@@ -0,0 +1,12 @@
+package net.onrc.onos.core.util;
+
+/**
+ * The Flow Entry state as set by the controller.
+ */
+public enum FlowEntrySwitchState {
+ FE_SWITCH_UNKNOWN, // Initialization value: state unknown
+ FE_SWITCH_NOT_UPDATED, // Switch not updated with this entry
+ FE_SWITCH_UPDATE_IN_PROGRESS, // Switch update in progress
+ FE_SWITCH_UPDATED, // Switch updated with this entry
+ FE_SWITCH_UPDATE_FAILED // Error updating the switch with this entry
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowEntryUserState.java b/src/main/java/net/onrc/onos/core/util/FlowEntryUserState.java
new file mode 100644
index 0000000..64d283a
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowEntryUserState.java
@@ -0,0 +1,11 @@
+package net.onrc.onos.core.util;
+
+/**
+ * The Flow Entry state as set by the user (via the ONOS API).
+ */
+public enum FlowEntryUserState {
+ FE_USER_UNKNOWN, // Initialization value: state unknown
+ FE_USER_ADD, // Flow entry that is added
+ FE_USER_MODIFY, // Flow entry that is modified
+ FE_USER_DELETE // Flow entry that is deleted
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowId.java b/src/main/java/net/onrc/onos/core/util/FlowId.java
new file mode 100644
index 0000000..a6ceed8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowId.java
@@ -0,0 +1,102 @@
+package net.onrc.onos.core.util;
+
+import java.math.BigInteger;
+
+import net.onrc.onos.core.util.serializers.FlowIdDeserializer;
+import net.onrc.onos.core.util.serializers.FlowIdSerializer;
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * The class representing a Flow ID.
+ */
+@JsonDeserialize(using=FlowIdDeserializer.class)
+@JsonSerialize(using=FlowIdSerializer.class)
+public class FlowId implements Comparable<FlowId> {
+ private long value;
+
+ /**
+ * Default constructor.
+ */
+ public FlowId() {
+ this.value = -1;
+ }
+
+ /**
+ * Constructor from an integer value.
+ *
+ * @param value the value to use.
+ */
+ public FlowId(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public FlowId(String value) {
+ //
+ // Use the help of BigInteger to parse strings representing
+ // large unsigned hex long values.
+ //
+ char c = 0;
+ if (value.length() > 2)
+ c = value.charAt(1);
+ if ((c == 'x') || (c == 'X'))
+ this.value = new BigInteger(value.substring(2), 16).longValue();
+ else
+ this.value = Long.decode(value);
+ }
+
+ /**
+ * Get the value of the Flow ID.
+ *
+ * @return the value of the Flow ID.
+ */
+ public long value() { return value; }
+
+ /**
+ * Set the value of the Flow ID.
+ *
+ * @param value the value to set.
+ */
+ public void setValue(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Test whether the Flow ID is valid.
+ *
+ * @return true if the Flow ID is valid, otherwise false.
+ */
+ @JsonIgnore
+ public boolean isValid() {
+ return (this.value() != -1);
+ }
+
+ /**
+ * Convert the Flow ID value to a hexadecimal string.
+ *
+ * @return the Flow ID value to a hexadecimal string.
+ */
+ @Override
+ public String toString() {
+ return "0x" + Long.toHexString(this.value);
+ }
+
+ /**
+ * Compare two FlowId objects numerically using their Flow IDs.
+ *
+ * @return the value 0 if the Flow ID is equal to the argument's Flow ID;
+ * a value less than 0 if the Flow ID is numerically less than the argument's Flow ID;
+ * and a value greater than 0 if the Flow ID is numerically greater than the argument's Flow ID.
+ */
+ @Override
+ public int compareTo(FlowId o) {
+ return Long.valueOf(this.value).compareTo(o.value());
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowPath.java b/src/main/java/net/onrc/onos/core/util/FlowPath.java
new file mode 100644
index 0000000..2bb5ad3
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowPath.java
@@ -0,0 +1,316 @@
+package net.onrc.onos.core.util;
+
+import java.util.ArrayList;
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing the Flow Path.
+ */
+public class FlowPath implements Comparable<FlowPath> {
+ public static final int PRIORITY_DEFAULT = 32768; // Default Flow Priority
+
+ private FlowId flowId; // The Flow ID
+ private CallerId installerId; // The Caller ID of the path installer
+ private FlowPathType flowPathType; // The Flow Path type
+ private FlowPathUserState flowPathUserState; // The Flow Path User state
+ private FlowPathFlags flowPathFlags; // The Flow Path flags
+ private int idleTimeout; // The Flow idle timeout
+ private int hardTimeout; // The Flow hard timeout
+ private int priority; // The Flow priority
+ private DataPath dataPath; // The data path
+ private FlowEntryMatch flowEntryMatch; // Common Flow Entry Match for all
+ // Flow Entries
+ private FlowEntryActions flowEntryActions; // The Flow Entry Actions for
+ // the first Flow Entry
+
+ /**
+ * Default constructor.
+ */
+ public FlowPath() {
+ flowPathType = FlowPathType.FP_TYPE_UNKNOWN;
+ flowPathUserState = FlowPathUserState.FP_USER_UNKNOWN;
+ flowPathFlags = new FlowPathFlags();
+ priority = FlowPath.PRIORITY_DEFAULT;
+ dataPath = new DataPath();
+ flowEntryActions = new FlowEntryActions();
+ }
+
+ /**
+ * Get the flow path Flow ID.
+ *
+ * @return the flow path Flow ID.
+ */
+ @JsonProperty("flowId")
+ public FlowId flowId() { return flowId; }
+
+ /**
+ * Set the flow path Flow ID.
+ *
+ * @param flowId the flow path Flow ID to set.
+ */
+ @JsonProperty("flowId")
+ public void setFlowId(FlowId flowId) {
+ this.flowId = flowId;
+ }
+
+ /**
+ * Test whether the Flow ID is valid.
+ *
+ * @return true if the Flow ID is valid, otherwise false.
+ */
+ @JsonIgnore
+ public boolean isValidFlowId() {
+ if (this.flowId == null)
+ return false;
+ return (this.flowId.isValid());
+ }
+
+ /**
+ * Get the Caller ID of the flow path installer.
+ *
+ * @return the Caller ID of the flow path installer.
+ */
+ @JsonProperty("installerId")
+ public CallerId installerId() { return installerId; }
+
+ /**
+ * Set the Caller ID of the flow path installer.
+ *
+ * @param installerId the Caller ID of the flow path installer.
+ */
+ @JsonProperty("installerId")
+ public void setInstallerId(CallerId installerId) {
+ this.installerId = installerId;
+ }
+
+ /**
+ * Get the flow path type.
+ *
+ * @return the flow path type.
+ */
+ @JsonProperty("flowPathType")
+ public FlowPathType flowPathType() { return flowPathType; }
+
+ /**
+ * Set the flow path type.
+ *
+ * @param flowPathType the flow path type to set.
+ */
+ @JsonProperty("flowPathType")
+ public void setFlowPathType(FlowPathType flowPathType) {
+ this.flowPathType = flowPathType;
+ }
+
+ /**
+ * Get the flow path user state.
+ *
+ * @return the flow path user state.
+ */
+ @JsonProperty("flowPathUserState")
+ public FlowPathUserState flowPathUserState() { return flowPathUserState; }
+
+ /**
+ * Set the flow path user state.
+ *
+ * @param flowPathUserState the flow path user state to set.
+ */
+ @JsonProperty("flowPathUserState")
+ public void setFlowPathUserState(FlowPathUserState flowPathUserState) {
+ this.flowPathUserState = flowPathUserState;
+ }
+
+ /**
+ * Get the flow path flags.
+ *
+ * @return the flow path flags.
+ */
+ @JsonProperty("flowPathFlags")
+ public FlowPathFlags flowPathFlags() { return flowPathFlags; }
+
+ /**
+ * Set the flow path flags.
+ *
+ * @param flowPathFlags the flow path flags to set.
+ */
+ @JsonProperty("flowPathFlags")
+ public void setFlowPathFlags(FlowPathFlags flowPathFlags) {
+ this.flowPathFlags = flowPathFlags;
+ }
+
+ /**
+ * Get the flow idle timeout in seconds.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ * If zero, the timeout is not set.
+ *
+ * @return the flow idle timeout.
+ */
+ @JsonProperty("idleTimeout")
+ public int idleTimeout() { return idleTimeout; }
+
+ /**
+ * Set the flow idle timeout in seconds.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ * If zero, the timeout is not set.
+ *
+ * @param idleTimeout the flow idle timeout to set.
+ */
+ @JsonProperty("idleTimeout")
+ public void setIdleTimeout(int idleTimeout) {
+ this.idleTimeout = 0xffff & idleTimeout;
+ }
+
+ /**
+ * Get the flow hard timeout in seconds.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ * If zero, the timeout is not set.
+ *
+ * @return the flow hard timeout.
+ */
+ @JsonProperty("hardTimeout")
+ public int hardTimeout() { return hardTimeout; }
+
+ /**
+ * Set the flow hard timeout.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ * If zero, the timeout is not set.
+ *
+ * @param hardTimeout the flow hard timeout to set.
+ */
+ @JsonProperty("hardTimeout")
+ public void setHardTimeout(int hardTimeout) {
+ this.hardTimeout = 0xffff & hardTimeout;
+ }
+
+ /**
+ * Get the flow priority.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ *
+ * @return the flow priority.
+ */
+ @JsonProperty("priority")
+ public int priority() { return priority; }
+
+ /**
+ * Set the flow priority.
+ *
+ * It should be an unsigned integer in the interval [0, 65535].
+ *
+ * @param priority the flow priority to set.
+ */
+ @JsonProperty("priority")
+ public void setPriority(int priority) {
+ this.priority = 0xffff & priority;
+ }
+
+ /**
+ * Get the flow path's data path.
+ *
+ * @return the flow path's data path.
+ */
+ @JsonProperty("dataPath")
+ public DataPath dataPath() { return dataPath; }
+
+ /**
+ * Set the flow path's data path.
+ *
+ * @param dataPath the flow path's data path to set.
+ */
+ @JsonProperty("dataPath")
+ public void setDataPath(DataPath dataPath) {
+ this.dataPath = dataPath;
+ }
+
+ /**
+ * Get the data path flow entries.
+ *
+ * @return the data path flow entries.
+ */
+ public ArrayList<FlowEntry> flowEntries() {
+ return this.dataPath.flowEntries();
+ }
+
+ /**
+ * Get the flow path's match conditions common for all Flow Entries.
+ *
+ * @return the flow path's match conditions common for all Flow Entries.
+ */
+ @JsonProperty("flowEntryMatch")
+ public FlowEntryMatch flowEntryMatch() { return flowEntryMatch; }
+
+ /**
+ * Set the flow path's match conditions common for all Flow Entries.
+ *
+ * @param flowEntryMatch the flow path's match conditions common for all
+ * Flow Entries.
+ */
+ @JsonProperty("flowEntryMatch")
+ public void setFlowEntryMatch(FlowEntryMatch flowEntryMatch) {
+ this.flowEntryMatch = flowEntryMatch;
+ }
+
+ /**
+ * Get the flow path's flow entry actions for the first Flow Entry.
+ *
+ * @return the flow path's flow entry actions for the first Flow Entry.
+ */
+ @JsonProperty("flowEntryActions")
+ public FlowEntryActions flowEntryActions() {
+ return flowEntryActions;
+ }
+
+ /**
+ * Set the flow path's flow entry actions for the first Flow Entry.
+ *
+ * @param flowEntryActions the flow path's flow entry actions for the first
+ * Flow Entry.
+ */
+ @JsonProperty("flowEntryActions")
+ public void setFlowEntryActions(FlowEntryActions flowEntryActions) {
+ this.flowEntryActions = flowEntryActions;
+ }
+
+ /**
+ * Convert the flow path to a string.
+ *
+ * The string has the following form:
+ * [flowId=XXX installerId=XXX flowPathType = XXX flowPathUserState = XXX
+ * flowPathFlags=XXX idleTimeout=XXX hardTimeout=XXX priority=XXX
+ * dataPath=XXX flowEntryMatch=XXX flowEntryActions=XXX]
+ *
+ * @return the flow path as a string.
+ */
+ @Override
+ public String toString() {
+ String ret = "[flowId=" + this.flowId.toString();
+ ret += " installerId=" + this.installerId.toString();
+ ret += " flowPathType=" + this.flowPathType;
+ ret += " flowPathUserState=" + this.flowPathUserState;
+ ret += " flowPathFlags=" + this.flowPathFlags.toString();
+ ret += " idleTimeout=" + this.idleTimeout;
+ ret += " hardTimeout=" + this.hardTimeout;
+ ret += " priority=" + this.priority;
+ if (dataPath != null)
+ ret += " dataPath=" + this.dataPath.toString();
+ if (flowEntryMatch != null)
+ ret += " flowEntryMatch=" + this.flowEntryMatch.toString();
+ if (flowEntryActions != null)
+ ret += " flowEntryActions=" + this.flowEntryActions.toString();
+ ret += "]";
+ return ret;
+ }
+
+ /**
+ * CompareTo method to order flowPath by Id
+ */
+ @Override
+ public int compareTo(FlowPath f) {
+ return (int) (this.flowId.value() - f.flowId.value());
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowPathFlags.java b/src/main/java/net/onrc/onos/core/util/FlowPathFlags.java
new file mode 100644
index 0000000..b52d888
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowPathFlags.java
@@ -0,0 +1,129 @@
+package net.onrc.onos.core.util;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing the Flow Path flags.
+ */
+public class FlowPathFlags {
+ private long flags;
+
+ // Discard the first-hop Flow Entry
+ public static final long DISCARD_FIRST_HOP_ENTRY = (1 << 0);
+
+ // Keep only the first-hop Flow Entry
+ public static final long KEEP_ONLY_FIRST_HOP_ENTRY = (1 << 1);
+
+ /**
+ * Default constructor.
+ */
+ public FlowPathFlags() {
+ this.flags = 0;
+ }
+
+ /**
+ * Constructor for given flags.
+ *
+ * @param flags the flags value to set.
+ */
+ public FlowPathFlags(long flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Constructor for given flags as a string.
+ *
+ * The string value should contain the name of each flags to set. E.g.:
+ * "DISCARD_FIRST_HOP_ENTRY,KEEP_ONLY_FIRST_HOP_ENTRY"
+ * @param flagsStr the string value of the flags to set.
+ */
+ public FlowPathFlags(String flagsStr) {
+ this.setFlagsStr(flagsStr);
+ }
+
+ /**
+ * Get the flags.
+ *
+ * @return the flags.
+ */
+ @JsonProperty("flags")
+ public long flags() { return flags; }
+
+ /**
+ * Set the flags.
+ *
+ * @param flags the flags value to set.
+ */
+ @JsonProperty("flags")
+ public void setFlags(long flags) {
+ this.flags = flags;
+ }
+
+ /**
+ * Set the flags as a string.
+ *
+ * The string value should contain the name of each flags to set. E.g.:
+ * "DISCARD_FIRST_HOP_ENTRY,KEEP_ONLY_FIRST_HOP_ENTRY"
+ * @param flagsStr the string value of the flags to set.
+ */
+ @JsonProperty("flagsStr")
+ public void setFlagsStr(String flagsStr) {
+ this.flags = 0L;
+
+ // Test all flags
+ if (flagsStr.contains("DISCARD_FIRST_HOP_ENTRY"))
+ this.flags |= DISCARD_FIRST_HOP_ENTRY;
+ if (flagsStr.contains("KEEP_ONLY_FIRST_HOP_ENTRY"))
+ this.flags |= KEEP_ONLY_FIRST_HOP_ENTRY;
+ }
+
+ /**
+ * Test whether the DISCARD_FIRST_HOP_ENTRY flag is set.
+ *
+ * @return true if the DISCARD_FIRST_HOP_ENTRY flag is set,
+ * otherwise false.
+ */
+ public boolean isDiscardFirstHopEntry() {
+ return ((flags & DISCARD_FIRST_HOP_ENTRY) != 0);
+ }
+
+ /**
+ * Test whether the KEEP_ONLY_FIRST_HOP_ENTRY flag is set.
+ *
+ * @return true if the KEEP_ONLY_FIRST_HOP_ENTRY flag is set,
+ * otherwise false.
+ */
+ public boolean isKeepOnlyFirstHopEntry() {
+ return ((flags & KEEP_ONLY_FIRST_HOP_ENTRY) != 0);
+ }
+
+ /**
+ * Convert the Flow Path Flags to a string.
+ *
+ * The string has the following form:
+ * [flags=DISCARD_FIRST_HOP_ENTRY,KEEP_ONLY_FIRST_HOP_ENTRY]
+ *
+ * @return the Flow Path flags as a string.
+ */
+ @Override
+ public String toString() {
+ String flagsStr = null;
+ String ret = "[flags=";
+
+ // Test all flags
+ if ((this.flags & DISCARD_FIRST_HOP_ENTRY) != 0) {
+ flagsStr += "DISCARD_FIRST_HOP_ENTRY";
+ }
+ if ((this.flags & KEEP_ONLY_FIRST_HOP_ENTRY) != 0) {
+ if (flagsStr != null)
+ flagsStr += ",";
+ flagsStr += "KEEP_ONLY_FIRST_HOP_ENTRY";
+ }
+ if (flagsStr != null)
+ ret += flagsStr;
+ ret += "]";
+
+ return ret;
+ }
+
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowPathType.java b/src/main/java/net/onrc/onos/core/util/FlowPathType.java
new file mode 100644
index 0000000..4b1214e
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowPathType.java
@@ -0,0 +1,10 @@
+package net.onrc.onos.core.util;
+
+/**
+ * The Flow Path types.
+ */
+public enum FlowPathType {
+ FP_TYPE_UNKNOWN, // Initialization value: state unknown
+ FP_TYPE_SHORTEST_PATH, // Shortest path flow
+ FP_TYPE_EXPLICIT_PATH // Flow path with explicit flow entries
+}
diff --git a/src/main/java/net/onrc/onos/core/util/FlowPathUserState.java b/src/main/java/net/onrc/onos/core/util/FlowPathUserState.java
new file mode 100644
index 0000000..bc91c2a
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/FlowPathUserState.java
@@ -0,0 +1,11 @@
+package net.onrc.onos.core.util;
+
+/**
+ * The Flow Path state as set by the user (via the ONOS API).
+ */
+public enum FlowPathUserState {
+ FP_USER_UNKNOWN, // Initialization value: state unknown
+ FP_USER_ADD, // Flow path that is added
+ FP_USER_MODIFY, // Flow path that is modified
+ FP_USER_DELETE // Flow path that is deleted
+}
diff --git a/src/main/java/net/onrc/onos/core/util/IPv4.java b/src/main/java/net/onrc/onos/core/util/IPv4.java
new file mode 100644
index 0000000..86795aa
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/IPv4.java
@@ -0,0 +1,88 @@
+package net.onrc.onos.core.util;
+
+import net.onrc.onos.core.util.serializers.IPv4Deserializer;
+import net.onrc.onos.core.util.serializers.IPv4Serializer;
+
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * The class representing an IPv4 address.
+ */
+@JsonDeserialize(using=IPv4Deserializer.class)
+@JsonSerialize(using=IPv4Serializer.class)
+public class IPv4 {
+ private int value;
+
+ /**
+ * Default constructor.
+ */
+ public IPv4() {
+ this.value = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public IPv4(IPv4 other) {
+ this.value = other.value;
+ }
+
+ /**
+ * Constructor from an integer value.
+ *
+ * @param value the value to use.
+ */
+ public IPv4(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public IPv4(String value) {
+ String[] splits = value.split("\\.");
+ if (splits.length != 4)
+ throw new IllegalArgumentException("Specified IPv4 address must contain four " +
+ "numerical digits separated by '.'");
+
+ int result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result |= Integer.valueOf(splits[i]) << ((3-i)*8);
+ }
+ this.value = result;
+ }
+
+ /**
+ * Get the value of the IPv4 address.
+ *
+ * @return the value of the IPv4 address.
+ */
+ public int value() { return value; }
+
+ /**
+ * Set the value of the IPv4 address.
+ *
+ * @param value the value to set.
+ */
+ public void setValue(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Convert the IPv4 value to a '.' separated string.
+ *
+ * @return the IPv4 value as a '.' separated string.
+ */
+ @Override
+ public String toString() {
+ return ((this.value >> 24) & 0xFF) + "." +
+ ((this.value >> 16) & 0xFF) + "." +
+ ((this.value >> 8) & 0xFF) + "." +
+ (this.value & 0xFF);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/IPv4Net.java b/src/main/java/net/onrc/onos/core/util/IPv4Net.java
new file mode 100644
index 0000000..82f2fc6
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/IPv4Net.java
@@ -0,0 +1,114 @@
+package net.onrc.onos.core.util;
+
+import net.onrc.onos.core.util.serializers.IPv4NetDeserializer;
+import net.onrc.onos.core.util.serializers.IPv4NetSerializer;
+
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * The class representing an IPv4 network address.
+ */
+@JsonDeserialize(using=IPv4NetDeserializer.class)
+@JsonSerialize(using=IPv4NetSerializer.class)
+public class IPv4Net {
+ private IPv4 address; // The IPv4 address
+ private short prefixLen; // The prefix length
+
+ /**
+ * Default constructor.
+ */
+ public IPv4Net() {
+ this.prefixLen = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public IPv4Net(IPv4Net other) {
+ if (other.address != null)
+ this.address = new IPv4(other.address);
+ this.prefixLen = other.prefixLen;
+ }
+
+ /**
+ * Constructor for a given address and prefix length.
+ *
+ * @param address the address to use.
+ * @param prefixLen the prefix length to use.
+ */
+ public IPv4Net(IPv4 address, short prefixLen) {
+ this.address = address;
+ this.prefixLen = prefixLen;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public IPv4Net(String value) {
+ String[] splits = value.split("/");
+ if (splits.length != 2) {
+ throw new IllegalArgumentException("Specified IPv4Net address must contain an IPv4 " +
+ "address and a prefix length separated by '/'");
+ }
+ this.address = new IPv4(splits[0]);
+ this.prefixLen = Short.decode(splits[1]);
+ }
+
+ /**
+ * Get the address value of the IPv4Net address.
+ *
+ * @return the address value of the IPv4Net address.
+ */
+ public IPv4 address() { return address; }
+
+ /**
+ * Set the address value of the IPv4Net address.
+ *
+ * @param address the address to use.
+ */
+ public void setAddress(IPv4 address) {
+ this.address = address;
+ }
+
+ /**
+ * Get the prefix length value of the IPv4Net address.
+ *
+ * @return the prefix length value of the IPv4Net address.
+ */
+ public short prefixLen() { return prefixLen; }
+
+ /**
+ * Set the prefix length value of the IPv4Net address.
+ *
+ * @param prefixLen the prefix length to use.
+ */
+ public void setPrefixLen(short prefixLen) {
+ this.prefixLen = prefixLen;
+ }
+
+ /**
+ * Set the value of the IPv4Net address.
+ *
+ * @param address the address to use.
+ * @param prefixLen the prefix length to use.
+ */
+ public void setValue(IPv4 address, short prefixLen) {
+ this.address = address;
+ this.prefixLen = prefixLen;
+ }
+
+ /**
+ * Convert the IPv4Net value to an "address/prefixLen" string.
+ *
+ * @return the IPv4Net value as an "address/prefixLen" string.
+ */
+ @Override
+ public String toString() {
+ return this.address.toString() + "/" + this.prefixLen;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/IPv6.java b/src/main/java/net/onrc/onos/core/util/IPv6.java
new file mode 100644
index 0000000..1648723
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/IPv6.java
@@ -0,0 +1,114 @@
+package net.onrc.onos.core.util;
+
+import net.onrc.onos.core.util.serializers.IPv6Deserializer;
+import net.onrc.onos.core.util.serializers.IPv6Serializer;
+
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.openflow.util.HexString;
+
+/**
+ * The class representing an IPv6 address.
+ */
+@JsonDeserialize(using=IPv6Deserializer.class)
+@JsonSerialize(using=IPv6Serializer.class)
+public class IPv6 {
+ private long valueHigh; // The higher (more significant) 64 bits
+ private long valueLow; // The lower (less significant) 64 bits
+
+ /**
+ * Default constructor.
+ */
+ public IPv6() {
+ this.valueHigh = 0;
+ this.valueLow = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public IPv6(IPv6 other) {
+ this.valueHigh = other.valueHigh;
+ this.valueLow = other.valueLow;
+ }
+
+ /**
+ * Constructor from integer values.
+ *
+ * @param valueHigh the higher (more significant) 64 bits of the address.
+ * @param valueLow the lower (less significant) 64 bits of the address.
+ */
+ public IPv6(long valueHigh, long valueLow) {
+ this.valueHigh = valueHigh;
+ this.valueLow = valueLow;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public IPv6(String value) {
+ // TODO: Implement it!
+ this.valueHigh = 0;
+ this.valueLow = 0;
+ }
+
+ /**
+ * Get the value of the higher (more significant) 64 bits of the address.
+ *
+ * @return the value of the higher (more significant) 64 bits of the
+ * address.
+ */
+ public long valueHigh() { return valueHigh; }
+
+ /**
+ * Set the value of the higher (more significant) 64 bits of the address.
+ *
+ * @param valueHigh the higher (more significant) 64 bits of the address.
+ */
+ public void setValueHigh(long valueHigh) {
+ this.valueHigh = valueHigh;
+ }
+
+ /**
+ * Get the value of the lower (less significant) 64 bits of the address.
+ *
+ * @return the value of the lower (less significant) 64 bits of the
+ * address.
+ */
+ public long valueLow() { return valueLow; }
+
+ /**
+ * Get the value of the lower (less significant) 64 bits of the address.
+ *
+ * @param valueLow the lower (less significant) 64 bits of the address.
+ */
+ public void setValueLow(long valueLow) {
+ this.valueLow = valueLow;
+ }
+
+ /**
+ * Set the value of the IPv6 address.
+ *
+ * @param valueHigh the higher (more significant) 64 bits of the address.
+ * @param valueLow the lower (less significant) 64 bits of the address.
+ */
+ public void setValue(long valueHigh, long valueLow) {
+ this.valueHigh = valueHigh;
+ this.valueLow = valueLow;
+ }
+
+ /**
+ * Convert the IPv6 value to a ':' separated string.
+ *
+ * @return the IPv6 value as a ':' separated string.
+ */
+ @Override
+ public String toString() {
+ return HexString.toHexString(this.valueHigh) + ":" +
+ HexString.toHexString(this.valueLow);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/IPv6Net.java b/src/main/java/net/onrc/onos/core/util/IPv6Net.java
new file mode 100644
index 0000000..486f8e8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/IPv6Net.java
@@ -0,0 +1,114 @@
+package net.onrc.onos.core.util;
+
+import net.onrc.onos.core.util.serializers.IPv6NetDeserializer;
+import net.onrc.onos.core.util.serializers.IPv6NetSerializer;
+
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * The class representing an IPv6 network address.
+ */
+@JsonDeserialize(using=IPv6NetDeserializer.class)
+@JsonSerialize(using=IPv6NetSerializer.class)
+public class IPv6Net {
+ private IPv6 address; // The IPv6 address
+ private short prefixLen; // The prefix length
+
+ /**
+ * Default constructor.
+ */
+ public IPv6Net() {
+ this.prefixLen = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public IPv6Net(IPv6Net other) {
+ if (other.address != null)
+ this.address = new IPv6(other.address);
+ this.prefixLen = other.prefixLen;
+ }
+
+ /**
+ * Constructor for a given address and prefix length.
+ *
+ * @param address the address to use.
+ * @param prefixLen the prefix length to use.
+ */
+ public IPv6Net(IPv6 address, short prefixLen) {
+ this.address = address;
+ this.prefixLen = prefixLen;
+ }
+
+ /**
+ * Constructor from a string.
+ *
+ * @param value the value to use.
+ */
+ public IPv6Net(String value) {
+ String[] splits = value.split("/");
+ if (splits.length != 2) {
+ throw new IllegalArgumentException("Specified IPv6Net address must contain an IPv6 " +
+ "address and a prefix length separated by '/'");
+ }
+ this.address = new IPv6(splits[0]);
+ this.prefixLen = Short.decode(splits[1]);
+ }
+
+ /**
+ * Get the address value of the IPv6Net address.
+ *
+ * @return the address value of the IPv6Net address.
+ */
+ public IPv6 address() { return address; }
+
+ /**
+ * Set the address value of the IPv6Net address.
+ *
+ * @param address the address to use.
+ */
+ public void setAddress(IPv6 address) {
+ this.address = address;
+ }
+
+ /**
+ * Get the prefix length value of the IPv6Net address.
+ *
+ * @return the prefix length value of the IPv6Net address.
+ */
+ public short prefixLen() { return prefixLen; }
+
+ /**
+ * Set the prefix length value of the IPv6Net address.
+ *
+ * @param prefixLen the prefix length to use.
+ */
+ public void setPrefixLen(short prefixLen) {
+ this.prefixLen = prefixLen;
+ }
+
+ /**
+ * Set the value of the IPv6Net address.
+ *
+ * @param address the address to use.
+ * @param prefixLen the prefix length to use.
+ */
+ public void setValue(IPv6 address, short prefixLen) {
+ this.address = address;
+ this.prefixLen = prefixLen;
+ }
+
+ /**
+ * Convert the IPv6Net value to an "address/prefixLen" string.
+ *
+ * @return the IPv6Net value as an "address/prefixLen" string.
+ */
+ @Override
+ public String toString() {
+ return this.address.toString() + "/" + this.prefixLen;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/Pair.java b/src/main/java/net/onrc/onos/core/util/Pair.java
new file mode 100644
index 0000000..36a76bb
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/Pair.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.core.util;
+
+/**
+ * A generic class representing a pair of two values.
+ */
+public class Pair<F, S> {
+ public F first; // The first value in the pair
+ public S second; // The second value in the pair
+
+ /**
+ * Constructor for a pair of two values.
+ *
+ * @param first the first value in the pair.
+ * @param second the second value in the pair.
+ */
+ public Pair(F first, S second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("<%s, %s>", first, second);
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/PerformanceMonitor.java b/src/main/java/net/onrc/onos/core/util/PerformanceMonitor.java
new file mode 100644
index 0000000..dd5e36f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/PerformanceMonitor.java
@@ -0,0 +1,292 @@
+package net.onrc.onos.core.util;
+
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class for collecting performance measurements
+ */
+public class PerformanceMonitor {
+ private final static Logger log = LoggerFactory.getLogger(PerformanceMonitor.class);
+
+ // experiment name -> PerformanceMonitor
+ private static final ConcurrentHashMap<String,PerformanceMonitor> perfMons = new ConcurrentHashMap<>();
+ public static PerformanceMonitor experiment(String name) {
+ PerformanceMonitor pm = perfMons.get(name);
+ if (pm == null) {
+ pm = new PerformanceMonitor();
+ PerformanceMonitor existing = perfMons.putIfAbsent(name, pm);
+ if (existing != null) {
+ pm = existing;
+ }
+ }
+ return pm;
+ }
+
+ // tag -> Measurements
+ private final ConcurrentHashMap<String, Queue<Measurement>> map = new ConcurrentHashMap<>();
+ private long overhead;
+ private long experimentStart = Long.MAX_VALUE;
+ private final static double normalization = Math.pow(10, 6);
+
+ /**
+ * Start a performance measurement, identified by a tag
+ *
+ * Note: Only a single measurement can use the same tag at a time.
+ * ..... not true anymore.
+ *
+ * @param tag for performance measurement
+ */
+ public Measurement startStep(String tag) {
+ long start = System.nanoTime();
+ if(start < experimentStart) {
+ experimentStart = start;
+ }
+ Queue<Measurement> list = map.get(tag);
+ if(list == null) {
+ list = new ConcurrentLinkedQueue<Measurement>();
+ Queue<Measurement> existing_list = map.putIfAbsent(tag, list);
+ if (existing_list != null) {
+ // someone concurrently added, using theirs
+ list = existing_list;
+ }
+ }
+ Measurement m = new Measurement();
+ list.add(m);
+ m.start();
+ overhead += System.nanoTime() - start;
+ return m;
+ }
+
+ /**
+ * Stop a performance measurement.
+ *
+ * You must have already started a measurement with tag.
+ *
+ * @param tag for performance measurement
+ */
+ public void stopStep(String tag) {
+ long time = System.nanoTime();
+ Queue<Measurement> list = map.get(tag);
+ if(list == null || list.size() == 0) {
+ log.error("Tag {} does not exist", tag);
+ }
+ list.peek().stop(time);
+ if(list.size() > 1) {
+ log.error("Tag {} has multiple measurements", tag);
+ }
+ overhead += System.nanoTime() - time;
+ }
+
+ /**
+ * Clear all performance measurements.
+ */
+ public void reset() {
+ map.clear();
+ overhead = 0;
+ experimentStart = Long.MAX_VALUE;
+ }
+
+ /**
+ * Write all performance measurements to the log
+ */
+ public void reportAll() {
+ String result = "Performance Results: (avg/start/stop/count)\n";
+ if(map.size() == 0) {
+ result += "No Measurements";
+ log.error(result);
+ return;
+ }
+ long experimentEnd = -1;
+ for(Entry<String, Queue<Measurement>> e : map.entrySet()) {
+ String key = e.getKey();
+ Queue<Measurement> list = e.getValue();
+ long total = 0, count = 0;
+ long start = Long.MAX_VALUE, stop = -1;
+ for(Measurement m : list) {
+ if(m.stop < 0) {
+ continue; // measurement has not been stopped
+ }
+ // Collect overall start and end times
+ if(m.start < start) {
+ start = m.start;
+ }
+ if(m.stop > stop) {
+ stop = m.stop;
+ if(stop > experimentEnd) {
+ experimentEnd = stop;
+ }
+ }
+ // Collect statistics for average
+ total += m.elapsed();
+ count++;
+ }
+ double avg = (double) total / count;
+ // Normalize start/stop
+ start -= experimentStart;
+ stop -= experimentStart;
+ result += key + '=' +
+ (avg / normalization) + '/' +
+ (start / normalization) + '/' +
+ (stop / normalization) + '/' +
+ count + '\n';
+ }
+ double overheadMs = overhead / normalization;
+ double experimentElapsed = (experimentEnd - experimentStart) / normalization;
+ result += "TotalTime:" + experimentElapsed + "/Overhead:" + overheadMs;
+ log.error(result);
+// log.error("Performance Results: {} with measurement overhead: {} ms", map, overheadMilli);
+ }
+
+ /**
+ * Write the performance measurement for a tag to the log
+ *
+ * @param tag the tag name.
+ */
+ public void reportStep(String tag) {
+ Queue<Measurement> list = map.get(tag);
+ if(list == null) {
+ return; //TODO
+ }
+ //TODO: fix this;
+ Measurement m = list.peek();
+ if (m != null) {
+ log.error("Performance Result: tag = {} start = {} stop = {} elapsed = {}",
+ tag, m.start, m.stop, m.toString());
+ } else {
+ log.error("Performance Result: unknown tag {}", tag);
+ }
+ }
+
+ /**
+ * A single performance measurement
+ */
+ public static class Measurement {
+ long start;
+ long stop = -1;
+
+ /**
+ * Start the measurement
+ */
+ public void start() {
+ if(start <= 0) {
+ start = System.nanoTime();
+ }
+ }
+
+ /**
+ * Stop the measurement
+ */
+ public void stop() {
+ long now = System.nanoTime();
+ stop(now);
+ }
+
+ /**
+ * Stop the measurement at a specific time
+ * @param time to stop
+ */
+ public void stop(long time){
+ if(stop <= 0) {
+ stop = time;
+ }
+ }
+
+ /**
+ * Compute the elapsed time of the measurement in nanoseconds
+ *
+ * @return the measurement time in nanoseconds, or -1 if the measurement is stil running.
+ */
+ public long elapsed() {
+ if(stop <= 0) {
+ return -1;
+ }
+ else {
+ return stop - start;
+ }
+ }
+
+ /**
+ * Returns the number of milliseconds for the measurement as a String.
+ */
+ @Override
+ public String toString() {
+ double milli = elapsed() / normalization;
+ double startMs = start / normalization;
+ double stopMs = stop / normalization;
+
+ return milli + "ms/" + startMs + '/' + stopMs;
+ }
+ }
+
+ @Deprecated
+ private static final PerformanceMonitor theInstance = new PerformanceMonitor();
+
+ @Deprecated
+ public static Measurement start(String tag) {
+ return theInstance.startStep(tag);
+ }
+
+ @Deprecated
+ public static void stop(String tag) {
+ theInstance.stopStep(tag);;
+ }
+
+ @Deprecated
+ public static void clear() {
+ theInstance.reset();;
+ }
+
+ @Deprecated
+ public static void report() {
+ theInstance.reportAll();;
+ }
+
+ @Deprecated
+ public static void report(String tag) {
+ theInstance.reportStep(tag);
+ }
+
+ public static void main(String args[]){
+ // test the measurement overhead
+ String tag;
+ for(int i = 0; i < 2; i++){
+ tag = "foo foo foo";
+ Measurement m;
+ m = start(tag); System.out.println(tag); m.stop();
+ m = start(tag); System.out.println(tag); m.stop();
+ m = start(tag); System.out.println(tag); m.stop();
+ m = start(tag); System.out.println(tag); m.stop();
+ tag = "bar";
+ start(tag); stop(tag);
+ tag = "baz";
+ start(tag); stop(tag);
+ report();
+ clear();
+ }
+ for(int i = 0; i < 100; i++){
+ tag = "a";
+ start(tag); stop(tag);
+ start(tag); stop(tag);
+
+ start(tag); stop(tag);
+ start(tag); stop(tag);
+ start(tag); stop(tag);
+ start(tag); stop(tag);
+ start(tag); stop(tag);
+ start(tag); stop(tag);
+
+ tag = "b";
+ start(tag); stop(tag);
+ tag = "c";
+ start(tag); stop(tag);
+ report();
+ clear();
+ }
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/Port.java b/src/main/java/net/onrc/onos/core/util/Port.java
new file mode 100644
index 0000000..722c802
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/Port.java
@@ -0,0 +1,153 @@
+package net.onrc.onos.core.util;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing a network port of a switch.
+ */
+
+public class Port {
+ /**
+ * Special port values.
+ *
+ * Those values are taken as-is from the OpenFlow-v1.0.0 specification
+ * (pp 18-19).
+ */
+ public enum PortValues {
+ /* Maximum number of physical switch ports. */
+ PORT_MAX ((short)0xff00),
+
+ /* Fake output "ports". */
+
+ /* Send the packet out the input port. This
+ virtual port must be explicitly used
+ in order to send back out of the input
+ port. */
+ PORT_IN_PORT ((short)0xfff8),
+
+ /* Perform actions in flow table.
+ NB: This can only be the destination
+ port for packet-out messages. */
+ PORT_TABLE ((short)0xfff9),
+
+ /* Process with normal L2/L3 switching. */
+ PORT_NORMAL ((short)0xfffa),
+
+ /* All physical ports except input port and
+ those disabled by STP. */
+ PORT_FLOOD ((short)0xfffb),
+
+ /* All physical ports except input port. */
+ PORT_ALL ((short)0xfffc),
+
+ /* Send to controller. */
+ PORT_CONTROLLER ((short)0xfffd),
+
+ /* Local openflow "port". */
+ PORT_LOCAL ((short)0xfffe),
+
+ /* Not associated with a physical port. */
+ PORT_NONE ((short)0xffff);
+
+ private final short value; // The value
+
+ /**
+ * Constructor for a given value.
+ *
+ * @param value the value to use for the initialization.
+ */
+ private PortValues(short value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the value as a short integer.
+ *
+ * @return the value as a short integer.
+ */
+ private short value() { return this.value; }
+ }
+
+ private short value;
+
+ /**
+ * Default constructor.
+ */
+ public Port() {
+ this.value = 0;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other the object to copy from.
+ */
+ public Port(Port other) {
+ this.value = other.value();
+ }
+
+ /**
+ * Constructor from a short integer value.
+ *
+ * @param value the value to use.
+ */
+ public Port(short value) {
+ this.value = value;
+ }
+
+ /**
+ * Constructor from a PortValues enum value.
+ *
+ * @param value the value to use.
+ */
+ public Port(PortValues value) {
+ this.value = value.value();
+ }
+
+ /**
+ * Get the value of the port.
+ *
+ * @return the value of the port.
+ */
+ @JsonProperty("value")
+ public short value() { return value; }
+
+ /**
+ * Set the value of the port.
+ *
+ * @param value the value to set.
+ */
+ @JsonProperty("value")
+ public void setValue(short value) {
+ this.value = value;
+ }
+
+ /**
+ * Convert the port value to a string.
+ *
+ * @return the port value as a string.
+ */
+ @Override
+ public String toString() {
+ return Short.toString(this.value);
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Port)) {
+ return false;
+ }
+
+ Port otherPort = (Port) other;
+
+ return value == otherPort.value;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 17;
+ hash += 31 * hash + (int)value;
+ return hash;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/Switch.java b/src/main/java/net/onrc/onos/core/util/Switch.java
new file mode 100644
index 0000000..e3c525f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/Switch.java
@@ -0,0 +1,116 @@
+package net.onrc.onos.core.util;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing a Switch.
+ * NOTE: Currently this class is (almost) not used.
+ */
+public class Switch {
+ /**
+ * The Switch state.
+ */
+ public enum SwitchState {
+ INACTIVE,
+ ACTIVE,
+ }
+
+ private Dpid dpid; // The DPID of the switch
+ private SwitchState state; // The state of the switch
+
+ /**
+ * Default constructor.
+ *
+ * NOTE: The default state for the switch is INACTIVE.
+ */
+ public Switch() {
+ this.dpid = new Dpid();
+ this.state = SwitchState.INACTIVE;
+ }
+
+ /**
+ * Constructor for a given DPID.
+ *
+ * NOTE: The state for the switch with a given DPID is ACTIVE.
+ *
+ * @param dpid the DPID to use.
+ */
+ public Switch(Dpid dpid) {
+ this.dpid = dpid;
+ this.state = SwitchState.ACTIVE;
+ }
+
+ /**
+ * Constructor for a given DPID and Switch State.
+ *
+ * @param dpid the DPID to use.
+ * @param state the Switch State to use.
+ */
+ public Switch(Dpid dpid, SwitchState state) {
+ this.dpid = dpid;
+ this.state = state;
+ }
+
+ /**
+ * Get the DPID.
+ *
+ * @return the DPID.
+ */
+ @JsonProperty("dpid")
+ public Dpid dpid() { return dpid; }
+
+ /**
+ * Set the DPID.
+ *
+ * @param dpid the DPID to use.
+ */
+ @JsonProperty("dpid")
+ public void setDpid(Dpid dpid) {
+ this.dpid = dpid;
+ }
+
+ /**
+ * Get the state.
+ *
+ * @return the state.
+ */
+ @JsonProperty("state")
+ public SwitchState state() { return state; }
+
+ /**
+ * Set the state.
+ *
+ * @param state the state to use.
+ */
+ @JsonProperty("state")
+ public void setState(SwitchState state) {
+ this.state = state;
+ }
+
+ /**
+ * Set the Switch State to ACTIVE.
+ */
+ public void setStateActive() {
+ this.state = SwitchState.ACTIVE;
+ }
+
+ /**
+ * Set the Switch State to INACTIVE.
+ */
+ public void setStateInactive() {
+ this.state = SwitchState.INACTIVE;
+ }
+
+ /**
+ * Convert the Switch value to a string.
+ *
+ * The string has the following form:
+ * dpid/state
+ *
+ * @return the Switch value as a string.
+ */
+ @Override
+ public String toString() {
+ return this.dpid.toString() + "/" + this.state.toString();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/SwitchPort.java b/src/main/java/net/onrc/onos/core/util/SwitchPort.java
new file mode 100644
index 0000000..edc8905
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/SwitchPort.java
@@ -0,0 +1,109 @@
+package net.onrc.onos.core.util;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * The class representing a Switch-Port.
+ */
+public class SwitchPort {
+ private Dpid dpid; // The DPID of the switch
+ private Port port; // The port of the switch
+
+ /**
+ * Default constructor.
+ */
+ public SwitchPort() {
+ }
+
+ /**
+ * Constructor for a given DPID and a port.
+ *
+ * @param dpid the DPID to use.
+ * @param port the port to use.
+ */
+ public SwitchPort(Dpid dpid, Port port) {
+ this.dpid = dpid;
+ this.port = port;
+ }
+
+ /**
+ * Get the DPID value of the Switch-Port.
+ *
+ * @return the DPID value of the Switch-Port.
+ */
+ @JsonProperty("dpid")
+ public Dpid dpid() { return dpid; }
+
+ /**
+ * Set the DPID value of the Switch-Port.
+ *
+ * @param dpid the DPID to use.
+ */
+ @JsonProperty("dpid")
+ public void setDpid(Dpid dpid) {
+ this.dpid = dpid;
+ }
+
+ /**
+ * Get the port value of the Switch-Port.
+ *
+ * @return the port value of the Switch-Port.
+ */
+ @JsonProperty("port")
+ public Port port() { return port; }
+
+ /**
+ * Set the port value of the Switch-Port.
+ *
+ * @param port the port to use.
+ */
+ @JsonProperty("port")
+ public void setPort(Port port) {
+ this.port = port;
+ }
+
+ /**
+ * Set the DPID and port values of the Switch-Port.
+ *
+ * @param dpid the DPID to use.
+ * @param port the port to use.
+ */
+ public void setValue(Dpid dpid, Port port) {
+ this.dpid = dpid;
+ this.port = port;
+ }
+
+ /**
+ * Convert the Switch-Port value to a string.
+ *
+ * The string has the following form:
+ * 01:02:03:04:05:06:07:08/1234
+ *
+ * @return the Switch-Port value as a string.
+ */
+ @Override
+ public String toString() {
+ return this.dpid.toString() + "/" + this.port;
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof SwitchPort)) {
+ return false;
+ }
+
+ SwitchPort otherSwitchPort = (SwitchPort) other;
+
+ return (dpid.equals(otherSwitchPort.dpid) &&
+ port.equals(otherSwitchPort.port));
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 17;
+ hash += 31 * hash + dpid.hashCode();
+ hash += 31 * hash + port.hashCode();
+ return hash;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/DpidDeserializer.java b/src/main/java/net/onrc/onos/core/util/serializers/DpidDeserializer.java
new file mode 100644
index 0000000..7a8c570
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/DpidDeserializer.java
@@ -0,0 +1,40 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.Dpid;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize a DPID from a string.
+ */
+public class DpidDeserializer extends JsonDeserializer<Dpid> {
+
+ protected final static Logger log = LoggerFactory.getLogger(DpidDeserializer.class);
+
+ @Override
+ public Dpid deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ Dpid dpid = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: {} Value: {}", fieldname, value);
+ dpid = new Dpid(value);
+ }
+ }
+ return dpid;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/DpidSerializer.java b/src/main/java/net/onrc/onos/core/util/serializers/DpidSerializer.java
new file mode 100644
index 0000000..4e784f2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/DpidSerializer.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.Dpid;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize a DPID as a string.
+ */
+public class DpidSerializer extends JsonSerializer<Dpid> {
+
+ @Override
+ public void serialize(Dpid dpid, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeStartObject();
+ jGen.writeStringField("value", dpid.toString());
+ jGen.writeEndObject();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/FlowEntryIdDeserializer.java b/src/main/java/net/onrc/onos/core/util/serializers/FlowEntryIdDeserializer.java
new file mode 100644
index 0000000..712daa8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/FlowEntryIdDeserializer.java
@@ -0,0 +1,40 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.FlowEntryId;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize a Flow Entry ID from a string.
+ */
+public class FlowEntryIdDeserializer extends JsonDeserializer<FlowEntryId> {
+
+ protected final static Logger log = LoggerFactory.getLogger(FlowEntryIdDeserializer.class);
+
+ @Override
+ public FlowEntryId deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ FlowEntryId flowEntryId = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: " + fieldname + " Value: " + value);
+ flowEntryId = new FlowEntryId(value);
+ }
+ }
+ return flowEntryId;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/FlowEntryIdSerializer.java b/src/main/java/net/onrc/onos/core/util/serializers/FlowEntryIdSerializer.java
new file mode 100644
index 0000000..3911a4c
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/FlowEntryIdSerializer.java
@@ -0,0 +1,23 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.FlowEntryId;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize a Flow Entry ID as a hexadecimal string.
+ */
+public class FlowEntryIdSerializer extends JsonSerializer<FlowEntryId> {
+
+ @Override
+ public void serialize(FlowEntryId flowEntryId, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeString(flowEntryId.toString());
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/FlowIdDeserializer.java b/src/main/java/net/onrc/onos/core/util/serializers/FlowIdDeserializer.java
new file mode 100644
index 0000000..d1b91a8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/FlowIdDeserializer.java
@@ -0,0 +1,40 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.FlowId;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize a Flow ID from a string.
+ */
+public class FlowIdDeserializer extends JsonDeserializer<FlowId> {
+
+ protected final static Logger log = LoggerFactory.getLogger(FlowIdDeserializer.class);
+
+ @Override
+ public FlowId deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ FlowId flowId = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: {} Value: {}", fieldname, value);
+ flowId = new FlowId(value);
+ }
+ }
+ return flowId;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/FlowIdSerializer.java b/src/main/java/net/onrc/onos/core/util/serializers/FlowIdSerializer.java
new file mode 100644
index 0000000..b0d020e
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/FlowIdSerializer.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.FlowId;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize a Flow ID as a hexadecimal string.
+ */
+public class FlowIdSerializer extends JsonSerializer<FlowId> {
+
+ @Override
+ public void serialize(FlowId flowId, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeStartObject();
+ jGen.writeStringField("value", flowId.toString());
+ jGen.writeEndObject();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/IPv4Deserializer.java b/src/main/java/net/onrc/onos/core/util/serializers/IPv4Deserializer.java
new file mode 100644
index 0000000..2992bfd
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/IPv4Deserializer.java
@@ -0,0 +1,40 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.IPv4;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize an IPv4 address from a string.
+ */
+public class IPv4Deserializer extends JsonDeserializer<IPv4> {
+
+ protected final static Logger log = LoggerFactory.getLogger(IPv4Deserializer.class);
+
+ @Override
+ public IPv4 deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ IPv4 ipv4 = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: {} Value: {}", fieldname, value);
+ ipv4 = new IPv4(value);
+ }
+ }
+ return ipv4;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/IPv4NetDeserializer.java b/src/main/java/net/onrc/onos/core/util/serializers/IPv4NetDeserializer.java
new file mode 100644
index 0000000..68f74c4
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/IPv4NetDeserializer.java
@@ -0,0 +1,40 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.IPv4Net;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize an IPv4Net address from a string.
+ */
+public class IPv4NetDeserializer extends JsonDeserializer<IPv4Net> {
+
+ protected final static Logger log = LoggerFactory.getLogger(IPv4NetDeserializer.class);
+
+ @Override
+ public IPv4Net deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ IPv4Net ipv4Net = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: {} Value: {}", fieldname, value);
+ ipv4Net = new IPv4Net(value);
+ }
+ }
+ return ipv4Net;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/IPv4NetSerializer.java b/src/main/java/net/onrc/onos/core/util/serializers/IPv4NetSerializer.java
new file mode 100644
index 0000000..697f846
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/IPv4NetSerializer.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.IPv4Net;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize an IPv4Net address as a string.
+ */
+public class IPv4NetSerializer extends JsonSerializer<IPv4Net> {
+
+ @Override
+ public void serialize(IPv4Net ipv4Net, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeStartObject();
+ jGen.writeStringField("value", ipv4Net.toString());
+ jGen.writeEndObject();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/IPv4Serializer.java b/src/main/java/net/onrc/onos/core/util/serializers/IPv4Serializer.java
new file mode 100644
index 0000000..773fd1d
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/IPv4Serializer.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.IPv4;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize an IPv4 address as a string.
+ */
+public class IPv4Serializer extends JsonSerializer<IPv4> {
+
+ @Override
+ public void serialize(IPv4 ipv4, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeStartObject();
+ jGen.writeStringField("value", ipv4.toString());
+ jGen.writeEndObject();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/IPv6Deserializer.java b/src/main/java/net/onrc/onos/core/util/serializers/IPv6Deserializer.java
new file mode 100644
index 0000000..6627d2a
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/IPv6Deserializer.java
@@ -0,0 +1,40 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.IPv6;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize an IPv6 address from a string.
+ */
+public class IPv6Deserializer extends JsonDeserializer<IPv6> {
+
+ protected final static Logger log = LoggerFactory.getLogger(IPv6Deserializer.class);
+
+ @Override
+ public IPv6 deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ IPv6 ipv6 = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: {} Value: {}", fieldname, value);
+ ipv6 = new IPv6(value);
+ }
+ }
+ return ipv6;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/IPv6NetDeserializer.java b/src/main/java/net/onrc/onos/core/util/serializers/IPv6NetDeserializer.java
new file mode 100644
index 0000000..08b8018
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/IPv6NetDeserializer.java
@@ -0,0 +1,40 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.IPv6Net;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize an IPv6Net address from a string.
+ */
+public class IPv6NetDeserializer extends JsonDeserializer<IPv6Net> {
+
+ protected final static Logger log = LoggerFactory.getLogger(IPv6NetDeserializer.class);
+
+ @Override
+ public IPv6Net deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ IPv6Net ipv6Net = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: {} Value: {}", fieldname, value);
+ ipv6Net = new IPv6Net(value);
+ }
+ }
+ return ipv6Net;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/IPv6NetSerializer.java b/src/main/java/net/onrc/onos/core/util/serializers/IPv6NetSerializer.java
new file mode 100644
index 0000000..99bd654
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/IPv6NetSerializer.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.IPv6Net;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize an IPv6Net address as a string.
+ */
+public class IPv6NetSerializer extends JsonSerializer<IPv6Net> {
+
+ @Override
+ public void serialize(IPv6Net ipv6Net, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeStartObject();
+ jGen.writeStringField("value", ipv6Net.toString());
+ jGen.writeEndObject();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/IPv6Serializer.java b/src/main/java/net/onrc/onos/core/util/serializers/IPv6Serializer.java
new file mode 100644
index 0000000..b165658
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/IPv6Serializer.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.onrc.onos.core.util.IPv6;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize an IPv6 address as a string.
+ */
+public class IPv6Serializer extends JsonSerializer<IPv6> {
+
+ @Override
+ public void serialize(IPv6 ipv6, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeStartObject();
+ jGen.writeStringField("value", ipv6.toString());
+ jGen.writeEndObject();
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/KryoFactory.java b/src/main/java/net/onrc/onos/core/util/serializers/KryoFactory.java
new file mode 100644
index 0000000..4cd4a00
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/KryoFactory.java
@@ -0,0 +1,238 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.apps.proxyarp.ArpReplyNotification;
+import net.onrc.onos.apps.proxyarp.BroadcastPacketOutNotification;
+import net.onrc.onos.apps.proxyarp.PacketOutNotification;
+import net.onrc.onos.apps.proxyarp.SinglePacketOutNotification;
+import net.onrc.onos.core.devicemanager.OnosDevice;
+import net.onrc.onos.core.intent.ConstrainedShortestPathIntent;
+import net.onrc.onos.core.intent.ErrorIntent;
+import net.onrc.onos.core.intent.Intent;
+import net.onrc.onos.core.intent.IntentOperation;
+import net.onrc.onos.core.intent.IntentOperationList;
+import net.onrc.onos.core.intent.PathIntent;
+import net.onrc.onos.core.intent.ShortestPathIntent;
+import net.onrc.onos.core.intent.runtime.IntentStateList;
+import net.onrc.onos.core.util.CallerId;
+import net.onrc.onos.core.util.DataPath;
+import net.onrc.onos.core.util.DataPathEndpoints;
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.FlowEntry;
+import net.onrc.onos.core.util.FlowEntryAction;
+import net.onrc.onos.core.util.FlowEntryActions;
+import net.onrc.onos.core.util.FlowEntryErrorState;
+import net.onrc.onos.core.util.FlowEntryId;
+import net.onrc.onos.core.util.FlowEntryMatch;
+import net.onrc.onos.core.util.FlowEntrySwitchState;
+import net.onrc.onos.core.util.FlowEntryUserState;
+import net.onrc.onos.core.util.FlowId;
+import net.onrc.onos.core.util.FlowPath;
+import net.onrc.onos.core.util.FlowPathFlags;
+import net.onrc.onos.core.util.FlowPathType;
+import net.onrc.onos.core.util.FlowPathUserState;
+import net.onrc.onos.core.util.IPv4;
+import net.onrc.onos.core.util.IPv4Net;
+import net.onrc.onos.core.util.IPv6;
+import net.onrc.onos.core.util.IPv6Net;
+import net.onrc.onos.core.util.Port;
+import net.onrc.onos.core.util.Switch;
+import net.onrc.onos.ofcontroller.networkgraph.DeviceEvent;
+import net.onrc.onos.ofcontroller.networkgraph.LinkEvent;
+import net.onrc.onos.ofcontroller.networkgraph.Path;
+import net.onrc.onos.ofcontroller.networkgraph.PortEvent;
+import net.onrc.onos.ofcontroller.networkgraph.SwitchEvent;
+import net.onrc.onos.ofcontroller.networkgraph.TopologyEvent;
+// import net.onrc.onos.core.util.SwitchPort;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+import com.esotericsoftware.kryo.Kryo;
+
+/**
+ * Class factory for allocating Kryo instances for
+ * serialization/deserialization of classes.
+ */
+public class KryoFactory {
+ private static final int DEFAULT_PREALLOCATIONS = 100;
+ private ArrayList<Kryo> kryoList = new ArrayList<Kryo>();
+
+ /**
+ * Default constructor.
+ *
+ * Preallocates {@code DEFAULT_PREALLOCATIONS} Kryo instances.
+ */
+ public KryoFactory() {
+ this(DEFAULT_PREALLOCATIONS);
+ }
+
+ /**
+ * Constructor to explicitly specify number of Kryo instances to pool
+ *
+ * @param initialCapacity number of Kryo instance to preallocate
+ */
+ public KryoFactory(final int initialCapacity) {
+ // Preallocate
+ kryoList.ensureCapacity(initialCapacity);
+ for (int i = 0; i < initialCapacity; i++) {
+ Kryo kryo = newKryoImpl();
+ kryoList.add(kryo);
+ }
+ }
+
+ /**
+ * Create and initialize a new Kryo object.
+ *
+ * @return the created Kryo object.
+ */
+ public Kryo newKryo() {
+ return newDeleteKryo(null);
+ }
+
+ /**
+ * Delete an existing Kryo object.
+ *
+ * @param deleteKryo the object to delete.
+ */
+ public void deleteKryo(Kryo deleteKryo) {
+ newDeleteKryo(deleteKryo);
+ }
+
+ /**
+ * Create or delete a Kryo object.
+ *
+ * @param deleteKryo if null, then allocate and return a new object,
+ * otherwise delete the provided object.
+ * @return a new Kryo object if needed, otherwise null.
+ */
+ synchronized private Kryo newDeleteKryo(Kryo deleteKryo) {
+ if (deleteKryo != null) {
+ // Delete an entry by moving it back to the buffer
+ kryoList.add(deleteKryo);
+ return null;
+ } else {
+ Kryo kryo = null;
+ if (kryoList.isEmpty()) {
+ // Preallocate
+ for (int i = 0; i < 100; i++) {
+ kryo = newKryoImpl();
+ kryoList.add(kryo);
+ }
+ }
+
+ kryo = kryoList.remove(kryoList.size() - 1);
+ return kryo;
+ }
+ }
+
+ /**
+ * Create and initialize a new Kryo object.
+ *
+ * @return the created Kryo object.
+ */
+ private Kryo newKryoImpl() {
+ Kryo kryo = new Kryo();
+ kryo.setRegistrationRequired(true);
+ //
+ // WARNING: Order of register() calls affects serialized bytes.
+ // - Do no insert new entry in the middle, always add to the end.
+ // - Do not simply remove existing entry
+ //
+
+ // kryo.setReferences(false);
+ //
+ kryo.register(ArrayList.class);
+
+ // FlowPath and related classes
+ kryo.register(CallerId.class);
+ kryo.register(DataPath.class);
+ kryo.register(DataPathEndpoints.class);
+ kryo.register(Dpid.class);
+ kryo.register(FlowEntryAction.class);
+ kryo.register(FlowEntryAction.ActionEnqueue.class);
+ kryo.register(FlowEntryAction.ActionOutput.class);
+ kryo.register(FlowEntryAction.ActionSetEthernetAddr.class);
+ kryo.register(FlowEntryAction.ActionSetIpToS.class);
+ kryo.register(FlowEntryAction.ActionSetIPv4Addr.class);
+ kryo.register(FlowEntryAction.ActionSetTcpUdpPort.class);
+ kryo.register(FlowEntryAction.ActionSetVlanId.class);
+ kryo.register(FlowEntryAction.ActionSetVlanPriority.class);
+ kryo.register(FlowEntryAction.ActionStripVlan.class);
+ kryo.register(FlowEntryAction.ActionValues.class);
+ kryo.register(FlowEntryActions.class);
+ kryo.register(FlowEntryErrorState.class);
+ kryo.register(FlowEntryId.class);
+ kryo.register(FlowEntry.class);
+ kryo.register(FlowEntryMatch.class);
+ kryo.register(FlowEntryMatch.Field.class);
+ kryo.register(FlowEntrySwitchState.class);
+ kryo.register(FlowEntryUserState.class);
+ kryo.register(FlowId.class);
+ kryo.register(FlowPath.class);
+ kryo.register(FlowPathFlags.class);
+ kryo.register(FlowPathType.class);
+ kryo.register(FlowPathUserState.class);
+ kryo.register(IPv4.class);
+ kryo.register(IPv4Net.class);
+ kryo.register(IPv6.class);
+ kryo.register(IPv6Net.class);
+ kryo.register(byte[].class);
+ kryo.register(MACAddress.class);
+ kryo.register(Port.class);
+ kryo.register(Switch.class);
+ // kryo.register(SwitchPort.class);
+
+ // New data model-related classes
+ kryo.register(DeviceEvent.class);
+ kryo.register(InetAddress.class);
+ kryo.register(LinkEvent.class);
+ kryo.register(PortEvent.class);
+ kryo.register(PortEvent.SwitchPort.class);
+ kryo.register(SwitchEvent.class);
+ kryo.register(TopologyEvent.class);
+
+ // Intent-related classes
+ kryo.register(Path.class);
+ kryo.register(Intent.class);
+ kryo.register(Intent.IntentState.class);
+ kryo.register(PathIntent.class);
+ kryo.register(ShortestPathIntent.class);
+ kryo.register(ConstrainedShortestPathIntent.class);
+ kryo.register(ErrorIntent.class);
+ kryo.register(ErrorIntent.ErrorType.class);
+ kryo.register(IntentOperation.class);
+ kryo.register(IntentOperation.Operator.class);
+ kryo.register(IntentOperationList.class);
+ kryo.register(IntentStateList.class);
+
+ // Device-related classes
+ kryo.register(HashSet.class);
+ kryo.register(Inet4Address.class);
+ kryo.register(OnosDevice.class);
+ kryo.register(Date.class);
+
+ // ProxyArp-related classes
+ kryo.register(BroadcastPacketOutNotification.class);
+ kryo.register(SinglePacketOutNotification.class);
+ kryo.register(ArpReplyNotification.class);
+
+ return kryo;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/MACAddressDeserializer.java b/src/main/java/net/onrc/onos/core/util/serializers/MACAddressDeserializer.java
new file mode 100644
index 0000000..4ed31a5
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/MACAddressDeserializer.java
@@ -0,0 +1,40 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Deserialize a MAC address from a string.
+ */
+public class MACAddressDeserializer extends JsonDeserializer<MACAddress> {
+
+ protected final static Logger log = LoggerFactory.getLogger(MACAddressDeserializer.class);
+
+ @Override
+ public MACAddress deserialize(JsonParser jp,
+ DeserializationContext ctxt)
+ throws IOException, JsonProcessingException {
+
+ MACAddress mac = null;
+
+ jp.nextToken(); // Move to JsonToken.START_OBJECT
+ while (jp.nextToken() != JsonToken.END_OBJECT) {
+ String fieldname = jp.getCurrentName();
+ if ("value".equals(fieldname)) {
+ String value = jp.getText();
+ log.debug("Fieldname: {} Value: {}", fieldname, value);
+ mac = MACAddress.valueOf(value);
+ }
+ }
+ return mac;
+ }
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/MACAddressSerializer.java b/src/main/java/net/onrc/onos/core/util/serializers/MACAddressSerializer.java
new file mode 100644
index 0000000..e57a777
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/util/serializers/MACAddressSerializer.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.core.util.serializers;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize a MAC address as a string.
+ */
+public class MACAddressSerializer extends JsonSerializer<MACAddress> {
+
+ @Override
+ public void serialize(MACAddress mac, JsonGenerator jGen,
+ SerializerProvider serializer)
+ throws IOException, JsonProcessingException {
+ jGen.writeStartObject();
+ jGen.writeStringField("value", mac.toString());
+ jGen.writeEndObject();
+ }
+}