diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
index fb7cd7d44d2167386a78534725956737adffdaad..428dc7f7e07ceb0598b2854668d6705a2cfa7efe 100644
--- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
+++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java
@@ -59,7 +59,8 @@ public interface IOFSwitch {
         NORMAL("normal"),         // normal port (default)
         TUNNEL("tunnel"),         // tunnel port
         UPLINK("uplink"),         // uplink port (on a virtual switch)
-        MANAGEMENT("management"); // for in-band management
+        MANAGEMENT("management"), // for in-band management
+        TUNNEL_LOOPBACK("tunnel-loopback");
         
         private String value;
         OFPortType(String v) {
diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java
index 8998472cfc808bbcf5dac4e8b4f1c08f953626d4..17bbcf07e292b25b7ad2dae2c675a80389aa199e 100644
--- a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java
+++ b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java
@@ -97,9 +97,9 @@ public abstract class OFSwitchBase implements IOFSwitch {
     // Lock to protect modification of the port maps. We only need to 
     // synchronize on modifications. For read operations we are fine since
     // we rely on ConcurrentMaps which works for our use case.
-    private Object portLock;
+    protected Object portLock;
     // Map port numbers to the appropriate OFPhysicalPort
-    private ConcurrentHashMap<Short, OFPhysicalPort> portsByNumber;
+    protected ConcurrentHashMap<Short, OFPhysicalPort> portsByNumber;
     // Map port names to the appropriate OFPhyiscalPort
     // XXX: The OF spec doesn't specify if port names need to be unique but
     //      according it's always the case in practice. 
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
index 84a76fd17d272312fc66f1fe0e0ab98440abb34d..84d1cc958ef8354819e5eabe3faf04ecb915e5a6 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
@@ -19,7 +19,6 @@ package net.floodlightcontroller.linkdiscovery;
 
 import java.util.Map;
 import java.util.Set;
-
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.routing.Link;
 import net.floodlightcontroller.topology.NodePortTuple;
@@ -87,4 +86,10 @@ public interface ILinkDiscoveryService extends IFloodlightService {
      * @param autoPortFastFeature
      */
     public void setAutoPortFastFeature(boolean autoPortFastFeature);
+
+    /**
+     * Get the map of node-port tuples from link DB
+     */
+    public Map<NodePortTuple, Set<Link>> getPortLinks();
+    
 }
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index 24dd4442584015900fa3adfc991f42851fcd52f8..3191ab7a9a724c599704122bc22ea90e5cfa614e 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -599,10 +599,10 @@ public class LinkDiscoveryManager implements IOFMessageListener,
      * @param port
      * @return
      */
-    protected List<OFAction> getDiscoveryActions (short port){
+    protected List<OFAction> getDiscoveryActions (IOFSwitch sw, OFPhysicalPort port){
         // set actions
         List<OFAction> actions = new ArrayList<OFAction>();
-        actions.add(new OFActionOutput(port, (short) 0));
+        actions.add(new OFActionOutput(port.getPortNumber(), (short) 0));
         return actions;
     }
 
@@ -731,9 +731,15 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
         po.setInPort(OFPort.OFPP_NONE);
 
-        List<OFAction> actions = getDiscoveryActions(port);
+        List<OFAction> actions = getDiscoveryActions(iofSwitch, ofpPort);
         po.setActions(actions);
-        po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
+        
+        short  actionLength = 0;
+        Iterator <OFAction> actionIter = actions.iterator();
+        while (actionIter.hasNext()) {
+            actionLength += actionIter.next().getLength();
+        }
+        po.setActionsLength(actionLength);
 
         // set data
         po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength()
@@ -2284,4 +2290,4 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         else
             log.info("Setting autoportfast feature to OFF");
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LBVip.java b/src/main/java/net/floodlightcontroller/loadbalancer/LBVip.java
index fd8c0bd451ab87c75bdecbcc94016126006bb945..7872175d45517fa9f21a1fcceee20a06087f76e7 100644
--- a/src/main/java/net/floodlightcontroller/loadbalancer/LBVip.java
+++ b/src/main/java/net/floodlightcontroller/loadbalancer/LBVip.java
@@ -32,6 +32,8 @@ public class LBVip {
     
     protected MACAddress proxyMac;
     
+    public static String LB_PROXY_MAC= "12:34:56:78:90:12";
+    
     public LBVip() {
         this.id = String.valueOf((int) (Math.random()*10000));
         this.name = null;
@@ -47,7 +49,7 @@ public class LBVip {
         this.address = 0;
         this.status = 0;
         
-        this.proxyMac = MACAddress.valueOf("12:34:56:78:90:12");
+        this.proxyMac = MACAddress.valueOf(LB_PROXY_MAC);
     }
     
     public String pickPool(IPClient client) {
diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
index 3893ed5708d29acf52e80c78b922ee261cee1d88..25303373c5b363b85a408e30cd1e4b01e155d61e 100644
--- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
+++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java
@@ -133,7 +133,8 @@ public class LoadBalancer implements IFloodlightModule,
     public boolean isCallbackOrderingPrereq(OFType type, String name) {
         return (type.equals(OFType.PACKET_IN) && 
                 (name.equals("topology") || 
-                 name.equals("devicemanager")));
+                 name.equals("devicemanager") ||
+                 name.equals("virtualizer")));
     }
 
     @Override
@@ -504,8 +505,8 @@ public class LoadBalancer implements IFloodlightModule,
                fm.setPriority(Short.MAX_VALUE);
                
                if (inBound) {
-                   entryName = "inbound-vip-"+ member.vipId+"client-"+client.ipAddress+"-port-"+client.targetPort
-                           +"srcswitch-"+path.get(0).getNodeId()+"sw-"+sw;
+                   entryName = "inbound-vip-"+ member.vipId+"-client-"+client.ipAddress+"-port-"+client.targetPort
+                           +"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw;
                    matchString = "nw_src="+IPv4.fromIPv4Address(client.ipAddress)+","
                                + "nw_proto="+String.valueOf(client.nw_proto)+","
                                + "tp_src="+String.valueOf(client.srcPort & 0xffff)+","
@@ -521,8 +522,8 @@ public class LoadBalancer implements IFloodlightModule,
                                "output="+path.get(i+1).getPortId();
                    }
                } else {
-                   entryName = "outbound-vip-"+ member.vipId+"client-"+client.ipAddress+"-port-"+client.targetPort
-                           +"srcswitch-"+path.get(0).getNodeId()+"sw-"+sw;
+                   entryName = "outbound-vip-"+ member.vipId+"-client-"+client.ipAddress+"-port-"+client.targetPort
+                           +"-srcswitch-"+path.get(0).getNodeId()+"-sw-"+sw;
                    matchString = "nw_dst="+IPv4.fromIPv4Address(client.ipAddress)+","
                                + "nw_proto="+String.valueOf(client.nw_proto)+","
                                + "tp_dst="+String.valueOf(client.srcPort & 0xffff)+","
@@ -619,11 +620,13 @@ public class LoadBalancer implements IFloodlightModule,
             pool = new LBPool();
         
         pools.put(pool.id, pool);
-        if (pool.vipId != null)
+        if (pool.vipId != null && vips.containsKey(pool.vipId))
             vips.get(pool.vipId).pools.add(pool.id);
-        else
-            log.error("pool must be specified with non-null vip-id");
-
+        else {
+            log.error("specified vip-id must exist");
+            pool.vipId = null;
+            pools.put(pool.id, pool);
+        }
         return pool;
     }
 
@@ -635,7 +638,12 @@ public class LoadBalancer implements IFloodlightModule,
 
     @Override
     public int removePool(String poolId) {
-        if(pools.containsKey(poolId)){
+        LBPool pool;
+        pool = pools.get(poolId);
+        
+        if(pools!=null){
+            if (pool.vipId != null)
+                vips.get(pool.vipId).pools.remove(poolId);
             pools.remove(poolId);
             return 0;
         } else {
@@ -671,12 +679,14 @@ public class LoadBalancer implements IFloodlightModule,
     public LBMember createMember(LBMember member) {
         if (member == null)
             member = new LBMember();
-        
+
+        members.put(member.id, member);
+        memberIpToId.put(member.address, member.id);
+
         if (member.poolId != null && pools.get(member.poolId) != null) {
             member.vipId = pools.get(member.poolId).vipId;
-            members.put(member.id, member);
-            pools.get(member.poolId).members.add(member.id);
-            memberIpToId.put(member.address, member.id);
+            if (!pools.get(member.poolId).members.contains(member.id))
+                pools.get(member.poolId).members.add(member.id);
         } else
             log.error("member must be specified with non-null pool_id");
         
@@ -691,7 +701,12 @@ public class LoadBalancer implements IFloodlightModule,
 
     @Override
     public int removeMember(String memberId) {
-        if(members.containsKey(memberId)){
+        LBMember member;
+        member = members.get(memberId);
+        
+        if(member != null){
+            if (member.poolId != null)
+                pools.get(member.poolId).members.remove(memberId);
             members.remove(memberId);
             return 0;
         } else {
diff --git a/src/main/java/net/floodlightcontroller/packet/IPv4.java b/src/main/java/net/floodlightcontroller/packet/IPv4.java
index 81d2c6c079308d268b68bcda1f7707ed492160ad..76a140ed002bad4ab93db3488a771b2141223376 100644
--- a/src/main/java/net/floodlightcontroller/packet/IPv4.java
+++ b/src/main/java/net/floodlightcontroller/packet/IPv4.java
@@ -403,7 +403,11 @@ public class IPv4 extends BasePacket {
 
         int result = 0;
         for (int i = 0; i < 4; ++i) {
-            result |= Integer.valueOf(octets[i]) << ((3-i)*8);
+            int oct = Integer.valueOf(octets[i]);
+            if (oct > 255 || oct < 0)
+                throw new IllegalArgumentException("Octet values in specified" +
+                        " IPv4 address must be 0 <= value <= 255");
+            result |=  oct << ((3-i)*8);
         }
         return result;
     }
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
index 5eacd5895d2c194062d26a0de3fbb7d247153de5..e0496a4e54530193e2c307540d8f191c26710efb 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
@@ -120,6 +120,10 @@ public class TopologyManager implements
      */
     protected boolean dtLinksUpdated;
 
+    /** Flag that indicates if tunnel ports were updated or not
+     */
+    protected boolean tunnelPortsUpdated;
+
     protected final int TOPOLOGY_COMPUTE_INTERVAL_MS = 500;
 
     /**
@@ -152,6 +156,7 @@ public class TopologyManager implements
         boolean newInstanceFlag;
         linksUpdated = false;
         dtLinksUpdated = false;
+        tunnelPortsUpdated = false;
         applyUpdates();
         newInstanceFlag = createNewInstance();
         lastUpdateTime = new Date();
@@ -952,11 +957,13 @@ public class TopologyManager implements
     public void addTunnelPort(long sw, short port) {
         NodePortTuple npt = new NodePortTuple(sw, port);
         tunnelPorts.add(npt);
+        tunnelPortsUpdated = true;
     }
 
     public void removeTunnelPort(long sw, short port) {
         NodePortTuple npt = new NodePortTuple(sw, port);
         tunnelPorts.remove(npt);
+        tunnelPortsUpdated = true;
     }
 
     /**
@@ -1257,6 +1264,7 @@ public class TopologyManager implements
         this.clear();
         linksUpdated = true;
         dtLinksUpdated = true;
+        tunnelPortsUpdated = true;
         createNewInstance();
         lastUpdateTime = new Date();
     }
diff --git a/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java b/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java
index 9b3a6ca608aee1b37498e1b53f623ab28cb1aa10..e5a9c01e0093e29055bb75943909860490347e43 100644
--- a/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java
+++ b/src/main/java/org/openflow/protocol/OFStatisticsMessageBase.java
@@ -88,7 +88,10 @@ public abstract class OFStatisticsMessageBase extends OFMessage implements
      *        element
      */
     public OFStatistics getFirstStatistics() {
-        if (statistics == null || statistics.size() != 1) {
+        if (statistics == null ) {
+            throw new IllegalArgumentException("Invariant violation: statistics message of type "+statisticType+" is null");
+        }
+        if (statistics.size() != 1) {
             throw new IllegalArgumentException("Invariant violation: statistics message of type "+statisticType+" contains "+statistics.size() +" statreq/reply messages in its body (should be 1)");
         }
 
diff --git a/src/main/java/org/openflow/protocol/action/OFActionType.java b/src/main/java/org/openflow/protocol/action/OFActionType.java
index b0c2c47d9af7b3181539f9f7a9174bf709a96bc4..18229170b77dc1a01984cfc9f156272f5ad858c5 100644
--- a/src/main/java/org/openflow/protocol/action/OFActionType.java
+++ b/src/main/java/org/openflow/protocol/action/OFActionType.java
@@ -1,7 +1,7 @@
 /**
 *    Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
 *    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
@@ -94,7 +94,7 @@ public enum OFActionType {
     VENDOR              (0xffff, OFActionVendor.class, new Instantiable<OFAction>() {
                             @Override
                             public OFAction instantiate() {
-                                return new OFActionVendor();
+                                return new OFActionVendorGeneric();
                             }});
 
     protected static OFActionType[] mapping;
diff --git a/src/main/java/org/openflow/protocol/action/OFActionVendor.java b/src/main/java/org/openflow/protocol/action/OFActionVendor.java
index b5a15c280cd5342cd28171f378f0afa212a1d3f3..5860ef1160ae43fe3606a582c8e7d7ae842820eb 100644
--- a/src/main/java/org/openflow/protocol/action/OFActionVendor.java
+++ b/src/main/java/org/openflow/protocol/action/OFActionVendor.java
@@ -1,7 +1,7 @@
 /**
 *    Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
 *    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
@@ -24,7 +24,7 @@ import org.jboss.netty.buffer.ChannelBuffer;
  *
  * @author David Erickson (daviderickson@cs.stanford.edu)
  */
-public class OFActionVendor extends OFAction {
+public abstract class OFActionVendor extends OFAction {
     public static int MINIMUM_LENGTH = 8;
 
     protected int vendor;
@@ -86,4 +86,9 @@ public class OFActionVendor extends OFAction {
         }
         return true;
     }
+
+    @Override
+    public String toString() {
+        return super.toString() + "; vendor=" + vendor;
+    }
 }
diff --git a/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java b/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f7859f5ec43e8e13ff8340c50f5cda6f2d2311c
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/action/OFActionVendorGeneric.java
@@ -0,0 +1,96 @@
+/**
+*    Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
+*    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 org.openflow.protocol.action;
+
+
+import java.util.Arrays;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/** A generic / unparsed vendor action. This action is returned by
+ *  BasicFactory.readFromWire if no more specific OFVendorActionFactory
+ *  is registered.
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+public class OFActionVendorGeneric extends OFActionVendor {
+    public static int MINIMUM_LENGTH = 8;
+
+    private final static byte[] EMPTY_ARRAY = new byte[0];
+
+    protected byte[] vendorData;
+
+    public OFActionVendorGeneric() {
+        super();
+    }
+
+    public byte[] getVendorData() {
+        return vendorData;
+    }
+
+    public void setVendorData(byte[] vendorData) {
+        this.vendorData = vendorData;
+    }
+
+    @Override
+    public void readFrom(ChannelBuffer data) {
+        super.readFrom(data);
+
+        int vendorDataLength = this.getLength() - MINIMUM_LENGTH;
+        if (vendorDataLength > 0) {
+            vendorData = new byte[vendorDataLength];
+            data.readBytes(vendorData);
+        } else {
+            vendorData = EMPTY_ARRAY;
+        }
+    }
+
+    @Override
+    public void writeTo(ChannelBuffer data) {
+        super.writeTo(data);
+        data.writeInt(this.vendor);
+        data.writeBytes(vendorData);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 379;
+        int result = super.hashCode();
+        result = prime * result + Arrays.hashCode(vendorData);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (!(obj instanceof OFActionVendorGeneric)) {
+            return false;
+        }
+        OFActionVendorGeneric other = (OFActionVendorGeneric) obj;
+        if (!Arrays.equals(vendorData, other.vendorData)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/org/openflow/protocol/factory/BasicFactory.java b/src/main/java/org/openflow/protocol/factory/BasicFactory.java
index 7b06f2c0c91a5a8a966be3430d34f4dbbb9f23cc..c4d148337526ecbae5ea8d9aa67d89e657404da7 100644
--- a/src/main/java/org/openflow/protocol/factory/BasicFactory.java
+++ b/src/main/java/org/openflow/protocol/factory/BasicFactory.java
@@ -1,7 +1,7 @@
 /**
 *    Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
 *    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
@@ -25,6 +25,7 @@ import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFType;
 import org.openflow.protocol.action.OFAction;
 import org.openflow.protocol.action.OFActionType;
+import org.openflow.protocol.action.OFActionVendor;
 import org.openflow.protocol.statistics.OFStatistics;
 import org.openflow.protocol.statistics.OFStatisticsType;
 import org.openflow.protocol.statistics.OFVendorStatistics;
@@ -45,6 +46,12 @@ import org.openflow.protocol.vendor.OFVendorId;
 public class BasicFactory implements OFMessageFactory, OFActionFactory,
         OFStatisticsFactory, OFVendorDataFactory {
 
+    private final OFVendorActionRegistry vendorActionRegistry;
+
+    public BasicFactory() {
+        vendorActionRegistry = OFVendorActionRegistry.getInstance();
+    }
+
     /**
      * create and return a new instance of a message for OFType t. Also injects
      * factories for those message types that implement the *FactoryAware
@@ -169,19 +176,42 @@ public class BasicFactory implements OFMessageFactory, OFActionFactory,
                 (data.readerIndex() + demux.getLengthU()) > end))
                 return results;
 
-            ofa = getAction(demux.getType());
-            ofa.readFrom(data);
-            if (OFAction.class.equals(ofa.getClass())) {
-                // advance the position for un-implemented messages
-                data.readerIndex(data.readerIndex()+(ofa.getLengthU() -
-                        OFAction.MINIMUM_LENGTH));
-            }
+            ofa = parseActionOne(demux.getType(), data);
             results.add(ofa);
         }
 
         return results;
     }
 
+    private OFAction parseActionOne(OFActionType type, ChannelBuffer data) {
+        OFAction ofa;
+        data.markReaderIndex();
+        ofa = getAction(type);
+        ofa.readFrom(data);
+
+        if(type == OFActionType.VENDOR) {
+            OFActionVendor vendorAction = (OFActionVendor) ofa;
+
+            OFVendorActionFactory vendorActionFactory = vendorActionRegistry.get(vendorAction.getVendor());
+
+            if(vendorActionFactory != null) {
+                // if we have a specific vendorActionFactory for this vendor id,
+                // delegate to it for vendor-specific reparsing of the message
+                data.resetReaderIndex();
+                OFActionVendor newAction = vendorActionFactory.readFrom(data);
+                if(newAction != null)
+                    ofa = newAction;
+            }
+        }
+
+        if (OFAction.class.equals(ofa.getClass())) {
+            // advance the position for un-implemented messages
+            data.readerIndex(data.readerIndex()+(ofa.getLengthU() -
+                    OFAction.MINIMUM_LENGTH));
+        }
+        return ofa;
+    }
+
     @Override
     public OFActionFactory getActionFactory() {
         return this;
diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java b/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea27b6119820c834a00b5723f8c73efd910fbefc
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/factory/OFVendorActionFactory.java
@@ -0,0 +1,27 @@
+package org.openflow.protocol.factory;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.openflow.protocol.action.OFActionVendor;
+
+/** Interface contract for an actionfactory that creates vendor-specific actions.
+ *  VendorActionFactories are registered with the BasicFactory for a specific
+ *  vendor id.
+ *  <p>
+ *  <b>Note:</b> Implementations are expected to be thread-safe.
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+public interface OFVendorActionFactory {
+
+    /** parse the data from the wire, create and return a vendor-specific action.
+     *
+     * @param data contains a serialized vendor action at the current readerPosition.
+     *    The full message is guaranteed to be available in the buffer.
+     *
+     * @return upon success returns a newly allocated vendor-specific
+     *   action instance, and advances the readerPosition in data for the
+     *   entire length. Upon failure, returns null and leaves the readerPosition
+     *   in data unmodified.
+     */
+    OFActionVendor readFrom(ChannelBuffer data);
+}
diff --git a/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java b/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..190bdd4fd42f7bf4ef58dc88a9ea52399eb5a512
--- /dev/null
+++ b/src/main/java/org/openflow/protocol/factory/OFVendorActionRegistry.java
@@ -0,0 +1,34 @@
+package org.openflow.protocol.factory;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** Singleton registry object that holds a mapping from vendor ids to vendor-specific
+ *  mapping factories. Threadsafe.
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+public class OFVendorActionRegistry {
+    private static class InstanceHolder {
+        private final static OFVendorActionRegistry instance = new OFVendorActionRegistry();
+    }
+
+    public static OFVendorActionRegistry getInstance() {
+        return InstanceHolder.instance;
+    }
+    private final Map <Integer, OFVendorActionFactory> vendorActionFactories;
+
+    public OFVendorActionRegistry() {
+        vendorActionFactories = new ConcurrentHashMap<Integer, OFVendorActionFactory>();
+    }
+
+    public OFVendorActionFactory register(int vendorId, OFVendorActionFactory factory) {
+        return vendorActionFactories.put(vendorId, factory);
+    }
+
+    public OFVendorActionFactory get(int vendorId) {
+        return vendorActionFactories.get(vendorId);
+    }
+
+
+}
diff --git a/src/main/java/org/openflow/util/IProducer.java b/src/main/java/org/openflow/util/IProducer.java
index 789f5a2ba9ebb0a41f3bf3a54ffd3cde08636d56..52ae79a52b5dc47af63f71d0aceeb3ee26105727 100644
--- a/src/main/java/org/openflow/util/IProducer.java
+++ b/src/main/java/org/openflow/util/IProducer.java
@@ -2,7 +2,8 @@ package org.openflow.util;
 
 public interface IProducer {
 
-	public void registerConsumer   (Class<?> iface, Object anObj);
-	public void deregisterConsumer (Class<?> iface, Object anObj);
+    public void registerConsumer(Class<?> iface, Object anObj);
+
+    public void deregisterConsumer(Class<?> iface, Object anObj);
 
 }
diff --git a/src/main/java/org/openflow/util/ProducerConsumer.java b/src/main/java/org/openflow/util/ProducerConsumer.java
index 16f71868f07ee94c7feb3def7bf1b3539e7030bb..f2244ef3524483b0a2fc70e8f2ba433efd363c01 100644
--- a/src/main/java/org/openflow/util/ProducerConsumer.java
+++ b/src/main/java/org/openflow/util/ProducerConsumer.java
@@ -5,213 +5,219 @@ import java.util.Hashtable;
 import java.util.Map;
 import java.util.Set;
 
-
 /**
- * The following implement a producer/consumer design pattern in which
- * both producers and consumers explicitly employ a centralized
- * registration mechanism, and java Interfaces are used as contracts.<br>
- *
+ * The following implement a producer/consumer design pattern in which both
+ * producers and consumers explicitly employ a centralized registration
+ * mechanism, and java Interfaces are used as contracts.<br>
  */
 public class ProducerConsumer {
 
-	/*
-	 * Class variables
-	 */
-	protected static ProducerConsumer singleton;
-	
-	/*
-	 * Default constructor
-	 */
-	protected ProducerConsumer ()
-	{
-		producerMap = new Hashtable<Class<?>, Set<IProducer>> ();
-	}
-
-	/*
-	 * Instance variables
-	 */
-
-	// Interface/IProducer map
-	protected Map<Class<?>, Set<IProducer>> producerMap;
-	
-
-	/*
-	 * Protected methods
-	 */
-
-	protected void _registerConsumer (Object consumer, Class<?> [] interfaces, Set<Class<?>> iSet, Set<Class<?>> iUniqueSet)
-	{
-		//*...Process all interfaces...*/
-		for (Class<?> iface: interfaces) {
-			
-			//*...Protect against repeated interfaces...*/
-			if (!iUniqueSet.contains (iface)) {
-				iUniqueSet.add (iface);
-
-				Set<IProducer> producers = producerMap.get (iface);
-
-				if (producers != null) {
-					for (IProducer producer: producers)
-						producer.registerConsumer (iface, consumer);
-					iSet.add (iface);
-				}
-
-				//*...Recurse...*/
-				_registerConsumer (consumer, iface.getInterfaces (), iSet, iUniqueSet);
-			}
-		}
-	}
-	
-	protected void _registerConsumer (Object consumer, Class<?> clazz, Set<Class<?>> iSet, Set<Class<?>> iUniqueSet)
-	{
-		if (clazz != null) {
-			//*...Process all interfaces...*/
-			_registerConsumer (consumer, clazz.getInterfaces (), iSet, iUniqueSet);
-			
-			//*...Recurse the class hierarchy...*/
-			_registerConsumer (consumer, clazz.getSuperclass (), iSet, iUniqueSet);
-		}
-	}
-
-	protected int _deregisterConsumer (Object consumer, Class<?> [] interfaces, Set<Class<?>> iUniqueSet)
-	{
-		int count = 0;
-
-		//*...Process all interfaces...*/
-		for (Class<?> iface: interfaces) {
-			
-			//*...Protect against repeated interfaces...*/
-			if (!iUniqueSet.contains (iface)) {
-				iUniqueSet.add (iface);
-
-				Set<IProducer> producers = producerMap.get (iface);
-
-				if (producers != null) {
-					for (IProducer producer: producers)
-						producer.deregisterConsumer (iface, consumer);
-
-					count ++;
-				}
-
-				//*...Recurse...*/
-				count += _deregisterConsumer (consumer, iface.getInterfaces (), iUniqueSet);
-			}
-		}
-		
-		return count;
-	}
-	
-	protected int _deregisterConsumer (Object consumer, Class<?> clazz, Set<Class<?>> iUniqueSet)
-	{
-		int count = 0;
-
-		if (clazz != null) {
-			//*...Process all interfaces...*/
-			count += _deregisterConsumer (consumer, clazz.getInterfaces (), iUniqueSet);
-			
-			//*...Recurse the class hierarchy...*/
-			count += _deregisterConsumer (consumer, clazz.getSuperclass (), iUniqueSet);
-		}
-		
-		return count;
-	}
-
-	/*
-	 * Singleton API
-	 */
-	
-	/**
-	 * @return singleton ProducerConsumer
-	 */
-	public static synchronized ProducerConsumer getSingleton ()
-	{
-		if (singleton == null)
-			singleton = new ProducerConsumer ();
-		
-		return singleton;
-	}
-	
-	/*
-	 * Producer APIs
-	 */
-
-	/**
-	 * Producer registration
-	 * 
-	 * @param producer object that implements IProducer
-	 * @param iface interface supported by the producer
-	 * @return whether there was a previously registered producer,
-	 * 		   or true if one or more the arguments were invalid
-	 */
-	public boolean registerProducer (IProducer producer, Class<?> iface)
-	{
-		if (producer != null && iface != null && iface.isInterface ()) {
-			Set<IProducer> producers = producerMap.get (iface);
-			
-			if (producers == null) {
-				producers = new HashSet<IProducer> ();
-				producerMap.put (iface, producers);
-			}
-
-			return producers.add (producer);
-		}
-		else
-			return true;
-	}
-	
-	/**
-	 * Producer deregistration
-	 * 
-	 * @param producer object that implements IProducer
-	 * @param iface interface supported by the producer
-	 * @return whether the interface/producer pair was removed,
-	 * 		   or false if one or more the arguments were invalid
-	 */
-	public boolean deregisterProducer (IProducer producer, Class<?> iface)
-	{
-		if (producer != null && iface != null && iface.isInterface ()) {
-			Set<IProducer> producers = producerMap.get (iface);
-			
-			if (producers != null)
-				return producers.remove (producer);
-		}
-
-		return false;
-	}
-
-	
-	/*
-	 * Consumer APIs
-	 */
-
-	/**
-	 * Consumer registration
-	 * 
-	 * @param consumer object that implements producer-specific interfaces
-	 * @return set of supported interfaces
-	 */
-	public Set<Class<?>> registerConsumer (Object consumer)
-	{
-		Set<Class<?>> iSet = new HashSet<Class<?>> ();
-		
-		if (consumer != null)
-			_registerConsumer (consumer, consumer.getClass (), iSet, new HashSet<Class<?>> ());
-		
-		return iSet;
-	}
-
-	/**
-	 * Consumer deregistration 
-	 * 
-	 * @param consumer object to deregister
-	 * @return number of unregistered interfaces
-	 */
-	public int deregisterConsumer (Object consumer)
-	{
-		if (consumer != null)
-			return _deregisterConsumer (consumer, consumer.getClass (), new HashSet<Class<?>> ());
-		else
-			return 0;
-	}
+    /*
+     * Class variables
+     */
+    protected static ProducerConsumer singleton;
+
+    /*
+     * Default constructor
+     */
+    protected ProducerConsumer() {
+        producerMap = new Hashtable<Class<?>, Set<IProducer>>();
+    }
+
+    /*
+     * Instance variables
+     */
+
+    // Interface/IProducer map
+    protected Map<Class<?>, Set<IProducer>> producerMap;
+
+    /*
+     * Protected methods
+     */
+
+    protected void _registerConsumer(Object consumer, Class<?>[] interfaces,
+                                     Set<Class<?>> iSet,
+                                     Set<Class<?>> iUniqueSet) {
+        // *...Process all interfaces...*/
+        for (Class<?> iface : interfaces) {
+
+            // *...Protect against repeated interfaces...*/
+            if (!iUniqueSet.contains(iface)) {
+                iUniqueSet.add(iface);
+
+                Set<IProducer> producers = producerMap.get(iface);
+
+                if (producers != null) {
+                    for (IProducer producer : producers)
+                        producer.registerConsumer(iface, consumer);
+                    iSet.add(iface);
+                }
+
+                // *...Recurse...*/
+                _registerConsumer(consumer, iface.getInterfaces(), iSet,
+                                  iUniqueSet);
+            }
+        }
+    }
+
+    protected void _registerConsumer(Object consumer, Class<?> clazz,
+                                     Set<Class<?>> iSet,
+                                     Set<Class<?>> iUniqueSet) {
+        if (clazz != null) {
+            // *...Process all interfaces...*/
+            _registerConsumer(consumer, clazz.getInterfaces(), iSet,
+                              iUniqueSet);
+
+            // *...Recurse the class hierarchy...*/
+            _registerConsumer(consumer, clazz.getSuperclass(), iSet,
+                              iUniqueSet);
+        }
+    }
+
+    protected int _deregisterConsumer(Object consumer,
+                                      Class<?>[] interfaces,
+                                      Set<Class<?>> iUniqueSet) {
+        int count = 0;
+
+        // *...Process all interfaces...*/
+        for (Class<?> iface : interfaces) {
+
+            // *...Protect against repeated interfaces...*/
+            if (!iUniqueSet.contains(iface)) {
+                iUniqueSet.add(iface);
+
+                Set<IProducer> producers = producerMap.get(iface);
+
+                if (producers != null) {
+                    for (IProducer producer : producers)
+                        producer.deregisterConsumer(iface, consumer);
+
+                    count++;
+                }
+
+                // *...Recurse...*/
+                count += _deregisterConsumer(consumer,
+                                             iface.getInterfaces(),
+                                             iUniqueSet);
+            }
+        }
+
+        return count;
+    }
+
+    protected int _deregisterConsumer(Object consumer, Class<?> clazz,
+                                      Set<Class<?>> iUniqueSet) {
+        int count = 0;
+
+        if (clazz != null) {
+            // *...Process all interfaces...*/
+            count += _deregisterConsumer(consumer, clazz.getInterfaces(),
+                                         iUniqueSet);
+
+            // *...Recurse the class hierarchy...*/
+            count += _deregisterConsumer(consumer, clazz.getSuperclass(),
+                                         iUniqueSet);
+        }
+
+        return count;
+    }
+
+    /*
+     * Singleton API
+     */
+
+    /**
+     * @return singleton ProducerConsumer
+     */
+    public static synchronized ProducerConsumer getSingleton() {
+        if (singleton == null) singleton = new ProducerConsumer();
+
+        return singleton;
+    }
+
+    /*
+     * Producer APIs
+     */
+
+    /**
+     * Producer registration
+     * 
+     * @param producer
+     *            object that implements IProducer
+     * @param iface
+     *            interface supported by the producer
+     * @return whether there was a previously registered producer, or true if
+     *         one or more the arguments were invalid
+     */
+    public boolean registerProducer(IProducer producer, Class<?> iface) {
+        if (producer != null && iface != null && iface.isInterface()) {
+            Set<IProducer> producers = producerMap.get(iface);
+
+            if (producers == null) {
+                producers = new HashSet<IProducer>();
+                producerMap.put(iface, producers);
+            }
+
+            return producers.add(producer);
+        } else
+            return true;
+    }
+
+    /**
+     * Producer deregistration
+     * 
+     * @param producer
+     *            object that implements IProducer
+     * @param iface
+     *            interface supported by the producer
+     * @return whether the interface/producer pair was removed, or false if one
+     *         or more the arguments were invalid
+     */
+    public boolean deregisterProducer(IProducer producer, Class<?> iface) {
+        if (producer != null && iface != null && iface.isInterface()) {
+            Set<IProducer> producers = producerMap.get(iface);
+
+            if (producers != null) return producers.remove(producer);
+        }
+
+        return false;
+    }
+
+    /*
+     * Consumer APIs
+     */
+
+    /**
+     * Consumer registration
+     * 
+     * @param consumer
+     *            object that implements producer-specific interfaces
+     * @return set of supported interfaces
+     */
+    public Set<Class<?>> registerConsumer(Object consumer) {
+        Set<Class<?>> iSet = new HashSet<Class<?>>();
+
+        if (consumer != null)
+                             _registerConsumer(consumer,
+                                               consumer.getClass(), iSet,
+                                               new HashSet<Class<?>>());
+
+        return iSet;
+    }
+
+    /**
+     * Consumer deregistration
+     * 
+     * @param consumer
+     *            object to deregister
+     * @return number of unregistered interfaces
+     */
+    public int deregisterConsumer(Object consumer) {
+        if (consumer != null)
+            return _deregisterConsumer(consumer, consumer.getClass(),
+                                       new HashSet<Class<?>>());
+        else
+            return 0;
+    }
 
 }
diff --git a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c97018385eb21e710cd503422259f46d7a99a10
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java
@@ -0,0 +1,677 @@
+package net.floodlightcontroller.loadbalancer;
+
+import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.anyShort;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.easymock.Capture;
+import org.easymock.CaptureType;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.OFPacketIn.OFPacketInReason;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.util.HexString;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.test.MockFloodlightProvider;
+import net.floodlightcontroller.core.test.MockThreadPoolService;
+import net.floodlightcontroller.counter.CounterStore;
+import net.floodlightcontroller.counter.ICounterStoreService;
+import net.floodlightcontroller.devicemanager.IDeviceService;
+import net.floodlightcontroller.devicemanager.IEntityClassifierService;
+import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier;
+import net.floodlightcontroller.devicemanager.test.MockDeviceManager;
+import net.floodlightcontroller.flowcache.FlowReconcileManager;
+import net.floodlightcontroller.flowcache.IFlowReconcileService;
+import net.floodlightcontroller.packet.ARP;
+import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.packet.ICMP;
+import net.floodlightcontroller.packet.IPacket;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.restserver.RestApiServer;
+import net.floodlightcontroller.routing.IRoutingService;
+import net.floodlightcontroller.routing.Route;
+import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
+import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.storage.IStorageSourceService;
+import net.floodlightcontroller.storage.memory.MemoryStorageSource;
+import net.floodlightcontroller.test.FloodlightTestCase;
+import net.floodlightcontroller.threadpool.IThreadPoolService;
+import net.floodlightcontroller.topology.ITopologyService;
+import net.floodlightcontroller.topology.NodePortTuple;
+
+public class LoadBalancerTest extends FloodlightTestCase {
+    protected LoadBalancer lb;
+    
+    protected FloodlightContext cntx;
+    protected FloodlightModuleContext fmc;
+    protected MockFloodlightProvider mockFloodlightProvider;
+    protected MockDeviceManager deviceManager;
+    protected MockThreadPoolService tps;
+    protected FlowReconcileManager frm;
+    protected DefaultEntityClassifier entityClassifier;
+    protected IRoutingService routingEngine;
+    protected ITopologyService topology;
+    protected StaticFlowEntryPusher sfp;
+    protected MemoryStorageSource storage;
+    protected RestApiServer restApi;
+    protected VipsResource vipsResource;
+    protected PoolsResource poolsResource;
+    protected MembersResource membersResource;
+    
+    protected LBVip vip1, vip2;
+    protected LBPool pool1, pool2, pool3;
+    protected LBMember member1, member2, member3, member4;
+    
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        lb = new LoadBalancer();
+        
+        cntx = new FloodlightContext();
+        fmc = new FloodlightModuleContext();
+        entityClassifier = new DefaultEntityClassifier(); // dependency for device manager
+        frm = new FlowReconcileManager(); //dependency for device manager
+        tps = new MockThreadPoolService(); //dependency for device manager
+        deviceManager = new MockDeviceManager();
+        topology = createMock(ITopologyService.class);
+        routingEngine = createMock(IRoutingService.class);
+        restApi = new RestApiServer();
+        sfp = new StaticFlowEntryPusher();
+        storage = new MemoryStorageSource(); //dependency for sfp
+
+        fmc.addService(IRestApiService.class, restApi);
+        fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider());
+        fmc.addService(IEntityClassifierService.class, entityClassifier);
+        fmc.addService(IFlowReconcileService.class, frm);
+        fmc.addService(IThreadPoolService.class, tps);
+        fmc.addService(IDeviceService.class, deviceManager);
+        fmc.addService(ITopologyService.class, topology);
+        fmc.addService(IRoutingService.class, routingEngine);
+        fmc.addService(ICounterStoreService.class, new CounterStore());
+        fmc.addService(IStaticFlowEntryPusherService.class, sfp);
+        fmc.addService(ILoadBalancerService.class, lb);
+        fmc.addService(IStorageSourceService.class, storage);
+        
+        lb.init(fmc);
+        getMockFloodlightProvider().init(fmc);
+        entityClassifier.init(fmc);
+        frm.init(fmc);
+        tps.init(fmc);
+        deviceManager.init(fmc);
+        restApi.init(fmc);
+        sfp.init(fmc);
+        storage.init(fmc);
+
+        topology.addListener(deviceManager);
+        expectLastCall().times(1);
+        replay(topology);        
+
+        lb.startUp(fmc);
+        getMockFloodlightProvider().startUp(fmc);
+        entityClassifier.startUp(fmc);
+        frm.startUp(fmc);
+        tps.startUp(fmc);
+        deviceManager.startUp(fmc);
+        restApi.startUp(fmc);
+        sfp.startUp(fmc);
+        storage.startUp(fmc); 
+
+        verify(topology);
+
+        vipsResource = new VipsResource();
+        poolsResource = new PoolsResource();
+        membersResource = new MembersResource();                
+
+        vip1=null;
+        vip2=null;
+        
+        pool1=null;
+        pool2=null;
+        pool3=null;
+        
+        member1=null;
+        member2=null;
+        member3=null;
+        member4=null;
+    }
+    
+    @Test
+    public void testCreateVip() {
+        String postData1, postData2;
+        IOException error = null;
+        
+        postData1 = "{\"id\":\"1\",\"name\":\"vip1\",\"protocol\":\"icmp\",\"address\":\"10.0.0.100\",\"port\":\"8\"}";
+        postData2 = "{\"id\":\"2\",\"name\":\"vip2\",\"protocol\":\"tcp\",\"address\":\"10.0.0.200\",\"port\":\"100\"}";
+        
+        try {
+            vip1 = vipsResource.jsonToVip(postData1);                
+        } catch (IOException e) {
+            error = e;
+        }
+        try {
+            vip2 = vipsResource.jsonToVip(postData2);                
+        } catch (IOException e) {
+            error = e;
+        }
+        
+        // verify correct parsing
+        assertFalse(vip1==null);
+        assertFalse(vip2==null);
+        assertTrue(error==null);
+        
+        lb.createVip(vip1);
+        lb.createVip(vip2);
+        
+        // verify correct creation
+        assertTrue(lb.vips.containsKey(vip1.id));
+        assertTrue(lb.vips.containsKey(vip2.id));        
+    }
+    
+    @Test
+    public void testRemoveVip() {
+     
+        testCreateVip();
+        
+        // verify correct initial condition
+        assertFalse(vip1==null);
+        assertFalse(vip2==null);
+        
+        lb.removeVip(vip1.id);
+        lb.removeVip(vip2.id);
+        
+        // verify correct removal
+        assertFalse(lb.vips.containsKey(vip1.id));
+        assertFalse(lb.vips.containsKey(vip2.id));        
+
+    }
+
+    @Test
+    public void testCreatePool() {
+        String postData1, postData2, postData3;
+        IOException error = null;
+        
+        testCreateVip();
+        
+        postData1 = "{\"id\":\"1\",\"name\":\"pool1\",\"protocol\":\"icmp\",\"vip_id\":\"1\"}";
+        postData2 = "{\"id\":\"2\",\"name\":\"pool2\",\"protocol\":\"tcp\",\"vip_id\":\"2\"}";
+        postData3 = "{\"id\":\"3\",\"name\":\"pool3\",\"protocol\":\"udp\",\"vip_id\":\"3\"}";
+        
+        try {
+            pool1 = poolsResource.jsonToPool(postData1);                
+        } catch (IOException e) {
+            error = e;
+        }
+        try {
+            pool2 = poolsResource.jsonToPool(postData2);                
+        } catch (IOException e) {
+            error = e;
+        }
+        try {
+            pool3 = poolsResource.jsonToPool(postData3);                
+        } catch (IOException e) {
+            error = e;
+        }
+        
+        // verify correct parsing
+        assertFalse(pool1==null);
+        assertFalse(pool2==null);
+        assertFalse(pool3==null);
+        assertTrue(error==null);
+        
+        lb.createPool(pool1);
+        lb.createPool(pool2);
+        lb.createPool(pool3);
+
+        // verify successful creates; two registered with vips and one not
+        assertTrue(lb.pools.containsKey(pool1.id));
+        assertTrue(lb.vips.get(pool1.vipId).pools.contains(pool1.id));
+        assertTrue(lb.pools.containsKey(pool2.id));        
+        assertTrue(lb.vips.get(pool2.vipId).pools.contains(pool2.id));
+        assertTrue(lb.pools.containsKey(pool3.id));
+        assertFalse(lb.vips.containsKey(pool3.vipId));
+        
+    }
+    
+    @Test
+    public void testRemovePool() {
+        testCreateVip();
+        testCreatePool();
+        
+        // verify correct initial condition
+        assertFalse(vip1==null);
+        assertFalse(vip2==null);
+        assertFalse(pool1==null);
+        assertFalse(pool2==null);
+        assertFalse(pool3==null);
+        
+        lb.removePool(pool1.id);
+        lb.removePool(pool2.id);
+        lb.removePool(pool3.id);
+        
+        // verify correct removal
+        assertFalse(lb.pools.containsKey(pool1.id));
+        assertFalse(lb.pools.containsKey(pool2.id));
+        assertFalse(lb.pools.containsKey(pool3.id));
+        
+        //verify pool cleanup from vip
+        assertFalse(lb.vips.get(pool1.vipId).pools.contains(pool1.id));
+        assertFalse(lb.vips.get(pool2.vipId).pools.contains(pool2.id));
+    }
+
+    @Test
+    public void testCreateMember() {
+        String postData1, postData2, postData3, postData4;
+        IOException error = null;
+        
+        testCreateVip();
+        testCreatePool();
+        
+        postData1 = "{\"id\":\"1\",\"address\":\"10.0.0.3\",\"port\":\"8\",\"pool_id\":\"1\"}";
+        postData2 = "{\"id\":\"2\",\"address\":\"10.0.0.4\",\"port\":\"8\",\"pool_id\":\"1\"}";
+        postData3 = "{\"id\":\"3\",\"address\":\"10.0.0.5\",\"port\":\"100\",\"pool_id\":\"2\"}";
+        postData4 = "{\"id\":\"4\",\"address\":\"10.0.0.6\",\"port\":\"100\",\"pool_id\":\"2\"}";
+        
+        try {
+            member1 = membersResource.jsonToMember(postData1);                
+        } catch (IOException e) {
+            error = e;
+        }
+        try {
+            member2 = membersResource.jsonToMember(postData2);                
+        } catch (IOException e) {
+            error = e;
+        }
+        try {
+            member3 = membersResource.jsonToMember(postData3);                
+        } catch (IOException e) {
+            error = e;
+        }
+        try {
+            member4 = membersResource.jsonToMember(postData4);                
+        } catch (IOException e) {
+            error = e;
+        }
+        
+        // verify correct parsing
+        assertFalse(member1==null);
+        assertFalse(member2==null);
+        assertFalse(member3==null);
+        assertFalse(member4==null);
+        assertTrue(error==null);
+        
+        lb.createMember(member1);
+        lb.createMember(member2);
+        lb.createMember(member3);
+        lb.createMember(member4);
+        
+        // add the same server a second time
+        lb.createMember(member1);
+
+        // verify successful creates
+        assertTrue(lb.members.containsKey(member1.id));
+        assertTrue(lb.members.containsKey(member2.id));
+        assertTrue(lb.members.containsKey(member3.id));
+        assertTrue(lb.members.containsKey(member4.id));
+        
+        assertTrue(lb.pools.get(member1.poolId).members.size()==2);
+        assertTrue(lb.pools.get(member3.poolId).members.size()==2);
+        
+        // member1 should inherit valid vipId from pool
+        assertTrue(lb.vips.get(member1.vipId)!=null);
+    }
+    
+    @Test
+    public void testRemoveMember() {
+        testCreateVip();
+        testCreatePool();
+        testCreateMember();
+        
+        // verify correct initial condition
+        assertFalse(vip1==null);
+        assertFalse(vip2==null);
+        assertFalse(pool1==null);
+        assertFalse(pool2==null);
+        assertFalse(pool3==null);
+        assertFalse(member1==null);
+        assertFalse(member2==null);
+        assertFalse(member3==null);
+        assertFalse(member4==null);
+        
+        lb.removeMember(member1.id);
+        lb.removeMember(member2.id);
+        lb.removeMember(member3.id);
+        lb.removeMember(member4.id);
+        
+        // verify correct removal
+        assertFalse(lb.members.containsKey(member1.id));
+        assertFalse(lb.members.containsKey(member2.id));
+        assertFalse(lb.members.containsKey(member3.id));
+        assertFalse(lb.members.containsKey(member4.id));
+        
+        //verify member cleanup from pool
+        assertFalse(lb.pools.get(member1.poolId).members.contains(member1.id));
+        assertFalse(lb.pools.get(member2.poolId).members.contains(member2.id));
+        assertFalse(lb.pools.get(member3.poolId).members.contains(member3.id));
+        assertFalse(lb.pools.get(member4.poolId).members.contains(member4.id));
+
+    }
+    
+    @Test
+    public void testTwoSubsequentIcmpRequests() throws Exception {
+     testCreateVip();
+     testCreatePool();
+     testCreateMember();
+     
+     IOFSwitch sw1;
+     
+     IPacket arpRequest1, arpReply1, icmpPacket1, icmpPacket2;
+     
+     byte[] arpRequest1Serialized;
+     byte[] arpReply1Serialized;
+     byte[] icmpPacket1Serialized, icmpPacket2Serialized;
+          
+     OFPacketIn arpRequestPacketIn1;
+     OFPacketIn icmpPacketIn1, icmpPacketIn2;
+     
+     OFPacketOut arpReplyPacketOut1; 
+
+     Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
+     Capture<FloodlightContext> bc1 = 
+             new Capture<FloodlightContext>(CaptureType.ALL);
+
+     int fastWildcards = 
+             OFMatch.OFPFW_IN_PORT | 
+             OFMatch.OFPFW_NW_PROTO | 
+             OFMatch.OFPFW_TP_SRC | 
+             OFMatch.OFPFW_TP_DST | 
+             OFMatch.OFPFW_NW_SRC_ALL | 
+             OFMatch.OFPFW_NW_DST_ALL |
+             OFMatch.OFPFW_NW_TOS;
+
+     sw1 = EasyMock.createNiceMock(IOFSwitch.class);
+     expect(sw1.getId()).andReturn(1L).anyTimes();
+     expect(sw1.getStringId()).andReturn("00:00:00:00:00:01").anyTimes();
+     expect(sw1.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn((Integer)fastWildcards).anyTimes();
+     expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
+     sw1.write(capture(wc1), capture(bc1));
+     expectLastCall().anyTimes(); 
+     sw1.flush();
+     expectLastCall().anyTimes();
+
+     replay(sw1);
+     sfp.addedSwitch(sw1);
+     verify(sw1);
+     
+     /* Test plan:
+      * - two clients and two servers on sw1 port 1, 2, 3, 4
+      * - mock arp request received towards vip1 from (1L, 1)
+      * - proxy arp got pushed out to (1L, 1)- check sw1 getting the packetout
+      * - mock icmp request received towards vip1 from (1L, 1)
+      * - device manager list of devices queried to identify source and dest devices
+      * - routing engine queried to get inbound and outbound routes
+      * - check getRoute calls and responses
+      * - sfp called to install flows
+      * - check sfp calls
+      */
+     
+     // Build topology
+     reset(topology);
+     expect(topology.isIncomingBroadcastAllowed(anyLong(), anyShort())).andReturn(true).anyTimes();
+     expect(topology.getL2DomainId(1L)).andReturn(1L).anyTimes();
+     expect(topology.isAttachmentPointPort(1L, (short)1)).andReturn(true).anyTimes();
+     expect(topology.isAttachmentPointPort(1L, (short)2)).andReturn(true).anyTimes();
+     expect(topology.isAttachmentPointPort(1L, (short)3)).andReturn(true).anyTimes();
+     expect(topology.isAttachmentPointPort(1L, (short)4)).andReturn(true).anyTimes();
+     replay(topology);
+     
+
+
+     // Build arp packets
+     arpRequest1 = new Ethernet()
+     .setSourceMACAddress("00:00:00:00:00:01")
+     .setDestinationMACAddress("ff:ff:ff:ff:ff:ff")
+     .setEtherType(Ethernet.TYPE_ARP)
+     .setVlanID((short) 0)
+     .setPriorityCode((byte) 0)
+     .setPayload(
+         new ARP()
+         .setHardwareType(ARP.HW_TYPE_ETHERNET)
+         .setProtocolType(ARP.PROTO_TYPE_IP)
+         .setHardwareAddressLength((byte) 6)
+         .setProtocolAddressLength((byte) 4)
+         .setOpCode(ARP.OP_REQUEST)
+         .setSenderHardwareAddress(HexString.fromHexString("00:00:00:00:00:01"))
+         .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1"))
+         .setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:00"))
+         .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100")));
+
+     arpRequest1Serialized = arpRequest1.serialize();
+
+     arpRequestPacketIn1 = 
+             ((OFPacketIn) getMockFloodlightProvider().getOFMessageFactory().
+                     getMessage(OFType.PACKET_IN))
+                     .setBufferId(-1)
+                     .setInPort((short) 1)
+                     .setPacketData(arpRequest1Serialized)
+                     .setReason(OFPacketInReason.NO_MATCH)
+                     .setTotalLength((short) arpRequest1Serialized.length);
+     
+     IFloodlightProviderService.bcStore.put(cntx,
+                                            IFloodlightProviderService.CONTEXT_PI_PAYLOAD, 
+                                            (Ethernet) arpRequest1);
+
+     // Mock proxy arp packet-out
+     arpReply1 = new Ethernet()
+     .setSourceMACAddress(LBVip.LB_PROXY_MAC)
+     .setDestinationMACAddress(HexString.fromHexString("00:00:00:00:00:01"))
+     .setEtherType(Ethernet.TYPE_ARP)
+     .setVlanID((short) 0)
+     .setPriorityCode((byte) 0)
+     .setPayload(
+         new ARP()
+         .setHardwareType(ARP.HW_TYPE_ETHERNET)
+         .setProtocolType(ARP.PROTO_TYPE_IP)
+         .setHardwareAddressLength((byte) 6)
+         .setProtocolAddressLength((byte) 4)
+         .setOpCode(ARP.OP_REPLY)
+         .setSenderHardwareAddress(HexString.fromHexString(LBVip.LB_PROXY_MAC))
+         .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100"))
+         .setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:01"))
+         .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1")));
+     
+     arpReply1Serialized = arpReply1.serialize();
+     
+     arpReplyPacketOut1 = 
+             (OFPacketOut) getMockFloodlightProvider().getOFMessageFactory().
+                 getMessage(OFType.PACKET_OUT);
+     arpReplyPacketOut1.setBufferId(OFPacketOut.BUFFER_ID_NONE)
+         .setInPort(OFPort.OFPP_NONE.getValue());
+     List<OFAction> poactions = new ArrayList<OFAction>();
+     poactions.add(new OFActionOutput(arpRequestPacketIn1.getInPort(), (short) 0xffff));
+     arpReplyPacketOut1.setActions(poactions)
+         .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
+         .setPacketData(arpReply1Serialized)
+         .setLengthU(OFPacketOut.MINIMUM_LENGTH+
+                     arpReplyPacketOut1.getActionsLength()+
+                     arpReply1Serialized.length);
+
+     lb.receive(sw1, arpRequestPacketIn1, cntx);
+     verify(sw1, topology);
+     
+     assertTrue(wc1.hasCaptured());  // wc1 should get packetout
+     
+     List<OFMessage> msglist1 = wc1.getValues();
+     
+     for (OFMessage m: msglist1) {
+         if (m instanceof OFPacketOut)
+             assertEquals(arpReplyPacketOut1, m);
+         else
+             assertTrue(false); // unexpected message
+     }
+     
+     //
+     // Skip arpRequest2 test - in reality this will happen, but for unit test the same logic
+     // is already validated with arpRequest1 test above
+     //
+     
+     // Build icmp packets
+     icmpPacket1 = new Ethernet()
+     .setSourceMACAddress("00:00:00:00:00:01")
+     .setDestinationMACAddress(LBVip.LB_PROXY_MAC)
+     .setEtherType(Ethernet.TYPE_IPv4)
+     .setVlanID((short) 0)
+     .setPriorityCode((byte) 0)
+     .setPayload(
+         new IPv4()
+         .setSourceAddress("10.0.0.1")
+         .setDestinationAddress("10.0.0.100")
+         .setProtocol(IPv4.PROTOCOL_ICMP)
+         .setPayload(new ICMP()
+         .setIcmpCode((byte) 0)
+         .setIcmpType((byte) 0)));
+
+     icmpPacket1Serialized = icmpPacket1.serialize();
+
+     icmpPacketIn1 = 
+             ((OFPacketIn) getMockFloodlightProvider().getOFMessageFactory().
+                     getMessage(OFType.PACKET_IN))
+                     .setBufferId(-1)
+                     .setInPort((short) 1)
+                     .setPacketData(icmpPacket1Serialized)
+                     .setReason(OFPacketInReason.NO_MATCH)
+                     .setTotalLength((short) icmpPacket1Serialized.length);
+
+     icmpPacket2 = new Ethernet()
+     .setSourceMACAddress("00:00:00:00:00:02")
+     .setDestinationMACAddress(LBVip.LB_PROXY_MAC)
+     .setEtherType(Ethernet.TYPE_IPv4)
+     .setVlanID((short) 0)
+     .setPriorityCode((byte) 0)
+     .setPayload(
+         new IPv4()
+         .setSourceAddress("10.0.0.2")
+         .setDestinationAddress("10.0.0.100")
+         .setProtocol(IPv4.PROTOCOL_ICMP)
+         .setPayload(new ICMP()
+         .setIcmpCode((byte) 0)
+         .setIcmpType((byte) 0)));
+
+     icmpPacket2Serialized = icmpPacket2.serialize();
+
+     icmpPacketIn2 = 
+             ((OFPacketIn) getMockFloodlightProvider().getOFMessageFactory().
+                     getMessage(OFType.PACKET_IN))
+                     .setBufferId(-1)
+                     .setInPort((short) 2)
+                     .setPacketData(icmpPacket2Serialized)
+                     .setReason(OFPacketInReason.NO_MATCH)
+                     .setTotalLength((short) icmpPacket2Serialized.length);
+ 
+     byte[] dataLayerSource1 = ((Ethernet)icmpPacket1).getSourceMACAddress();
+     int networkSource1 = ((IPv4)((Ethernet)icmpPacket1).getPayload()).getSourceAddress();
+     byte[] dataLayerDest1 = HexString.fromHexString("00:00:00:00:00:03");
+     int networkDest1 = IPv4.toIPv4Address("10.0.0.3");
+     byte[] dataLayerSource2 = ((Ethernet)icmpPacket2).getSourceMACAddress();
+     int networkSource2 = ((IPv4)((Ethernet)icmpPacket2).getPayload()).getSourceAddress();
+     byte[] dataLayerDest2 = HexString.fromHexString("00:00:00:00:00:04");
+     int networkDest2 = IPv4.toIPv4Address("10.0.0.4");
+     
+     deviceManager.learnEntity(Ethernet.toLong(dataLayerSource1), 
+                               null, networkSource1,
+                               1L, 1);
+     deviceManager.learnEntity(Ethernet.toLong(dataLayerSource2), 
+                               null, networkSource2,
+                               1L, 2);
+     deviceManager.learnEntity(Ethernet.toLong(dataLayerDest1), 
+                               null, networkDest1,
+                               1L, 3);
+     deviceManager.learnEntity(Ethernet.toLong(dataLayerDest2), 
+                               null, networkDest2,
+                               1L, 4);
+
+     // in bound #1
+     Route route1 = new Route(1L, 1L);
+     List<NodePortTuple> nptList1 = new ArrayList<NodePortTuple>();
+     nptList1.add(new NodePortTuple(1L, (short)1));
+     nptList1.add(new NodePortTuple(1L, (short)3));
+     route1.setPath(nptList1);
+     expect(routingEngine.getRoute(1L, (short)1, 1L, (short)3, 0)).andReturn(route1).atLeastOnce();
+
+     // outbound #1
+     Route route2 = new Route(1L, 1L);
+     List<NodePortTuple> nptList2 = new ArrayList<NodePortTuple>();
+     nptList2.add(new NodePortTuple(1L, (short)3));
+     nptList2.add(new NodePortTuple(1L, (short)1));
+     route2.setPath(nptList2);
+     expect(routingEngine.getRoute(1L, (short)3, 1L, (short)1, 0)).andReturn(route2).atLeastOnce();
+
+     // inbound #2
+     Route route3 = new Route(1L, 1L);
+     List<NodePortTuple> nptList3 = new ArrayList<NodePortTuple>();
+     nptList3.add(new NodePortTuple(1L, (short)2));
+     nptList3.add(new NodePortTuple(1L, (short)4));
+     route3.setPath(nptList3);
+     expect(routingEngine.getRoute(1L, (short)2, 1L, (short)4, 0)).andReturn(route3).atLeastOnce();
+
+     // outbound #2
+     Route route4 = new Route(1L, 1L);
+     List<NodePortTuple> nptList4 = new ArrayList<NodePortTuple>();
+     nptList4.add(new NodePortTuple(1L, (short)4));
+     nptList4.add(new NodePortTuple(1L, (short)2));
+     route4.setPath(nptList3);
+     expect(routingEngine.getRoute(1L, (short)4, 1L, (short)2, 0)).andReturn(route4).atLeastOnce();
+
+     replay(routingEngine);
+     
+     wc1.reset(); 
+     
+     IFloodlightProviderService.bcStore.put(cntx,
+                                            IFloodlightProviderService.CONTEXT_PI_PAYLOAD, 
+                                            (Ethernet) icmpPacket1);
+     lb.receive(sw1, icmpPacketIn1, cntx);
+     
+     IFloodlightProviderService.bcStore.put(cntx,
+                                            IFloodlightProviderService.CONTEXT_PI_PAYLOAD, 
+                                            (Ethernet) icmpPacket2);
+     lb.receive(sw1, icmpPacketIn2, cntx);
+     
+     assertTrue(wc1.hasCaptured());  // wc1 should get packetout
+     
+     List<OFMessage> msglist2 = wc1.getValues();
+
+     assertTrue(msglist2.size()==2); // has inbound and outbound packetouts
+                                     // TODO: not seeing flowmods yet ...
+     
+     Map<String, OFFlowMod> map = sfp.getFlows("00:00:00:00:00:00:00:01");
+     
+     assertTrue(map.size()==4);
+    }
+
+
+}
diff --git a/src/test/java/org/openflow/protocol/BasicFactoryTest.java b/src/test/java/org/openflow/protocol/BasicFactoryTest.java
index 6825008d98b0f9c0134b77a3f13dc39b54911fd7..312fcd3f57d616c6716e7f702e56a086a44609d0 100644
--- a/src/test/java/org/openflow/protocol/BasicFactoryTest.java
+++ b/src/test/java/org/openflow/protocol/BasicFactoryTest.java
@@ -1,7 +1,7 @@
 /**
 *    Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
 *    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
@@ -17,16 +17,23 @@
 
 package org.openflow.protocol;
 
+import static org.junit.Assert.assertArrayEquals;
+
 import java.util.List;
 
+import junit.framework.TestCase;
+
 import org.jboss.netty.buffer.ChannelBuffer;
 import org.jboss.netty.buffer.ChannelBuffers;
+import org.openflow.protocol.action.MockVendorAction;
+import org.openflow.protocol.action.MockVendorActionFactory;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionVendorGeneric;
 import org.openflow.protocol.factory.BasicFactory;
 import org.openflow.protocol.factory.MessageParseException;
+import org.openflow.protocol.factory.OFVendorActionRegistry;
 import org.openflow.util.U16;
 
-import junit.framework.TestCase;
-
 public class BasicFactoryTest extends TestCase {
 
     public void testCreateAndParse() throws MessageParseException {
@@ -78,4 +85,50 @@ public class BasicFactoryTest extends TestCase {
         }
     }
 
+    public void testCustomVendorAction() throws MessageParseException {
+        BasicFactory factory = new BasicFactory();
+        OFVendorActionRegistry.getInstance().register(
+                MockVendorAction.VENDOR_ID, new MockVendorActionFactory());
+
+
+        byte[] deadBeefMessage = {
+            (byte) 0xff, (byte) 0xff,          // action vendor
+            0x00, 0x10,                        // length
+            (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte)0xef,            // deadbeaf
+            0x01, 0x02, 0x03, 0x04,
+            0x05, 0x06, 0x07, 0x08               // pad
+        };
+
+        ChannelBuffer buf = ChannelBuffers.copiedBuffer(deadBeefMessage);
+
+        List<OFAction> actions = factory.parseActions(buf,deadBeefMessage.length);
+        assertEquals(1, actions.size());
+        OFAction ofAction = actions.get(0);
+        assertTrue("Action should be MockVendorAction, but is "+ofAction.getClass(), ofAction instanceof MockVendorAction);
+        assertArrayEquals( new byte[]  { 1,2,3,4,5,6,7,8}, ((MockVendorAction)ofAction).getMockData());
+
+
+    }
+
+    public void testGenericVendorAction() throws MessageParseException {
+        byte[] nonDeadBeefMessage = {
+                (byte) 0xff, (byte) 0xff,          // action vendor
+                0x00, 0x10,                        // length
+                (byte) 0x7e, (byte) 0xe7, (byte) 0xbe, (byte)0xef,            // deadbeaf
+                0x01, 0x02, 0x03, 0x04,
+                0x05, 0x06, 0x07, 0x08               // pad
+            };
+
+        BasicFactory factory = new BasicFactory();
+        OFVendorActionRegistry.getInstance().register(
+                MockVendorAction.VENDOR_ID, new MockVendorActionFactory());
+
+        ChannelBuffer buf = ChannelBuffers.copiedBuffer(nonDeadBeefMessage);
+
+        List<OFAction> actions = factory.parseActions(buf,nonDeadBeefMessage.length);
+        assertEquals(1, actions.size());
+        OFAction ofAction = actions.get(0);
+        assertTrue("Action should be OFActionVendorGeneric, but is "+ofAction.getClass(), ofAction instanceof OFActionVendorGeneric);
+    }
+
 }
diff --git a/src/test/java/org/openflow/protocol/action/MockVendorAction.java b/src/test/java/org/openflow/protocol/action/MockVendorAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..49b69fb8181d4a6dd6959a40786d643fe05ead20
--- /dev/null
+++ b/src/test/java/org/openflow/protocol/action/MockVendorAction.java
@@ -0,0 +1,41 @@
+package org.openflow.protocol.action;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+
+public class MockVendorAction extends OFActionVendor {
+    public static final int VENDOR_ID = 0xdeadbeef;
+
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+    private byte[] mockData;
+
+    public byte[] getMockData() {
+        return mockData;
+    }
+
+    public void setMockData(byte[] mockData) {
+        this.mockData = mockData;
+    }
+
+    @Override
+    public void readFrom(ChannelBuffer data) {
+        super.readFrom(data);
+
+        int dataLength = getLength() - MINIMUM_LENGTH;
+        if(dataLength > 0) {
+            mockData = new byte[dataLength];
+            data.readBytes(mockData);
+        } else {
+            mockData = EMPTY_BYTE_ARRAY;
+        }
+
+    }
+
+    @Override
+    public void writeTo(ChannelBuffer data) {
+        super.writeTo(data);
+        data.writeBytes(mockData);
+    }
+
+
+}
diff --git a/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java b/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..bbc254ca0074cc9e418c5541f976d000074cf0b1
--- /dev/null
+++ b/src/test/java/org/openflow/protocol/action/MockVendorActionFactory.java
@@ -0,0 +1,15 @@
+package org.openflow.protocol.action;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.openflow.protocol.factory.OFVendorActionFactory;
+
+public class MockVendorActionFactory implements OFVendorActionFactory {
+
+    @Override
+    public OFActionVendor readFrom(ChannelBuffer data) {
+        MockVendorAction action = new MockVendorAction();
+        action.readFrom(data);
+        return action;
+    }
+
+}
diff --git a/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java b/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..31ad675d6a0a708c5a69a7ae3b0dcb21bc24e5c5
--- /dev/null
+++ b/src/test/java/org/openflow/protocol/action/OFVendorActionRegistryTest.java
@@ -0,0 +1,17 @@
+package org.openflow.protocol.action;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.openflow.protocol.factory.OFVendorActionRegistry;
+
+public class OFVendorActionRegistryTest {
+
+    @Test
+    public void test() {
+        MockVendorActionFactory factory = new MockVendorActionFactory();
+        OFVendorActionRegistry.getInstance().register(MockVendorAction.VENDOR_ID, factory);
+        assertEquals(factory, OFVendorActionRegistry.getInstance().get(MockVendorAction.VENDOR_ID));
+    }
+
+}