/** * 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 com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.projectfloodlight.openflow.protocol.match.MatchField; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.IPv4AddressWithMask; import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.TransportPort; 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; @JsonSerialize(using=FirewallRuleSerializer.class) public class FirewallRule implements Comparable<FirewallRule> { @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; FirewallRule other = (FirewallRule) obj; if (action != other.action) return false; if (any_dl_dst != other.any_dl_dst) return false; if (any_dl_src != other.any_dl_src) return false; if (any_dl_type != other.any_dl_type) return false; if (any_dpid != other.any_dpid) return false; if (any_in_port != other.any_in_port) return false; if (any_nw_dst != other.any_nw_dst) return false; if (any_nw_proto != other.any_nw_proto) return false; if (any_nw_src != other.any_nw_src) return false; if (any_tp_dst != other.any_tp_dst) return false; if (any_tp_src != other.any_tp_src) return false; if (dl_dst == null) { if (other.dl_dst != null) return false; } else if (!dl_dst.equals(other.dl_dst)) return false; if (dl_src == null) { if (other.dl_src != null) return false; } else if (!dl_src.equals(other.dl_src)) return false; if (dl_type == null) { if (other.dl_type != null) return false; } else if (!dl_type.equals(other.dl_type)) return false; if (dpid == null) { if (other.dpid != null) return false; } else if (!dpid.equals(other.dpid)) return false; if (in_port == null) { if (other.in_port != null) return false; } else if (!in_port.equals(other.in_port)) return false; if (nw_dst_prefix_and_mask == null) { if (other.nw_dst_prefix_and_mask != null) return false; } else if (!nw_dst_prefix_and_mask.equals(other.nw_dst_prefix_and_mask)) return false; if (nw_proto == null) { if (other.nw_proto != null) return false; } else if (!nw_proto.equals(other.nw_proto)) return false; if (nw_src_prefix_and_mask == null) { if (other.nw_src_prefix_and_mask != null) return false; } else if (!nw_src_prefix_and_mask.equals(other.nw_src_prefix_and_mask)) return false; if (priority != other.priority) return false; if (ruleid != other.ruleid) return false; if (tp_dst == null) { if (other.tp_dst != null) return false; } else if (!tp_dst.equals(other.tp_dst)) return false; if (tp_src == null) { if (other.tp_src != null) return false; } else if (!tp_src.equals(other.tp_src)) return false; return true; } public int ruleid; public DatapathId dpid; public OFPort in_port; public MacAddress dl_src; public MacAddress dl_dst; public EthType dl_type; public IPv4AddressWithMask nw_src_prefix_and_mask; public IPv4AddressWithMask nw_dst_prefix_and_mask; public IpProtocol nw_proto; public TransportPort tp_src; public TransportPort tp_dst; /* Specify whether or not a match field is relevant. * true = anything goes; don't care what it is * false = field must match (w or w/o mask depending on field) */ public boolean any_dpid; public boolean any_in_port; public boolean any_dl_src; public boolean any_dl_dst; public boolean any_dl_type; public boolean any_nw_src; public boolean any_nw_dst; public boolean any_nw_proto; public boolean any_tp_src; public boolean any_tp_dst; public int priority = 0; public FirewallAction action; public enum FirewallAction { /* * DROP: Drop rule * ALLOW: Allow rule */ DROP, ALLOW } /** * The default rule is to match on anything. */ public FirewallRule() { this.dpid = DatapathId.NONE; this.in_port = OFPort.ANY; this.dl_src = MacAddress.NONE; this.dl_dst = MacAddress.NONE; this.dl_type = EthType.NONE; this.nw_src_prefix_and_mask = IPv4AddressWithMask.NONE; this.nw_dst_prefix_and_mask = IPv4AddressWithMask.NONE; this.nw_proto = IpProtocol.NONE; this.tp_src = TransportPort.NONE; this.tp_dst = TransportPort.NONE; this.any_dpid = true; this.any_in_port = true; this.any_dl_src = true; this.any_dl_dst = true; this.any_dl_type = true; this.any_nw_src = true; this.any_nw_dst = true; this.any_nw_proto = true; this.any_tp_src = true; this.any_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.any_dl_type != r.any_dl_type || (this.any_dl_type == false && !this.dl_type.equals(r.dl_type)) || this.any_tp_src != r.any_tp_src || (this.any_tp_src == false && !this.tp_src.equals(r.tp_src)) || this.any_tp_dst != r.any_tp_dst || (this.any_tp_dst == false && !this.tp_dst.equals(r.tp_dst)) || this.any_dpid != r.any_dpid || (this.any_dpid == false && !this.dpid.equals(r.dpid)) || this.any_in_port != r.any_in_port || (this.any_in_port == false && !this.in_port.equals(r.in_port)) || this.any_nw_src != r.any_nw_src || (this.any_nw_src == false && !this.nw_src_prefix_and_mask.equals(r.nw_src_prefix_and_mask)) || this.any_dl_src != r.any_dl_src || (this.any_dl_src == false && !this.dl_src.equals(r.dl_src)) || this.any_nw_proto != r.any_nw_proto || (this.any_nw_proto == false && !this.nw_proto.equals(r.nw_proto)) || this.any_nw_dst != r.any_nw_dst || (this.any_nw_dst == false && !this.nw_dst_prefix_and_mask.equals(r.nw_dst_prefix_and_mask)) || this.any_dl_dst != r.any_dl_dst || (this.any_dl_dst == false && !this.dl_dst.equals(r.dl_dst))) { return false; } return true; } /** * Checks if this rule is a match for the incoming packet's MatchFields * * @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 allow-drop-pair * the pair of matches (allow and drop) given by the Firewall * module that is used by the Firewall module's matchWithRule * method to derive the match object for the decision to be taken * @return true if the rule matches the given packet-in, false otherwise */ public boolean matchesThisPacket(DatapathId switchDpid, OFPort inPort, Ethernet packet, AllowDropPair adp) { 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) TransportPort pkt_tp_src = TransportPort.NONE; TransportPort pkt_tp_dst = TransportPort.NONE; // switchID matches? if (any_dpid == false && !dpid.equals(switchDpid)) return false; // in_port matches? if (any_in_port == false && !in_port.equals(inPort)) return false; if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_IN_PORT; if (!OFPort.ANY.equals(this.in_port)) { adp.drop.setExact(MatchField.IN_PORT, this.in_port); } } else { //wildcards.allow &= ~OFMatch.OFPFW_IN_PORT; if (!OFPort.ANY.equals(this.in_port)) { adp.allow.setExact(MatchField.IN_PORT, this.in_port); } } // mac address (src and dst) match? if (any_dl_src == false && !dl_src.equals(packet.getSourceMACAddress())) return false; if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_DL_SRC; if (!MacAddress.NONE.equals(this.dl_src)) { adp.drop.setExact(MatchField.ETH_SRC, this.dl_src); } } else { //wildcards.allow &= ~OFMatch.OFPFW_DL_SRC; if (!MacAddress.NONE.equals(this.dl_src)) { adp.allow.setExact(MatchField.ETH_SRC, this.dl_src); } } if (any_dl_dst == false && !dl_dst.equals(packet.getDestinationMACAddress())) return false; if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_DL_DST; if (!MacAddress.NONE.equals(this.dl_dst)) { adp.drop.setExact(MatchField.ETH_DST, this.dl_dst); } } else { //wildcards.allow &= ~OFMatch.OFPFW_DL_DST; if (!MacAddress.NONE.equals(this.dl_dst)) { adp.allow.setExact(MatchField.ETH_DST, this.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 (any_dl_type == false) { if (dl_type.equals(EthType.ARP)) { if (packet.getEtherType() != EthType.ARP) /* shallow check for equality is okay for EthType */ return false; else { if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_DL_TYPE; if (!EthType.NONE.equals(this.dl_type)) { adp.drop.setExact(MatchField.ETH_TYPE, this.dl_type); } } else { //wildcards.allow &= ~OFMatch.OFPFW_DL_TYPE; if (!EthType.NONE.equals(this.dl_type)) { adp.allow.setExact(MatchField.ETH_TYPE, this.dl_type); } } } } else if (dl_type.equals(EthType.IPv4)) { if (packet.getEtherType() != EthType.IPv4) /* shallow check for equality is okay for EthType */ return false; else { if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_NW_PROTO; if (!IpProtocol.NONE.equals(this.nw_proto)) { adp.drop.setExact(MatchField.IP_PROTO, this.nw_proto); } } else { //wildcards.allow &= ~OFMatch.OFPFW_NW_PROTO; if (!IpProtocol.NONE.equals(this.nw_proto)) { adp.allow.setExact(MatchField.IP_PROTO, this.nw_proto); } } // IP packets, proceed with ip address check pkt_ip = (IPv4) pkt; // IP addresses (src and dst) match? if (any_nw_src == false && !nw_src_prefix_and_mask.matches(pkt_ip.getSourceAddress())) return false; if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_NW_SRC_ALL; //wildcards.drop |= (nw_src_maskbits << OFMatch.OFPFW_NW_SRC_SHIFT); if (!IPv4AddressWithMask.NONE.equals(this.nw_src_prefix_and_mask)) { adp.drop.setMasked(MatchField.IPV4_SRC, nw_src_prefix_and_mask); } } else { //wildcards.allow &= ~OFMatch.OFPFW_NW_SRC_ALL; //wildcards.allow |= (nw_src_maskbits << OFMatch.OFPFW_NW_SRC_SHIFT); if (!IPv4AddressWithMask.NONE.equals(this.nw_src_prefix_and_mask)) { adp.allow.setMasked(MatchField.IPV4_SRC, nw_src_prefix_and_mask); } } if (any_nw_dst == false && !nw_dst_prefix_and_mask.matches(pkt_ip.getDestinationAddress())) return false; if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_NW_DST_ALL; //wildcards.drop |= (nw_dst_maskbits << OFMatch.OFPFW_NW_DST_SHIFT); if (!IPv4AddressWithMask.NONE.equals(this.nw_dst_prefix_and_mask)) { adp.drop.setMasked(MatchField.IPV4_DST, nw_dst_prefix_and_mask); } } else { //wildcards.allow &= ~OFMatch.OFPFW_NW_DST_ALL; //wildcards.allow |= (nw_dst_maskbits << OFMatch.OFPFW_NW_DST_SHIFT); if (!IPv4AddressWithMask.NONE.equals(this.nw_dst_prefix_and_mask)) { adp.allow.setMasked(MatchField.IPV4_DST, nw_dst_prefix_and_mask); } } // nw_proto check if (any_nw_proto == false) { if (nw_proto.equals(IpProtocol.TCP)) { if (!pkt_ip.getProtocol().equals(IpProtocol.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.equals(IpProtocol.UDP)) { if (!pkt_ip.getProtocol().equals(IpProtocol.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.equals(IpProtocol.ICMP)) { if (!pkt_ip.getProtocol().equals(IpProtocol.ICMP)) { return false; } else { // nothing more needed for ICMP } } if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_NW_PROTO; if (!IpProtocol.NONE.equals(this.nw_proto)) { adp.drop.setExact(MatchField.IP_PROTO, this.nw_proto); } } else { //wildcards.allow &= ~OFMatch.OFPFW_NW_PROTO; if (!IpProtocol.NONE.equals(this.nw_proto)) { adp.allow.setExact(MatchField.IP_PROTO, this.nw_proto); } } // TCP/UDP source and destination ports match? if (pkt_tcp != null || pkt_udp != null) { // does the source port match? if (tp_src.getPort() != 0 && tp_src.getPort() != pkt_tp_src.getPort()) { return false; } if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_TP_SRC; if (pkt_tcp != null) { if (!TransportPort.NONE.equals(this.tp_src)) { adp.drop.setExact(MatchField.TCP_SRC, this.tp_src); } } else { if (!TransportPort.NONE.equals(this.tp_src)) { adp.drop.setExact(MatchField.UDP_SRC, this.tp_src); } } } else { //wildcards.allow &= ~OFMatch.OFPFW_TP_SRC; if (pkt_tcp != null) { if (!TransportPort.NONE.equals(this.tp_src)) { adp.allow.setExact(MatchField.TCP_SRC, this.tp_src); } } else { if (!TransportPort.NONE.equals(this.tp_src)) { adp.allow.setExact(MatchField.UDP_SRC, this.tp_src); } } } // does the destination port match? if (tp_dst.getPort() != 0 && tp_dst.getPort() != pkt_tp_dst.getPort()) { return false; } if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_TP_DST; if (pkt_tcp != null) { if (!TransportPort.NONE.equals(this.tp_dst)) { adp.drop.setExact(MatchField.TCP_DST, this.tp_dst); } } else { if (!TransportPort.NONE.equals(this.tp_dst)) { adp.drop.setExact(MatchField.UDP_DST, this.tp_dst); } } } else { //wildcards.allow &= ~OFMatch.OFPFW_TP_DST; if (pkt_tcp != null) { if (!TransportPort.NONE.equals(this.tp_dst)) { adp.allow.setExact(MatchField.TCP_DST, this.tp_dst); } } else { if (!TransportPort.NONE.equals(this.tp_dst)) { adp.allow.setExact(MatchField.UDP_DST, this.tp_dst); } } } } } } } else { // non-IP packet - not supported - report no match return false; } } if (action == FirewallRule.FirewallAction.DROP) { //wildcards.drop &= ~OFMatch.OFPFW_DL_TYPE; if (!EthType.NONE.equals(this.dl_type)) { adp.drop.setExact(MatchField.ETH_TYPE, this.dl_type); } } else { //wildcards.allow &= ~OFMatch.OFPFW_DL_TYPE; if (!EthType.NONE.equals(this.dl_type)) { adp.allow.setExact(MatchField.ETH_TYPE, this.dl_type); } } // all applicable checks passed return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((action == null) ? 0 : action.hashCode()); result = prime * result + (any_dl_dst ? 1231 : 1237); result = prime * result + (any_dl_src ? 1231 : 1237); result = prime * result + (any_dl_type ? 1231 : 1237); result = prime * result + (any_dpid ? 1231 : 1237); result = prime * result + (any_in_port ? 1231 : 1237); result = prime * result + (any_nw_dst ? 1231 : 1237); result = prime * result + (any_nw_proto ? 1231 : 1237); result = prime * result + (any_nw_src ? 1231 : 1237); result = prime * result + (any_tp_dst ? 1231 : 1237); result = prime * result + (any_tp_src ? 1231 : 1237); result = prime * result + ((dl_dst == null) ? 0 : dl_dst.hashCode()); result = prime * result + ((dl_src == null) ? 0 : dl_src.hashCode()); result = prime * result + ((dl_type == null) ? 0 : dl_type.hashCode()); result = prime * result + ((dpid == null) ? 0 : dpid.hashCode()); result = prime * result + ((in_port == null) ? 0 : in_port.hashCode()); result = prime * result + ((nw_dst_prefix_and_mask == null) ? 0 : nw_dst_prefix_and_mask.hashCode()); result = prime * result + ((nw_proto == null) ? 0 : nw_proto.hashCode()); result = prime * result + ((nw_src_prefix_and_mask == null) ? 0 : nw_src_prefix_and_mask.hashCode()); result = prime * result + priority; result = prime * result + ruleid; result = prime * result + ((tp_dst == null) ? 0 : tp_dst.hashCode()); result = prime * result + ((tp_src == null) ? 0 : tp_src.hashCode()); return result; } }