##! Zeek's OpenFlow control framework. ##! ##! This plugin-based framework allows to control OpenFlow capable ##! switches by implementing communication to an OpenFlow controller ##! via plugins. The framework has to be instantiated via the new function ##! in one of the plugins. This framework only offers very low-level ##! functionality; if you want to use OpenFlow capable switches, e.g., ##! for shunting, please look at the NetControl framework, which provides higher ##! level functions and can use the OpenFlow framework as a backend. module OpenFlow; @load ./consts @load ./types export { ## Global flow_mod function. ## ## controller: The controller which should execute the flow modification. ## ## match: The ofp_match record which describes the flow to match. ## ## flow_mod: The openflow flow_mod record which describes the action to take. ## ## Returns: F on error or if the plugin does not support the operation, T when the operation was queued. global flow_mod: function(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool; ## Clear the current flow table of the controller. ## ## controller: The controller which should execute the flow modification. ## ## Returns: F on error or if the plugin does not support the operation, T when the operation was queued. global flow_clear: function(controller: Controller): bool; ## Event confirming successful modification of a flow rule. ## ## name: The unique name of the OpenFlow controller from which this event originated. ## ## match: The ofp_match record which describes the flow to match. ## ## flow_mod: The openflow flow_mod record which describes the action to take. ## ## msg: An optional informational message by the plugin. global flow_mod_success: event(name: string, match: ofp_match, flow_mod: ofp_flow_mod, msg: string &default=""); ## Reports an error while installing a flow Rule. ## ## name: The unique name of the OpenFlow controller from which this event originated. ## ## match: The ofp_match record which describes the flow to match. ## ## flow_mod: The openflow flow_mod record which describes the action to take. ## ## msg: Message to describe the event. global flow_mod_failure: event(name: string, match: ofp_match, flow_mod: ofp_flow_mod, msg: string &default=""); ## Reports that a flow was removed by the switch because of either the hard or the idle timeout. ## This message is only generated by controllers that indicate that they support flow removal ## in supports_flow_removed. ## ## name: The unique name of the OpenFlow controller from which this event originated. ## ## match: The ofp_match record which was used to create the flow. ## ## cookie: The cookie that was specified when creating the flow. ## ## priority: The priority that was specified when creating the flow. ## ## reason: The reason for flow removal (OFPRR_*). ## ## duration_sec: Duration of the flow in seconds. ## ## packet_count: Packet count of the flow. ## ## byte_count: Byte count of the flow. global flow_removed: event(name: string, match: ofp_match, cookie: count, priority: count, reason: count, duration_sec: count, idle_timeout: count, packet_count: count, byte_count: count); ## Convert a conn_id record into an ofp_match record that can be used to ## create match objects for OpenFlow. ## ## id: The conn_id record that describes the record. ## ## reverse: Reverse the sources and destinations when creating the match record (default F). ## ## Returns: ofp_match object for the conn_id record. global match_conn: function(id: conn_id, reverse: bool &default=F): ofp_match; # ### # ### Low-level functions for cookie handling and plugin registration. # ### ## Function to get the unique id out of a given cookie. ## ## cookie: The openflow match cookie. ## ## Returns: The cookie unique id. global get_cookie_uid: function(cookie: count): count; ## Function to get the group id out of a given cookie. ## ## cookie: The openflow match cookie. ## ## Returns: The cookie group id. global get_cookie_gid: function(cookie: count): count; ## Function to generate a new cookie using our group id. ## ## cookie: The openflow match cookie. ## ## Returns: The cookie group id. global generate_cookie: function(cookie: count &default=0): count; ## Function to register a controller instance. This function ## is called automatically by the plugin _new functions. ## ## tpe: Type of this plugin. ## ## name: Unique name of this controller instance. ## ## controller: The controller to register. global register_controller: function(tpe: OpenFlow::Plugin, name: string, controller: Controller); ## Function to unregister a controller instance. This function ## should be called when a specific controller should no longer ## be used. ## ## controller: The controller to unregister. global unregister_controller: function(controller: Controller); ## Function to signal that a controller finished activation and is ## ready to use. Will throw the ``OpenFlow::controller_activated`` ## event. global controller_init_done: function(controller: Controller); ## Event that is raised once a controller finishes initialization ## and is completely activated. ## name: Unique name of this controller instance. ## ## controller: The controller that finished activation. global OpenFlow::controller_activated: event(name: string, controller: Controller); ## Function to lookup a controller instance by name. ## ## name: Unique name of the controller to look up. ## ## Returns: One element vector with controller, if found. Empty vector otherwise. global lookup_controller: function(name: string): vector of Controller; } global name_to_controller: table[string] of Controller; function match_conn(id: conn_id, reverse: bool &default=F): ofp_match { local dl_type = ETH_IPv4; local proto = IP_TCP; local orig_h: addr; local orig_p: port; local resp_h: addr; local resp_p: port; if ( reverse == F ) { orig_h = id$orig_h; orig_p = id$orig_p; resp_h = id$resp_h; resp_p = id$resp_p; } else { orig_h = id$resp_h; orig_p = id$resp_p; resp_h = id$orig_h; resp_p = id$orig_p; } if ( is_v6_addr(orig_h) ) dl_type = ETH_IPv6; if ( is_udp_port(orig_p) ) proto = IP_UDP; else if ( is_icmp_port(orig_p) ) proto = IP_ICMP; return ofp_match( $dl_type=dl_type, $nw_proto=proto, $nw_src=addr_to_subnet(orig_h), $tp_src=port_to_count(orig_p), $nw_dst=addr_to_subnet(resp_h), $tp_dst=port_to_count(resp_p) ); } # local function to forge a flow_mod cookie for this framework. # all flow entries from the openflow framework should have the # 42 bit of the cookie set. function generate_cookie(cookie: count &default=0): count { local c = ZEEK_COOKIE_ID * COOKIE_BID_START; if ( cookie >= COOKIE_UID_SIZE ) Reporter::warning(fmt("The given cookie uid '%d' is > 32bit and will be discarded", cookie)); else c += cookie; return c; } # local function to check if a given flow_mod cookie is forged from this framework. function is_valid_cookie(cookie: count): bool { if ( cookie / COOKIE_BID_START == ZEEK_COOKIE_ID ) return T; Reporter::warning(fmt("The given Openflow cookie '%d' is not valid", cookie)); return F; } function get_cookie_uid(cookie: count): count { if( is_valid_cookie(cookie) ) return (cookie - ((cookie / COOKIE_GID_START) * COOKIE_GID_START)); return INVALID_COOKIE; } function get_cookie_gid(cookie: count): count { if( is_valid_cookie(cookie) ) return ( (cookie - (COOKIE_BID_START * ZEEK_COOKIE_ID) - (cookie - ((cookie / COOKIE_GID_START) * COOKIE_GID_START))) / COOKIE_GID_START ); return INVALID_COOKIE; } function controller_init_done(controller: Controller) { if ( controller$state$_name !in name_to_controller ) { Reporter::error(fmt("Openflow initialized unknown plugin %s successfully?", controller$state$_name)); return; } controller$state$_activated = T; event OpenFlow::controller_activated(controller$state$_name, controller); } # Functions that are called from cluster.zeek and non-cluster.zeek function register_controller_impl(tpe: OpenFlow::Plugin, name: string, controller: Controller) { if ( controller$state$_name in name_to_controller ) { Reporter::error(fmt("OpenFlow Controller %s was already registered. Ignored duplicate registration", controller$state$_name)); return; } name_to_controller[controller$state$_name] = controller; if ( controller?$init ) controller$init(controller$state); else controller_init_done(controller); } function unregister_controller_impl(controller: Controller) { if ( controller$state$_name in name_to_controller ) delete name_to_controller[controller$state$_name]; else Reporter::error("OpenFlow Controller %s was not registered in unregister."); if ( controller?$destroy ) controller$destroy(controller$state); } function lookup_controller_impl(name: string): vector of Controller { if ( name in name_to_controller ) return vector(name_to_controller[name]); else return vector(); }