diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java b/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java
index ac60d098daf1435df4c7437e424523c17a76aaaf..7c5564fd6150e6a8d331d9bf039df819bcfb29e0 100644
--- a/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java
+++ b/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java
@@ -5,30 +5,35 @@ import net.floodlightcontroller.util.MACAddress;
 
 public interface IVirtualNetworkService extends IFloodlightService {
     /**
-     * Creates a new virtual network.
+     * Creates a new virtual network. This can also be called
+     * to modify a virtual network. To update a network you specify the GUID
+     * and the fields you want to update.
      * @param network The network name. Must be unique.
      * @param guid The ID of the network. Must be unique.
-     * @param gateway The IP address of the network gateway, 0 if none. Must be unique.
+     * @param gateway The IP address of the network gateway, null if none. Must be unique.
      */
-    public void createNetwork(String network, String guid, Integer gateway);
+    public void createNetwork(String guid, String network, Integer gateway);
     
     /**
      * Deletes a virtual network.
-     * @param network The virtual network to delete.
+     * @param guid The ID (not name) of virtual network to delete.
      */
-    public void deleteNetwork(String network);
+    public void deleteNetwork(String guid);
     
     /**
-     * Adds a host to a virtual network.
+     * Adds a host to a virtual network. If a mapping already exists the
+     * new one will override the old mapping.
      * @param mac The MAC address of the host to add.
-     * @param network The network to add the host to..
+     * @param network The network to add the host to.
+     * @param port The logical port name to attach the host to. Must be unique.
      */
-    public void addHost(MACAddress mac, String network); 
+    public void addHost(MACAddress mac, String network, String port); 
     
     /**
-     * Deletes a host from a virtual network.
+     * Deletes a host from a virtual network. Either the MAC or Port must
+     * be specified.
      * @param mac The MAC address to delete.
-     * @param network The network the host belongs to.
+     * @param port The logical port the host is attached to.
      */
-    public void deleteHost(MACAddress mac, String network);
+    public void deleteHost(MACAddress mac, String port);
 }
diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/HostResource.java b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/HostResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..1dacddce1e8fe3357e72f9fba78c9f91bc81434f
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/HostResource.java
@@ -0,0 +1,89 @@
+package net.floodlightcontroller.virtualnetwork.forwarding;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.floodlightcontroller.virtualnetwork.IVirtualNetworkService;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.MappingJsonFactory;
+import org.restlet.resource.Delete;
+import org.restlet.resource.Put;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HostResource extends org.restlet.resource.ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(HostResource.class);
+    
+    public class HostDefinition {
+        String port = null; // Logical port name
+        String guid = null; // Network ID
+        String mac = null; // MAC Address
+    }
+    
+    protected void jsonToHostDefinition(String json, HostDefinition host) throws IOException {
+        MappingJsonFactory f = new MappingJsonFactory();
+        JsonParser jp;
+        
+        try {
+            jp = f.createJsonParser(json);
+        } catch (JsonParseException e) {
+            throw new IOException(e);
+        }
+        
+        jp.nextToken();
+        if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+            throw new IOException("Expected START_OBJECT");
+        }
+        
+        while (jp.nextToken() != JsonToken.END_OBJECT) {
+            if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+                throw new IOException("Expected FIELD_NAME");
+            }
+            
+            String n = jp.getCurrentName();
+            jp.nextToken();
+            if (jp.getText().equals("")) 
+                continue;
+            else if (n.equals("attachment")) {
+                while (jp.nextToken() != JsonToken.END_OBJECT) {
+                    String field = jp.getCurrentName();
+                    if (field.equals("id")) {
+                        host.guid = jp.getText();
+                    } else if (field.equals("mac")) {
+                        host.mac = jp.getText();
+                    }
+                }
+            }
+        }
+        
+        jp.close();
+    }
+    
+    @Put
+    public void addHost(String postData) {
+        IVirtualNetworkService vns =
+                (IVirtualNetworkService)getContext().getAttributes().
+                    get(IVirtualNetworkService.class.getCanonicalName());
+        HostDefinition host = new HostDefinition();
+        host.port = (String) getRequestAttributes().get("port");
+        try {
+            jsonToHostDefinition(postData, host);
+        } catch (IOException e) {
+            log.error("Could not parse JSON {}", e.getMessage());
+        }
+        vns.addHost(MACAddress.valueOf(host.mac), host.guid, host.port);
+    }
+    
+    
+    @Delete
+    public void deleteHost() {
+        String port = (String) getRequestAttributes().get("port");
+        IVirtualNetworkService vns =
+                (IVirtualNetworkService)getContext().getAttributes().
+                    get(IVirtualNetworkService.class.getCanonicalName());
+        vns.deleteHost(null, port);
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/NetworkResource.java b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/NetworkResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..7faf454d2a4c051a220507f99ca7265c43e539b2
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/NetworkResource.java
@@ -0,0 +1,105 @@
+package net.floodlightcontroller.virtualnetwork.forwarding;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.virtualnetwork.IVirtualNetworkService;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.MappingJsonFactory;
+import org.restlet.resource.Delete;
+import org.restlet.resource.Post;
+import org.restlet.resource.Put;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetworkResource extends ServerResource {
+    protected static Logger log = LoggerFactory.getLogger(NetworkResource.class);
+    
+    public class NetworkDefinition {
+        public String name = null;
+        public String guid = null;
+        public String gateway = null;
+    }
+    
+    protected void jsonToNetworkDefinition(String json, NetworkDefinition network) throws IOException {
+        MappingJsonFactory f = new MappingJsonFactory();
+        JsonParser jp;
+        
+        try {
+            jp = f.createJsonParser(json);
+        } catch (JsonParseException e) {
+            throw new IOException(e);
+        }
+        
+        jp.nextToken();
+        if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+            throw new IOException("Expected START_OBJECT");
+        }
+        
+        while (jp.nextToken() != JsonToken.END_OBJECT) {
+            if (jp.getCurrentToken() != JsonToken.FIELD_NAME) {
+                throw new IOException("Expected FIELD_NAME");
+            }
+            
+            String n = jp.getCurrentName();
+            jp.nextToken();
+            if (jp.getText().equals("")) 
+                continue;
+            else if (n.equals("network")) {
+                while (jp.nextToken() != JsonToken.END_OBJECT) {
+                    String field = jp.getCurrentName();
+                    if (field.equals("name")) {
+                        network.name = jp.getText();
+                    } else if (field.equals("gateway")) {
+                        network.gateway = jp.getText();
+                    }
+                }
+            }
+        }
+        
+        jp.close();
+    }
+    
+    
+    @Put
+    @Post
+    public void createNetwork(String postData) {        
+        String guid = (String) getRequestAttributes().get("network");
+        NetworkDefinition network = new NetworkDefinition();
+        network.guid = guid;
+        
+        try {
+            jsonToNetworkDefinition(postData, network);
+        } catch (IOException e) {
+            log.error("Could not parse JSON {}", e.getMessage());
+        }
+        
+        IVirtualNetworkService vns =
+                (IVirtualNetworkService)getContext().getAttributes().
+                    get(IVirtualNetworkService.class.getCanonicalName());
+        
+        Integer gw = null;
+        if (network.gateway != null) {
+            try {
+                gw = IPv4.toIPv4Address(network.gateway);
+            } catch (IllegalArgumentException e) {
+                log.warn("Could not parse gateway {} as IP for network {}",
+                         network.name, network.gateway);
+            }
+        }
+        vns.createNetwork(network.guid, network.name, gw);
+    }
+    
+    @Delete
+    public void deleteNetwork() {
+        IVirtualNetworkService vns =
+                (IVirtualNetworkService)getContext().getAttributes().
+                    get(IVirtualNetworkService.class.getCanonicalName());
+        String guid = (String) getRequestAttributes().get("network");
+        vns.deleteNetwork(guid);
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilter.java b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilter.java
index 403c56bac8436e59c12980f4a1905b0ac2df5652..94678900a23a6d8a64598b71094f928326f81017 100644
--- a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilter.java
+++ b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilter.java
@@ -3,9 +3,13 @@ package net.floodlightcontroller.virtualnetwork.forwarding;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.openflow.protocol.OFFlowMod;
@@ -56,83 +60,140 @@ public class VirtualNetworkFilter
     // Our internal state
     protected Map<String, String> nameToGuid; // Logical name -> Network ID
     protected Map<String, Integer> guidToGateway; // Network ID -> Gateway IP
-    protected Map<Integer, String> gatewayToGuid; // Gateway IP -> Network ID
+    protected Map<Integer, Set<String>> gatewayToGuid; // Gateway IP -> Network ID
     protected Map<MACAddress, String> macToGuid; // Host MAC -> Network ID
-    
+    protected Map<String, MACAddress> portToMac; // Host MAC -> logical port name
     
     protected void addGateway(String guid, Integer ip) {
         if (ip.intValue() != 0) {
             guidToGateway.put(guid, ip);
-            gatewayToGuid.put(ip, guid);
+            if (gatewayToGuid.containsKey(ip)) {
+                Set<String> gSet = gatewayToGuid.get(ip);
+                gSet.add(guid);
+            } else {
+                Set<String> gSet = Collections.synchronizedSet(new HashSet<String>());
+                gSet.add(guid);
+                gatewayToGuid.put(ip, gSet);
+            }
         }
     }
     
     protected void deleteGateway(String guid) {
         Integer gwIp = guidToGateway.remove(guid);
         if (gwIp == null) return;
-        gatewayToGuid.remove(gwIp);
+        Set<String> gSet = gatewayToGuid.get(gwIp);
+        gSet.remove(guid);
     }
     
     // IVirtualNetworkService
     
     @Override
-    public void createNetwork(String network, String guid, Integer gateway) {
-        if (log.isDebugEnabled()) 
+    public void createNetwork(String guid, String network, Integer gateway) {
+        if (log.isDebugEnabled()) {
+            String gw = null;
+            try {
+                gw = IPv4.fromIPv4Address(gateway);
+            } catch (Exception e) {
+                // fail silently
+            }
             log.debug("Creating network {} with ID {} and gateway {}", 
-                      new Object[] {network, guid, IPv4.fromIPv4Address(gateway)});
-        if (!nameToGuid.containsKey(network)) {
-            nameToGuid.put(network, guid);
-            addGateway(guid, gateway);
-        } else {
-            if (log.isDebugEnabled())
-                log.debug("Network {} already exists, ignoring", network);
+                      new Object[] {network, guid, gw});
+        }
+        
+        if (!nameToGuid.isEmpty()) {
+            // We have to iterate all the networks to handle name/gateway changes
+            for (Entry<String, String> entry : nameToGuid.entrySet()) {
+                if (entry.getValue().equals(guid)) {
+                    nameToGuid.remove(entry.getKey());
+                    break;
+                }
+            }
         }
+        nameToGuid.put(network, guid);
+        // If they don't specify a new gateway the old one will be preserved
+        if ((gateway != null) && (gateway != 0))
+            addGateway(guid, gateway);
     }
 
     @Override
-    public void deleteNetwork(String name) {
-        if (log.isDebugEnabled()) log.debug("Deleting network name {}", name);
-        if (nameToGuid.containsKey(name)) {
-            String guid = nameToGuid.remove(name);
-            deleteGateway(guid);
-            Collection<MACAddress> deleteList = new ArrayList<MACAddress>();
-            for (MACAddress host : macToGuid.keySet()) {
-                if (macToGuid.get(host).equals(guid)) {
-                    deleteList.add(host);
-                }
+    public void deleteNetwork(String guid) {
+        String name = null;
+        if (nameToGuid.isEmpty()) {
+            log.warn("Could not delete network with ID {}, network doesn't exist",
+                     guid);
+            return;
+        }
+        for (Entry<String, String> entry : nameToGuid.entrySet()) {
+            if (entry.getValue().equals(guid)) {
+                name = entry.getKey();
+                break;
+            }
+            log.warn("Could not delete network with ID {}, network doesn't exist",
+                     guid);
+        }
+        
+        if (log.isDebugEnabled()) 
+            log.debug("Deleting network with name {} ID {}", name, guid);
+        
+        nameToGuid.remove(name);
+        deleteGateway(guid);
+        Collection<MACAddress> deleteList = new ArrayList<MACAddress>();
+        for (MACAddress host : macToGuid.keySet()) {
+            if (macToGuid.get(host).equals(guid)) {
+                deleteList.add(host);
             }
-            for (MACAddress mac : deleteList) {
-                if (log.isDebugEnabled()) {
-                    log.debug("Removing host {} from network {}", 
-                              HexString.toHexString(mac.toBytes()), guid);
+        }
+        for (MACAddress mac : deleteList) {
+            if (log.isDebugEnabled()) {
+                log.debug("Removing host {} from network {}", 
+                          HexString.toHexString(mac.toBytes()), guid);
+            }
+            macToGuid.remove(mac);
+            for (Entry<String, MACAddress> entry : portToMac.entrySet()) {
+                if (entry.getValue().equals(mac)) {
+                    portToMac.remove(entry.getKey());
+                    break;
                 }
-                macToGuid.remove(mac);
             }
         }
     }
 
     @Override
-    public void addHost(MACAddress mac, String network) {
+    public void addHost(MACAddress mac, String network, String port) {
         String guid = nameToGuid.get(network);
         if (guid != null) {
             if (log.isDebugEnabled()) {
-                log.debug("Adding {} to network {}", mac, network);
+                log.debug("Adding {} to network {} on port {}",
+                          new Object[] {mac, network, port});
             }
-            macToGuid.put(mac, guid); // TODO what if a mapping exists?
+            // We ignore old mappings
+            macToGuid.put(mac, guid);
+            portToMac.put(port, mac);
         } else {
-            // TODO - throw an exception
+            log.warn("Could not add MAC {} to network {} on port {}, the network does not exist",
+                     new Object[] {mac, network, port});
         }
     }
 
     @Override
-    public void deleteHost(MACAddress mac, String network) {
-        if (macToGuid.remove(mac) != null) {
-            if (log.isDebugEnabled()) {
-                log.debug("Removing {} from network {}", mac, network);
+    public void deleteHost(MACAddress mac, String port) {
+        if (log.isDebugEnabled()) {
+            log.debug("Removing host {} from port {}", mac, port);
+        }
+        if (mac == null && port == null) return;
+        if (port != null) {
+            MACAddress host = portToMac.remove(port);
+            macToGuid.remove(host);
+        } else if (mac != null) {
+            if (!portToMac.isEmpty()) {
+                for (Entry<String, MACAddress> entry : portToMac.entrySet()) {
+                    if (entry.getValue().equals(mac)) {
+                        portToMac.remove(entry.getKey());
+                        macToGuid.remove(entry.getValue());
+                        return;
+                    }
+                }
             }
-        } else {
-            log.warn("Tried to remove {} from network {}, but no mapping was found",
-                     mac, network);
         }
     }
 
@@ -156,8 +217,7 @@ public class VirtualNetworkFilter
     }
 
     @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleDependencies() {
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
         Collection<Class<? extends IFloodlightService>> l = 
                 new ArrayList<Class<? extends IFloodlightService>>();
         l.add(IFloodlightProviderService.class);
@@ -173,8 +233,9 @@ public class VirtualNetworkFilter
         
         nameToGuid = new ConcurrentHashMap<String, String>();
         guidToGateway = new ConcurrentHashMap<String, Integer>();
-        gatewayToGuid = new ConcurrentHashMap<Integer, String>();
+        gatewayToGuid = new ConcurrentHashMap<Integer, Set<String>>();
         macToGuid = new ConcurrentHashMap<MACAddress, String>();
+        portToMac = new ConcurrentHashMap<String, MACAddress>();
     }
 
     @Override
@@ -192,8 +253,8 @@ public class VirtualNetworkFilter
 
     @Override
     public boolean isCallbackOrderingPrereq(OFType type, String name) {
-        // We don't care who goes before us
-        return false;
+        // Link discovery should go before us so we don't block LLDPs
+        return (type.equals(OFType.PACKET_IN) && name.equals("linkdiscovery"));
     }
 
     @Override
@@ -228,30 +289,39 @@ public class VirtualNetworkFilter
     protected Command processPacketIn(IOFSwitch sw, OFPacketIn msg, FloodlightContext cntx) {
         Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, 
                                               IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-        if (eth.isBroadcast() || eth.isMulticast()) {
-            // TODO - handle this shit somehow
-            return Command.CONTINUE;
-        }
-
         Command ret = Command.STOP;
         String srcNetwork = macToGuid.get(eth.getSourceMAC());
-        if (oneSameNetwork(eth.getSourceMAC(), eth.getDestinationMAC())) {
-            // if they are on the same network continue
-            ret = Command.CONTINUE;
-        } else if ((eth.getPayload() instanceof IPv4) 
-                && isDefaultGatewayIp(srcNetwork, (IPv4)eth.getPayload())) {
-            // or if the host is talking to the gateway continue
-            ret = Command.CONTINUE;
-        }
-        
-        if (ret == Command.CONTINUE) {
-            if (log.isTraceEnabled()) {
-                log.trace("Allowing flow between {} and {} on network {}", 
-                          new Object[] {eth.getSourceMAC(), eth.getDestinationMAC(), srcNetwork});
+        if (srcNetwork == null) {
+            log.debug("Blocking traffic from host {} because it is not attached to any network.",
+                      HexString.toHexString(eth.getSourceMACAddress()));
+            ret = Command.STOP;
+        } else {
+            if (eth.isBroadcast() || eth.isMulticast()) {
+                return Command.CONTINUE;
+            }
+            
+            if (oneSameNetwork(eth.getSourceMAC(), eth.getDestinationMAC())) {
+                // if they are on the same network continue
+                ret = Command.CONTINUE;
+            } else if ((eth.getPayload() instanceof IPv4) 
+                    && isDefaultGatewayIp(srcNetwork, (IPv4)eth.getPayload())) {
+                // or if the host is talking to the gateway continue
+                ret = Command.CONTINUE;
+            }
+            
+            if (ret == Command.CONTINUE) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Allowing flow between {} and {} on network {}", 
+                              new Object[] {eth.getSourceMAC(), eth.getDestinationMAC(), srcNetwork});
+                }
+            } else if (ret == Command.STOP) {
+                // they are on different virtual networks so we drop the flow
+                if (log.isTraceEnabled()) {
+                    log.trace("Dropping flow between {} and {} because they are on different networks", 
+                              new Object[] {eth.getSourceMAC(), eth.getDestinationMAC()});
+                }
+                doDropFlow(sw, msg, cntx);
             }
-        } else if (ret == Command.STOP) {
-            // they are on different virtual networks so we drop the flow
-            doDropFlow(sw, msg, cntx);
         }
         
         return ret;
diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkWebRoutable.java b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkWebRoutable.java
index aeef390cd08674a96a4f1f2fd4f5e6c70752e30d..157d2816c2c224faaceb805f92bec4a8f5a2815a 100644
--- a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkWebRoutable.java
+++ b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkWebRoutable.java
@@ -11,8 +11,8 @@ public class VirtualNetworkWebRoutable implements RestletRoutable {
     @Override
     public Restlet getRestlet(Context context) {
         Router router = new Router(context);
-        //router.attach("/tenants/{tenant}/networks", NetworkResource.java);
-        //router.attach("/");
+        router.attach("/tenants/{tenant}/networks/{network}", NetworkResource.class);
+        router.attach("/tenants/{tenant}/networks/{network}/ports/{port}/attachment", HostResource.class);
         return router;
     }
 
@@ -20,4 +20,4 @@ public class VirtualNetworkWebRoutable implements RestletRoutable {
     public String basePath() {
         return "/quantum/1.0";
     }
-}
+}
\ No newline at end of file
diff --git a/src/test/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilterTest.java b/src/test/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f59183b7a21d6e5f2627359d409b4571d6945760
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilterTest.java
@@ -0,0 +1,293 @@
+package net.floodlightcontroller.virtualnetwork.forwarding;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.OFPacketIn.OFPacketInReason;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFMessageListener.Command;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.packet.Data;
+import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.packet.IPacket;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.packet.UDP;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.restserver.RestApiServer;
+import net.floodlightcontroller.test.FloodlightTestCase;
+import net.floodlightcontroller.util.MACAddress;
+
+public class VirtualNetworkFilterTest extends FloodlightTestCase {
+    protected VirtualNetworkFilter vns;
+    
+    protected static String guid1 = "guid1";
+    protected static String net1 = "net1";
+    protected static String gw1 = "1.1.1.1";
+    protected static String guid2 = "guid2";
+    protected static String net2 = "net2";
+    protected static String guid3 = "guid3";
+    protected static String net3 = "net3";
+    protected static String gw2 = "2.2.2.2";
+    
+    protected static MACAddress mac1 = 
+            new MACAddress(Ethernet.toMACAddress("00:11:22:33:44:55"));
+    protected static MACAddress mac2 = 
+            new MACAddress(Ethernet.toMACAddress("00:11:22:33:44:66"));
+    protected static MACAddress mac3 = 
+            new MACAddress(Ethernet.toMACAddress("00:11:22:33:44:77"));
+    protected static MACAddress mac4 = 
+            new MACAddress(Ethernet.toMACAddress("00:11:22:33:44:88"));
+    protected static String hostPort1 = "port1";
+    protected static String hostPort2 = "port2";
+    protected static String hostPort3 = "port3";
+    protected static String hostPort4 = "port4";
+    
+    // For testing forwarding behavior
+    protected IOFSwitch sw1;
+    protected FloodlightContext cntx;
+    protected OFPacketIn mac1ToMac2PacketIn;
+    protected IPacket mac1ToMac2PacketIntestPacket;
+    protected byte[] mac1ToMac2PacketIntestPacketSerialized;
+    protected OFPacketIn mac1ToMac4PacketIn;
+    protected IPacket mac1ToMac4PacketIntestPacket;
+    protected byte[] mac1ToMac4PacketIntestPacketSerialized;
+    protected OFPacketIn mac1ToGwPacketIn;
+    protected IPacket mac1ToGwPacketIntestPacket;
+    protected byte[] mac1ToGwPacketIntestPacketSerialized;
+    
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // Module loading stuff
+        FloodlightModuleContext fmc = new FloodlightModuleContext();
+        RestApiServer restApi = new RestApiServer();
+        vns = new VirtualNetworkFilter();
+        fmc.addService(IRestApiService.class, restApi);
+        fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider());
+        restApi.init(fmc);
+        getMockFloodlightProvider().init(fmc);
+        vns.init(fmc);
+        restApi.startUp(fmc);
+        getMockFloodlightProvider().startUp(fmc);
+        vns.startUp(fmc);
+        
+        // Mock switches
+        //fastWilcards mocked as this constant
+        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.getAttribute(IOFSwitch.PROP_FASTWILDCARDS)).andReturn((Integer)fastWildcards).anyTimes();
+        expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
+        replay(sw1);
+        
+        // Mock packets
+        // Mock from MAC1 -> MAC2
+        mac1ToMac2PacketIntestPacket = new Ethernet()
+            .setDestinationMACAddress(mac2.toBytes())
+            .setSourceMACAddress(mac1.toBytes())
+            .setEtherType(Ethernet.TYPE_IPv4)
+            .setPayload(
+                new IPv4()
+                .setTtl((byte) 128)
+                .setSourceAddress("192.168.1.1")
+                .setDestinationAddress("192.168.1.2")
+                .setPayload(new UDP()
+                            .setSourcePort((short) 5000)
+                            .setDestinationPort((short) 5001)
+                            .setPayload(new Data(new byte[] {0x01}))));
+        mac1ToMac2PacketIntestPacketSerialized = mac1ToMac2PacketIntestPacket.serialize();
+        mac1ToMac2PacketIn = 
+                ((OFPacketIn) mockFloodlightProvider.getOFMessageFactory().
+                        getMessage(OFType.PACKET_IN))
+                        .setBufferId(-1)
+                        .setInPort((short) 1)
+                        .setPacketData(mac1ToMac2PacketIntestPacketSerialized)
+                        .setReason(OFPacketInReason.NO_MATCH)
+                        .setTotalLength((short) mac1ToMac2PacketIntestPacketSerialized.length);
+        
+        // Mock from MAC1 -> MAC4
+        mac1ToMac4PacketIntestPacket = new Ethernet()
+        .setDestinationMACAddress(mac4.toBytes())
+        .setSourceMACAddress(mac1.toBytes())
+        .setEtherType(Ethernet.TYPE_IPv4)
+        .setPayload(
+            new IPv4()
+            .setTtl((byte) 128)
+            .setSourceAddress("192.168.1.1")
+            .setDestinationAddress("192.168.1.2")
+            .setPayload(new UDP()
+                        .setSourcePort((short) 5000)
+                        .setDestinationPort((short) 5001)
+                        .setPayload(new Data(new byte[] {0x01}))));
+        mac1ToMac4PacketIntestPacketSerialized = mac1ToMac4PacketIntestPacket.serialize();
+        mac1ToMac4PacketIn = 
+            ((OFPacketIn) mockFloodlightProvider.getOFMessageFactory().
+                    getMessage(OFType.PACKET_IN))
+                    .setBufferId(-1)
+                    .setInPort((short) 1)
+                    .setPacketData(mac1ToMac4PacketIntestPacketSerialized)
+                    .setReason(OFPacketInReason.NO_MATCH)
+                    .setTotalLength((short) mac1ToMac4PacketIntestPacketSerialized.length);
+        
+        // Mock from MAC1 to gateway1
+        mac1ToGwPacketIntestPacket = new Ethernet()
+        .setDestinationMACAddress("00:11:44:33:44:55") // mac shouldn't matter, can't be other host
+        .setSourceMACAddress(mac1.toBytes())
+        .setEtherType(Ethernet.TYPE_IPv4)
+        .setPayload(
+            new IPv4()
+            .setTtl((byte) 128)
+            .setSourceAddress("192.168.1.1")
+            .setDestinationAddress(gw1)
+            .setPayload(new UDP()
+                        .setSourcePort((short) 5000)
+                        .setDestinationPort((short) 5001)
+                        .setPayload(new Data(new byte[] {0x01}))));
+        mac1ToGwPacketIntestPacketSerialized = mac1ToGwPacketIntestPacket.serialize();
+        mac1ToGwPacketIn = 
+            ((OFPacketIn) mockFloodlightProvider.getOFMessageFactory().
+                    getMessage(OFType.PACKET_IN))
+                    .setBufferId(-1)
+                    .setInPort((short) 1)
+                    .setPacketData(mac1ToGwPacketIntestPacketSerialized)
+                    .setReason(OFPacketInReason.NO_MATCH)
+                    .setTotalLength((short) mac1ToGwPacketIntestPacketSerialized.length);
+    }
+    
+    @Test
+    public void testCreateNetwork() {
+        // Test creating a network with all parameters
+        vns.createNetwork(guid1, net1, IPv4.toIPv4Address(gw1));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid1));
+        assertTrue(vns.nameToGuid.get(net1).equals(guid1));
+        assertTrue(vns.guidToGateway.get(guid1).equals(IPv4.toIPv4Address(gw1)));
+        
+        // Test creating network without a gateway
+        vns.createNetwork(guid2, net2, null);
+        assertTrue(vns.nameToGuid.get(net2).equals(guid2));
+        assertTrue(vns.guidToGateway.get(guid2) == null);
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).size() == 1);
+        
+        // Test creating a network that shares the gateway with net1
+        vns.createNetwork(guid3, net3, IPv4.toIPv4Address(gw1));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid1));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid3));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).size() == 2);
+        assertTrue(vns.nameToGuid.get(net3).equals(guid3));
+        assertTrue(vns.guidToGateway.get(guid3).equals(IPv4.toIPv4Address(gw1)));
+    }
+    
+    @Test
+    public void testModifyNetwork() {
+        // Create some networks
+        testCreateNetwork();
+        // Modify net2 to add a gateway
+        vns.createNetwork(guid2, net2, IPv4.toIPv4Address(gw1));
+        assertTrue(vns.nameToGuid.get(net2).equals(guid2));
+        assertTrue(vns.guidToGateway.get(guid2).equals(IPv4.toIPv4Address(gw1)));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid1));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid2));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid3));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).size() == 3);
+        // Modify net2 to change it's name
+        vns.createNetwork(guid2, "newnet2", null);
+        // Make sure the gateway is still there
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid2));
+        // make sure the new name mapping was learned
+        assertTrue(vns.nameToGuid.get("newnet2").equals(guid2));
+        // and the old one was deleted
+        assertFalse(vns.nameToGuid.containsKey(net2));
+    }
+    
+    @Test
+    public void testDeleteNetwork() {
+        testModifyNetwork();
+        // Delete newnet2
+        vns.deleteNetwork(guid2);
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid1));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).contains(guid3));
+        assertTrue(vns.gatewayToGuid.get(IPv4.toIPv4Address(gw1)).size() == 2);
+        assertFalse(vns.nameToGuid.containsKey(net2));
+        assertFalse(vns.guidToGateway.containsKey(net2));
+    }
+    
+    @Test
+    public void testAddHost() {
+        testModifyNetwork();
+        vns.addHost(mac1, net1, hostPort1);
+        assertTrue(vns.macToGuid.get(mac1).equals(guid1));
+        assertTrue(vns.portToMac.get(hostPort1).equals(mac1));
+        vns.addHost(mac2, net1, hostPort2);
+        assertTrue(vns.macToGuid.get(mac2).equals(guid1));
+        assertTrue(vns.portToMac.get(hostPort2).equals(mac2));
+        vns.addHost(mac3, net3, hostPort3);
+        vns.addHost(mac4, net3, hostPort4);
+    }
+    
+    @Test
+    public void testDeleteHost() {
+        testAddHost();
+        vns.deleteHost(mac1, null);
+        assertFalse(vns.macToGuid.containsKey(mac1));
+        assertFalse(vns.portToMac.containsKey(hostPort1));
+        vns.deleteHost(null, hostPort2);
+        assertFalse(vns.macToGuid.containsKey(mac2));
+        assertFalse(vns.portToMac.containsKey(hostPort2));
+        vns.deleteHost(mac3, hostPort3);
+        assertFalse(vns.macToGuid.containsKey(mac3));
+        assertFalse(vns.portToMac.containsKey(hostPort3));
+    }
+    
+    @Test
+    public void testForwarding() {
+        testAddHost();
+        // make sure mac1 can communicate with mac2
+        IOFMessageListener listener = mockFloodlightProvider.getListeners().
+                get(OFType.PACKET_IN).get(0);
+        cntx = new FloodlightContext();
+        IFloodlightProviderService.bcStore.put(cntx, 
+                           IFloodlightProviderService.CONTEXT_PI_PAYLOAD, 
+                               (Ethernet)mac1ToMac2PacketIntestPacket);
+        Command ret = listener.receive(sw1, mac1ToMac2PacketIn, cntx);
+        assertTrue(ret == Command.CONTINUE);
+        //reset(sw1);
+        // make sure mac1 can't communicate with mac4
+        cntx = new FloodlightContext();
+        IFloodlightProviderService.bcStore.put(cntx, 
+                           IFloodlightProviderService.CONTEXT_PI_PAYLOAD, 
+                               (Ethernet)mac1ToMac4PacketIntestPacket);
+        ret = listener.receive(sw1, mac1ToMac4PacketIn, cntx);
+        assertTrue(ret == Command.STOP);
+    }
+
+    @Test
+    public void testDefaultGateway() {
+        testAddHost();
+        IOFMessageListener listener = mockFloodlightProvider.getListeners().
+                get(OFType.PACKET_IN).get(0);
+        cntx = new FloodlightContext();
+        IFloodlightProviderService.bcStore.put(cntx, 
+                           IFloodlightProviderService.CONTEXT_PI_PAYLOAD, 
+                               (Ethernet)mac1ToGwPacketIntestPacket);
+        Command ret = listener.receive(sw1, mac1ToGwPacketIn, cntx);
+        assertTrue(ret == Command.CONTINUE);
+    }
+}