diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
new file mode 100644
index 0000000000000000000000000000000000000000..36b13123c233c2728ec87083fb608d651bd261f5
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java
@@ -0,0 +1,435 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.floodlightcontroller.accesscontrollist.ACLRule.Action;
+import net.floodlightcontroller.accesscontrollist.ap.AP;
+import net.floodlightcontroller.accesscontrollist.ap.APManager;
+import net.floodlightcontroller.accesscontrollist.util.IPAddressUtil;
+import net.floodlightcontroller.accesscontrollist.web.ACLWebRoutable;
+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.devicemanager.IDevice;
+import net.floodlightcontroller.devicemanager.IDeviceListener;
+import net.floodlightcontroller.devicemanager.IDeviceService;
+import net.floodlightcontroller.devicemanager.SwitchPort;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.storage.IStorageSourceService;
+
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ACL implements IACLService, IFloodlightModule, IDeviceListener {
+
+	// service modules needed
+	protected IRestApiService restApi;
+	protected IDeviceService deviceManager;
+	protected IStorageSourceService storageSource;
+	protected static Logger logger;
+	
+	private APManager apManager;
+
+	// variable used
+	private int lastRuleId = 1; // rule id counter
+	private List<ACLRule> ruleSet;
+	private Map<String, Integer> dpid2FlowPriority;
+	private Map<Integer, Set<String>> ruleId2Dpid;
+	private Map<Integer, Set<String>> ruleId2FlowName;
+
+	/**
+	 * used by REST API to query ACL rules
+	 */
+	@Override
+	public List<ACLRule> getRules() {
+		return this.ruleSet;
+	}
+
+	/**
+	 * check if the new rule matches an existing rule
+	 */
+	private boolean checkRuleMatch(ACLRule newRule) {
+		Iterator<ACLRule> iter = ruleSet.iterator();
+		while (iter.hasNext()) {
+			ACLRule existingRule = iter.next();
+			if(newRule.match(existingRule)){
+				logger.error("existing rule: " + existingRule);
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * used by REST API to add ACL rule
+	 * @return if the new ACL rule is added successfully
+	 */
+	@Override
+	public boolean addRule(ACLRule rule) {
+
+		if(checkRuleMatch(rule)){
+			return false;
+		}
+		
+		rule.setId(lastRuleId++);
+		this.ruleSet.add(rule);
+		logger.info("No.{} ACL rule added.", rule.getId());
+		enforceAddedRule(rule);
+		return true;
+	}
+
+	/**
+	 * used by REST API to remove ACL rule
+	 */
+	@Override
+	public void removeRule(int ruleid) {
+
+		Iterator<ACLRule> iter = this.ruleSet.iterator();
+		while (iter.hasNext()) {
+			ACLRule rule = iter.next();
+			if (rule.getId() == ruleid) {
				iter.remove();
+				break;
+			}
+		}
+
+		logger.info("No.{} ACL rule removed.", ruleid);
+		enforceRemovedRule(ruleid);
+	}
+
+	/**
+	 * used by REST API to clear ACL
+	 */
+	@Override
+	public void removeAllRules() {
+		
+		this.lastRuleId = 1;
+		this.ruleSet = new ArrayList<ACLRule>();
+		this.dpid2FlowPriority = new HashMap<String, Integer>();
+		this.ruleId2Dpid = new HashMap<Integer, Set<String>>();
+
+		Iterator<Integer> ruleIdIter = ruleId2FlowName.keySet().iterator();
+		while (ruleIdIter.hasNext()) {
+			int ruleId = ruleIdIter.next();
+			Set<String> flowNameSet = ruleId2FlowName.get(ruleId);
+			logger.info("No.{} ACL rule removed.", ruleId);
+			for (String flowName : flowNameSet) {
+				removeFlow(flowName);
+				logger.info("ACL flow {} removed.", flowName);
+			}
+		}
+		this.ruleId2FlowName = new HashMap<Integer, Set<String>>();
+	}
+
+	/**
+	 * enforce new added rule
+	 */
+	private void enforceAddedRule(ACLRule rule) {
+
+		Set<String> dpidSet;
+		if (rule.getNw_src() != null) {
+			dpidSet = apManager.getDpidSet(rule.getNw_src_prefix(),rule.getNw_src_maskbits());
+		} else {
+			dpidSet = apManager.getDpidSet(rule.getNw_dst_prefix(),rule.getNw_dst_maskbits());
+		}
+
+		Iterator<String> dpidIter = dpidSet.iterator();
+		Set<String> nameSet = new HashSet<String>();
+
+		while (dpidIter.hasNext()) {
+			String dpid = dpidIter.next();
+			String flowName = "ACLRule_" + rule.getId() + "_" + dpid;
+			generateFlow(rule, dpid, flowName);
+			nameSet.add(flowName);
+		}
+		ruleId2FlowName.put(rule.getId(), nameSet);
+		ruleId2Dpid.put(rule.getId(), dpidSet);
+	}
+
+	/**
+	 * enforce removed rule
+	 */
+	private void enforceRemovedRule(int ruleId) {
+
+		Set<String> flowEntryName = ruleId2FlowName.get(ruleId);
+		Iterator<String> iter = flowEntryName.iterator();
+		while (iter.hasNext()) {
+			String name = iter.next();
+			removeFlow(name);
+			logger.info("ACL flow " + name + " removed.");
+		}
+
+	}
+		
+	/**
+	 * generate and push ACL flow entry
+	 */
+	private void generateFlow(ACLRule rule, String dpid, String flowName) {
+
+		int priority;
+		// get priority for the new flow entry
+		if (dpid2FlowPriority.get(dpid) == null) {
+			dpid2FlowPriority.put(dpid, 30000);
+			priority = 30000;
+		} else {
+			priority = dpid2FlowPriority.get(dpid);
+		}
+
+		if (rule.getNw_src() != null) {
+			
+			HashMap<String,Object> flow = new HashMap<String,Object>();
+	        
+			flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, dpid);
+			flow.put(StaticFlowEntryPusher.COLUMN_NAME, flowName);
+			flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
+			flow.put(StaticFlowEntryPusher.COLUMN_COOKIE, "0");
+			flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(priority));
+			dpid2FlowPriority.put(dpid, --priority);
+			
+			
+			flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048");
+			flow.put(StaticFlowEntryPusher.COLUMN_NW_SRC, rule.getNw_src());
+			
+			// process for the nw_dst attribute
+			if (rule.getNw_dst() != null) {
+				flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, rule.getNw_dst());
+			}
+			if (rule.getNw_proto() != 0) {
+				flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Integer.toString(rule.getNw_proto()));
+			}
+			if (rule.getAction() == Action.ALLOW) {
+				flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller");
+			}
+			if (rule.getTp_dst() != 0) {
+				flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, Integer.toString(rule.getTp_dst()));
+			}
+	        
+	        storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
+			
+		} else {
+			
+			HashMap<String,Object> flow = new HashMap<String,Object>();
+	        
+			flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, dpid);
+			flow.put(StaticFlowEntryPusher.COLUMN_NAME, flowName);
+			flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
+			flow.put(StaticFlowEntryPusher.COLUMN_COOKIE, "0");
+			flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(priority));
+			dpid2FlowPriority.put(dpid, --priority);
+			
+			flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048");
+			flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, rule.getNw_dst());
+
+			if (rule.getNw_proto() != 0) {
+				flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Integer.toString(rule.getNw_proto()));
+			}
+			if (rule.getAction() == Action.ALLOW) {
+				flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller");
+			}
+			if (rule.getTp_dst() != 0) {
+				flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, Integer.toString(rule.getTp_dst()));
+			}
+	        
+	        storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
+	        
+		}
+		logger.info("ACL flow " + flowName + " added in " + dpid);
+	}
+
+	/**
+	 * remove ACL flow entry
+	 */
+	private void removeFlow(String name) {
+		storageSource.deleteRowAsync("controller_staticflowtableentry", name);
+	}
+
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IACLService.class);
+		return l;
+	}
+
+	@Override
+	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+		Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+		// We are the class that implements the service
+		m.put(IACLService.class, this);
+		return m;
+	}
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IRestApiService.class);
+		l.add(IDeviceService.class);
+		return l;
+	}
+
+	@Override
+	public void init(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+		restApi = context.getServiceImpl(IRestApiService.class);
+		deviceManager = context.getServiceImpl(IDeviceService.class);
+		logger = LoggerFactory.getLogger(ACL.class);
+		storageSource = context.getServiceImpl(IStorageSourceService.class);
+
+		ruleSet = new ArrayList<ACLRule>();
+		apManager = new APManager();
+		ruleId2FlowName = new HashMap<Integer, Set<String>>();
+		ruleId2Dpid =  new HashMap<Integer, Set<String>>();
+		dpid2FlowPriority = new HashMap<String, Integer>();
+	}
+
+	@Override
+	public void startUp(FloodlightModuleContext context) {
+		// register REST interface
+		restApi.addRestletRoutable(new ACLWebRoutable());
+		deviceManager.addListener(this);
+	}
+
+	/**
+	 * listen for new device
+	 */
+	@Override
+	public void deviceAdded(IDevice device) {
+		SwitchPort[] switchPort = device.getAttachmentPoints();
+		IPv4Address[] ips = device.getIPv4Addresses();
+		if(ips.length == 0){
+			// A new no-ip device added
+			return;
+		}
+		String dpid = HexString.toHexString(switchPort[0].getSwitchDPID().getLong());
+		String ip = IPv4.fromIPv4Address(ips[0].getInt());
+		logger.info("New AP added. [dpid:" + dpid + " ip:" + ip + "]");
+
+		AP ap = new AP(ip,dpid);
+		apManager.addAP(ap);
+		processAPAdded(ap);
+	}
+
+	/**
+	 * push ACL flow given the new device
+	 */
+	private void processAPAdded(AP ap) {
+
+		String dpid = ap.getDpid();
+		int ip = IPv4.toIPv4Address(ap.getIp());
+
+		Iterator<ACLRule> iter = this.ruleSet.iterator();
+		while (iter.hasNext()) {
+			ACLRule rule = iter.next();
+			if (rule.getNw_src() != null) {
+				if (IPAddressUtil.containIP(rule.getNw_src_prefix(),
+						rule.getNw_src_maskbits(), ip)) {
+					// check if there is a flow entry in the switch for the rule
+					if (ruleId2Dpid.get(rule.getId()).contains(dpid)) {
+						continue;
+					}
+					String flowName = "ACLRule_" + rule.getId() + "_" + dpid;
+					ruleId2FlowName.get(rule.getId()).add(flowName);
+					ruleId2Dpid.get(rule.getId()).add(dpid);
+					generateFlow(rule, dpid, flowName);
+				}
+			} else {
+				if (IPAddressUtil.containIP(rule.getNw_dst_prefix(),
+						rule.getNw_dst_maskbits(), ip)) {
+					// check if there is a flow entry in the switch for the rule
+					if (ruleId2Dpid.get(rule.getId()).contains(dpid)) {
+						continue;
+					}
+					String flowName = "ACLRule_" + rule.getId() + "_" + dpid;
+					ruleId2FlowName.get(rule.getId()).add(flowName);
+					ruleId2Dpid.get(rule.getId()).add(dpid);
+					generateFlow(rule, dpid, flowName);
+				}
+			}
+		}
+	}
+
+	@Override
+	public void deviceRemoved(IDevice device) {
+
+	}
+
+	@Override
+	public void deviceMoved(IDevice device) {
+
+	}
+
+	@Override
+	public void deviceIPV4AddrChanged(IDevice device) {
+		
+		SwitchPort[] switchPort = device.getAttachmentPoints();
+		IPv4Address[] ips = device.getIPv4Addresses();
+		
+		String dpid = HexString.toHexString(switchPort[0].getSwitchDPID().getLong());
+		String ip = null;
+		
+		// some device may first appear with no IP address(default set to 0.0.0.0), ignore it
+		for(IPv4Address i : ips){
+			if(i.getInt() != 0){
+				ip = IPv4.fromIPv4Address(i.getInt());
+				break;
+			}
+		}
+		
+		logger.info("New AP added. [dpid:" + dpid + " ip:" + ip + "]");
+		AP ap = new AP(ip, dpid);
+		apManager.addAP(ap);
+		processAPAdded(ap);
+	}
+
+	@Override
+	public void deviceVlanChanged(IDevice device) {
+
+	}
+
+	@Override
+	public String getName() {
+		return null;
+	}
+
+	@Override
+	public boolean isCallbackOrderingPrereq(String type, String name) {
+		return false;
+	}
+
+	@Override
+	public boolean isCallbackOrderingPostreq(String type, String name) {
+		return false;
+	}
+
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ACLRule.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ACLRule.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d580e88eed87ca6693d84f09d91bfc11eed6ff8
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ACLRule.java
@@ -0,0 +1,220 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import static net.floodlightcontroller.accesscontrollist.util.IPAddressUtil.isSubnet;
+
+public class ACLRule {
+
+	private int id;
+	private String nw_src; // CIDR IP address
+	private String nw_dst; // CIDR IP address
+	private int nw_src_prefix;
+	private int nw_src_maskbits;
+	private int nw_dst_prefix;
+	private int nw_dst_maskbits;
+	private int nw_proto;
+	private int tp_dst;
+	private Action action;
+
+	public enum Action {
+		DENY, ALLOW
+	}
+
+	public ACLRule() {
+		this.id = 0;
+		this.nw_src = null;
+		this.nw_dst = null;
+		this.nw_src_prefix = 0;
+		this.nw_src_maskbits = 0;
+		this.nw_dst_prefix = 0;
+		this.nw_dst_maskbits = 0;
+		this.nw_proto = 0;
+		this.tp_dst = 0;
+		this.action = Action.DENY;
+	}
+
+	/**
+	 * check if this rule matches an existing rule:
+	 * a matching means this rule won't take effect at all for reason that
+	 * its working scope is already covered by an existing rule
+	 */
+	public boolean match(ACLRule r) {
+		if ((this.tp_dst == r.tp_dst || r.tp_dst == 0)
+				&& (this.nw_proto == r.nw_proto || r.nw_proto == 0)
+				&& (isSubnet(this.getNw_src(), r.getNw_src()) == true)
+				&& (isSubnet(this.getNw_dst(), r.getNw_dst()) == true)) {
+			return true;
+		}
+		return false;
+	}
+	
+	public int getId() {
+		return id;
+	}
+
+	public void setId(int id) {
+		this.id = id;
+	}
+
+	public void setNw_src(String nw_src) {
+		this.nw_src = nw_src;
+	}
+
+	public String getNw_src() {
+		return nw_src;
+	}
+
+	public void setNw_dst(String nw_dst) {
+		this.nw_dst = nw_dst;
+	}
+
+	public String getNw_dst() {
+		return nw_dst;
+	}
+
+	public int getNw_src_prefix() {
+		return nw_src_prefix;
+	}
+
+	public void setNw_src_prefix(int nw_src_prefix) {
+		this.nw_src_prefix = nw_src_prefix;
+	}
+
+	public int getNw_src_maskbits() {
+		return nw_src_maskbits;
+	}
+
+	public void setNw_src_maskbits(int nw_src_maskbits) {
+		this.nw_src_maskbits = nw_src_maskbits;
+	}
+
+	public int getNw_dst_prefix() {
+		return nw_dst_prefix;
+	}
+
+	public void setNw_dst_prefix(int nw_dst_prefix) {
+		this.nw_dst_prefix = nw_dst_prefix;
+	}
+
+	public int getNw_dst_maskbits() {
+		return nw_dst_maskbits;
+	}
+
+	public void setNw_dst_maskbits(int nw_dst_maskbits) {
+		this.nw_dst_maskbits = nw_dst_maskbits;
+	}
+
+	public int getNw_proto() {
+		return nw_proto;
+	}
+
+	/**
+	 * @param nw_proto TCP:6, UDP:11, ICMP:1
+	 */
+	public void setNw_proto(int nw_proto) {
+		this.nw_proto = nw_proto;
+	}
+
+	public int getTp_dst() {
+		return tp_dst;
+	}
+
+	public void setTp_dst(int tp_dst) {
+		this.tp_dst = tp_dst;
+	}
+
+	public Action getAction() {
+		return action;
+	}
+
+	public void setAction(Action action) {
+		this.action = action;
+	}
+
+
+	@Override
+	public String toString() {
+		return "ACLRule [id=" + id + ", nw_src=" + nw_src + ", nw_dst="
+				+ nw_dst + ", nw_src_prefix=" + nw_src_prefix
+				+ ", nw_src_maskbits=" + nw_src_maskbits + ", nw_dst_prefix="
+				+ nw_dst_prefix + ", nw_dst_maskbits=" + nw_dst_maskbits
+				+ ", nw_proto=" + nw_proto + ", tp_dst=" + tp_dst + ", action="
+				+ action + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((action == null) ? 0 : action.hashCode());
+		result = prime * result + id;
+		result = prime * result + ((nw_dst == null) ? 0 : nw_dst.hashCode());
+		result = prime * result + nw_dst_maskbits;
+		result = prime * result + nw_dst_prefix;
+		result = prime * result + nw_proto;
+		result = prime * result + ((nw_src == null) ? 0 : nw_src.hashCode());
+		result = prime * result + nw_src_maskbits;
+		result = prime * result + nw_src_prefix;
+		result = prime * result + tp_dst;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (!(obj instanceof ACLRule))
+			return false;
+		ACLRule other = (ACLRule) obj;
+		if (action != other.action)
+			return false;
+		if (id != other.id)
+			return false;
+		if (nw_dst == null) {
+			if (other.nw_dst != null)
+				return false;
+		} else if (!nw_dst.equals(other.nw_dst))
+			return false;
+		if (nw_dst_maskbits != other.nw_dst_maskbits)
+			return false;
+		if (nw_dst_prefix != other.nw_dst_prefix)
+			return false;
+		if (nw_proto != other.nw_proto)
+			return false;
+		if (nw_src == null) {
+			if (other.nw_src != null)
+				return false;
+		} else if (!nw_src.equals(other.nw_src))
+			return false;
+		if (nw_src_maskbits != other.nw_src_maskbits)
+			return false;
+		if (nw_src_prefix != other.nw_src_prefix)
+			return false;
+		if (tp_dst != other.tp_dst)
+			return false;
+		return true;
+	}
+	
+
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java b/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ba9e3da0c649c86405a324777aa1742759b8fd2
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java
@@ -0,0 +1,111 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+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.staticflowentry.IStaticFlowEntryPusherService;
+import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.storage.IStorageSourceService;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FlowPusher implements IFloodlightModule {
+
+	protected IFloodlightProviderService floodlightProvider;
+	protected static Logger logger;
+	protected IStorageSourceService storageSource;
+	protected IStaticFlowEntryPusherService sfp;
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+		return null;
+	}
+
+	@Override
+	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+		return null;
+	}
+
+	@Override
+	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+		Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+		l.add(IFloodlightProviderService.class);
+		l.add(IStaticFlowEntryPusherService.class);
+        l.add(IStorageSourceService.class);
+		return l;
+	}
+
+	@Override
+	public void init(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+		floodlightProvider = context
+				.getServiceImpl(IFloodlightProviderService.class);
+		storageSource = context.getServiceImpl(IStorageSourceService.class);
+		sfp = context.getServiceImpl(IStaticFlowEntryPusherService.class);
+		logger = LoggerFactory.getLogger(FlowPusher.class);
+	}
+
+	@Override
+	public void startUp(FloodlightModuleContext context)
+			throws FloodlightModuleException {
+
+		HashMap<String,Object> flow = new HashMap<String,Object>();
+        
+        flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, "80");
+        flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, "10.0.0.2");
+        flow.put(StaticFlowEntryPusher.COLUMN_NW_SRC, "10.0.0.1");
+        flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, "30001");
+        flow.put(StaticFlowEntryPusher.COLUMN_NAME, "flow1");
+        flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true));
+        flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048");
+        flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, "6");
+        flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, "00:00:00:00:00:00:00:01");
+        flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller");
+        
+        storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow);
+        
+//        flow.put("tp_dst", "80");
+//        flow.put("nw_dst", "10.0.0.3");
+//        flow.put("nw_src", "10.0.0.1");
+//        flow.put("priority", "30001");
+//        flow.put("name", "flow2");
+//        flow.put("active", Boolean.toString(true));
+//        flow.put("dl_type", "2048");
+//        flow.put("nw_proto", "6");
+//        flow.put("switch_id", "00:00:00:00:00:00:00:01");
+//        flow.put("actions", "output=controller");
+//        
+//        storageSource.insertRowAsync("controller_staticflowtableentry", flow);
+        
+//        storageSource.deleteRowAsync("controller_staticflowtableentry", "flow1");
+	}
+
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java b/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ce23238e8ebefe34397cae2deaf9566e841fdbb
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java
@@ -0,0 +1,42 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import java.util.List;
+
+import net.floodlightcontroller.core.module.IFloodlightService;
+
+public interface IACLService extends IFloodlightService {
+	
+    public List<ACLRule> getRules();
+    
+    /**
+     * add a new ACL rule
+     * @param rule
+     * @return whether the rule is added successfully
+     */
+    public boolean addRule(ACLRule rule);
+
+    public void removeRule(int ruleid);
+    
+    public void removeAllRules();
+
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ap/AP.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ap/AP.java
new file mode 100644
index 0000000000000000000000000000000000000000..6feb9c92866bccc96fc9f9b7382be218ffc21b80
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ap/AP.java
@@ -0,0 +1,56 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist.ap;
+
+public class AP {
+
+	private String ip;
+	private String dpid;
+
+	public AP(String ip, String dpid) {
+		super();
+		this.ip = ip;
+		this.dpid = dpid;
+	}
+
+	public String getIp() {
+		return ip;
+	}
+
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
+
+	public String getDpid() {
+		return dpid;
+	}
+
+	public void setDpid(String dpid) {
+		this.dpid = dpid;
+	}
+
+
+	@Override
+	public String toString() {
+		return "AP [ip=" + ip + ", dpid=" + dpid + "]";
+	}
+
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ap/APManager.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ap/APManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..d49ee22caff0cb1ff2efb86d98a216c9e1595027
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ap/APManager.java
@@ -0,0 +1,66 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist.ap;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import net.floodlightcontroller.accesscontrollist.util.IPAddressUtil;
+import net.floodlightcontroller.packet.IPv4;
+
+public class APManager {
+
+	private Set<AP> apSet = new HashSet<AP>();
+
+	public void addAP(AP ap) {
+		this.apSet.add(ap);
+	}
+
+	/**
+	 * get dpid set relating to the given CIDR IP
+	 */
+	public Set<String> getDpidSet(int cidrPrefix, int cidrMaskBits) {
+		Set<String> dpidSet = new HashSet<String>();
+
+		Iterator<AP> iter = apSet.iterator();
+		if (cidrMaskBits != 32) {
+			while (iter.hasNext()) {
+				AP ap = iter.next();
+				if (IPAddressUtil.containIP(cidrPrefix, cidrMaskBits,
+						IPv4.toIPv4Address(ap.getIp()))) {
+					dpidSet.add(ap.getDpid());
+				}
+			}
+		} else {
+			while (iter.hasNext()) {
+				AP ap = iter.next();
+				if (IPAddressUtil.containIP(cidrPrefix, cidrMaskBits,
+						IPv4.toIPv4Address(ap.getIp()))) {
+					dpidSet.add(ap.getDpid());
+					return dpidSet;
+				}
+			}
+		}
+		return dpidSet;
+	}
+
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/util/IPAddressUtil.java b/src/main/java/net/floodlightcontroller/accesscontrollist/util/IPAddressUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..236bd778ac31a943ed20bdefd1094f20fcf96000
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/util/IPAddressUtil.java
@@ -0,0 +1,124 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist.util;
+
+import net.floodlightcontroller.packet.IPv4;
+
+public class IPAddressUtil {
+
+	/**
+	 * parse the given CIDR IP
+	 * 
+	 * @return an array contains the CIDR prefix and mask bits
+	 * 
+	 */
+	public static int[] parseCIDR(String cidr) {
+
+		int ret[] = new int[2];
+
+		String[] parts = cidr.split("/");
+		
+		if (parts.length == 1){
+			throw new IllegalArgumentException("CIDR mask bits must be specified.");
+		}
+		
+		String cidrPrefix = parts[0].trim();
+		int cidrMaskBits = 0;
+		if (parts.length == 2) {
+			try {
+				cidrMaskBits = Integer.parseInt(parts[1].trim());
+			} catch (Exception e) {
+				throw new NumberFormatException("CIDR mask bits must be specified as a number(0 ~ 32).");
+			}
+			if (cidrMaskBits < 0 || cidrMaskBits > 32) {
+				throw new NumberFormatException("CIDR mask bits must be 0 <= value <= 32.");
+			}
+		}
+		ret[0] = IPv4.toIPv4Address(cidrPrefix);
+		ret[1] = cidrMaskBits;
+
+		return ret;
+	}
+
+	/**
+	 * check whether the CIDR address contains the IP address
+	 */
+	public static boolean containIP(int cidrPrefix, int cidrMaskBits, int ip) {
+
+		boolean matched = true;
+		int bitsToShift = 32 - cidrMaskBits;
+
+		if (bitsToShift > 0) {
+			cidrPrefix = cidrPrefix >> bitsToShift;
+			ip = ip >> bitsToShift;
+			cidrPrefix = cidrPrefix << bitsToShift;
+			ip = ip << bitsToShift;
+		}
+
+		if (cidrPrefix != ip) {
+			matched = false;
+		}
+
+		return matched;
+	}
+
+	/**
+	 * check whether cidr1 is a subnet of (or the same as) cidr2
+	 * 
+	 */
+	public static boolean isSubnet(String cidr1, String cidr2) {
+
+		if (cidr2 == null) {
+			return true;
+		} else if (cidr1 == null) {
+			return false;
+		}
+
+		int[] cidr = IPAddressUtil.parseCIDR(cidr1);
+		int cidr1Prefix = cidr[0];
+		int cidr1MaskBits = cidr[1];
+		cidr = IPAddressUtil.parseCIDR(cidr2);
+		int cidr2Prefix = cidr[0];
+		int cidr2MaskBits = cidr[1];
+
+		int bitsToShift_1 = 32 - cidr1MaskBits;
+		int bitsToShift_2 = 32 - cidr2MaskBits;
+
+		int offset = (bitsToShift_1 > bitsToShift_2) ? bitsToShift_1
+				: bitsToShift_2;
+
+		if (offset > 0) {
+			cidr1Prefix = cidr1Prefix >> offset;
+			cidr2Prefix = cidr2Prefix >> offset;
+			cidr1Prefix = cidr1Prefix << offset;
+			cidr2Prefix = cidr2Prefix << offset;
+		}
+
+		if (cidr1Prefix == cidr2Prefix) {
+			if (cidr1MaskBits >= cidr2MaskBits) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/web/ACLRuleResource.java b/src/main/java/net/floodlightcontroller/accesscontrollist/web/ACLRuleResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a6661bb010020dc4b0b4820dfc16d4893190743
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/web/ACLRuleResource.java
@@ -0,0 +1,220 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist.web;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import net.floodlightcontroller.accesscontrollist.ACLRule;
+import net.floodlightcontroller.accesscontrollist.IACLService;
+import net.floodlightcontroller.accesscontrollist.util.IPAddressUtil;
+
+import org.restlet.resource.Delete;
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.MappingJsonFactory;
+
+public class ACLRuleResource extends ServerResource {
+	protected static Logger log = LoggerFactory
+			.getLogger(ACLRuleResource.class);
+
+	@Get("json")
+	public Object handleRequest() {
+		IACLService acl = (IACLService) getContext().getAttributes().get(
+				IACLService.class.getCanonicalName());
+
+		return acl.getRules();
+	}
+
+	/**
+	 * parse a JSON string into a ACL rule instance, then add it to the ACL
+	 * @return A string status message
+	 */
+	@Post
+	public String store(String json) {
+		IACLService aclService = (IACLService) getContext().getAttributes().get(
+				IACLService.class.getCanonicalName());
+		
+		ACLRule newRule;
+		try {
+			newRule = jsonToRule(json);
+		} catch (Exception e) {
+			log.error("Error parsing ACL rule: " + json, e);
+			return "{\"status\" : \"Failed! " + e.getMessage() + "\"}";
+		}
+		
+		String status = null;
+		
+		String nw_src = newRule.getNw_src();
+		String nw_dst = newRule.getNw_dst();
+		if (nw_src == null && nw_dst == null){
+			status = "Failed! Either nw_src or nw_dst must be specified.";
+			return ("{\"status\" : \"" + status + "\"}");
+		}
+		
+		if(aclService.addRule(newRule)){
+			status = "Success! New rule added.";
+		}else{
+			status = "Failed! The new ACL rule matches an existing rule.";
+		}
+			
+		return ("{\"status\" : \"" + status + "\"}");
+	}
+	
+	/**
+	 * parse a JSON string into a ACL rule instance, then remove it from the ACL
+	 * @return A string status message
+	 */
+	@Delete
+	public String remove(String json) {
+		IACLService ACL = (IACLService) getContext().getAttributes().get(
+				IACLService.class.getCanonicalName());
+
+		ACLRule rule;
+		try {
+			rule = jsonToRule(json);
+		} catch (Exception e) {
+			log.error("Error parsing ACL rule: " + json, e);
+			return "{\"status\" : \"Failed! " + e.getMessage() + "\"}";
+		}
+		
+		// check whether the rule exists
+		boolean exists = false;
+		Iterator<ACLRule> iter = ACL.getRules().iterator();
+		while (iter.hasNext()) {
+			ACLRule r = iter.next();
+			if (r.getId() == rule.getId()) {
+				exists = true;
+				break;
+			}
+		}
+		
+		String status = null;
+		if (!exists) {
+			status = "Failed! a rule with this ID doesn't exist.";
+			log.error(status);
+		} else {
+			ACL.removeRule(rule.getId());
+			status = "Success! Rule deleted";
+		}
+		
+		return ("{\"status\" : \"" + status + "\"}");
+	}
+
+
+	/**
+	 * Turns a JSON string into a ACL rule instance
+	 */
+	public static ACLRule jsonToRule(String json) throws IOException {
+		ACLRule rule = new ACLRule();
+		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 key = jp.getCurrentName();
+			jp.nextToken();
+			String value = jp.getText();
+			if (value.equals(""))
+				continue;
+
+			// used for removing rule
+			if ("ruleid".equals(key)) {
+				try{
+					rule.setId(Integer.parseInt(value));
+				} catch(NumberFormatException e){
+					throw new NumberFormatException("ruleid must be specified as a number.");
+				}
+				
+			}
+
+			else if ("src-ip".equals(key)) {
+				rule.setNw_src(value);
+				int[] cidr = IPAddressUtil.parseCIDR(value);
+				rule.setNw_src_prefix(cidr[0]);
+				rule.setNw_src_maskbits(cidr[1]);
+			}
+
+			else if ("dst-ip".equals(key)) {
+				rule.setNw_dst(value);
+				int[] cidr = IPAddressUtil.parseCIDR(value);
+				rule.setNw_dst_prefix(cidr[0]);
+				rule.setNw_dst_maskbits(cidr[1]);
+			}
+
+			else if ("nw-proto".equals(key)) {
+				if ("TCP".equalsIgnoreCase(value)) {
+					rule.setNw_proto(6);
+				} else if ("UDP".equalsIgnoreCase(value)) {
+					rule.setNw_proto(11);
+				} else if ("ICMP".equalsIgnoreCase(value)) {
+					rule.setNw_proto(1);
+				} else {
+					throw new IllegalArgumentException("nw-proto must be specified as (TCP || UDP || ICMP).");
+				}
+			}
+
+			else if ("tp-dst".equals(key)) {
+				// only when tp-dst == (TCP || UDP), tp-dst can have non-0 value
+				if(rule.getNw_proto() == 6 || rule.getNw_proto() == 11){
+					try{
+						rule.setTp_dst(Integer.parseInt(value));
+					}catch(NumberFormatException e){
+						throw new NumberFormatException("tp-dst must be specified as a number.");
+					}
+				}
+			}
+
+			else if (key == "action") {
+				if ("allow".equalsIgnoreCase(value)) {
+					rule.setAction(ACLRule.Action.ALLOW);
+				} else if ("deny".equalsIgnoreCase(value)) {
+					rule.setAction(ACLRule.Action.DENY);
+				} else{
+					throw new IllegalArgumentException("action must be specidied as (allow || deny).");
+				}
+			}
+		}
+
+		return rule;
+	}
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/web/ACLWebRoutable.java b/src/main/java/net/floodlightcontroller/accesscontrollist/web/ACLWebRoutable.java
new file mode 100644
index 0000000000000000000000000000000000000000..dab8233afe757756e862fb9ae5db6a9790b8ebb1
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/web/ACLWebRoutable.java
@@ -0,0 +1,51 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist.web;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+public class ACLWebRoutable implements RestletRoutable {
+
+
+    /**
+     * Create the Restlet router and bind to the proper resources.
+     */
+	@Override
+	public Restlet getRestlet(Context context) {
+        Router router = new Router(context);
+        router.attach("/rules/json", ACLRuleResource.class);
+        router.attach("/clear/json", ClearACRulesResource.class);
+        return router;
+	}
+
+    /**
+     * Set the base path for the ACL
+     */
+	@Override
+	public String basePath() {
+        return "/wm/acl";
+	}
+
+}
diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/web/ClearACRulesResource.java b/src/main/java/net/floodlightcontroller/accesscontrollist/web/ClearACRulesResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..edde6dfd227774d95e69f8bfe6087ef34fd19b56
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/accesscontrollist/web/ClearACRulesResource.java
@@ -0,0 +1,41 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist.web;
+
+import net.floodlightcontroller.accesscontrollist.IACLService;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClearACRulesResource extends ServerResource {
+	protected static Logger log = LoggerFactory
+			.getLogger(ClearACRulesResource.class);
+
+    @Get
+    public void ClearACRules() {
+		IACLService acl = (IACLService) getContext().getAttributes().get(
+				IACLService.class.getCanonicalName());
+        
+		acl.removeAllRules();
+    }
+}
diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryPusherResource.java b/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryPusherResource.java
index f43773ba15ac119ad9ed270c8b9338ba8cb8da6a..1f953b0e0a7117764e7ed6dc8fcf6889e1134dc4 100644
--- a/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryPusherResource.java
+++ b/src/main/java/net/floodlightcontroller/staticflowentry/web/StaticFlowEntryPusherResource.java
@@ -326,7 +326,7 @@ public class StaticFlowEntryPusherResource extends ServerResource {
 			return ("{\"status\" : \"" + status + "\"}");
 		} catch (IOException e) {
 			log.error("Error parsing push flow mod request: " + fmJson, e);
-			return "{\"status\" : \"Error! Could not parse flod mod, see log for details.\"}";
+			return "{\"status\" : \"Error! Could not parse flow mod, see log for details.\"}";
 		}        
 	}
 
diff --git a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
index 238fe7debdd33f9903082e2787a9a92af258852b..803441cf89f3373d048ddb0cc72b1cbfbbef9977 100644
--- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
+++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule
@@ -24,4 +24,5 @@ net.floodlightcontroller.loadbalancer.LoadBalancer
 net.floodlightcontroller.linkdiscovery.internal.LinkDiscoveryManager
 net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl
 net.floodlightcontroller.firewall.Firewall
-net.floodlightcontroller.dhcpserver.DHCPServer
\ No newline at end of file
+net.floodlightcontroller.accesscontrollist.ACL
+net.floodlightcontroller.dhcpserver.DHCPServer
diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties
index 6e6daf828e686c224f766f352498c07cfbca150e..c53734c8316cb63106fce97d227d749497713a19 100644
--- a/src/main/resources/floodlightdefault.properties
+++ b/src/main/resources/floodlightdefault.properties
@@ -14,7 +14,8 @@ net.floodlightcontroller.linkdiscovery.internal.LinkDiscoveryManager,\
 net.floodlightcontroller.ui.web.StaticWebRoutable,\
 net.floodlightcontroller.loadbalancer.LoadBalancer,\
 net.floodlightcontroller.firewall.Firewall,\
-net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl
+net.floodlightcontroller.devicemanager.internal.DeviceManagerImpl,\
+net.floodlightcontroller.accesscontrollist.ACL
 org.sdnplatform.sync.internal.SyncManager.authScheme=CHALLENGE_RESPONSE
 org.sdnplatform.sync.internal.SyncManager.keyStorePath=/etc/floodlight/auth_credentials.jceks
 org.sdnplatform.sync.internal.SyncManager.dbPath=/var/lib/floodlight/
diff --git a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLRuleResourceTest.java b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLRuleResourceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c8ec814d63ba977e5fca66b71c65c072c1e4ad2
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLRuleResourceTest.java
@@ -0,0 +1,185 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import net.floodlightcontroller.accesscontrollist.ACL;
+import net.floodlightcontroller.accesscontrollist.ACLRule;
+import net.floodlightcontroller.accesscontrollist.IACLService;
+import net.floodlightcontroller.accesscontrollist.util.IPAddressUtil;
+import net.floodlightcontroller.accesscontrollist.web.ACLRuleResource;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.test.FloodlightTestCase;
+
+import org.junit.Test;
+import org.restlet.Context;
+
+public class ACLRuleResourceTest extends FloodlightTestCase {
+	
+	@Test
+	public void testJsonToRule(){
+		
+		// check that all the key-value pairs are specified
+		String json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/32\",\"dst-ip\": \"10.0.0.2/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+		try {
+			ACLRule rule = ACLRuleResource.jsonToRule(json);
+			assertEquals(rule.getNw_src(),"10.0.0.1/32");
+			assertEquals(rule.getNw_dst(),"10.0.0.2/32");
+			int[] cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+			assertEquals(rule.getNw_src_prefix(), cidr[0]);
+			assertEquals(rule.getNw_src_maskbits(), cidr[1]);
+			cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+			assertEquals(rule.getNw_dst_prefix(), cidr[0]);
+			assertEquals(rule.getNw_dst_maskbits(), cidr[1]);
+			assertEquals(rule.getNw_proto(),6);
+			assertEquals(rule.getTp_dst(), 80);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		
+		// check that nw_proto is not specified correctly
+		json = "{\"nw-prot\":\"TCP\",\"src-ip\":\"10.0.0.1/32\",\"dst-ip\": \"10.0.0.2/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+		try {
+			ACLRule rule = ACLRuleResource.jsonToRule(json);
+			assertEquals(rule.getNw_src(),"10.0.0.1/32");
+			assertEquals(rule.getNw_dst(),"10.0.0.2/32");
+			int[] cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+			assertEquals(rule.getNw_src_prefix(), cidr[0]);
+			assertEquals(rule.getNw_src_maskbits(), cidr[1]);
+			cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+			assertEquals(rule.getNw_dst_prefix(), cidr[0]);
+			assertEquals(rule.getNw_dst_maskbits(), cidr[1]);
+			assertEquals(rule.getNw_proto(),0);
+			assertEquals(rule.getTp_dst(), 0);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		
+	}
+	
+	@Test
+	public void testStore(){
+		
+		ACL s = new ACL();
+        FloodlightModuleContext fmc = new FloodlightModuleContext();
+		try {
+			s.init(fmc);
+		} catch (FloodlightModuleException e) {
+			e.printStackTrace();
+		}
+		
+		ACLRuleResource r = new ACLRuleResource();
+		Context ctx = new Context();
+		r.init(ctx, null, null);
+		r.getContext().getAttributes().putIfAbsent(IACLService.class.getCanonicalName(), s);
+
+		// input a valid JSON string that adds a new ACL rule
+        String json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/32\",\"dst-ip\": \"10.0.0.2/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Success! New rule added." + "\"}");
+        
+        // input a valid JSON string that matches an existing rule
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/32\",\"dst-ip\": \"10.0.0.2/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! The new ACL rule matches an existing rule." + "\"}");
+        
+        // input a valid JSON string that adds a new ACL rule
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/8\",\"dst-ip\": \"10.0.0.2/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Success! New rule added." + "\"}");
+        
+        // input a valid JSON string that matches an existing rule
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.2/32\",\"dst-ip\": \"10.0.0.2/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! The new ACL rule matches an existing rule." + "\"}");
+
+        // input a invalid JSON string that contains neither nw_src and nw_dst
+        json = "{\"nw-proto\":\"TCP\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! Either nw_src or nw_dst must be specified." + "\"}");
+        
+        // input a invalid JSON string that doesn't contain CIDR mask bits
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! " + "CIDR mask bits must be specified." + "\"}");
+        
+        // input a invalid JSON string that contains a invalid IP address
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.256/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! Octet values in specified IPv4 address must be 0 <= value <= 255" + "\"}");
+        
+        // input a invalid JSON string that contains a invalid IP address
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.01/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! Specified IPv4 address mustcontain 4 sets of numerical digits separated by periods" + "\"}");
+        
+        // input a invalid JSON string that contains a invalid CIDR mask bits
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/a\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! CIDR mask bits must be specified as a number(0 ~ 32)." + "\"}");
+        
+        // input a invalid JSON string that contains a invalid CIDR mask bits
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/33\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! CIDR mask bits must be 0 <= value <= 32." + "\"}");
+        
+        // input a invalid JSON string that contains a invalid nw-proto value
+        json = "{\"nw-proto\":\"ARP\",\"src-ip\":\"10.0.0.1/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! nw-proto must be specified as (TCP || UDP || ICMP)." + "\"}");
+        
+        // input a invalid JSON string that contains a invalid tp-dst value
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/32\",\"tp-dst\":\"a\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! tp-dst must be specified as a number." + "\"}");
+        
+        // input a invalid JSON string that contains a invalid action value
+        json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/32\",\"tp-dst\":\"80\",\"action\":\"PERMIT\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Failed! action must be specidied as (allow || deny)." + "\"}");
+        
+	}
+	
+	@Test
+	public void testRemove(){
+		ACL s = new ACL();
+        FloodlightModuleContext fmc = new FloodlightModuleContext();
+		try {
+			s.init(fmc);
+		} catch (FloodlightModuleException e) {
+			e.printStackTrace();
+		}
+		
+		ACLRuleResource r = new ACLRuleResource();
+		Context ctx = new Context();
+		r.init(ctx, null, null);
+		r.getContext().getAttributes().putIfAbsent(IACLService.class.getCanonicalName(), s);
+
+		// input a valid JSON string
+        String json = "{\"nw-proto\":\"TCP\",\"src-ip\":\"10.0.0.1/32\",\"dst-ip\": \"10.0.0.2/32\",\"tp-dst\":\"80\",\"action\":\"ALLOW\"}";
+        assertEquals(r.store(json),"{\"status\" : \"" + "Success! New rule added." + "\"}");
+        
+        // input a invalid JSON string that contains a invalid ruleid value
+        json = "{\"ruleid\":\"a\"}";
+        assertEquals(r.remove(json),"{\"status\" : \"" + "Failed! ruleid must be specified as a number." + "\"}");
+        
+        // input a invalid JSON string that contains a non-existing ruleid value
+        json = "{\"ruleid\":\"2\"}";
+        assertEquals(r.remove(json),"{\"status\" : \"" + "Failed! a rule with this ID doesn't exist." + "\"}");
+        
+        // input a valid JSON string that removes an existing ACL rule
+        json = "{\"ruleid\":\"1\"}";
+        assertEquals(r.remove(json),"{\"status\" : \"" + "Success! Rule deleted" + "\"}");
+        
+	}
+	
+}
diff --git a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLRuleTest.java b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLRuleTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..58a2ff03a76910c03074581d54a7665e4ff388ac
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLRuleTest.java
@@ -0,0 +1,182 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import static org.junit.Assert.*;
+import net.floodlightcontroller.accesscontrollist.ACLRule;
+import net.floodlightcontroller.accesscontrollist.util.IPAddressUtil;
+import net.floodlightcontroller.test.FloodlightTestCase;
+
+import org.junit.Test;
+
+public class ACLRuleTest extends FloodlightTestCase{
+	
+	@Test
+	public void testMatch(){
+		
+		int[] cidr = new int[2];
+		ACLRule rule1, rule2;
+		
+		// rule1 & rule2 are the same
+		rule1 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule1.setTp_dst(80);
+		rule2 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule2.setNw_src_prefix(cidr[0]);
+		rule2.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		rule2.setNw_proto(6);
+		rule2.setTp_dst(80);
+		assertTrue(rule1.match(rule2));
+		
+		// rule1 & rule2 are different in nw_proto
+		rule1 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule2 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule2.setNw_src_prefix(cidr[0]);
+		rule2.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		rule2.setNw_proto(11);
+		assertFalse(rule1.match(rule2));
+		
+		// rule1's nw_src is a subnet of rule2's nw_src
+		rule1 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(1);
+		rule2 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/8");
+		rule2.setNw_src_prefix(cidr[0]);
+		rule2.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		rule2.setNw_proto(1);
+		assertTrue(rule1.match(rule2));
+		
+		// rule1's nw_dst is a subnet of rule2's nw_dst
+		rule1 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(1);
+		rule2 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule2.setNw_src_prefix(cidr[0]);
+		rule2.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/8");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		rule2.setNw_proto(1);
+		assertTrue(rule1.match(rule2));
+		
+		// rule1's nw_src is specified while rule2's is not
+		rule1 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule2 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		assertTrue(rule1.match(rule2));
+		
+		// rule1's nw_dst is specified while rule2's is not
+		rule1 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule2 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule2.setNw_src_prefix(cidr[0]);
+		rule2.setNw_src_maskbits(cidr[1]);
+		assertTrue(rule1.match(rule2));
+		
+		// rule1's nw_proto is specified while rule2's is not
+		rule1 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule2 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule2.setNw_src_prefix(cidr[0]);
+		rule2.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		assertTrue(rule1.match(rule2));
+		
+		// rule1's tp_dst is specified while rule2's is not
+		rule1 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule1.setTp_dst(80);
+		rule2 = new ACLRule();
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule2.setNw_src_prefix(cidr[0]);
+		rule2.setNw_src_maskbits(cidr[1]);
+		cidr = IPAddressUtil.parseCIDR("10.0.0.2/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		rule2.setNw_proto(6);
+		assertTrue(rule1.match(rule2));
+	}
+}
diff --git a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..230f1e93436e498cfafd4789eb0811c80fce1abc
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java
@@ -0,0 +1,685 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+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 static org.junit.Assert.assertEquals;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import net.floodlightcontroller.accesscontrollist.ACL;
+import net.floodlightcontroller.accesscontrollist.ACLRule;
+import net.floodlightcontroller.accesscontrollist.IACLService;
+import net.floodlightcontroller.accesscontrollist.ACLRule.Action;
+import net.floodlightcontroller.accesscontrollist.util.IPAddressUtil;
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.internal.IOFSwitchService;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.test.MockThreadPoolService;
+import net.floodlightcontroller.debugcounter.IDebugCounterService;
+import net.floodlightcontroller.debugcounter.MockDebugCounterService;
+import net.floodlightcontroller.debugevent.IDebugEventService;
+import net.floodlightcontroller.debugevent.MockDebugEventService;
+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.packet.Ethernet;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.restserver.RestApiServer;
+import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.storage.IResultSet;
+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 org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFPort;
+
+public class ACLTest extends FloodlightTestCase {
+
+	protected FloodlightContext cntx;
+	protected IOFSwitch sw;
+	
+	private MockDebugEventService debugEventService; // dependency for device manager
+	private DefaultEntityClassifier entityClassifier; // dependency for device manager
+	private MockThreadPoolService tps; // dependency for device manager
+	private ITopologyService topology; // dependency for device manager
+	private MockDeviceManager deviceManager;
+
+	private MockDebugCounterService debugCounterService;
+	private MemoryStorageSource storageService;
+
+	private RestApiServer restApi;
+	private ACL acl;
+
+	public static String TestSwitch1DPID = "00:00:00:00:00:00:00:01";
+
+	@Override
+	@Before
+	public void setUp() throws Exception {
+
+		super.setUp();
+		cntx = new FloodlightContext();
+		mockFloodlightProvider = getMockFloodlightProvider();
+		mockSwitchManager = getMockSwitchService();
+
+		debugEventService = new MockDebugEventService();
+		entityClassifier = new DefaultEntityClassifier();
+		tps = new MockThreadPoolService();
+		deviceManager = new MockDeviceManager();
+		topology = createMock(ITopologyService.class);
+		debugCounterService = new MockDebugCounterService();
+		storageService = new MemoryStorageSource();
+		restApi = new RestApiServer();
+		acl = new ACL();
+
+		// Mock switches
+		DatapathId dpid = DatapathId.of(TestSwitch1DPID);
+		sw = EasyMock.createNiceMock(IOFSwitch.class);
+		expect(sw.getId()).andReturn(dpid).anyTimes();
+		expect(sw.getOFFactory()).andReturn(
+				OFFactories.getFactory(OFVersion.OF_13)).anyTimes();
+		replay(sw);
+		// Load the switch map
+		Map<DatapathId, IOFSwitch> switches = new HashMap<DatapathId, IOFSwitch>();
+		switches.put(dpid, sw);
+		mockSwitchManager.setSwitches(switches);
+
+		FloodlightModuleContext fmc = new FloodlightModuleContext();
+		fmc.addService(IFloodlightProviderService.class, mockFloodlightProvider);
+		fmc.addService(IOFSwitchService.class, mockSwitchManager);
+		fmc.addService(IDebugCounterService.class, debugCounterService);
+		fmc.addService(IStorageSourceService.class, storageService);
+		fmc.addService(IDebugEventService.class, debugEventService);
+		fmc.addService(IEntityClassifierService.class, entityClassifier);
+		fmc.addService(IThreadPoolService.class, tps);
+		fmc.addService(IDeviceService.class, deviceManager);
+		fmc.addService(ITopologyService.class, topology);
+		fmc.addService(IRestApiService.class, restApi);
+		fmc.addService(IACLService.class, acl);
+
+		topology.addListener(deviceManager);
+		expectLastCall().times(1);
+		replay(topology);
+		
+		debugCounterService.init(fmc);
+		entityClassifier.init(fmc);
+		tps.init(fmc);
+		deviceManager.init(fmc);
+		storageService.init(fmc);
+		restApi.init(fmc);
+		acl.init(fmc);
+		
+		debugCounterService.startUp(fmc);
+		deviceManager.startUp(fmc);
+		entityClassifier.startUp(fmc);
+		tps.startUp(fmc);
+		storageService.startUp(fmc);
+		acl.startUp(fmc);
+		verify(topology);
+
+		storageService.createTable(StaticFlowEntryPusher.TABLE_NAME, null);
+		storageService.setTablePrimaryKeyName(StaticFlowEntryPusher.TABLE_NAME,
+				StaticFlowEntryPusher.COLUMN_NAME);
+
+	}
+
+	@Test
+	public void testAddRule() {
+		
+		reset(topology);
+//		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
+//		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(1))).andReturn(true).anyTimes();
+		replay(topology);
+
+		int[] cidr = new int[2];
+		ACLRule rule1, rule2, rule3, rule4;
+		IResultSet resultSet;
+		Iterator<IResultSet> it;
+		Map<String, Object> row;
+
+		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
+				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		
+		// a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")),
+				null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1);
+		
+		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
+		rule1 = new ACLRule();
+		rule1.setNw_src("10.0.0.0/28");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.0/28");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		rule1.setNw_dst("10.0.0.254/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.254/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule1.setTp_dst(80);
+		rule1.setAction(Action.DENY);
+
+		assertEquals(acl.addRule(rule1), true);
+		assertEquals(acl.getRules().size(), 1);
+
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:01");
+			assertEquals(row.get("priority").toString(),"30000");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(),"10.0.0.0/28");
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.254/32");
+			assertEquals(row.get("ip_proto").toString(),"6");
+			assertEquals(row.get("tp_dst").toString(),"80");
+			assertEquals(row.get("actions"), null);
+		}
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:02");
+			assertEquals(row.get("priority").toString(),"30000");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(),"10.0.0.0/28");
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.254/32");
+			assertEquals(row.get("ip_proto").toString(),"6");
+			assertEquals(row.get("tp_dst").toString(),"80");
+			assertEquals(row.get("actions"), null);
+		}
+		
+		// rule2 matches rule1
+		rule2 = new ACLRule();
+		rule2.setNw_src("10.0.0.1/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule2.setNw_src_prefix(cidr[0]);
+		rule2.setNw_src_maskbits(cidr[1]);
+		rule2.setNw_dst("10.0.0.254/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.254/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		rule2.setNw_proto(6);
+		rule2.setTp_dst(80);
+		rule2.setAction(Action.DENY);
+
+		// there will be no extra flow entry
+		assertEquals(acl.addRule(rule1), false);
+		assertEquals(acl.getRules().size(), 1);
+		
+		// rule3 indicates that no ICMP packets can reach host[10.0.0.3/32]
+		rule3 = new ACLRule();
+		rule3.setNw_dst("10.0.0.3/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.3/32");
+		rule3.setNw_dst_prefix(cidr[0]);
+		rule3.setNw_dst_maskbits(cidr[1]);
+		rule3.setNw_proto(1);
+		rule3.setAction(Action.DENY);
+
+		assertEquals(acl.addRule(rule3), true);
+		assertEquals(acl.getRules().size(), 2);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:02");
+			assertEquals(row.get("priority").toString(),"29999");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src"), null);
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.3/32");
+			assertEquals(row.get("ip_proto").toString(),"1");
+			assertEquals(row.get("tp_dst"), null);
+			assertEquals(row.get("actions"), null);
+		}
+		
+		// rule4 indicates that host(10.0.0.1/32) can access host(10.0.0.3/32)
+		rule4 = new ACLRule();
+		rule4.setNw_src("10.0.0.1/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		rule4.setNw_src_prefix(cidr[0]);
+		rule4.setNw_src_maskbits(cidr[1]);
+		rule4.setNw_dst("10.0.0.3/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.3/32");
+		rule4.setNw_dst_prefix(cidr[0]);
+		rule4.setNw_dst_maskbits(cidr[1]);
+		rule4.setAction(Action.ALLOW);
+
+		assertEquals(acl.addRule(rule4), true);
+		assertEquals(acl.getRules().size(), 3);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_3_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:01");
+			assertEquals(row.get("priority").toString(),"29999");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(), "10.0.0.1/32");
+			assertEquals(row.get("ipv4_dst").toString(), "10.0.0.3/32");
+			assertEquals(row.get("ip_proto"), null);
+			assertEquals(row.get("tp_dst"), null);
+			assertEquals(row.get("actions"), "output=controller");
+		}
+		
+	}
+	
+	@Test
+	public void testDeviceAdded() {
+		
+		reset(topology);
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(2))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(2))).andReturn(true).anyTimes();
+		replay(topology);
+		
+		int[] cidr = new int[2];
+		ACLRule rule1, rule2;
+		IResultSet resultSet;
+		Iterator<IResultSet> it;
+		Map<String, Object> row;
+
+		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
+		rule1 = new ACLRule();
+		rule1.setNw_src("10.0.0.0/28");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.0/28");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		rule1.setNw_dst("10.0.0.254/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.254/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule1.setTp_dst(80);
+		rule1.setAction(Action.DENY);
+
+		assertEquals(acl.addRule(rule1), true);
+		assertEquals(acl.getRules().size(), 1);
+		
+		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
+				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:01");
+			assertEquals(row.get("priority").toString(),"30000");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(),"10.0.0.0/28");
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.254/32");
+			assertEquals(row.get("ip_proto").toString(),"6");
+			assertEquals(row.get("tp_dst").toString(),"80");
+			assertEquals(row.get("actions"), null);
+		}
+		
+		// a new AP[dpid:00:00:00:00:00:00:00:01 port:2 ip:10.0.0.2] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:02")),
+				null, IPv4.toIPv4Address("10.0.0.2"), 1L, 2);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		int count = 0;
+		while(it.hasNext()){
+			row = it.next().getRow();
+			count++;
+		}
+		// there is no extra flow entry added
+		assertEquals(count, 1);
+		
+		// rule2 indicates that no ICMP packets can reach host[10.0.0.3/32]
+		rule2 = new ACLRule();
+		rule2.setNw_dst("10.0.0.3/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.3/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		rule2.setNw_proto(1);
+		rule2.setAction(Action.DENY);
+
+		assertEquals(acl.addRule(rule2), true);
+		assertEquals(acl.getRules().size(), 2);
+
+		// a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")),
+				null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:02");
+			assertEquals(row.get("priority").toString(),"29999");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src"), null);
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.3/32");
+			assertEquals(row.get("ip_proto").toString(),"1");
+			assertEquals(row.get("tp_dst"), null);
+			assertEquals(row.get("actions"), null);
+		}
+	}
+	
+	@Test
+	public void testDeviceIPV4AddrChanged() {
+		
+		reset(topology);
+		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(1))).andReturn(true).anyTimes();
+		replay(topology);
+
+		int[] cidr = new int[2];
+		ACLRule rule1;
+		IResultSet resultSet;
+		Iterator<IResultSet> it;
+		Map<String, Object> row;
+	
+		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
+				null, null, 1L, 1);
+		
+		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
+		rule1 = new ACLRule();
+		rule1.setNw_src("10.0.0.0/28");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.0/28");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		rule1.setNw_dst("10.0.0.254/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.254/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule1.setTp_dst(80);
+		rule1.setAction(Action.DENY);
+
+		assertEquals(acl.addRule(rule1), true);
+		assertEquals(acl.getRules().size(), 1);
+
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		assertEquals(it.hasNext(), false);
+		
+		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
+				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:01");
+			assertEquals(row.get("priority").toString(),"30000");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(),"10.0.0.0/28");
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.254/32");
+			assertEquals(row.get("ip_proto").toString(),"6");
+			assertEquals(row.get("tp_dst").toString(),"80");
+			assertEquals(row.get("actions"), null);
+		}
+	}
+	
+	@Test
+	public void testDeleteRule(){
+		reset(topology);
+//		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
+//		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(1))).andReturn(true).anyTimes();
+		replay(topology);
+
+		int[] cidr = new int[2];
+		ACLRule rule1;
+		IResultSet resultSet;
+		Iterator<IResultSet> it;
+		Map<String, Object> row;
+
+		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
+				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		
+		// a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")),
+				null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1);
+		
+		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
+		rule1 = new ACLRule();
+		rule1.setNw_src("10.0.0.0/28");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.0/28");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		rule1.setNw_dst("10.0.0.254/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.254/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule1.setTp_dst(80);
+		rule1.setAction(Action.DENY);
+
+		assertEquals(acl.addRule(rule1), true);
+		assertEquals(acl.getRules().size(), 1);
+
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:01");
+			assertEquals(row.get("priority").toString(),"30000");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(),"10.0.0.0/28");
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.254/32");
+			assertEquals(row.get("ip_proto").toString(),"6");
+			assertEquals(row.get("tp_dst").toString(),"80");
+			assertEquals(row.get("actions"), null);
+		}
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:02");
+			assertEquals(row.get("priority").toString(),"30000");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(),"10.0.0.0/28");
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.254/32");
+			assertEquals(row.get("ip_proto").toString(),"6");
+			assertEquals(row.get("tp_dst").toString(),"80");
+			assertEquals(row.get("actions"), null);
+		}
+		
+		// remove rule1 and all relevant flow entries will be removed
+		acl.removeRule(1);
+		assertEquals(acl.getRules().size(),0);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		assertEquals(it.hasNext(), false);
+
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		assertEquals(it.hasNext(), false);
+		
+	}
+	
+	@Test
+	public void testDeleteAllRules(){
+		reset(topology);
+//		expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
+//		expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
+		expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(1))).andReturn(true).anyTimes();
+		replay(topology);
+
+		int[] cidr = new int[2];
+		ACLRule rule1, rule2;
+		IResultSet resultSet;
+		Iterator<IResultSet> it;
+		Map<String, Object> row;
+
+		// a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")),
+				null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1);
+		
+		// a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears
+		deviceManager.learnEntity(
+				Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")),
+				null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1);
+		
+		// rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32)
+		rule1 = new ACLRule();
+		rule1.setNw_src("10.0.0.0/28");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.0/28");
+		rule1.setNw_src_prefix(cidr[0]);
+		rule1.setNw_src_maskbits(cidr[1]);
+		rule1.setNw_dst("10.0.0.254/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.254/32");
+		rule1.setNw_dst_prefix(cidr[0]);
+		rule1.setNw_dst_maskbits(cidr[1]);
+		rule1.setNw_proto(6);
+		rule1.setTp_dst(80);
+		rule1.setAction(Action.DENY);
+
+		assertEquals(acl.addRule(rule1), true);
+		assertEquals(acl.getRules().size(), 1);
+
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:01");
+			assertEquals(row.get("priority").toString(),"30000");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(),"10.0.0.0/28");
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.254/32");
+			assertEquals(row.get("ip_proto").toString(),"6");
+			assertEquals(row.get("tp_dst").toString(),"80");
+			assertEquals(row.get("actions"), null);
+		}
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:02");
+			assertEquals(row.get("priority").toString(),"30000");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src").toString(),"10.0.0.0/28");
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.254/32");
+			assertEquals(row.get("ip_proto").toString(),"6");
+			assertEquals(row.get("tp_dst").toString(),"80");
+			assertEquals(row.get("actions"), null);
+		}
+		
+		// rule3 indicates that no ICMP packets can reach host[10.0.0.3/32]
+		rule2 = new ACLRule();
+		rule2.setNw_dst("10.0.0.3/32");
+		cidr = IPAddressUtil.parseCIDR("10.0.0.3/32");
+		rule2.setNw_dst_prefix(cidr[0]);
+		rule2.setNw_dst_maskbits(cidr[1]);
+		rule2.setNw_proto(1);
+		rule2.setAction(Action.DENY);
+
+		assertEquals(acl.addRule(rule2), true);
+		assertEquals(acl.getRules().size(), 2);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		while(it.hasNext()){
+			row = it.next().getRow();
+			assertEquals(row.get("switch").toString(),"00:00:00:00:00:00:00:02");
+			assertEquals(row.get("priority").toString(),"29999");
+			assertEquals(row.get("eth_type").toString(),"2048");
+			assertEquals(row.get("ipv4_src"), null);
+			assertEquals(row.get("ipv4_dst").toString(),"10.0.0.3/32");
+			assertEquals(row.get("ip_proto").toString(),"1");
+			assertEquals(row.get("tp_dst"), null);
+			assertEquals(row.get("actions"), null);
+		}
+		
+		// remove all rules and all relevant flow entries will be removed
+		acl.removeAllRules();
+		assertEquals(acl.getRules().size(),0);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01");
+		it = resultSet.iterator();
+		assertEquals(it.hasNext(), false);
+
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		assertEquals(it.hasNext(), false);
+		
+		resultSet = storageService.getRow(
+				StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02");
+		it = resultSet.iterator();
+		assertEquals(it.hasNext(), false);
+		
+	}
+}
diff --git a/src/test/java/net/floodlightcontroller/accesscontrollist/APManagerTest.java b/src/test/java/net/floodlightcontroller/accesscontrollist/APManagerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..06378e72d9544adbc2812f8886d97822f33d3e52
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/accesscontrollist/APManagerTest.java
@@ -0,0 +1,74 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import static org.junit.Assert.*;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import net.floodlightcontroller.accesscontrollist.ap.AP;
+import net.floodlightcontroller.accesscontrollist.ap.APManager;
+import net.floodlightcontroller.accesscontrollist.util.IPAddressUtil;
+import net.floodlightcontroller.test.FloodlightTestCase;
+
+import org.junit.Test;
+
+public class APManagerTest extends FloodlightTestCase {
+	
+	@Test
+	public void testGetDpidSet(){
+		
+		AP ap1 = new AP("10.0.0.1","00:00:00:00:00:00:00:01");
+		AP ap2 = new AP("10.0.0.2","00:00:00:00:00:00:00:02");
+		AP ap3 = new AP("10.0.0.3","00:00:00:00:00:00:00:03");
+		
+		APManager apManager = new APManager();
+		apManager.addAP(ap1);
+		apManager.addAP(ap2);
+		apManager.addAP(ap3);
+		
+		int cidr[];
+		
+		// test CIDR IP with suffix that equals "/32"
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/32");
+		Set<String> resultSet = apManager.getDpidSet(cidr[0],cidr[1]);
+		Set<String> expectedSet = new HashSet<String>();
+		expectedSet.add("00:00:00:00:00:00:00:01");
+		assertEquals(resultSet, expectedSet);
+		
+		// test CIDR IP with suffix that does not equal "/32"
+		cidr = IPAddressUtil.parseCIDR("10.0.0.1/30");
+		resultSet = apManager.getDpidSet(cidr[0],cidr[1]);
+		expectedSet = new HashSet<String>();
+		expectedSet.add("00:00:00:00:00:00:00:01");
+		expectedSet.add("00:00:00:00:00:00:00:02");
+		expectedSet.add("00:00:00:00:00:00:00:03");
+		assertEquals(resultSet, expectedSet);
+		
+		// test CIDR IP does not exist in the network
+		cidr = IPAddressUtil.parseCIDR("10.0.0.4/32");
+		resultSet = apManager.getDpidSet(cidr[0],cidr[1]);
+		expectedSet = new HashSet<String>();
+		assertEquals(resultSet, expectedSet);
+		
+	}
+}
diff --git a/src/test/java/net/floodlightcontroller/accesscontrollist/IPAddressUtilTest.java b/src/test/java/net/floodlightcontroller/accesscontrollist/IPAddressUtilTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d6b477c2e40f53e10e4c6998b8d13b4b0409aac
--- /dev/null
+++ b/src/test/java/net/floodlightcontroller/accesscontrollist/IPAddressUtilTest.java
@@ -0,0 +1,60 @@
+/**
+ *    Copyright 2015, Big Switch Networks, Inc.
+ *    Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China 
+ *    Advisers: Keqiu Li and Heng Qi 
+ *    This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) 
+ *    and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute.
+ *    
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may 
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *    
+ *         http://www.apache.org/licenses/LICENSE-2.0 
+ *    
+ *    Unless required by applicable law or agreed to in writing, software 
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.accesscontrollist;
+
+import static org.junit.Assert.*;
+import net.floodlightcontroller.accesscontrollist.util.IPAddressUtil;
+import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.test.FloodlightTestCase;
+
+import org.junit.Test;
+
+public class IPAddressUtilTest extends FloodlightTestCase {
+	
+	@Test
+	public void testParseCIDR(){
+		
+		String cidr = "10.0.0.1/32";
+		int[] resultArray = IPAddressUtil.parseCIDR(cidr);
+		assertEquals(resultArray[0],IPv4.toIPv4Address("10.0.0.1"));
+		assertEquals(resultArray[1],32);
+	}
+	
+	@Test
+	public void testContainIP(){
+		
+		int[] cidr = IPAddressUtil.parseCIDR("10.0.0.0/8");
+		int ip = IPv4.toIPv4Address("10.0.0.1");
+		assertTrue(IPAddressUtil.containIP(cidr[0], cidr[1], ip));
+	}
+	
+	@Test
+	public void testIsSubnet(){
+		
+		assertFalse(IPAddressUtil.isSubnet("10.0.0.1/32", "10.0.0.2/32"));
+		assertTrue(IPAddressUtil.isSubnet("10.0.0.1/8", "10.0.0.2/8"));
+		assertTrue(IPAddressUtil.isSubnet("10.0.0.1/32", "10.0.0.2/8"));
+		assertFalse(IPAddressUtil.isSubnet("10.0.0.1/8", "10.0.0.2/32"));
+		assertTrue(IPAddressUtil.isSubnet("10.0.0.1/8", null));
+		assertFalse(IPAddressUtil.isSubnet(null, "10.0.0.2/32"));
+	}
+
+}