Import Floodlight v0.90
diff --git a/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/HostResource.java b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/HostResource.java
new file mode 100644
index 0000000..6021e3d
--- /dev/null
+++ b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/HostResource.java
@@ -0,0 +1,95 @@
+package net.floodlightcontroller.virtualnetwork;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.MappingJsonFactory;
+import org.restlet.data.Status;
+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
+        String attachment = null; // Attachment name
+    }
+    
+    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.attachment = jp.getText();
+                    } else if (field.equals("mac")) {
+                        host.mac = jp.getText();
+                    }
+                }
+            }
+        }
+        
+        jp.close();
+    }
+    
+    @Put
+    public String addHost(String postData) {
+        IVirtualNetworkService vns =
+                (IVirtualNetworkService)getContext().getAttributes().
+                    get(IVirtualNetworkService.class.getCanonicalName());
+        HostDefinition host = new HostDefinition();
+        host.port = (String) getRequestAttributes().get("port");
+        host.guid = (String) getRequestAttributes().get("network");
+        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);
+        setStatus(Status.SUCCESS_OK);
+        return "{\"status\":\"ok\"}";
+    }
+    
+    
+    @Delete
+    public String deleteHost() {
+        String port = (String) getRequestAttributes().get("port");
+        IVirtualNetworkService vns =
+                (IVirtualNetworkService)getContext().getAttributes().
+                    get(IVirtualNetworkService.class.getCanonicalName());
+        vns.deleteHost(null, port);
+        setStatus(Status.SUCCESS_OK);
+        return "{\"status\":\"ok\"}";
+    }
+}
diff --git a/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java
new file mode 100644
index 0000000..4304a33
--- /dev/null
+++ b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java
@@ -0,0 +1,46 @@
+package net.floodlightcontroller.virtualnetwork;
+
+import java.util.Collection;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.util.MACAddress;
+
+public interface IVirtualNetworkService extends IFloodlightService {
+    /**
+     * 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, null if none.
+     */
+    public void createNetwork(String guid, String network, Integer gateway);
+    
+    /**
+     * Deletes a virtual network.
+     * @param guid The ID (not name) of virtual network to delete.
+     */
+    public void deleteNetwork(String guid);
+    
+    /**
+     * 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 port The logical port name to attach the host to. Must be unique.
+     */
+    public void addHost(MACAddress mac, String network, String port); 
+    
+    /**
+     * Deletes a host from a virtual network. Either the MAC or Port must
+     * be specified.
+     * @param mac The MAC address to delete.
+     * @param port The logical port the host is attached to.
+     */
+    public void deleteHost(MACAddress mac, String port);
+    
+    /**
+     * Return list of all virtual networks.
+     * @return Collection <VirtualNetwork>
+     */
+    public Collection <VirtualNetwork> listNetworks();
+}
diff --git a/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/NetworkResource.java b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/NetworkResource.java
new file mode 100644
index 0000000..2efe52a
--- /dev/null
+++ b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/NetworkResource.java
@@ -0,0 +1,133 @@
+package net.floodlightcontroller.virtualnetwork;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import net.floodlightcontroller.packet.IPv4;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonToken;
+import org.codehaus.jackson.map.MappingJsonFactory;
+import org.restlet.data.Status;
+import org.restlet.resource.Delete;
+import org.restlet.resource.Get;
+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")) {
+                    	String gw = jp.getText();
+                    	if ((gw != null) && (!gw.equals("null")))
+                    		network.gateway = gw;
+                    } else if (field.equals("id")) {
+                    	network.guid = jp.getText();
+                    } else {
+                        log.warn("Unrecognized field {} in " +
+                        		"parsing network definition", 
+                        		jp.getText());
+                    }
+                }
+            }
+        }
+        
+        jp.close();
+    }
+    
+    @Get("json")
+    public Collection <VirtualNetwork> retrieve() {
+        IVirtualNetworkService vns =
+                (IVirtualNetworkService)getContext().getAttributes().
+                    get(IVirtualNetworkService.class.getCanonicalName());
+        
+        return vns.listNetworks();               
+    }
+    
+    @Put
+    @Post
+    public String createNetwork(String postData) {        
+        NetworkDefinition network = new NetworkDefinition();
+        try {
+            jsonToNetworkDefinition(postData, network);
+        } catch (IOException e) {
+            log.error("Could not parse JSON {}", e.getMessage());
+        }
+        
+        // We try to get the ID from the URI only if it's not
+        // in the POST data 
+        if (network.guid == null) {
+	        String guid = (String) getRequestAttributes().get("network");
+	        if ((guid != null) && (!guid.equals("null")))
+	        	network.guid = guid;
+        }
+        
+        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 {}, setting as null",
+                         network.gateway, network.name);
+                network.gateway = null;
+            }
+        }
+        vns.createNetwork(network.guid, network.name, gw);
+        setStatus(Status.SUCCESS_OK);
+        return "{\"status\":\"ok\"}";
+    }
+    
+    @Delete
+    public String deleteNetwork() {
+        IVirtualNetworkService vns =
+                (IVirtualNetworkService)getContext().getAttributes().
+                    get(IVirtualNetworkService.class.getCanonicalName());
+        String guid = (String) getRequestAttributes().get("network");
+        vns.deleteNetwork(guid);
+        setStatus(Status.SUCCESS_OK);
+        return "{\"status\":\"ok\"}";
+    }
+}
diff --git a/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/NoOp.java b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/NoOp.java
new file mode 100644
index 0000000..a184a95
--- /dev/null
+++ b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/NoOp.java
@@ -0,0 +1,21 @@
+package net.floodlightcontroller.virtualnetwork;
+
+import org.restlet.data.Status;
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+import org.restlet.resource.Put;
+import org.restlet.resource.ServerResource;
+
+public class NoOp extends ServerResource {
+	/**
+	 * Does nothing and returns 200 OK with a status message
+	 * @return status: ok
+	 */
+	@Get
+	@Put
+	@Post
+	public String noOp(String postdata) {
+		setStatus(Status.SUCCESS_OK);
+        return "{\"status\":\"ok\"}";
+	}
+}
diff --git a/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetwork.java b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetwork.java
new file mode 100644
index 0000000..f5dfb21
--- /dev/null
+++ b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetwork.java
@@ -0,0 +1,88 @@
+package net.floodlightcontroller.virtualnetwork;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * Data structure for storing and outputing information of a virtual network created
+ * by VirtualNetworkFilter
+ * 
+ * @author KC Wang
+ */
+
+@JsonSerialize(using=VirtualNetworkSerializer.class)
+public class VirtualNetwork{
+    protected String name; // network name
+    protected String guid; // network id
+    protected String gateway; // network gateway
+    protected Collection<MACAddress> hosts; // array of hosts explicitly added to this network
+
+    /**
+     * Constructor requires network name and id
+     * @param name: network name
+     * @param guid: network id 
+     */
+    public VirtualNetwork(String name, String guid) {
+        this.name = name;
+        this.guid = guid;
+        this.gateway = null;
+        this.hosts = new ArrayList<MACAddress>();
+        return;        
+    }
+
+    /**
+     * Sets network name
+     * @param gateway: IP address as String
+     */
+    public void setName(String name){
+        this.name = name;
+        return;                
+    }
+    
+    /**
+     * Sets network gateway IP address
+     * @param gateway: IP address as String
+     */
+    public void setGateway(String gateway){
+        this.gateway = gateway;
+        return;                
+    }
+    
+    /**
+     * Adds a host to this network record
+     * @param host: MAC address as MACAddress
+     */
+    public void addHost(MACAddress host){
+        this.hosts.add(host);
+        return;        
+    }
+    
+    /**
+     * Removes a host from this network record
+     * @param host: MAC address as MACAddress
+     * @return boolean: true: removed, false: host not found
+     */
+    public boolean removeHost(MACAddress host){
+        Iterator<MACAddress> iter = this.hosts.iterator();
+        while(iter.hasNext()){
+            MACAddress element = iter.next();
+            if(element.equals(host) ){
+                //assuming MAC address for host is unique
+                iter.remove();
+                return true;
+            }                
+        }
+        return false;
+    }
+    
+    /**
+     * Removes all hosts from this network record
+     */
+    public void clearHosts(){
+        this.hosts.clear();
+    }
+}
\ No newline at end of file
diff --git a/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java
new file mode 100644
index 0000000..012dfb6
--- /dev/null
+++ b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java
@@ -0,0 +1,521 @@
+package net.floodlightcontroller.virtualnetwork;
+
+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;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPacketIn;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFMessageListener;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.core.util.AppCookie;
+import net.floodlightcontroller.devicemanager.IDevice;
+import net.floodlightcontroller.devicemanager.IDeviceListener;
+import net.floodlightcontroller.devicemanager.IDeviceService;
+import net.floodlightcontroller.packet.DHCP;
+import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.packet.IPacket;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.routing.ForwardingBase;
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * A simple Layer 2 (MAC based) network virtualization module. This module allows
+ * you to create simple L2 networks (host + gateway) and will drop traffic if
+ * they are not on the same virtual network.
+ * 
+ * LIMITATIONS
+ * - This module does not allow overlapping of IPs or MACs
+ * - You can only have 1 gateway per virtual network (can be shared)
+ * - There is filtering of multicast/broadcast traffic
+ * - All DHCP traffic will be allowed, regardless of unicast/broadcast
+ * 
+ * @author alexreimers
+ */
+public class VirtualNetworkFilter 
+    implements IFloodlightModule, IVirtualNetworkService, IOFMessageListener, IDeviceListener {
+    protected static Logger log = LoggerFactory.getLogger(VirtualNetworkFilter.class);
+    
+    private final short APP_ID = 20;
+    
+    // Our dependencies
+    IFloodlightProviderService floodlightProvider;
+    IRestApiService restApi;
+    IDeviceService deviceService;
+    
+    // Our internal state
+    protected Map<String, VirtualNetwork> vNetsByGuid; // List of all created virtual networks 
+    protected Map<String, String> nameToGuid; // Logical name -> Network ID
+    protected Map<String, Integer> guidToGateway; // Network ID -> Gateway IP
+    protected Map<Integer, Set<String>> gatewayToGuid; // Gateway IP -> Network ID
+    protected Map<MACAddress, Integer> macToGateway; // Gateway MAC -> Gateway IP
+    protected Map<MACAddress, String> macToGuid; // Host MAC -> Network ID
+    protected Map<String, MACAddress> portToMac; // Host MAC -> logical port name
+    
+    /**
+     * Adds a gateway to a virtual network.
+     * @param guid The ID (not name) of the network.
+     * @param ip The IP addresses of the gateway.
+     */
+    protected void addGateway(String guid, Integer ip) {
+        if (ip.intValue() != 0) {
+        	if (log.isDebugEnabled())
+        		log.debug("Adding {} as gateway for GUID {}",
+        				IPv4.fromIPv4Address(ip), guid);
+        	
+            guidToGateway.put(guid, ip);
+            if (vNetsByGuid.get(guid) != null)
+                vNetsByGuid.get(guid).setGateway(IPv4.fromIPv4Address(ip));
+            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);
+            }
+        }
+    }
+    
+    /**
+     * Deletes a gateway for a virtual network.
+     * @param guid The ID (not name) of the network to delete
+     * the gateway for.
+     */
+    protected void deleteGateway(String guid) {
+        Integer gwIp = guidToGateway.remove(guid);
+        if (gwIp == null) return;
+        Set<String> gSet = gatewayToGuid.get(gwIp);
+        gSet.remove(guid);
+        if(vNetsByGuid.get(guid)!=null)
+            vNetsByGuid.get(guid).setGateway(null);
+    }
+    
+    // IVirtualNetworkService
+    
+    @Override
+    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, 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 (vNetsByGuid.containsKey(guid))
+            vNetsByGuid.get(guid).setName(network); //network already exists, just updating name
+        else
+            vNetsByGuid.put(guid, new VirtualNetwork(network, guid)); //new network
+        
+        // If they don't specify a new gateway the old one will be preserved
+        if ((gateway != null) && (gateway != 0)) {
+            addGateway(guid, gateway);
+            if(vNetsByGuid.get(guid)!=null)
+                vNetsByGuid.get(guid).setGateway(IPv4.fromIPv4Address(gateway));
+        }
+    }
+
+    @Override
+    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);
+        if(vNetsByGuid.get(guid)!=null){
+            vNetsByGuid.get(guid).clearHosts();
+            vNetsByGuid.remove(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);
+            }
+            macToGuid.remove(mac);
+            for (Entry<String, MACAddress> entry : portToMac.entrySet()) {
+                if (entry.getValue().equals(mac)) {
+                    portToMac.remove(entry.getKey());
+                    break;
+                }
+            }
+        }
+    }
+
+    @Override
+    public void addHost(MACAddress mac, String guid, String port) {
+        if (guid != null) {
+            if (log.isDebugEnabled()) {
+                log.debug("Adding {} to network ID {} on port {}",
+                          new Object[] {mac, guid, port});
+            }
+            // We ignore old mappings
+            macToGuid.put(mac, guid);
+            portToMac.put(port, mac);
+            if(vNetsByGuid.get(guid)!=null)
+                vNetsByGuid.get(guid).addHost(new MACAddress(mac.toBytes()));
+        } else {
+            log.warn("Could not add MAC {} to network ID {} on port {}, the network does not exist",
+                     new Object[] {mac, guid, port});
+        }
+    }
+
+    @Override
+    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);
+            if(vNetsByGuid.get(macToGuid.get(host)) != null)
+                vNetsByGuid.get(macToGuid.get(host)).removeHost(host);
+            macToGuid.remove(host);
+        } else if (mac != null) {
+            if (!portToMac.isEmpty()) {
+                for (Entry<String, MACAddress> entry : portToMac.entrySet()) {
+                    if (entry.getValue().equals(mac)) {
+                        if(vNetsByGuid.get(macToGuid.get(entry.getValue())) != null)
+                            vNetsByGuid.get(macToGuid.get(entry.getValue())).removeHost(entry.getValue());
+                        portToMac.remove(entry.getKey());
+                        macToGuid.remove(entry.getValue());
+                        return;
+                    }
+                }
+            }
+        }
+    }
+    
+    // IFloodlightModule
+    
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        Collection<Class<? extends IFloodlightService>> l = 
+                new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(IVirtualNetworkService.class);
+        return l;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService>
+            getServiceImpls() {
+        Map<Class<? extends IFloodlightService>,
+            IFloodlightService> m = 
+                new HashMap<Class<? extends IFloodlightService>,
+                    IFloodlightService>();
+        m.put(IVirtualNetworkService.class, this);
+        return m;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+        Collection<Class<? extends IFloodlightService>> l = 
+                new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(IFloodlightProviderService.class);
+        l.add(IRestApiService.class);
+        l.add(IDeviceService.class);
+        return l;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context)  
+                                 throws FloodlightModuleException {
+        floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+        restApi = context.getServiceImpl(IRestApiService.class);
+        deviceService = context.getServiceImpl(IDeviceService.class);
+        
+        vNetsByGuid = new ConcurrentHashMap<String, VirtualNetwork>();
+        nameToGuid = new ConcurrentHashMap<String, String>();
+        guidToGateway = new ConcurrentHashMap<String, Integer>();
+        gatewayToGuid = new ConcurrentHashMap<Integer, Set<String>>();
+        macToGuid = new ConcurrentHashMap<MACAddress, String>();
+        portToMac = new ConcurrentHashMap<String, MACAddress>();
+        macToGateway = new ConcurrentHashMap<MACAddress, Integer>();
+    }
+
+    @Override
+    public void startUp(FloodlightModuleContext context) {
+        floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+        restApi.addRestletRoutable(new VirtualNetworkWebRoutable());
+        deviceService.addListener(this);
+    }
+
+    // IOFMessageListener
+    
+    @Override
+    public String getName() {
+        return "virtualizer";
+    }
+
+    @Override
+    public boolean isCallbackOrderingPrereq(OFType type, String name) {
+        // Link discovery should go before us so we don't block LLDPs
+        return (type.equals(OFType.PACKET_IN) && 
+        		(name.equals("linkdiscovery") || (name.equals("devicemanager"))));
+    }
+
+    @Override
+    public boolean isCallbackOrderingPostreq(OFType type, String name) {
+        // We need to go before forwarding
+        return (type.equals(OFType.PACKET_IN) && name.equals("forwarding"));
+    }
+
+    @Override
+    public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+        switch (msg.getType()) {
+            case PACKET_IN:
+                return processPacketIn(sw, (OFPacketIn)msg, cntx);
+            default:
+            	break;
+        }
+        log.warn("Received unexpected message {}", msg);
+        return Command.CONTINUE;
+    }
+    
+    /**
+     * Checks whether the frame is destined to or from a gateway.
+     * @param frame The ethernet frame to check.
+     * @return True if it is to/from a gateway, false otherwise.
+     */
+    protected boolean isDefaultGateway(Ethernet frame) {
+    	if (macToGateway.containsKey(frame.getSourceMAC()))
+    		return true;
+    	
+    	Integer gwIp = macToGateway.get(frame.getDestinationMAC());
+    	if (gwIp != null) {
+    		MACAddress host = frame.getSourceMAC();
+    		String srcNet = macToGuid.get(host);
+    		if (srcNet != null) {
+	    		Integer gwIpSrcNet = guidToGateway.get(srcNet);
+	    		if ((gwIpSrcNet != null) && (gwIp.equals(gwIpSrcNet)))
+	    			return true;
+    		}
+    	}
+
+    	return false;
+    }
+    
+    /**
+     * Checks to see if two MAC Addresses are on the same network.
+     * @param m1 The first MAC.
+     * @param m2 The second MAC.
+     * @return True if they are on the same virtual network,
+     * 		   false otherwise.
+     */
+    protected boolean oneSameNetwork(MACAddress m1, MACAddress m2) {
+        String net1 = macToGuid.get(m1);
+        String net2 = macToGuid.get(m2);
+        if (net1 == null) return false;
+        if (net2 == null) return false;
+        return net1.equals(net2);
+    }
+    
+    /**
+     * Checks to see if an Ethernet frame is a DHCP packet.
+     * @param frame The Ethernet frame.
+     * @return True if it is a DHCP frame, false otherwise.
+     */
+    protected boolean isDhcpPacket(Ethernet frame) {
+        IPacket payload = frame.getPayload(); // IP
+        if (payload == null) return false;
+        IPacket p2 = payload.getPayload(); // TCP or UDP
+        if (p2 == null) return false;
+        IPacket p3 = p2.getPayload(); // Application
+        if ((p3 != null) && (p3 instanceof DHCP)) return true;
+        return false;
+    }
+    
+    /**
+     * Processes an OFPacketIn message and decides if the OFPacketIn should be dropped
+     * or the processing should continue.
+     * @param sw The switch the PacketIn came from.
+     * @param msg The OFPacketIn message from the switch.
+     * @param cntx The FloodlightContext for this message.
+     * @return Command.CONTINUE if processing should be continued, Command.STOP otherwise.
+     */
+    protected Command processPacketIn(IOFSwitch sw, OFPacketIn msg, FloodlightContext cntx) {
+        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, 
+                                              IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+        Command ret = Command.STOP;
+        String srcNetwork = macToGuid.get(eth.getSourceMAC());
+        // If the host is on an unknown network we deny it.
+        // We make exceptions for ARP and DHCP.
+        if (eth.isBroadcast() || eth.isMulticast() || isDefaultGateway(eth) || isDhcpPacket(eth)) {
+        	ret = Command.CONTINUE;
+        } else if (srcNetwork == null) {
+            log.trace("Blocking traffic from host {} because it is not attached to any network.",
+                      HexString.toHexString(eth.getSourceMACAddress()));
+            ret = Command.STOP;
+        } else if (oneSameNetwork(eth.getSourceMAC(), eth.getDestinationMAC())) {
+            // if they are on the same network continue
+            ret = Command.CONTINUE;
+        }
+        
+        if (log.isTraceEnabled())
+        	log.trace("Results for flow between {} and {} is {}",
+        			new Object[] {eth.getSourceMAC(), eth.getDestinationMAC(), ret});
+        /*
+         * TODO - figure out how to still detect gateways while using
+         * drop mods 
+        if (ret == Command.STOP) {
+            if (!(eth.getPayload() instanceof ARP))
+            	doDropFlow(sw, msg, cntx);
+        }
+        */
+        return ret;
+    }
+    
+    /**
+     * Writes a FlowMod to a switch that inserts a drop flow.
+     * @param sw The switch to write the FlowMod to.
+     * @param pi The corresponding OFPacketIn. Used to create the OFMatch structure.
+     * @param cntx The FloodlightContext that gets passed to the switch.
+     */
+    protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
+        if (log.isTraceEnabled()) {
+            log.trace("doDropFlow pi={} srcSwitch={}",
+                    new Object[] { pi, sw });
+        }
+
+        if (sw == null) {
+            log.warn("Switch is null, not installing drop flowmod for PacketIn {}", pi);
+            return;
+        }
+
+        // Create flow-mod based on packet-in and src-switch
+        OFFlowMod fm = 
+            (OFFlowMod) floodlightProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
+        OFMatch match = new OFMatch();
+        match.loadFromPacket(pi.getPacketData(), pi.getInPort());
+        List<OFAction> actions = new ArrayList<OFAction>(); // no actions = drop
+        long cookie = AppCookie.makeCookie(APP_ID, 0);
+        fm.setCookie(cookie)
+        .setIdleTimeout(ForwardingBase.FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+        .setHardTimeout(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT)
+        .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+        .setMatch(match)
+        .setActions(actions)
+        .setLengthU(OFFlowMod.MINIMUM_LENGTH);
+        fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
+        try {
+            if (log.isTraceEnabled()) {
+                log.trace("write drop flow-mod srcSwitch={} match={} " + 
+                          "pi={} flow-mod={}",
+                          new Object[] {sw, match, pi, fm});
+            }
+            sw.write(fm, cntx);
+        } catch (IOException e) {
+            log.error("Failure writing drop flow mod", e);
+        }
+        return;
+    }
+
+    // IDeviceListener
+    
+	@Override
+	public void deviceAdded(IDevice device) {
+		if (device.getIPv4Addresses() == null) return;
+		for (Integer i : device.getIPv4Addresses()) {
+			if (gatewayToGuid.containsKey(i)) {
+				MACAddress mac = MACAddress.valueOf(device.getMACAddress());
+				if (log.isDebugEnabled())
+					log.debug("Adding MAC {} with IP {} a a gateway",
+							HexString.toHexString(mac.toBytes()),
+							IPv4.fromIPv4Address(i));
+				macToGateway.put(mac, i);
+			}
+		}
+	}
+
+	@Override
+	public void deviceRemoved(IDevice device) {
+		// if device is a gateway remove
+		MACAddress mac = MACAddress.valueOf(device.getMACAddress());
+		if (macToGateway.containsKey(mac)) {
+			if (log.isDebugEnabled())
+				log.debug("Removing MAC {} as a gateway",
+						HexString.toHexString(mac.toBytes()));
+			macToGateway.remove(mac);
+		}
+	}
+
+	@Override
+	public void deviceIPV4AddrChanged(IDevice device) {
+		// add or remove entry as gateway
+		deviceAdded(device);
+	}
+
+	@Override
+	public void deviceMoved(IDevice device) {
+		// ignore
+	}
+	
+	@Override
+	public void deviceVlanChanged(IDevice device) {
+		// ignore
+	}
+
+    @Override
+    public Collection <VirtualNetwork> listNetworks() {
+        return vNetsByGuid.values();
+        
+    }
+}
diff --git a/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkSerializer.java b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkSerializer.java
new file mode 100644
index 0000000..6902f6c
--- /dev/null
+++ b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkSerializer.java
@@ -0,0 +1,38 @@
+package net.floodlightcontroller.virtualnetwork;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import net.floodlightcontroller.util.MACAddress;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+/**
+ * Serialize a VirtualNetwork object
+ * @author KC Wang
+ */
+public class VirtualNetworkSerializer extends JsonSerializer<VirtualNetwork> {
+
+    @Override
+    public void serialize(VirtualNetwork vNet, JsonGenerator jGen,
+            SerializerProvider serializer) throws IOException,
+            JsonProcessingException {
+        jGen.writeStartObject();
+        
+        jGen.writeStringField("name", vNet.name);
+        jGen.writeStringField("guid", vNet.guid);
+        jGen.writeStringField("gateway", vNet.gateway);
+
+        jGen.writeArrayFieldStart("mac");
+        Iterator<MACAddress> hit = vNet.hosts.iterator();
+        while (hit.hasNext())
+            jGen.writeString(hit.next().toString());
+        jGen.writeEndArray();
+        
+        jGen.writeEndObject();
+    }
+
+}
diff --git a/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkWebRoutable.java b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkWebRoutable.java
new file mode 100644
index 0000000..61769ec
--- /dev/null
+++ b/src/ext/floodlight/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkWebRoutable.java
@@ -0,0 +1,26 @@
+package net.floodlightcontroller.virtualnetwork;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+public class VirtualNetworkWebRoutable implements RestletRoutable {
+
+    @Override
+    public Restlet getRestlet(Context context) {
+        Router router = new Router(context);
+        router.attach("/tenants/{tenant}/networks", NetworkResource.class); // GET
+        router.attach("/tenants/{tenant}/networks/{network}", NetworkResource.class); // PUT, DELETE
+        router.attach("/tenants/{tenant}/networks", NetworkResource.class); // POST
+        router.attach("/tenants/{tenant}/networks/{network}/ports/{port}/attachment", HostResource.class);
+        router.attachDefault(NoOp.class);
+        return router;
+    }
+
+    @Override
+    public String basePath() {
+        return "/quantum/v1.0";
+    }
+}
\ No newline at end of file