/* * Author: Nick Karanatsios * * Copyright (C) 2008-2012 NEC Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "trema.h" #include "ruby.h" #include "action-common.h" extern VALUE mTrema; VALUE cStatsReply; /* * A {StatsReply} instance that encapsulates the header part of the * +OFPT_STATS_REPLY+ message. The body of the reply message may be an array * of one or more specific reply objects designated by the type. * The user would not instantiate stats. reply objects explicitly, the stats. * reply handler would normally do that while parsing the message. * * @overload initialize(options={}) * * @example * StatsReply.new( * :datapath_id => 0xabc, * :transaction_id => 123, * :type => OFPST_FLOW * :flags => 0, * :stats => [FlowStatsReply] * ) * * @param [Hash] options the options hash. * * @option options [Number] :datapath_id * message originator identifier. * * @option options [Number] :transaction_id * transaction_id value carried over from request. * * @option options [Number] :type * type id for the reply. * * @option options [Number] :flags * if set to 1 more replies would follow, 0 for the last reply. * * @option options [Array] :stats * an array of objects associated with the reply instance. * * @return [StatsReply] * an object that encapsulates the +OFPT_STATS_REPLY+ openflow message. */ static VALUE stats_reply_init( VALUE self, VALUE options ) { rb_iv_set( self, "@attribute", options ); return self; } /* * Message originator identifier. * * @return [Number] the value of datapath_id. */ static VALUE stats_reply_datapath_id( VALUE self ) { return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "datapath_id" ) ) ); } /* * Transaction ids, message sequence numbers matching requests to replies. * * @return [Number] the value of transaction_id. */ static VALUE stats_reply_transaction_id( VALUE self ) { return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "transaction_id" ) ) ); } /* * The type of this reply. * * @return [Number] the value of type. */ static VALUE stats_reply_type( VALUE self ) { return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "type" ) ) ); } /* * Flag that indicates if more reply message(s) expected to follow. * * @return [Number] the value of flags. */ static VALUE stats_reply_flags( VALUE self ) { return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "flags" ) ) ); } /* * A list of reply type objects for this message. * * @return [Array] * an array of {FlowStatsReply} objects if type is +OFPST_FLOW+. * @return [Array] * an array of {TableStatsReply} objects if type is +OFPST_TABLE+. * @return [AggregateStatsReply] * a {AggregateStatsReply} object if type is +OFPST_AGGREGATE+. * @return [Array] * an array of {PortStatsReply} objects if type is +OFPST_PORT+. * @return [Array] * an array of {QueueStatsReply} objects if type is +OFPST_QUEUE+. * @return [VendorStatsReply] * a {VendorStatsReply} object if type is +OFPST_VENDOR+. */ static VALUE stats_reply_stats( VALUE self ) { return rb_hash_aref( rb_iv_get( self, "@attribute" ), ID2SYM( rb_intern( "stats" ) ) ); } void Init_stats_reply() { rb_require( "trema/flow-stats-reply" ); rb_require( "trema/aggregate-stats-reply" ); rb_require( "trema/table-stats-reply" ); rb_require( "trema/port-stats-reply" ); rb_require( "trema/queue-stats-reply" ); rb_require( "trema/vendor-stats-reply" ); cStatsReply = rb_define_class_under( mTrema, "StatsReply", rb_cObject ); rb_define_method( cStatsReply, "initialize", stats_reply_init, 1 ); rb_define_method( cStatsReply, "datapath_id", stats_reply_datapath_id, 0 ); rb_define_method( cStatsReply, "transaction_id", stats_reply_transaction_id, 0 ); rb_define_method( cStatsReply, "type", stats_reply_type, 0 ); rb_define_method( cStatsReply, "flags", stats_reply_flags, 0 ); rb_define_method( cStatsReply, "stats", stats_reply_stats, 0 ); } static VALUE get_action( const struct ofp_action_header *ah ) { VALUE action = Qnil; VALUE options = rb_hash_new(); switch ( ah->type ) { case OFPAT_OUTPUT: { const struct ofp_action_output *ao = ( const struct ofp_action_output * ) ah; rb_hash_aset( options, ID2SYM( rb_intern( "port" ) ), UINT2NUM( ao->port ) ); action = rb_funcall( rb_eval_string( "Trema::ActionOutput" ), rb_intern( "new" ), 1, options ); } break; case OFPAT_SET_VLAN_VID: { const struct ofp_action_vlan_vid *action_vlan_vid = ( const struct ofp_action_vlan_vid * ) ah; rb_hash_aset( options, ID2SYM( rb_intern( "vlan_vid" ) ), UINT2NUM( action_vlan_vid->vlan_vid ) ); action = rb_funcall( rb_eval_string( "Trema::ActionSetVlanVid" ), rb_intern( "new" ), 1, options ); } break; case OFPAT_SET_VLAN_PCP: { const struct ofp_action_vlan_pcp *action_vlan_pcp = ( const struct ofp_action_vlan_pcp * ) ah; rb_hash_aset( options, ID2SYM( rb_intern( "vlan_pcp" ) ), UINT2NUM( action_vlan_pcp->vlan_pcp ) ); action = rb_funcall( rb_eval_string( "Trema::ActionSetVlanPcp" ), rb_intern( "new" ), 1, options ); } break; case OFPAT_STRIP_VLAN: { action = rb_funcall( rb_eval_string( "Trema::ActionStripVlan" ), rb_intern( "new" ), 0 ); } break; case OFPAT_SET_DL_SRC: case OFPAT_SET_DL_DST: { VALUE dl_addr; const struct ofp_action_dl_addr *action_dl_addr = ( const struct ofp_action_dl_addr * ) ah; dl_addr = rb_funcall( rb_eval_string( "Trema::Mac" ), rb_intern( "new" ), 1, ULL2NUM( mac_to_uint64( action_dl_addr->dl_addr ) ) ); if ( ah->type == OFPAT_SET_DL_SRC ) { rb_hash_aset( options, ID2SYM( rb_intern( "dl_src" ) ), dl_addr ); action = rb_funcall( rb_eval_string( "Trema::ActionSetDlSrc" ), rb_intern( "new" ), 1, options ); } else { rb_hash_aset( options, ID2SYM( rb_intern( "dl_dst" ) ), dl_addr ); action = rb_funcall( rb_eval_string( "Trema::ActionSetDlDst" ), rb_intern( "new" ), 1, options ); } } break; case OFPAT_SET_NW_SRC: case OFPAT_SET_NW_DST: { VALUE nw_addr; const struct ofp_action_nw_addr *action_nw_addr = ( const struct ofp_action_nw_addr * ) ah; nw_addr = rb_funcall( rb_eval_string( "Trema::IP " ), rb_intern( "new" ), 1, UINT2NUM( action_nw_addr->nw_addr ) ); if ( ah->type == OFPAT_SET_NW_SRC ) { rb_hash_aset( options, ID2SYM( rb_intern( "nw_src" ) ), nw_addr ); action = rb_funcall( rb_eval_string( "Trema::ActionSetNwSrc" ), rb_intern( "new" ), 1, options ); } else { rb_hash_aset( options, ID2SYM( rb_intern( "nw_dst" ) ), nw_addr ); action = rb_funcall( rb_eval_string( "Trema::ActionSetNwDst" ), rb_intern( "new" ), 1, options ); } } break; case OFPAT_SET_NW_TOS: { const struct ofp_action_nw_tos *action_nw_tos = ( const struct ofp_action_nw_tos * ) ah; rb_hash_aset( options, ID2SYM( rb_intern( "nw_tos" ) ), ULL2NUM( action_nw_tos->nw_tos ) ); action = rb_funcall( rb_eval_string( "Trema::ActionSetNwTos" ), rb_intern( "new" ), 1, options ); } break; case OFPAT_SET_TP_SRC: { const struct ofp_action_tp_port *action_tp_port = ( const struct ofp_action_tp_port * ) ah; rb_hash_aset( options, ID2SYM( rb_intern( "tp_src" ) ), ULL2NUM( action_tp_port->tp_port ) ); action = rb_funcall( rb_eval_string( "Trema::ActionSetTpSrc" ), rb_intern( "new" ), 1, options ); } break; case OFPAT_SET_TP_DST: { const struct ofp_action_tp_port *action_tp_port = ( const struct ofp_action_tp_port * ) ah; rb_hash_aset( options, ID2SYM( rb_intern( "tp_dst" ) ), ULL2NUM( action_tp_port->tp_port ) ); action = rb_funcall( rb_eval_string( "Trema::ActionSetTpDst" ), rb_intern( "new" ), 1, options ); } break; case OFPAT_ENQUEUE: { const struct ofp_action_enqueue *action_enqueue = ( const struct ofp_action_enqueue * ) ah; rb_hash_aset( options, ID2SYM( rb_intern( "port" ) ), ULL2NUM( action_enqueue->port ) ); rb_hash_aset( options, ID2SYM( rb_intern( "queue_id" ) ), ULL2NUM( action_enqueue->queue_id ) ); action = rb_funcall( rb_eval_string( "Trema::ActionEnqueue" ), rb_intern( "new" ), 1, options ); } break; case OFPAT_VENDOR: { const struct ofp_action_vendor_header *action_vendor = ( const struct ofp_action_vendor_header * ) ah; rb_hash_aset( options, ID2SYM( rb_intern( "vendor" ) ), ULL2NUM( action_vendor->vendor ) ); action = rb_funcall( rb_eval_string( "Trema::ActionVendor" ), rb_intern( "new" ), 1, options ); } break; } return action; } void handle_stats_reply( uint64_t datapath_id, uint32_t transaction_id, uint16_t type, uint16_t flags, const buffer *body, void *user_data ) { VALUE controller = ( VALUE ) user_data; if ( rb_respond_to( controller, rb_intern( "stats_reply" ) ) == Qfalse ) { return; } if ( body == NULL ) { return; } if ( !body->length ) { return; } VALUE attributes = rb_hash_new(); rb_hash_aset( attributes, ID2SYM( rb_intern( "datapath_id" ) ), ULL2NUM( datapath_id ) ); rb_hash_aset( attributes, ID2SYM( rb_intern( "transaction_id" ) ), UINT2NUM( transaction_id ) ); rb_hash_aset( attributes, ID2SYM( rb_intern( "type" ) ), UINT2NUM( type ) ); rb_hash_aset( attributes, ID2SYM( rb_intern( "flags" ) ), UINT2NUM( flags ) ); uint16_t body_length = ( uint16_t ) body->length; switch ( type ) { case OFPST_FLOW: { struct ofp_flow_stats *flow_stats; struct ofp_action_header *ah; VALUE flow_stats_arr = rb_ary_new(); VALUE flow_stats_reply; VALUE match_obj; VALUE options = rb_hash_new(); VALUE actions_arr = rb_ary_new(); flow_stats = ( struct ofp_flow_stats * ) body->data; while ( body_length > 0 ) { match_obj = rb_funcall( rb_eval_string( "Match.new" ), rb_intern( "replace" ), 1, Data_Wrap_Struct( cStatsReply, NULL, NULL, &flow_stats->match ) ); rb_hash_aset( options, ID2SYM( rb_intern( "length" ) ), UINT2NUM( flow_stats->length ) ); rb_hash_aset( options, ID2SYM( rb_intern( "table_id" ) ), UINT2NUM( flow_stats->table_id ) ); rb_hash_aset( options, ID2SYM( rb_intern( "match" ) ), match_obj ); rb_hash_aset( options, ID2SYM( rb_intern( "duration_sec" ) ), UINT2NUM( flow_stats->duration_sec ) ); rb_hash_aset( options, ID2SYM( rb_intern( "duration_nsec" ) ), UINT2NUM( flow_stats->duration_nsec ) ); rb_hash_aset( options, ID2SYM( rb_intern( "priority" ) ), UINT2NUM( flow_stats->priority ) ); rb_hash_aset( options, ID2SYM( rb_intern( "idle_timeout" ) ), UINT2NUM( flow_stats->idle_timeout ) ); rb_hash_aset( options, ID2SYM( rb_intern( "hard_timeout" ) ), UINT2NUM( flow_stats->hard_timeout ) ); rb_hash_aset( options, ID2SYM( rb_intern( "cookie" ) ), ULL2NUM( flow_stats->cookie ) ); rb_hash_aset( options, ID2SYM( rb_intern( "packet_count" ) ), ULL2NUM( flow_stats->packet_count ) ); rb_hash_aset( options, ID2SYM( rb_intern( "byte_count" ) ), ULL2NUM( flow_stats->byte_count ) ); uint16_t actions_length = ( uint16_t ) ( flow_stats->length - offsetof( struct ofp_flow_stats, actions ) ); ah = ( struct ofp_action_header * ) flow_stats->actions; while ( actions_length > 0 ) { rb_ary_push( actions_arr, get_action( ah ) ); actions_length = ( uint16_t ) ( actions_length - ah->len ); if ( actions_length ) { ah = ( struct ofp_action_header * ) ( ( char * ) ah + ah->len ); } } rb_hash_aset( options, ID2SYM( rb_intern( "actions" ) ), actions_arr ); flow_stats_reply = rb_funcall( rb_eval_string( "Trema::FlowStatsReply" ), rb_intern( "new" ), 1, options ); rb_ary_push( flow_stats_arr, flow_stats_reply ); // here create flow_stats object and insert into array body_length = ( uint16_t ) ( body_length - flow_stats->length ); if ( body_length ) { // move pointer to next entry flow_stats = ( struct ofp_flow_stats * ) ( ( char * ) flow_stats + flow_stats->length ); } } rb_hash_aset( attributes, ID2SYM( rb_intern( "stats" ) ), flow_stats_arr ); } break; case OFPST_AGGREGATE: { struct ofp_aggregate_stats_reply *aggregate_stats = ( struct ofp_aggregate_stats_reply * ) body->data; VALUE options = rb_hash_new(); VALUE aggregate_stats_reply; VALUE aggregate_stats_arr = rb_ary_new(); rb_hash_aset( options, ID2SYM( rb_intern( "packet_count" ) ), ULL2NUM( aggregate_stats->packet_count ) ); rb_hash_aset( options, ID2SYM( rb_intern( "byte_count" ) ), ULL2NUM( aggregate_stats->byte_count ) ); rb_hash_aset( options, ID2SYM( rb_intern( "flow_count" ) ), UINT2NUM( aggregate_stats->flow_count ) ); aggregate_stats_reply = rb_funcall( rb_eval_string( " Trema::AggregateStatsReply" ), rb_intern( "new" ), 1, options ); rb_ary_push( aggregate_stats_arr, aggregate_stats_reply ); rb_hash_aset( attributes, ID2SYM( rb_intern( "stats" ) ), aggregate_stats_arr ); } break; case OFPST_TABLE: { struct ofp_table_stats *table_stats; VALUE table_stats_arr = rb_ary_new(); VALUE options = rb_hash_new(); VALUE table_stats_reply; table_stats = ( struct ofp_table_stats * ) body->data; while ( body_length > 0 ) { rb_hash_aset( options, ID2SYM( rb_intern( "table_id" ) ), UINT2NUM( table_stats->table_id ) ); rb_hash_aset( options, ID2SYM( rb_intern( "name" ) ), rb_str_new2( table_stats->name ) ); rb_hash_aset( options, ID2SYM( rb_intern( "wildcards" ) ), UINT2NUM( table_stats->wildcards ) ); rb_hash_aset( options, ID2SYM( rb_intern( "max_entries" ) ), UINT2NUM( table_stats->max_entries ) ); rb_hash_aset( options, ID2SYM( rb_intern( "active_count" ) ), UINT2NUM( table_stats->active_count ) ); rb_hash_aset( options, ID2SYM( rb_intern( "lookup_count" ) ), ULL2NUM( table_stats->active_count ) ); rb_hash_aset( options, ID2SYM( rb_intern( "matched_count" ) ), ULL2NUM( table_stats->matched_count ) ); table_stats_reply = rb_funcall( rb_eval_string( "Trema::TableStatsReply" ), rb_intern( "new" ), 1, options ); rb_ary_push( table_stats_arr, table_stats_reply ); body_length = ( uint16_t ) ( body_length - sizeof ( struct ofp_table_stats ) ); if ( body_length ) { table_stats++; } } rb_hash_aset( attributes, ID2SYM( rb_intern( "stats" ) ), table_stats_arr ); } break; case OFPST_PORT: { struct ofp_port_stats *port_stats; VALUE port_stats_arr = rb_ary_new(); VALUE options = rb_hash_new(); VALUE port_stats_reply; port_stats = ( struct ofp_port_stats * ) body->data; while ( body_length > 0 ) { rb_hash_aset( options, ID2SYM( rb_intern( "port_no" ) ), UINT2NUM( port_stats->port_no ) ); rb_hash_aset( options, ID2SYM( rb_intern( "rx_packets" ) ), ULL2NUM( port_stats->rx_packets ) ); rb_hash_aset( options, ID2SYM( rb_intern( "tx_packets" ) ), ULL2NUM( port_stats->tx_packets ) ); rb_hash_aset( options, ID2SYM( rb_intern( "rx_bytes" ) ), ULL2NUM( port_stats->rx_bytes ) ); rb_hash_aset( options, ID2SYM( rb_intern( "tx_bytes" ) ), ULL2NUM( port_stats->tx_bytes ) ); rb_hash_aset( options, ID2SYM( rb_intern( "rx_dropped" ) ), ULL2NUM( port_stats->rx_dropped ) ); rb_hash_aset( options, ID2SYM( rb_intern( "tx_dropped" ) ), ULL2NUM( port_stats->tx_dropped ) ); rb_hash_aset( options, ID2SYM( rb_intern( "rx_errors" ) ), ULL2NUM( port_stats->rx_errors ) ); rb_hash_aset( options, ID2SYM( rb_intern( "tx_errors" ) ), ULL2NUM( port_stats->tx_errors ) ); rb_hash_aset( options, ID2SYM( rb_intern( "rx_frame_err" ) ), ULL2NUM( port_stats->rx_frame_err ) ); rb_hash_aset( options, ID2SYM( rb_intern( "rx_over_err" ) ), ULL2NUM( port_stats->rx_over_err ) ); rb_hash_aset( options, ID2SYM( rb_intern( "rx_crc_err" ) ), ULL2NUM( port_stats->rx_crc_err ) ); rb_hash_aset( options, ID2SYM( rb_intern( "collisions" ) ), ULL2NUM( port_stats->collisions ) ); port_stats_reply = rb_funcall( rb_eval_string( "Trema::PortStatsReply" ), rb_intern( "new" ), 1, options ); rb_ary_push( port_stats_arr, port_stats_reply ); body_length = ( uint16_t ) ( body_length - sizeof ( struct ofp_port_stats ) ); if ( body_length ) { port_stats++; } } rb_hash_aset( attributes, ID2SYM( rb_intern( "stats" ) ), port_stats_arr ); } break; case OFPST_QUEUE: { struct ofp_queue_stats *queue_stats; VALUE queue_stats_arr = rb_ary_new(); VALUE options = rb_hash_new(); VALUE queue_stats_reply; queue_stats = ( struct ofp_queue_stats * ) body->data; while ( body_length > 0 ) { rb_hash_aset( options, ID2SYM( rb_intern( "port_no" ) ), UINT2NUM( queue_stats->port_no ) ); rb_hash_aset( options, ID2SYM( rb_intern( "queue_id" ) ), UINT2NUM( queue_stats->queue_id ) ); queue_stats_reply = rb_funcall( rb_eval_string( "Trema::QueueStatsReply" ), rb_intern( "new" ), 1, options ); rb_ary_push( queue_stats_arr, queue_stats_reply ); body_length = ( uint16_t ) ( body_length - sizeof ( struct ofp_queue_stats ) ); if ( body_length ) { queue_stats++; } } rb_hash_aset( attributes, ID2SYM( rb_intern( "stats" ) ), queue_stats_arr ); } break; case OFPST_VENDOR: { uint32_t *vendor_id; VALUE options = rb_hash_new(); VALUE vendor_stats_arr = rb_ary_new2( 1 ); VALUE vendor_stats_reply; vendor_id = ( uint32_t * ) body->data; rb_hash_aset( options, ID2SYM( rb_intern( "vendor_id" ) ), UINT2NUM( *vendor_id ) ); vendor_stats_reply = rb_funcall( rb_eval_string( "Trema::VendorStatsReply" ), rb_intern( "new" ), 1, options ); rb_ary_push( vendor_stats_arr, vendor_stats_reply ); rb_hash_aset( attributes, ID2SYM( rb_intern( "stats" ) ), vendor_stats_arr ); } break; default: critical( "Unhandled stats type ( type = %u ),", type ); break; } VALUE r_stats_reply = rb_funcall( cStatsReply, rb_intern( "new" ), 1, attributes ); rb_funcall( controller, rb_intern( "stats_reply" ), 1, r_stats_reply ); } /* * Local variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */