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();
+    }
+}