Source code for sts.fingerprints.messages

# Copyright 2011-2013 Colin Scott
# Copyright 2011-2013 Andreas Wundsam
#
# 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.

import binascii

from sts.fingerprints.base import Fingerprint
from pox.openflow.libopenflow_01 import *
from pox.lib.packet.ethernet import *
from pox.lib.packet.lldp import *
from pox.lib.packet.arp import *
from pox.lib.packet.ipv4 import *
import sts.headerspace.config_parser.openflow_parser as hsa

[docs]def process_data(msg): if msg.data == b'': return () else: dp_packet = ethernet(msg.data) return DPFingerprint.from_pkt(dp_packet)
[docs]def process_actions(msg): return tuple("output(%d)" % a.port if isinstance(a, ofp_action_output) else str(type(action)) for a in msg.actions)
[docs]class OFFingerprint(Fingerprint): ''' Fingerprints for openflow messages ''' # ofp_type -> fields to include in fingerprint # TODO(cs): I'm erring on the side of sparseness rather than completeness. We # may need to include more fields here to get an unambiguous fingerprint pkt_type_to_fields = { "ofp_features_reply" : ["datapath_id"], "ofp_switch_config" : ["flags"], "ofp_flow_mod" : ["command", "match", "idle_timeout", "hard_timeout", "priority", "out_port", "flags", "actions"], "ofp_port_mod" : ["port_no", "config", "mask", "advertise"], "ofp_queue_get_config_request" : [], "ofp_queue_get_config_reply" : [], "ofp_stats_request" : ["type", "flags"], "ofp_stats_reply" : ["type", "flags"], "ofp_desc_stats" : [], "ofp_flow_stats_request" : [], "ofp_flow_stats" : [], "ofp_aggregate_stats_request" : [], "ofp_aggregate_stats" : [], "ofp_port_stats_request" : [], "ofp_port_stats" : [], "ofp_queue_stats_request" : [], "ofp_queue_stats" : [], "ofp_packet_out" : ["data", "in_port", "actions"], "ofp_barrier_reply" : [], "ofp_barrier_request" : [], "ofp_packet_in" : ["in_port", "data"], "ofp_flow_removed" : ["match", "reason", "priority"], "ofp_port_status" : ["reason", "desc"], "ofp_error" : ["type", "code"], "ofp_hello" : [], "ofp_echo_request" : [], "ofp_echo_reply" : [], "ofp_vendor_header" : [], "ofp_vendor" : [], # (body of ofp_vendor_header) "ofp_features_request" : [], "ofp_get_config_request" : [], "ofp_get_config_reply" : [], "ofp_set_config" : [] } flow_mod_commands = { v: k.replace("OFPFC_","").lower() for (k,v) in ofp_flow_mod_command_rev_map.iteritems() } special_fields = { # data needs a nested fingerprint 'data' : process_data, # desc is a ofp_phy_port object 'desc' : lambda pkt: (pkt.desc.port_no, pkt.desc.hw_addr.toStr()), # actions is an ordered list # for now, store it as a tuple of just the names of the action types 'actions' : process_actions, # match has a bunch of crazy fields # Trick: convert it to an hsa match, and extract the human readable string # for the hsa match 'match' : lambda pkt: hsa.hs_format["display"](hsa.ofp_match_to_hsa_match(pkt.match)), 'command': lambda ofp: OFFingerprint.flow_mod_commands[ofp.command] }
[docs] def __init__(self, field2value): if type(field2value) == OFFingerprint: field2value = field2value._field2value # Convert matches to DPFingerprint objects for field, value in field2value.iteritems(): if type(value) == dict: field2value[field] = DPFingerprint(value) super(OFFingerprint, self).__init__(field2value)
@staticmethod
[docs] def from_pkt(pkt): pkt_type = type(pkt).__name__ if pkt_type not in OFFingerprint.pkt_type_to_fields: raise ValueError("Unknown pkt_type %s" % pkt_type) field2value = {} field2value["class"] = pkt_type fields = OFFingerprint.pkt_type_to_fields[pkt_type] for field in fields: if field in OFFingerprint.special_fields: value = OFFingerprint.special_fields[field](pkt) else: value = getattr(pkt, field) field2value[field] = value return OFFingerprint(field2value)
[docs] def human_str(self): return "%s: " % self._field2value["class"] + \ ", ".join("%s=%s" % (k, v) for (k,v) in self._field2value.iteritems() if k != "class" )
def __hash__(self): hash = 0 class_name = self._field2value["class"] hash += class_name.__hash__() # Note that the order is important for field in self.pkt_type_to_fields[class_name]: hash += self._field2value[field].__hash__() return hash def __eq__(self, other): if type(other) != OFFingerprint: return False if self._field2value["class"] != other._field2value["class"]: return False klass = self._field2value["class"] for field in self.pkt_type_to_fields[klass]: ###### NOTE: do /not/ use the '!=' operator here, this doesn't invoke an override __eq__ method if not (self._field2value[field] == other._field2value[field]): return False return True
[docs]class DPFingerprint(Fingerprint): ''' Fingerprints for dataplane messages ''' fields = ['dl_src', 'dl_dst', 'nw_src', 'nw_dst']
[docs] def __init__(self, field2value): if type(field2value) == DPFingerprint: field2value = field2value._field2value super(DPFingerprint, self).__init__(field2value)
@staticmethod
[docs] def from_pkt(pkt): # For now, just take (src MAC, dst MAC, src IP, dst IP) as the fingerprint for # dataplane packets # TODO(cs): might finer granularity later eth = pkt ip = pkt.next if type(ip) == lldp: return DPFingerprint({'class': 'lldp'}) elif type(ip) == ipv4: field2value = {'dl_src': eth.src.toStr(), 'dl_dst': eth.dst.toStr(), 'nw_src': ip.srcip.toStr(), 'nw_dst': ip.dstip.toStr()} return DPFingerprint(field2value) elif type(ip) == arp: # TODO(cs): should include more context return DPFingerprint({'class': 'arp'}) elif type(ip) == str: return DPFingerprint({'dl_type' : eth.type }) else: raise ValueError("Unknown dataplane packet type %s (eth type 0x%x)" % (str(type(ip)), eth.type))
def __hash__(self): hash = 0 if 'class' in self._field2value and len(self._field2value) == 1: # This is not an IP packet -- it could be, e.g., an LLDAP packet hash += self._field2value['class'].__hash__() return hash if 'dl_type' in self._field2value and len(self._field2value) == 1: # This is not an IP packet -- it could be, e.g., an LLDAP packet hash += self._field2value['dl_type'].__hash__() return hash # Else it's an IP packet # Note that the order is important for field in self.fields: hash += self._field2value[field].__hash__() return hash def __eq__(self, other): if type(other) != DPFingerprint: return False if len(self._field2value) != len(other._field2value): return False if 'dl_type' in self._field2value: return ('dl_type' in other._field2value and self._field2value['dl_type'] == other._field2value['dl_type']) if 'class' in self._field2value: return ('class' in other._field2value and self._field2value['class'] == other._field2value['class']) for field in self.fields: if self._field2value[field] != other._field2value[field]: return False return True