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