/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by Amer Tahir
*
* 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.firewall;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.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 List<FirewallRule> retrieve() {
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);
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);
return ("{\"status\" : \"" + status + "\"}");
} else {
// add rule to firewall
firewall.addRule(rule);
status = "Rule added";
return ("{\"status\" : \"" + status + "\", \"rule-id\" : \""+ Integer.toString(rule.ruleid) + "\"}");
}
}
/**
* 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);
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(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;
}
if (tmp.equalsIgnoreCase("IPv4")) {
rule.wildcard_dl_type = false;
rule.dl_type = Ethernet.TYPE_IPv4;
}
}
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;
}
}