| package net.floodlightcontroller.firewall; |
| |
| import java.io.IOException; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.codehaus.jackson.JsonParseException; |
| import org.codehaus.jackson.JsonParser; |
| import org.codehaus.jackson.JsonToken; |
| import org.codehaus.jackson.map.MappingJsonFactory; |
| import org.openflow.util.HexString; |
| import org.restlet.resource.Delete; |
| import org.restlet.resource.Post; |
| import org.restlet.resource.Get; |
| import org.restlet.resource.ServerResource; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import net.floodlightcontroller.packet.Ethernet; |
| import net.floodlightcontroller.packet.IPv4; |
| |
| public class FirewallRulesResource extends ServerResource { |
| protected static Logger log = LoggerFactory.getLogger(FirewallRulesResource.class); |
| |
| @Get("json") |
| public Object handleRequest() { |
| IFirewallService firewall = |
| (IFirewallService)getContext().getAttributes(). |
| get(IFirewallService.class.getCanonicalName()); |
| |
| return firewall.getRules(); |
| } |
| |
| /** |
| * Takes a Firewall Rule string in JSON format and parses it into |
| * our firewall rule data structure, then adds it to the firewall. |
| * @param fmJson The Firewall rule entry in JSON format. |
| * @return A string status message |
| */ |
| @Post |
| public String store(String fmJson) { |
| IFirewallService firewall = |
| (IFirewallService)getContext().getAttributes(). |
| get(IFirewallService.class.getCanonicalName()); |
| |
| FirewallRule rule; |
| try { |
| rule = jsonToFirewallRule(fmJson); |
| } catch (IOException e) { |
| log.error("Error parsing firewall rule: " + fmJson, e); |
| e.printStackTrace(); |
| return "{\"status\" : \"Error! Could not parse firewall rule, see log for details.\"}"; |
| } |
| String status = null; |
| if (checkRuleExists(rule, firewall.getRules())) { |
| status = "Error! A similar firewall rule already exists."; |
| log.error(status); |
| } else { |
| // add rule to firewall |
| firewall.addRule(rule); |
| status = "Rule added"; |
| } |
| return ("{\"status\" : \"" + status + "\"}"); |
| } |
| |
| /** |
| * Takes a Firewall Rule string in JSON format and parses it into |
| * our firewall rule data structure, then deletes it from the firewall. |
| * @param fmJson The Firewall rule entry in JSON format. |
| * @return A string status message |
| */ |
| |
| @Delete |
| public String remove(String fmJson) { |
| IFirewallService firewall = |
| (IFirewallService)getContext().getAttributes(). |
| get(IFirewallService.class.getCanonicalName()); |
| |
| FirewallRule rule; |
| try { |
| rule = jsonToFirewallRule(fmJson); |
| } catch (IOException e) { |
| log.error("Error parsing firewall rule: " + fmJson, e); |
| e.printStackTrace(); |
| return "{\"status\" : \"Error! Could not parse firewall rule, see log for details.\"}"; |
| } |
| String status = null; |
| boolean exists = false; |
| Iterator<FirewallRule> iter = firewall.getRules().iterator(); |
| while (iter.hasNext()) { |
| FirewallRule r = iter.next(); |
| if (r.ruleid == rule.ruleid) { |
| exists = true; |
| break; |
| } |
| } |
| if (!exists) { |
| status = "Error! Can't delete, a rule with this ID doesn't exist."; |
| log.error(status); |
| } else { |
| // delete rule from firewall |
| firewall.deleteRule(rule.ruleid); |
| status = "Rule deleted"; |
| } |
| return ("{\"status\" : \"" + status + "\"}"); |
| } |
| |
| /** |
| * Turns a JSON formatted Firewall Rule string into a FirewallRule instance |
| * @param fmJson The JSON formatted static firewall rule |
| * @return The FirewallRule instance |
| * @throws IOException If there was an error parsing the JSON |
| */ |
| |
| public static FirewallRule jsonToFirewallRule(String fmJson) throws IOException { |
| FirewallRule rule = new FirewallRule(); |
| MappingJsonFactory f = new MappingJsonFactory(); |
| JsonParser jp; |
| |
| try { |
| jp = f.createJsonParser(fmJson); |
| } catch (JsonParseException e) { |
| throw new IOException(e); |
| } |
| |
| jp.nextToken(); |
| if (jp.getCurrentToken() != JsonToken.START_OBJECT) { |
| throw new IOException("Expected START_OBJECT"); |
| } |
| |
| while (jp.nextToken() != JsonToken.END_OBJECT) { |
| if (jp.getCurrentToken() != JsonToken.FIELD_NAME) { |
| throw new IOException("Expected FIELD_NAME"); |
| } |
| |
| String n = jp.getCurrentName(); |
| jp.nextToken(); |
| if (jp.getText().equals("")) |
| continue; |
| |
| String tmp; |
| |
| // This is currently only applicable for remove(). In store(), ruleid takes a random number |
| if (n == "ruleid") { |
| rule.ruleid = Integer.parseInt((String)jp.getText()); |
| } |
| |
| // This assumes user having dpid info for involved switches |
| else if (n == "switchid") { |
| tmp = jp.getText(); |
| if (tmp.equalsIgnoreCase("-1") == false) { |
| // user inputs hex format dpid |
| rule.dpid = HexString.toLong(tmp); |
| rule.wildcard_dpid = false; |
| } |
| } |
| |
| else if (n == "src-inport") { |
| rule.in_port = Short.parseShort(jp.getText()); |
| rule.wildcard_in_port = false; |
| } |
| |
| else if (n == "src-mac") { |
| tmp = jp.getText(); |
| if (tmp.equalsIgnoreCase("ANY") == false) { |
| rule.wildcard_dl_src = false; |
| rule.dl_src = Ethernet.toLong(Ethernet.toMACAddress(tmp)); |
| } |
| } |
| |
| else if (n == "dst-mac") { |
| tmp = jp.getText(); |
| if (tmp.equalsIgnoreCase("ANY") == false) { |
| rule.wildcard_dl_dst = false; |
| rule.dl_dst = Ethernet.toLong(Ethernet.toMACAddress(tmp)); |
| } |
| } |
| |
| else if (n == "dl-type") { |
| tmp = jp.getText(); |
| if (tmp.equalsIgnoreCase("ARP")) { |
| rule.wildcard_dl_type = false; |
| rule.dl_type = Ethernet.TYPE_ARP; |
| } |
| } |
| |
| else if (n == "src-ip") { |
| tmp = jp.getText(); |
| if (tmp.equalsIgnoreCase("ANY") == false) { |
| rule.wildcard_nw_src = false; |
| rule.wildcard_dl_type = false; |
| rule.dl_type = Ethernet.TYPE_IPv4; |
| int[] cidr = IPCIDRToPrefixBits(tmp); |
| rule.nw_src_prefix = cidr[0]; |
| rule.nw_src_maskbits = cidr[1]; |
| } |
| } |
| |
| else if (n == "dst-ip") { |
| tmp = jp.getText(); |
| if (tmp.equalsIgnoreCase("ANY") == false) { |
| rule.wildcard_nw_dst = false; |
| rule.wildcard_dl_type = false; |
| rule.dl_type = Ethernet.TYPE_IPv4; |
| int[] cidr = IPCIDRToPrefixBits(tmp); |
| rule.nw_dst_prefix = cidr[0]; |
| rule.nw_dst_maskbits = cidr[1]; |
| } |
| } |
| |
| else if (n == "nw-proto") { |
| tmp = jp.getText(); |
| if (tmp.equalsIgnoreCase("TCP")) { |
| rule.wildcard_nw_proto = false; |
| rule.nw_proto = IPv4.PROTOCOL_TCP; |
| rule.wildcard_dl_type = false; |
| rule.dl_type = Ethernet.TYPE_IPv4; |
| } else if (tmp.equalsIgnoreCase("UDP")) { |
| rule.wildcard_nw_proto = false; |
| rule.nw_proto = IPv4.PROTOCOL_UDP; |
| rule.wildcard_dl_type = false; |
| rule.dl_type = Ethernet.TYPE_IPv4; |
| } else if (tmp.equalsIgnoreCase("ICMP")) { |
| rule.wildcard_nw_proto = false; |
| rule.nw_proto = IPv4.PROTOCOL_ICMP; |
| rule.wildcard_dl_type = false; |
| rule.dl_type = Ethernet.TYPE_IPv4; |
| } |
| } |
| |
| else if (n == "tp-src") { |
| rule.wildcard_tp_src = false; |
| rule.tp_src = Short.parseShort(jp.getText()); |
| } |
| |
| else if (n == "tp-dst") { |
| rule.wildcard_tp_dst = false; |
| rule.tp_dst = Short.parseShort(jp.getText()); |
| } |
| |
| else if (n == "priority") { |
| rule.priority = Integer.parseInt(jp.getText()); |
| } |
| |
| else if (n == "action") { |
| if (jp.getText().equalsIgnoreCase("allow") == true) { |
| rule.action = FirewallRule.FirewallAction.ALLOW; |
| } else if (jp.getText().equalsIgnoreCase("deny") == true) { |
| rule.action = FirewallRule.FirewallAction.DENY; |
| } |
| } |
| } |
| |
| return rule; |
| } |
| |
| public static int[] IPCIDRToPrefixBits(String cidr) { |
| int ret[] = new int[2]; |
| |
| // as IP can also be a prefix rather than an absolute address |
| // split it over "/" to get the bit range |
| String[] parts = cidr.split("/"); |
| String cidr_prefix = parts[0].trim(); |
| int cidr_bits = 0; |
| if (parts.length == 2) { |
| try { |
| cidr_bits = Integer.parseInt(parts[1].trim()); |
| } catch (Exception exp) { |
| cidr_bits = 32; |
| } |
| } |
| ret[0] = IPv4.toIPv4Address(cidr_prefix); |
| ret[1] = cidr_bits; |
| |
| return ret; |
| } |
| |
| public static boolean checkRuleExists(FirewallRule rule, List<FirewallRule> rules) { |
| Iterator<FirewallRule> iter = rules.iterator(); |
| while (iter.hasNext()) { |
| FirewallRule r = iter.next(); |
| |
| // check if we find a similar rule |
| if (rule.isSameAs(r)) { |
| return true; |
| } |
| } |
| |
| // no rule matched, so it doesn't exist in the rules |
| return false; |
| } |
| } |