diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java index 1c87956f5332ac93e5ee3220d6ae20cde57ac1f8..b3c2a88f99bdb9836487cc53b6206fe8f100613a 100644 --- a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java +++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java @@ -24,10 +24,10 @@ 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 java.util.TreeMap; import net.floodlightcontroller.accesscontrollist.ACLRule.Action; import net.floodlightcontroller.accesscontrollist.ap.AP; @@ -54,226 +54,254 @@ 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 APManager apManager; private int lastRuleId = 1; // rule id counter - private List<ACLRule> ruleSet; + private Map<Integer, ACLRule> aclRules; private Map<String, Integer> dpid2FlowPriority; private Map<Integer, Set<String>> ruleId2Dpid; private Map<Integer, Set<String>> ruleId2FlowName; + private Map<Integer, List<Integer>> deny2Allow; + + private final int DEFAULT_PRIORITY = 30000; + + /** + * Checks if an existing ACL rule already works in a given switch. + */ + private boolean checkIfRuleWorksInSwitch(int ruleId, String dpid) { + return ruleId2Dpid.containsKey(ruleId) + && ruleId2Dpid.get(ruleId).contains(dpid); + } + + /** + * Adds a new mapping from ACL rule to ACL flow. + */ + private void addRuleToFlowMapping(int ruleId, String flowName) { + if (!ruleId2FlowName.containsKey(ruleId)) { + ruleId2FlowName.put(ruleId, new HashSet<String>()); + } + ruleId2FlowName.get(ruleId).add(flowName); + } + + /** + * Adds a new mapping from ACL rule to switch. + */ + private void addRuleToSwitchMapping(int ruleId, String dpid) { + if (!ruleId2Dpid.containsKey(ruleId)) { + ruleId2Dpid.put(ruleId, new HashSet<String>()); + } + ruleId2Dpid.get(ruleId).add(dpid); + } /** - * used by REST API to query ACL rules + * Gets the current priority for new ACL flow by device id. */ + private int getPriorityBySwitch(String dpid) { + if (!dpid2FlowPriority.containsKey(dpid)) { + dpid2FlowPriority.put(dpid, DEFAULT_PRIORITY - 1); + return DEFAULT_PRIORITY; + } else { + int priority = dpid2FlowPriority.get(dpid); + dpid2FlowPriority.put(dpid, priority - 1); + return priority; + } + } + @Override public List<ACLRule> getRules() { - return this.ruleSet; + return new ArrayList<ACLRule>(aclRules.values()); } /** - * check if the new rule matches an existing rule + * Checks if the new ACL rule matches an existing rule. If existing allowing + * rules matches the new denying rule, store the mappings. + * + * @return true if the new ACL rule matches an existing rule, false + * otherwise */ 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); + List<Integer> allowRuleList = new ArrayList<>(); + for (ACLRule existingRule : getRules()) { + if (newRule.match(existingRule)) { return true; } + + if (existingRule.getAction() == Action.ALLOW + && newRule.getAction() == Action.DENY) { + if (existingRule.match(newRule)) { + allowRuleList.add(existingRule.getId()); + } + } } + deny2Allow.put(newRule.getId(), allowRuleList); 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)){ + rule.setId(lastRuleId++); + if (checkRuleMatch(rule)) { + lastRuleId--; return false; } - - rule.setId(lastRuleId++); - this.ruleSet.add(rule); - logger.info("No.{} ACL rule added.", rule.getId()); - enforceAddedRule(rule); + aclRules.put(rule.getId(), rule); + logger.info("ACL rule(id:{}) is added.", rule.getId()); + if (rule.getAction() != Action.ALLOW) { + 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); + public void removeRule(int ruleId) { + aclRules.remove(ruleId); + logger.info("ACL rule(id:{}) is 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.debug("No.{} ACL rule removed.", ruleId); + this.aclRules = new TreeMap<>(); + this.dpid2FlowPriority = new HashMap<>(); + this.ruleId2Dpid = new HashMap<>(); + this.deny2Allow = new HashMap<>(); + + for (Set<String> flowNameSet : ruleId2FlowName.values()) { for (String flowName : flowNameSet) { - removeFlow(flowName); - logger.debug("ACL flow {} removed.", flowName); + storageSource.deleteRowAsync("controller_staticflowtableentry", + flowName); + logger.debug("ACL flow(id:{}) is removed.", flowName); } } - this.ruleId2FlowName = new HashMap<Integer, Set<String>>(); + this.ruleId2FlowName = new HashMap<>(); } /** - * enforce new added rule + * Enforces denying ACL rule by ACL flow. */ - private void enforceAddedRule(ACLRule rule) { - + private void enforceAddedRule(ACLRule denyRule) { Set<String> dpidSet; - if (rule.getNw_src() != null) { - dpidSet = apManager.getDpidSet(rule.getNw_src_prefix(),rule.getNw_src_maskbits()); + if (denyRule.getNw_src() != null) { + dpidSet = apManager.getDpidSet(denyRule.getNw_src_prefix(), + denyRule.getNw_src_maskbits()); } else { - dpidSet = apManager.getDpidSet(rule.getNw_dst_prefix(),rule.getNw_dst_maskbits()); + dpidSet = apManager.getDpidSet(denyRule.getNw_dst_prefix(), + denyRule.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); + for (String dpid : dpidSet) { + String flowName; + List<Integer> allowRuleList = deny2Allow.get(denyRule.getId()); + for (int allowRuleId : allowRuleList) { + flowName = "ACLRule_" + allowRuleId + "_" + dpid; + generateFlow(aclRules.get(allowRuleId), dpid, flowName); + } + flowName = "ACLRule_" + denyRule.getId() + "_" + dpid; + generateFlow(denyRule, dpid, flowName); } - ruleId2FlowName.put(rule.getId(), nameSet); - ruleId2Dpid.put(rule.getId(), dpidSet); } /** - * enforce removed rule + * Enforces removing an existing ACL 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.debug("ACL flow " + name + " removed."); + if (ruleId2FlowName.containsKey(ruleId)) { + for (String flowName : ruleId2FlowName.get(ruleId)) { + storageSource.deleteRowAsync("controller_staticflowtableentry", + flowName); + logger.debug("ACL flow(id:{}) is removed.", flowName); + } + ruleId2FlowName.remove(ruleId); } - + ruleId2Dpid.remove(ruleId); + deny2Allow.remove(ruleId); } - + /** - * generate and push ACL flow entry + * Generates ACL flow rule according to ACL rule + * and installs it into switch. */ 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 == null || checkIfRuleWorksInSwitch(rule.getId(), dpid)) { + return; } + int priority = getPriorityBySwitch(dpid); if (rule.getNw_src() != null) { - - HashMap<String,Object> flow = new HashMap<String,Object>(); - + + 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_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_PRIORITY, + Integer.toString(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())); + flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, + Integer.toString(rule.getNw_proto())); } if (rule.getAction() == Action.ALLOW) { - flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller"); + flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, + "output=controller"); } if (rule.getTp_dst() != 0) { - flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, Integer.toString(rule.getTp_dst())); + flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, + Integer.toString(rule.getTp_dst())); } - - storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow); - + + storageSource + .insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow); + } else { - - HashMap<String,Object> flow = new HashMap<String,Object>(); - + + 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_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_PRIORITY, + Integer.toString(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())); + flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, + Integer.toString(rule.getNw_proto())); } if (rule.getAction() == Action.ALLOW) { - flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller"); + flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, + "output=controller"); } if (rule.getTp_dst() != 0) { - flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, Integer.toString(rule.getTp_dst())); + 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); - } + storageSource + .insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow); + } + addRuleToSwitchMapping(rule.getId(), dpid); + addRuleToFlowMapping(rule.getId(), flowName); + logger.debug("ACL flow(id:{}) is added in {}.", flowName, dpid); + } @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { @@ -306,11 +334,12 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener { logger = LoggerFactory.getLogger(ACL.class); storageSource = context.getServiceImpl(IStorageSourceService.class); - ruleSet = new ArrayList<ACLRule>(); + aclRules = new TreeMap<>(); apManager = new APManager(); - ruleId2FlowName = new HashMap<Integer, Set<String>>(); - ruleId2Dpid = new HashMap<Integer, Set<String>>(); - dpid2FlowPriority = new HashMap<String, Integer>(); + ruleId2FlowName = new HashMap<>(); + ruleId2Dpid = new HashMap<>(); + dpid2FlowPriority = new HashMap<>(); + deny2Allow = new HashMap<>(); } @Override @@ -320,60 +349,55 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener { 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){ + if (ips.length == 0) { // A new no-ip device added return; } - String dpid = HexString.toHexString(switchPort[0].getSwitchDPID().getLong()); + + String dpid = HexString.toHexString(switchPort[0].getSwitchDPID() + .getLong()); String ip = IPv4.fromIPv4Address(ips[0].getInt()); - logger.debug("New AP added. [dpid:" + dpid + " ip:" + ip + "]"); + logger.debug("AP(dpid:{},ip:{}) is added", dpid, ip); - AP ap = new AP(ip,dpid); + AP ap = new AP(ip, dpid); apManager.addAP(ap); processAPAdded(ap); } /** - * push ACL flow given the new device + * Generates new ACL flow when a new device appears + * and existing ACL rules denies its traffic. */ 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; + for (ACLRule rule : getRules()) { + if (rule.getAction() != Action.ALLOW) { + if (rule.getNw_src() != null) { + if (IPAddressUtil.containIP(rule.getNw_src_prefix(), + rule.getNw_src_maskbits(), ip)) { + if (checkIfRuleWorksInSwitch(rule.getId(), dpid)) { + continue; + } + String flowName = "ACLRule_" + rule.getId() + "_" + + dpid; + generateFlow(rule, dpid, flowName); } - 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; + } else { + if (IPAddressUtil.containIP(rule.getNw_dst_prefix(), + rule.getNw_dst_maskbits(), ip)) { + if (checkIfRuleWorksInSwitch(rule.getId(), dpid)) { + continue; + } + String flowName = "ACLRule_" + rule.getId() + "_" + + dpid; + generateFlow(rule, dpid, flowName); } - String flowName = "ACLRule_" + rule.getId() + "_" + dpid; - ruleId2FlowName.get(rule.getId()).add(flowName); - ruleId2Dpid.get(rule.getId()).add(dpid); - generateFlow(rule, dpid, flowName); } } } @@ -391,22 +415,23 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener { @Override public void deviceIPV4AddrChanged(IDevice device) { - + SwitchPort[] switchPort = device.getAttachmentPoints(); IPv4Address[] ips = device.getIPv4Addresses(); - - String dpid = HexString.toHexString(switchPort[0].getSwitchDPID().getLong()); + + 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){ + // 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 + "]"); + + logger.debug("AP(dpid:{},ip:{}) is added", dpid, ip); AP ap = new AP(ip, dpid); apManager.addAP(ap); processAPAdded(ap); @@ -419,7 +444,7 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener { @Override public String getName() { - return null; + return "ACL manager"; } @Override diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java b/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java index 9ce23238e8ebefe34397cae2deaf9566e841fdbb..50e0c08044ecaa27a23b689359c7897b1c6388dc 100644 --- a/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java +++ b/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java @@ -1,42 +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; - -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(); - -} +/** + * 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; + +/** + * Service interface exported by ACL module + */ +public interface IACLService extends IFloodlightService { + + /** + * Gets a list containing all ACL rules. + * @return a list containing all ACL rules + */ + public List<ACLRule> getRules(); + + /** + * Add a new ACL rule. + * @param rule ACL rule + * @return true if successfully added, otherwise false + */ + public boolean addRule(ACLRule rule); + + /** + * Removes an existing ACL rule by rule id. + * @param ruleId ACL rule identifier + */ + public void removeRule(int ruleid); + + /** + * Clears ACL and resets all. + */ + public void removeAllRules(); + +}