From ebc5d944956efc2a90b2ec19e1877d344fbd5a67 Mon Sep 17 00:00:00 2001 From: Amer Tahir <amertahir@gmail.com> Date: Wed, 8 Aug 2012 12:14:57 -0400 Subject: [PATCH] bug fixes in REST API + cleanup + Storage support --- .../firewall/Firewall.java | 197 ++++++++++++++++-- .../firewall/FirewallResource.java | 4 +- .../firewall/FirewallRule.java | 4 +- .../firewall/FirewallRulesResource.java | 15 +- .../firewall/FirewallWebRoutable.java | 9 +- .../firewall/IFirewallService.java | 11 +- 6 files changed, 205 insertions(+), 35 deletions(-) diff --git a/src/main/java/net/floodlightcontroller/firewall/Firewall.java b/src/main/java/net/floodlightcontroller/firewall/Firewall.java index 6b7bf8e2a..3ddb58a6b 100644 --- a/src/main/java/net/floodlightcontroller/firewall/Firewall.java +++ b/src/main/java/net/floodlightcontroller/firewall/Firewall.java @@ -1,6 +1,5 @@ package net.floodlightcontroller.firewall; -import java.nio.ByteBuffer; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -23,8 +22,6 @@ import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.core.IFloodlightProviderService; import java.util.ArrayList; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.Set; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.ICMP; import net.floodlightcontroller.packet.IPacket; @@ -34,13 +31,14 @@ import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.routing.IRoutingDecision; import net.floodlightcontroller.routing.IRoutingService; -import net.floodlightcontroller.staticflowentry.web.StaticFlowEntryWebRoutable; +import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher; +import net.floodlightcontroller.storage.IResultSet; import net.floodlightcontroller.storage.IStorageSourceService; -import net.floodlightcontroller.topology.ITopologyService; +import net.floodlightcontroller.storage.StorageException; -import org.openflow.util.HexString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.UUID; public class Firewall implements IFirewallService, IOFMessageListener, IFloodlightModule { @@ -53,6 +51,36 @@ public class Firewall implements IFirewallService, IOFMessageListener, IFloodlig protected ArrayList<FirewallRule> rules; protected boolean enabled; + public static final String TABLE_NAME = "controller_firewallrules"; + public static final String COLUMN_RULEID = "ruleid"; + public static final String COLUMN_SWITCHID = "switchid"; + public static final String COLUMN_SRC_INPORT = "src_inport"; + public static final String COLUMN_SRC_IP_PREFIX = "src_ip_prefix"; + public static final String COLUMN_SRC_IP_BITS = "src_ip_bits"; + public static final String COLUMN_SRC_MAC = "src_mac"; + public static final String COLUMN_PROTO_TYPE = "proto_type"; + public static final String COLUMN_PROTO_SRCPORT = "proto_srcport"; + public static final String COLUMN_PROTO_DSTPORT = "proto_dstport"; + public static final String COLUMN_DST_IP_PREFIX = "dst_ip_prefix"; + public static final String COLUMN_DST_IP_BITS = "dst_ip_bits"; + public static final String COLUMN_DST_MAC = "dst_mac"; + public static final String COLUMN_WILDCARD_SWITCHID = "wildcard_switchid"; + public static final String COLUMN_WILDCARD_SRC_INPORT = "wildcard_src_inport"; + public static final String COLUMN_WILDCARD_SRC_MAC = "wildcard_src_mac"; + public static final String COLUMN_WILDCARD_SRC_IP = "wildcard_src_ip"; + public static final String COLUMN_WILDCARD_PROTO_TYPE = "wildcard_proto_type"; + public static final String COLUMN_WILDCARD_DST_MAC = "wildcard_dst_mac"; + public static final String COLUMN_WILDCARD_DST_IP = "wildcard_dst_ip"; + public static final String COLUMN_PRIORITY = "priority"; + public static final String COLUMN_IS_DENYRULE = "is_denyrule"; + public static String ColumnNames[] = { COLUMN_RULEID, COLUMN_SWITCHID, + COLUMN_SRC_INPORT, COLUMN_SRC_MAC, COLUMN_SRC_IP_PREFIX, COLUMN_SRC_IP_BITS, + COLUMN_PROTO_TYPE, COLUMN_PROTO_SRCPORT, COLUMN_PROTO_DSTPORT, + COLUMN_DST_MAC, COLUMN_DST_IP_PREFIX, COLUMN_DST_IP_BITS, + COLUMN_WILDCARD_SWITCHID, COLUMN_WILDCARD_SRC_INPORT, COLUMN_WILDCARD_SRC_MAC, + COLUMN_WILDCARD_SRC_IP, COLUMN_WILDCARD_PROTO_TYPE, COLUMN_WILDCARD_DST_MAC, + COLUMN_WILDCARD_DST_IP, COLUMN_PRIORITY, COLUMN_IS_DENYRULE }; + @Override public String getName() { @@ -110,6 +138,106 @@ public class Firewall implements IFirewallService, IOFMessageListener, IFloodlig public void setStorageSource(IStorageSourceService storageSource) { this.storageSource = storageSource; } + + protected ArrayList<FirewallRule> readRulesFromStorage() { + ArrayList<FirewallRule> l = new ArrayList<FirewallRule>(); + + try { + Map<String, Object> row; + // null1=no predicate, null2=no ordering + IResultSet resultSet = storageSource.executeQuery(TABLE_NAME, + ColumnNames, null, null); + for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { + row = it.next().getRow(); + // now, parse row + FirewallRule r = new FirewallRule(); + if (!row.containsKey(COLUMN_RULEID) || !row.containsKey(COLUMN_SWITCHID)) { + logger.error( + "skipping entry with missing required 'ruleid' or 'switchid' entry: {}", + row); + return l; + } + // most error checking done with ClassCastException + try { + // first, snag the required entries, for debugging info + r.ruleid = (String)row.get(COLUMN_RULEID); + r.switchid = Long.parseLong((String)row.get(COLUMN_SWITCHID)); + + for (String key : row.keySet()) { + if (row.get(key) == null) + continue; + if ( key.equals(COLUMN_RULEID) || key.equals(COLUMN_SWITCHID) || key.equals("id")) { + continue; // already handled + } else if ( key.equals(COLUMN_SRC_INPORT)) { + r.src_inport = Short.parseShort((String)row.get(COLUMN_SRC_INPORT)); + } else if ( key.equals(COLUMN_SRC_MAC)) { + r.src_mac = Long.parseLong((String)row.get(COLUMN_SRC_MAC)); + } else if ( key.equals(COLUMN_SRC_IP_PREFIX)) { + r.src_ip_prefix = Integer.parseInt((String)row.get(COLUMN_SRC_IP_PREFIX)); + } else if ( key.equals(COLUMN_SRC_IP_BITS)) { + r.src_ip_bits = Integer.parseInt((String)row.get(COLUMN_SRC_IP_BITS)); + } else if ( key.equals(COLUMN_PROTO_TYPE)) { + r.proto_type = Short.parseShort((String)row.get(COLUMN_PROTO_TYPE)); + } else if ( key.equals(COLUMN_PROTO_SRCPORT)) { + r.proto_srcport = Short.parseShort((String)row.get(COLUMN_PROTO_SRCPORT)); + } else if ( key.equals(COLUMN_PROTO_DSTPORT)) { + r.proto_dstport = Short.parseShort((String)row.get(COLUMN_PROTO_DSTPORT)); + } else if ( key.equals(COLUMN_DST_MAC)) { + r.dst_mac = Long.parseLong((String)row.get(COLUMN_DST_MAC)); + } else if ( key.equals(COLUMN_DST_IP_PREFIX)) { + r.dst_ip_prefix = Integer.parseInt((String)row.get(COLUMN_DST_IP_PREFIX)); + } else if ( key.equals(COLUMN_DST_IP_BITS)) { + r.dst_ip_bits = Integer.parseInt((String)row.get(COLUMN_DST_IP_BITS)); + } else if ( key.equals(COLUMN_WILDCARD_SWITCHID)) { + r.wildcard_switchid = Boolean.parseBoolean((String)row.get(COLUMN_WILDCARD_SWITCHID)); + } else if ( key.equals(COLUMN_WILDCARD_SRC_INPORT)) { + r.wildcard_src_inport = Boolean.parseBoolean((String)row.get(COLUMN_WILDCARD_SRC_INPORT)); + } else if ( key.equals(COLUMN_WILDCARD_SRC_MAC)) { + r.wildcard_src_mac = Boolean.parseBoolean((String)row.get(COLUMN_WILDCARD_SRC_MAC)); + } else if ( key.equals(COLUMN_WILDCARD_SRC_IP)) { + r.wildcard_src_ip = Boolean.parseBoolean((String)row.get(COLUMN_WILDCARD_SRC_IP)); + } else if ( key.equals(COLUMN_WILDCARD_PROTO_TYPE)) { + r.wildcard_proto_type = Boolean.parseBoolean((String)row.get(COLUMN_WILDCARD_PROTO_TYPE)); + } else if ( key.equals(COLUMN_WILDCARD_DST_MAC)) { + r.wildcard_dst_mac = Boolean.parseBoolean((String)row.get(COLUMN_WILDCARD_DST_MAC)); + } else if ( key.equals(COLUMN_WILDCARD_DST_IP)) { + r.wildcard_dst_ip = Boolean.parseBoolean((String)row.get(COLUMN_WILDCARD_DST_IP)); + } else if ( key.equals(COLUMN_PRIORITY)) { + r.priority = Integer.parseInt((String)row.get(COLUMN_PRIORITY)); + } else if ( key.equals(COLUMN_IS_DENYRULE)) { + r.is_denyrule = Boolean.parseBoolean((String)row.get(COLUMN_IS_DENYRULE)); + } + } + } catch (ClassCastException e) { + if (!r.ruleid.equals("")) + logger.error( + "skipping rule {} with bad data : " + + e.getMessage(), r.ruleid); + else + logger.error("skipping rule with bad data: {} :: {} ", + e.getMessage(), e.getStackTrace()); + } + l.add(r); + } + } catch (StorageException e) { + logger.error("failed to access storage: {}", e.getMessage()); + // if the table doesn't exist, then wait to populate later via + // setStorageSource() + } + + // now, sort the list based on priorities + Collections.sort(l); + + return l; + } + + /** + * used for debugging and unittests + * @return the number of rules + */ + public int countRules() { + return this.rules.size(); + } @Override public void init(FloodlightModuleContext context) @@ -117,13 +245,10 @@ public class Firewall implements IFirewallService, IOFMessageListener, IFloodlig floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); storageSource = context.getServiceImpl(IStorageSourceService.class); restApi = context.getServiceImpl(IRestApiService.class); - rules = new ArrayList<FirewallRule>(); + rules = new ArrayList<FirewallRule>(); logger = LoggerFactory.getLogger(Firewall.class); // start disabled enabled = false; - - // assumes no switches connected at startup() - //rules = readRulesFromStorage(); } @Override @@ -134,6 +259,10 @@ public class Firewall implements IFirewallService, IOFMessageListener, IFloodlig if (this.enabled == true) { floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); } + // storage, create table and read rules + storageSource.createTable(TABLE_NAME, null); + storageSource.setTablePrimaryKeyName(TABLE_NAME, COLUMN_RULEID); + this.rules = readRulesFromStorage(); } @Override @@ -183,20 +312,62 @@ public class Firewall implements IFirewallService, IOFMessageListener, IFloodlig return this.rules; } + @Override + public List<Map<String, Object>> getStorageRules() { + ArrayList<Map<String, Object>> l = new ArrayList<Map<String, Object>>(); + try { + // null1=no predicate, null2=no ordering + IResultSet resultSet = storageSource.executeQuery(TABLE_NAME, ColumnNames, null, null); + for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { + l.add(it.next().getRow()); + } + } catch (StorageException e) { + logger.error("failed to access storage: {}", e.getMessage()); + // if the table doesn't exist, then wait to populate later via + // setStorageSource() + } + return l; + } + @Override public void addRule(FirewallRule rule) { + rule.ruleid = UUID.randomUUID().toString(); this.rules.add(rule); // now re-sort the rules Collections.sort(this.rules); + // add rule to database + Map<String, Object> entry = new HashMap<String, Object>(); + entry.put(COLUMN_RULEID, rule.ruleid); + entry.put(COLUMN_SWITCHID, Long.toString(rule.switchid)); + entry.put(COLUMN_SRC_INPORT, Short.toString(rule.src_inport)); + entry.put(COLUMN_SRC_MAC, Long.toString(rule.src_mac)); + entry.put(COLUMN_SRC_IP_PREFIX, Integer.toString(rule.src_ip_prefix)); + entry.put(COLUMN_SRC_IP_BITS, Integer.toString(rule.src_ip_bits)); + entry.put(COLUMN_PROTO_TYPE, Short.toString(rule.proto_type)); + entry.put(COLUMN_PROTO_SRCPORT, Integer.toString(rule.proto_srcport)); + entry.put(COLUMN_PROTO_DSTPORT, Integer.toString(rule.proto_dstport)); + entry.put(COLUMN_DST_MAC, Long.toString(rule.dst_mac)); + entry.put(COLUMN_DST_IP_PREFIX, Integer.toString(rule.dst_ip_prefix)); + entry.put(COLUMN_DST_IP_BITS, Integer.toString(rule.dst_ip_bits)); + entry.put(COLUMN_WILDCARD_SWITCHID, Boolean.toString(rule.wildcard_switchid)); + entry.put(COLUMN_WILDCARD_SRC_INPORT, Boolean.toString(rule.wildcard_src_inport)); + entry.put(COLUMN_WILDCARD_SRC_MAC, Boolean.toString(rule.wildcard_src_mac)); + entry.put(COLUMN_WILDCARD_SRC_IP, Boolean.toString(rule.wildcard_src_ip)); + entry.put(COLUMN_WILDCARD_PROTO_TYPE, Boolean.toString(rule.wildcard_proto_type)); + entry.put(COLUMN_WILDCARD_DST_MAC, Boolean.toString(rule.wildcard_dst_mac)); + entry.put(COLUMN_WILDCARD_DST_IP, Boolean.toString(rule.wildcard_dst_ip)); + entry.put(COLUMN_PRIORITY, Integer.toString(rule.priority)); + entry.put(COLUMN_IS_DENYRULE, Boolean.toString(rule.is_denyrule)); + storageSource.insertRowAsync(TABLE_NAME, entry); } @Override - public void deleteRule(int ruleid) { + public void deleteRule(String ruleid) { boolean found = false; Iterator<FirewallRule> iter = this.rules.iterator(); while (iter.hasNext()) { FirewallRule r = iter.next(); - if (r.ruleid == ruleid) { + if (r.ruleid.equalsIgnoreCase(ruleid)) { // found the rule, now remove it iter.remove(); found = true; @@ -207,6 +378,8 @@ public class Firewall implements IFirewallService, IOFMessageListener, IFloodlig if (found) { Collections.sort(this.rules); } + // delete from database + storageSource.deleteRowAsync(TABLE_NAME, ruleid); } protected List<Object> matchWithRule(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { diff --git a/src/main/java/net/floodlightcontroller/firewall/FirewallResource.java b/src/main/java/net/floodlightcontroller/firewall/FirewallResource.java index e81623c49..010fdb187 100644 --- a/src/main/java/net/floodlightcontroller/firewall/FirewallResource.java +++ b/src/main/java/net/floodlightcontroller/firewall/FirewallResource.java @@ -31,8 +31,8 @@ public class FirewallResource extends ServerResource { } else if (op.equalsIgnoreCase("disable")) { firewall.disableFirewall(); return "{\"status\" : \"success\", \"details\" : \"firewall stopped\"}"; - } else if (op.equalsIgnoreCase("rules")) { - return firewall.getRules(); + } else if (op.equalsIgnoreCase("storageRules")) { + return firewall.getStorageRules(); } return "{\"status\" : \"failure\", \"details\" : \"invalid operation\"}"; diff --git a/src/main/java/net/floodlightcontroller/firewall/FirewallRule.java b/src/main/java/net/floodlightcontroller/firewall/FirewallRule.java index cf0f3374d..ecf08caad 100644 --- a/src/main/java/net/floodlightcontroller/firewall/FirewallRule.java +++ b/src/main/java/net/floodlightcontroller/firewall/FirewallRule.java @@ -1,7 +1,7 @@ package net.floodlightcontroller.firewall; public class FirewallRule implements Comparable { - public int ruleid; + public String ruleid; public short src_inport; public long src_mac; public int src_ip_prefix; @@ -34,7 +34,7 @@ public class FirewallRule implements Comparable { this.dst_mac = 0; this.dst_ip_prefix = 0; this.dst_ip_bits = 32; - this.switchid = 0; + this.switchid = -1; this.wildcard_src_inport = true; this.wildcard_src_mac = true; this.wildcard_src_ip = true; diff --git a/src/main/java/net/floodlightcontroller/firewall/FirewallRulesResource.java b/src/main/java/net/floodlightcontroller/firewall/FirewallRulesResource.java index f19950328..a05926645 100644 --- a/src/main/java/net/floodlightcontroller/firewall/FirewallRulesResource.java +++ b/src/main/java/net/floodlightcontroller/firewall/FirewallRulesResource.java @@ -26,16 +26,11 @@ public class FirewallRulesResource extends ServerResource { @Get("json") public Object handleRequest() { - String op = (String) getRequestAttributes().get("op"); IFirewallService firewall = (IFirewallService)getContext().getAttributes(). get(IFirewallService.class.getCanonicalName()); - if (op.equalsIgnoreCase("all")) { - return firewall.getRules(); - } - - return "{\"status\" : \"failure\", \"details\" : \"invalid operation\"}"; + return firewall.getRules(); } /** @@ -44,7 +39,7 @@ public class FirewallRulesResource extends ServerResource { * @param fmJson The Firewall rule entry in JSON format. * @return A string status message */ - @Post("json") + @Post public String store(String fmJson) { IFirewallService firewall = (IFirewallService)getContext().getAttributes(). @@ -76,7 +71,7 @@ public class FirewallRulesResource extends ServerResource { * @param fmJson The Firewall rule entry in JSON format. * @return A string status message */ - @Delete("json") + @Delete public String remove(String fmJson) { IFirewallService firewall = (IFirewallService)getContext().getAttributes(). @@ -95,7 +90,7 @@ public class FirewallRulesResource extends ServerResource { Iterator<FirewallRule> iter = firewall.getRules().iterator(); while (iter.hasNext()) { FirewallRule r = iter.next(); - if (r.ruleid == rule.ruleid) { + if (r.ruleid.equalsIgnoreCase(rule.ruleid)) { exists = true; break; } @@ -145,7 +140,7 @@ public class FirewallRulesResource extends ServerResource { String tmp; if (n == "ruleid") { - rule.ruleid = Integer.parseInt(jp.getText()); + rule.ruleid = jp.getText(); } else if (n == "switchid") { tmp = jp.getText(); if (tmp.equalsIgnoreCase("-1") == false) { diff --git a/src/main/java/net/floodlightcontroller/firewall/FirewallWebRoutable.java b/src/main/java/net/floodlightcontroller/firewall/FirewallWebRoutable.java index acec88fae..528db0564 100644 --- a/src/main/java/net/floodlightcontroller/firewall/FirewallWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/firewall/FirewallWebRoutable.java @@ -1,14 +1,7 @@ package net.floodlightcontroller.firewall; -import net.floodlightcontroller.linkdiscovery.web.LinksResource; import net.floodlightcontroller.restserver.RestletRoutable; -import net.floodlightcontroller.topology.web.BlockedPortsResource; -import net.floodlightcontroller.topology.web.BroadcastDomainResource; -import net.floodlightcontroller.topology.web.SwitchClustersResource; -import net.floodlightcontroller.topology.web.TunnelLinksResource; - import org.restlet.Context; -import org.restlet.Restlet; import org.restlet.routing.Router; public class FirewallWebRoutable implements RestletRoutable { @@ -18,7 +11,7 @@ public class FirewallWebRoutable implements RestletRoutable { @Override public Router getRestlet(Context context) { Router router = new Router(context); - router.attach("/{op}/json", FirewallResource.class); + router.attach("/module/{op}/json", FirewallResource.class); router.attach("/rules/json", FirewallRulesResource.class); return router; } diff --git a/src/main/java/net/floodlightcontroller/firewall/IFirewallService.java b/src/main/java/net/floodlightcontroller/firewall/IFirewallService.java index 9edd4bf3d..7d5064487 100644 --- a/src/main/java/net/floodlightcontroller/firewall/IFirewallService.java +++ b/src/main/java/net/floodlightcontroller/firewall/IFirewallService.java @@ -1,6 +1,8 @@ package net.floodlightcontroller.firewall; +import java.util.HashMap; import java.util.List; +import java.util.Map; import net.floodlightcontroller.core.module.IFloodlightService; @@ -22,6 +24,13 @@ public interface IFirewallService extends IFloodlightService { */ public List<FirewallRule> getRules(); + /** + * Returns all of the firewall rules in storage + * for debugging and unit-testing purposes + * @return List of all rules in storage + */ + public List<Map<String, Object>> getStorageRules(); + /** * Adds a new Firewall rule */ @@ -30,5 +39,5 @@ public interface IFirewallService extends IFloodlightService { /** * Deletes a Firewall rule */ - public void deleteRule(int ruleid); + public void deleteRule(String ruleid); } -- GitLab