blob: d9b26121690df67b26cb3121f5b7bc719cb4c936 [file] [log] [blame]
package net.floodlightcontroller.firewall;
import org.openflow.protocol.OFMatch;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPacket;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.packet.TCP;
import net.floodlightcontroller.packet.UDP;
public class FirewallRule implements Comparable<FirewallRule> {
public int ruleid;
public long dpid;
public short in_port;
public long dl_src;
public long dl_dst;
public short dl_type;
public int nw_src_prefix;
public int nw_src_maskbits;
public int nw_dst_prefix;
public int nw_dst_maskbits;
public short nw_proto;
public short tp_src;
public short tp_dst;
public boolean wildcard_dpid;
public boolean wildcard_in_port;
public boolean wildcard_dl_src;
public boolean wildcard_dl_dst;
public boolean wildcard_dl_type;
public boolean wildcard_nw_src;
public boolean wildcard_nw_dst;
public boolean wildcard_nw_proto;
public boolean wildcard_tp_src;
public boolean wildcard_tp_dst;
public int priority = 0;
public FirewallAction action;
public enum FirewallAction {
/*
* DENY: Deny rule
* ALLOW: Allow rule
*/
DENY, ALLOW
}
public FirewallRule() {
this.in_port = 0;
this.dl_src = 0;
this.nw_src_prefix = 0;
this.nw_src_maskbits = 0;
this.dl_dst = 0;
this.nw_proto = 0;
this.tp_src = 0;
this.tp_dst = 0;
this.dl_dst = 0;
this.nw_dst_prefix = 0;
this.nw_dst_maskbits = 0;
this.dpid = -1;
this.wildcard_dpid = true;
this.wildcard_in_port = true;
this.wildcard_dl_src = true;
this.wildcard_dl_dst = true;
this.wildcard_dl_type = true;
this.wildcard_nw_src = true;
this.wildcard_nw_dst = true;
this.wildcard_nw_proto = true;
this.wildcard_tp_src = true;
this.wildcard_tp_dst = true;
this.priority = 0;
this.action = FirewallAction.ALLOW;
this.ruleid = 0;
}
/**
* Generates a unique ID for the instance
*
* @return int representing the unique id
*/
public int genID() {
int uid = this.hashCode();
if (uid < 0) {
uid = Math.abs(uid);
uid = uid * 15551;
}
return uid;
}
/**
* Comparison method for Collections.sort method
*
* @param rule
* the rule to compare with
* @return number representing the result of comparison 0 if equal negative
* if less than 'rule' greater than zero if greater priority rule
* than 'rule'
*/
@Override
public int compareTo(FirewallRule rule) {
return this.priority - rule.priority;
}
/**
* Determines if this instance matches an existing rule instance
*
* @param r
* : the FirewallRule instance to compare with
* @return boolean: true if a match is found
**/
public boolean isSameAs(FirewallRule r) {
if (this.action != r.action
|| this.wildcard_dl_type != r.wildcard_dl_type
|| (this.wildcard_dl_type == false && this.dl_type == r.dl_type)
|| this.wildcard_tp_src != r.wildcard_tp_src
|| (this.wildcard_tp_src == false && this.tp_src != r.tp_src)
|| this.wildcard_tp_dst != r.wildcard_tp_dst
|| (this.wildcard_tp_dst == false &&this.tp_dst != r.tp_dst)
|| this.wildcard_dpid != r.wildcard_dpid
|| (this.wildcard_dpid == false && this.dpid != r.dpid)
|| this.wildcard_in_port != r.wildcard_in_port
|| (this.wildcard_in_port == false && this.in_port != r.in_port)
|| this.wildcard_nw_src != r.wildcard_nw_src
|| (this.wildcard_nw_src == false && (this.nw_src_prefix != r.nw_src_prefix || this.nw_src_maskbits != r.nw_src_maskbits))
|| this.wildcard_dl_src != r.wildcard_dl_src
|| (this.wildcard_dl_src == false && this.dl_src != r.dl_src)
|| this.wildcard_nw_proto != r.wildcard_nw_proto
|| (this.wildcard_nw_proto == false && this.nw_proto != r.nw_proto)
|| this.wildcard_nw_dst != r.wildcard_nw_dst
|| (this.wildcard_nw_dst == false && (this.nw_dst_prefix != r.nw_dst_prefix || this.nw_dst_maskbits != r.nw_dst_maskbits))
|| this.wildcard_dl_dst != r.wildcard_dl_dst
|| (this.wildcard_dl_dst == false && this.dl_dst != r.dl_dst)) {
return false;
}
return true;
}
/**
* Matches this rule to a given flow - incoming packet
*
* @param switchDpid
* the Id of the connected switch
* @param inPort
* the switch port where the packet originated from
* @param packet
* the Ethernet packet that arrives at the switch
* @param wildcards
* the pair of wildcards (allow and deny) given by Firewall
* module that is used by the Firewall module's matchWithRule
* method to derive wildcards for the decision to be taken
* @return true if the rule matches the given packet-in, false otherwise
*/
public boolean matchesFlow(long switchDpid, short inPort, Ethernet packet,
WildcardsPair wildcards) {
IPacket pkt = packet.getPayload();
// dl_type type
IPv4 pkt_ip = null;
// nw_proto types
TCP pkt_tcp = null;
UDP pkt_udp = null;
// tp_src and tp_dst (tp port numbers)
short pkt_tp_src = 0;
short pkt_tp_dst = 0;
// switchID matches?
if (wildcard_dpid == false && dpid != switchDpid)
return false;
// in_port matches?
if (wildcard_in_port == false && in_port != inPort)
return false;
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_IN_PORT;
} else {
wildcards.allow &= ~OFMatch.OFPFW_IN_PORT;
}
// mac address (src and dst) match?
if (wildcard_dl_src == false
&& dl_src != packet.getSourceMAC().toLong())
return false;
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_DL_SRC;
} else {
wildcards.allow &= ~OFMatch.OFPFW_DL_SRC;
}
if (wildcard_dl_dst == false
&& dl_dst != packet.getDestinationMAC().toLong())
return false;
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_DL_DST;
} else {
wildcards.allow &= ~OFMatch.OFPFW_DL_DST;
}
// dl_type check: ARP, IP
// if this is not an ARP rule but the pkt is ARP,
// return false match - no need to continue protocol specific check
if (wildcard_dl_type == false) {
if (dl_type == Ethernet.TYPE_ARP) {
if (packet.getEtherType() != Ethernet.TYPE_ARP)
return false;
else {
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_DL_TYPE;
} else {
wildcards.allow &= ~OFMatch.OFPFW_DL_TYPE;
}
}
} else if (dl_type == Ethernet.TYPE_IPv4) {
if (packet.getEtherType() != Ethernet.TYPE_IPv4)
return false;
else {
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_NW_PROTO;
} else {
wildcards.allow &= ~OFMatch.OFPFW_NW_PROTO;
}
// IP packets, proceed with ip address check
pkt_ip = (IPv4) pkt;
// IP addresses (src and dst) match?
if (wildcard_nw_src == false
&& this.matchIPAddress(nw_src_prefix,
nw_src_maskbits, pkt_ip.getSourceAddress()) == false)
return false;
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_NW_SRC_ALL;
wildcards.drop |= (nw_src_maskbits << OFMatch.OFPFW_NW_SRC_SHIFT);
} else {
wildcards.allow &= ~OFMatch.OFPFW_NW_SRC_ALL;
wildcards.allow |= (nw_src_maskbits << OFMatch.OFPFW_NW_SRC_SHIFT);
}
if (wildcard_nw_dst == false
&& this.matchIPAddress(nw_dst_prefix,
nw_dst_maskbits,
pkt_ip.getDestinationAddress()) == false)
return false;
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_NW_DST_ALL;
wildcards.drop |= (nw_dst_maskbits << OFMatch.OFPFW_NW_DST_SHIFT);
} else {
wildcards.allow &= ~OFMatch.OFPFW_NW_DST_ALL;
wildcards.allow |= (nw_dst_maskbits << OFMatch.OFPFW_NW_DST_SHIFT);
}
// nw_proto check
if (wildcard_nw_proto == false) {
if (nw_proto == IPv4.PROTOCOL_TCP) {
if (pkt_ip.getProtocol() != IPv4.PROTOCOL_TCP)
return false;
else {
pkt_tcp = (TCP) pkt_ip.getPayload();
pkt_tp_src = pkt_tcp.getSourcePort();
pkt_tp_dst = pkt_tcp.getDestinationPort();
}
} else if (nw_proto == IPv4.PROTOCOL_UDP) {
if (pkt_ip.getProtocol() != IPv4.PROTOCOL_UDP)
return false;
else {
pkt_udp = (UDP) pkt_ip.getPayload();
pkt_tp_src = pkt_udp.getSourcePort();
pkt_tp_dst = pkt_udp.getDestinationPort();
}
} else if (nw_proto == IPv4.PROTOCOL_ICMP) {
if (pkt_ip.getProtocol() != IPv4.PROTOCOL_ICMP)
return false;
else {
// nothing more needed for ICMP
}
}
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_NW_PROTO;
} else {
wildcards.allow &= ~OFMatch.OFPFW_NW_PROTO;
}
// TCP/UDP source and destination ports match?
if (pkt_tcp != null || pkt_udp != null) {
// does the source port match?
if (tp_src != 0 && tp_src != pkt_tp_src)
return false;
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_TP_SRC;
} else {
wildcards.allow &= ~OFMatch.OFPFW_TP_SRC;
}
// does the destination port match?
if (tp_dst != 0 && tp_dst != pkt_tp_dst)
return false;
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_TP_DST;
} else {
wildcards.allow &= ~OFMatch.OFPFW_TP_DST;
}
}
}
}
} else {
// non-IP packet - not supported - report no match
return false;
}
}
if (action == FirewallRule.FirewallAction.DENY) {
wildcards.drop &= ~OFMatch.OFPFW_DL_TYPE;
} else {
wildcards.allow &= ~OFMatch.OFPFW_DL_TYPE;
}
// all applicable checks passed
return true;
}
/**
* Determines if rule's CIDR address matches IP address of the packet
*
* @param rulePrefix
* prefix part of the CIDR address
* @param ruleBits
* the size of mask of the CIDR address
* @param packetAddress
* the IP address of the incoming packet to match with
* @return true if CIDR address matches the packet's IP address, false
* otherwise
*/
protected boolean matchIPAddress(int rulePrefix, int ruleBits,
int packetAddress) {
boolean matched = true;
int rule_iprng = 32 - ruleBits;
int rule_ipint = rulePrefix;
int pkt_ipint = packetAddress;
// if there's a subnet range (bits to be wildcarded > 0)
if (rule_iprng > 0) {
// right shift bits to remove rule_iprng of LSB that are to be
// wildcarded
rule_ipint = rule_ipint >> rule_iprng;
pkt_ipint = pkt_ipint >> rule_iprng;
// now left shift to return to normal range, except that the
// rule_iprng number of LSB
// are now zeroed
rule_ipint = rule_ipint << rule_iprng;
pkt_ipint = pkt_ipint << rule_iprng;
}
// check if we have a match
if (rule_ipint != pkt_ipint)
matched = false;
return matched;
}
@Override
public int hashCode() {
final int prime = 2521;
int result = super.hashCode();
result = prime * result + (int) dpid;
result = prime * result + in_port;
result = prime * result + (int) dl_src;
result = prime * result + (int) dl_dst;
result = prime * result + dl_type;
result = prime * result + nw_src_prefix;
result = prime * result + nw_src_maskbits;
result = prime * result + nw_dst_prefix;
result = prime * result + nw_dst_maskbits;
result = prime * result + nw_proto;
result = prime * result + tp_src;
result = prime * result + tp_dst;
result = prime * result + action.ordinal();
result = prime * result + priority;
result = prime * result + (new Boolean(wildcard_dpid)).hashCode();
result = prime * result + (new Boolean(wildcard_in_port)).hashCode();
result = prime * result + (new Boolean(wildcard_dl_src)).hashCode();
result = prime * result + (new Boolean(wildcard_dl_dst)).hashCode();
result = prime * result + (new Boolean(wildcard_dl_type)).hashCode();
result = prime * result + (new Boolean(wildcard_nw_src)).hashCode();
result = prime * result + (new Boolean(wildcard_nw_dst)).hashCode();
result = prime * result + (new Boolean(wildcard_nw_proto)).hashCode();
result = prime * result + (new Boolean(wildcard_tp_src)).hashCode();
result = prime * result + (new Boolean(wildcard_tp_dst)).hashCode();
return result;
}
}