OFDPE delete_flow_entries( const uint8_t table_id, const match *match, const uint64_t cookie, const uint64_t cookie_mask, uint32_t out_port, uint32_t out_group ) { if ( !valid_table_id( table_id ) && table_id != FLOW_TABLE_ALL ) { return ERROR_OFDPE_FLOW_MOD_FAILED_BAD_TABLE_ID; } if ( !lock_pipeline() ) { return ERROR_LOCK; } list_element *delete_us = NULL; if ( table_id != FLOW_TABLE_ALL ) { delete_us = lookup_flow_entries_with_table_id( table_id, match, 0, false, false ); } else { delete_us = lookup_flow_entries_from_all_tables( match, 0, false, false ); } delete_flow_entries_in_list( delete_us, cookie, cookie_mask, out_port, out_group, OFPRR_DELETE ); if ( delete_us != NULL ) { delete_list( delete_us ); } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }
OFDPE handle_received_frame( const switch_port *port, buffer *frame ) { assert( port != NULL ); assert( frame != NULL ); debug( "Handling received frame ( port_no = %u, frame = %p, user_data = %p ).", port->port_no, frame, frame->user_data ); if ( frame->user_data == NULL ) { bool ret = parse_packet( frame ); if ( !ret ) { warn( "Failed to parse a received frame ( port_no = %u, frame = %p ).", port->port_no, frame ); return OFDPE_FAILED; } } assert( frame->user_data != NULL ); ( ( packet_info * ) frame->user_data )->eth_in_port = port->port_no; ( ( packet_info * ) frame->user_data )->eth_in_phy_port = port->port_no; if ( !trylock_pipeline() ) { return ERROR_LOCK; } process_received_frame( port, frame ); if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }
OFDPE update_port( const uint32_t port_no, uint32_t config, uint32_t mask ) { assert( port_no > 0 && port_no <= OFPP_MAX ); if ( datapath_is_running() && !lock_pipeline() ) { return ERROR_LOCK; } switch_port *port = lookup_switch_port( port_no ); if ( port == NULL ) { return ERROR_OFDPE_PORT_MOD_FAILED_BAD_PORT; } bool ret = update_switch_port_config( port, config, mask ); if ( !ret ) { error( "Failed to update switch port config ( port_no = %u, config = %#x, mask = %#x ).", port_no, config, mask ); } if ( datapath_is_running() && !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret ? OFDPE_SUCCESS : ERROR_OFDPE_PORT_MOD_FAILED_BAD_CONFIG; }
OFDPE get_table_stats( table_stats **stats, uint8_t *n_tables ) { assert( stats != NULL ); assert( n_tables != NULL ); if ( !lock_pipeline() ) { return ERROR_LOCK; } *stats = xmalloc( sizeof( table_stats ) * N_FLOW_TABLES ); memset( *stats, 0, sizeof( table_stats ) * N_FLOW_TABLES ); *n_tables = 0; table_stats *stat = *stats; for ( uint8_t i = 0; i <= FLOW_TABLE_ID_MAX; i++ ) { stat->table_id = i; stat->active_count = get_active_count( i ); stat->lookup_count = get_lookup_count( i ); stat->matched_count = get_matched_count( i ); stat++; ( *n_tables )++; } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }
OFDPE get_meter_stats( const uint32_t meter_id, meter_entry** entries, uint32_t *count ) { OFDPE ret = OFDPE_SUCCESS; if ( !lock_pipeline() ) { return ERROR_UNLOCK; } *entries = NULL; *count = 0; if ( meter_id == OFPM_ALL ) { *count = list_length_of(table->entries); meter_entry *head = xcalloc(*count, sizeof(meter_entry)); int i=0; for ( list_element *e = table->entries; e != NULL; e=e->next,i++ ) { clone_meter_entry( head+i, e->data ); } *entries = head; } else { meter_entry *entry = lookup_meter_entry( meter_id ); if ( entry == NULL ) { ret = ERROR_OFDPE_METER_MOD_FAILED_UNKNOWN_METER; } else { *count = 1; *entries = clone_meter_entry( NULL, entry ); } } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
flow_entry * lookup_flow_entry_strict( const uint8_t table_id, const match *match, const uint16_t priority ) { assert( valid_table_id( table_id ) || table_id == FLOW_TABLE_ALL ); if ( !lock_pipeline() ) { return NULL; } // FIXME: allocating/freeing linked list elements may cost. list_element *list = NULL; if ( table_id != FLOW_TABLE_ALL ) { list = lookup_flow_entries_with_table_id( table_id, match, priority, true, true ); } else { list = lookup_flow_entries_from_all_tables( match, priority, true, true ); } flow_entry *entry = NULL; if ( list != NULL ) { entry = list->data; delete_list( list ); } if ( !unlock_pipeline() ) { return NULL; } return entry; }
group_entry * lookup_group_entry( const uint32_t group_id ) { assert( table != NULL ); assert( valid_group_id( group_id ) ); if ( !lock_pipeline() ) { return NULL; } group_entry *entry = NULL; bool found = false; for ( list_element *element = table->entries; element != NULL; element = element->next ) { if ( element->data == NULL ) { continue; } entry = element->data; if ( entry->group_id == group_id ) { found = true; break; } } if ( !unlock_pipeline() ) { return NULL; } return found == true ? entry : NULL; }
OFDPE add_group_entry( group_entry *entry ) { assert( table != NULL ); if ( entry == NULL ) { return ERROR_INVALID_PARAMETER; } if ( !valid_group_id( entry->group_id ) ) { return ERROR_OFDPE_GROUP_MOD_FAILED_INVALID_GROUP; } if ( group_exists( entry->group_id ) ) { return ERROR_OFDPE_GROUP_MOD_FAILED_GROUP_EXISTS; } if ( !lock_pipeline() ) { return ERROR_LOCK; } OFDPE ret = validate_group_entry( entry ); if ( ret == OFDPE_SUCCESS ) { append_to_tail( &table->entries, entry ); } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
OFDPE add_meter_entry( const uint16_t flags, const uint32_t meter_id, const list_element *bands ) { assert( table != NULL ); if ( meter_id == 0 || ( meter_id > OFPM_MAX && meter_id != OFPM_CONTROLLER )) { return ERROR_OFDPE_METER_MOD_FAILED_INVALID_METER; } meter_entry *entry = alloc_meter_entry( flags, meter_id, bands ); if ( entry == NULL ) { return ERROR_INVALID_PARAMETER; } OFDPE ret = OFDPE_SUCCESS; if ( !lock_pipeline() ) { free_meter_entry( entry ); return ERROR_LOCK; } if ( NULL != lookup_meter_entry( entry->meter_id ) ) { ret = ERROR_OFDPE_METER_MOD_FAILED_METER_EXISTS; free_meter_entry( entry ); } else { append_to_tail( &table->entries, entry ); } if ( !unlock_pipeline() ) { free_meter_entry( entry ); return ERROR_UNLOCK; } return ret; }
OFDPE delete_flow_entries_by_group_id( const uint32_t group_id ) { assert( valid_group_id( group_id ) ); if ( !lock_pipeline() ) { return ERROR_LOCK; } list_element *delete_us = NULL; create_list( &delete_us ); for ( uint8_t table_id = 0; table_id <= FLOW_TABLE_ID_MAX; table_id++ ) { flow_table *table = get_flow_table( table_id ); assert( table != NULL ); for ( list_element *e = table->entries; e != NULL; e = e->next ) { assert( e->data != NULL ); flow_entry *entry = e->data; if ( instructions_have_output_group( entry->instructions, group_id ) ) { append_to_tail( &delete_us, e->data ); } } } delete_flow_entries_in_list( delete_us, 0, 0, OFPP_ANY, OFPG_ANY, OFPRR_GROUP_DELETE ); if ( delete_us != NULL ) { delete_list( delete_us ); } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }
void dump_flow_table( const uint8_t table_id, void dump_function( const char *format, ... ) ) { assert( valid_table_id( table_id ) ); assert( dump_function != NULL ); if ( !lock_pipeline() ) { return; } flow_table *table = get_flow_table( table_id ); assert( table != NULL ); ( *dump_function )( "#### TABLE %#x (%s) ####", table_id, table->initialized ? "initialized" : "not initialized yet" ); ( *dump_function )( "[Features]" ); ( *dump_function )( "table_id: %#x", table->features.table_id ); ( *dump_function )( "name: %s", table->features.name ); ( *dump_function )( "metadata_match: %#" PRIx64, table->features.metadata_match ); ( *dump_function )( "metadata_write: %#" PRIx64, table->features.metadata_write ); ( *dump_function )( "config: %#x", table->features.config ); ( *dump_function )( "max_entries: %u", table->features.max_entries ); ( *dump_function )( "instructions: %#" PRIx64, table->features.instructions ); ( *dump_function )( "instructions_miss: %#" PRIx64, table->features.instructions_miss ); ( *dump_function )( "write_actions: %#" PRIx64, table->features.write_actions ); ( *dump_function )( "write_actions_miss: %#" PRIx64, table->features.write_actions_miss ); ( *dump_function )( "apply_actions: %#" PRIx64, table->features.apply_actions ); ( *dump_function )( "apply_actions_miss: %#" PRIx64, table->features.apply_actions_miss ); ( *dump_function )( "matches: %#" PRIx64, table->features.matches ); ( *dump_function )( "wildcards: %#" PRIx64, table->features.wildcards ); ( *dump_function )( "write_setfield: %#" PRIx64, table->features.write_setfield ); ( *dump_function )( "write_setfield_miss: %#" PRIx64, table->features.write_setfield_miss ); ( *dump_function )( "apply_setfield: %#" PRIx64, table->features.apply_setfield ); ( *dump_function )( "apply_setfield_miss: %#" PRIx64, table->features.apply_setfield_miss ); for ( uint8_t i = 0; i < N_FLOW_TABLES; i++ ) { if ( !table->features.next_table_ids[ i ] ) { ( *dump_function )( "next_table_id: %#x - %s", i, table->features.next_table_ids[ i ] ? "true" : "false" ); } } for ( uint8_t i = 0; i < N_FLOW_TABLES; i++ ) { if ( !table->features.next_table_ids_miss[ i ] ) { ( *dump_function )( "next_table_id_miss: %#x - %s", i, table->features.next_table_ids_miss[ i ] ? "true" : "false" ); } } ( *dump_function )( "[Stats]" ); ( *dump_function )( "active_count: %u", table->counters.active_count ); ( *dump_function )( "lookup_count: %" PRIu64, table->counters.lookup_count ); ( *dump_function )( "matched_count: %" PRIu64, table->counters.matched_count ); ( *dump_function )( "[Entries]" ); for ( list_element *e = table->entries; e != NULL; e = e->next ) { flow_entry *entry = e->data; assert( entry != NULL ); dump_flow_entry( entry, dump_function ); } unlock_pipeline(); }
OFDPE update_flow_entry_strict( const uint8_t table_id, const match *match, const uint64_t cookie, const uint64_t cookie_mask, const uint16_t priority, const uint16_t flags, instruction_set *instructions ) { /* * FLOW_TABLE_ALL (=OFPTT_ALL) is not allowed in the protocol specification. * But we allow it for internal use. */ if ( !valid_table_id( table_id ) && table_id != FLOW_TABLE_ALL ) { return ERROR_OFDPE_FLOW_MOD_FAILED_BAD_TABLE_ID; } if ( match == NULL ) { error( "Invalid match ( %p ).", match ); return ERROR_INVALID_PARAMETER; } if ( instructions == NULL ) { error( "Invalid instruction set ( %p ).", instructions ); return ERROR_INVALID_PARAMETER; } if ( !lock_pipeline() ) { return ERROR_LOCK; } flow_table *table = get_flow_table( table_id ); assert( table != NULL ); OFDPE ret = validate_instruction_set( instructions, table->features.metadata_write ); if ( ret != OFDPE_SUCCESS ) { error( "Invalid instruction set ( ret = %#x, instructions = %p ).", ret, instructions ); return ret; } list_element *list = NULL; if ( table_id != FLOW_TABLE_ALL ) { list = lookup_flow_entries_with_table_id( table_id, match, priority, true, false ); } else { list = lookup_flow_entries_from_all_tables( match, priority, true, false ); } update_flow_entries_in_list( list, cookie, cookie_mask, flags, instructions ); if ( list != NULL ) { delete_list( list ); } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }
OFDPE unref_meter_id( const uint32_t meter_id ) { if ( meter_id == 0 || meter_id > OFPM_MAX ) { return ERROR_OFDPE_METER_MOD_FAILED_INVALID_METER; } if ( !lock_pipeline() ) { return ERROR_UNLOCK; } meter_entry *entry = lookup_meter_entry( meter_id ); if ( entry == NULL ) { return ERROR_OFDPE_METER_MOD_FAILED_UNKNOWN_METER; } entry->ref_count--; if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }
void decrement_reference_count( const uint32_t group_id ) { assert( table != NULL ); assert( valid_group_id( group_id ) ); if ( !lock_pipeline() ) { error( "Failed to lock pipeline." ); return; } group_entry *entry = lookup_group_entry( group_id ); if ( entry != NULL ) { entry->ref_count--; } if ( !unlock_pipeline() ) { error( "Failed to unlock pipeline." ); } }
static void age_flow_entries( void *user_data ) { const uint8_t table_id = *( uint8_t * ) user_data; assert( valid_table_id( table_id ) ); if ( !lock_pipeline() ) { return; } struct timespec now = { 0, 0 }; time_now( &now ); foreach_flow_entry( table_id, age_flow_entries_walker, &now ); if ( !unlock_pipeline() ) { return; } }
OFDPE get_group_desc( group_desc **stats, uint16_t *n_groups ) { assert( table != NULL ); assert( n_groups != NULL ); if ( !lock_pipeline() ) { return ERROR_LOCK; } *n_groups = 0; for ( list_element *element = table->entries; element != NULL; element = element->next ) { if ( element->data == NULL ) { continue; } ( *n_groups )++; } size_t length = sizeof( group_desc ) * ( *n_groups ); *stats = NULL; if ( *n_groups > 0 ) { *stats = xmalloc( length ); memset( *stats, 0, length ); } group_desc *stat = *stats; for ( list_element *element = table->entries; element != NULL; element = element->next ) { if ( element->data == NULL ) { continue; } group_entry *entry = element->data; stat->type = entry->type; stat->group_id = entry->group_id; stat->buckets = duplicate_buckets( entry->buckets ); stat++; } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }
OFDPE delete_port( const uint32_t port_no ) { assert( port_no > 0 && port_no <= OFPP_MAX ); if ( datapath_is_running() && !lock_pipeline() ) { return ERROR_LOCK; } OFDPE ret = delete_ether_device_from_switch( port_no ); if ( ret != OFDPE_SUCCESS ) { error( "Failed to delete an Ethernet port from a switch ( ret = %d, port_no = %u ).", ret, port_no ); } if ( datapath_is_running() && !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
OFDPE add_port( const uint32_t port_no, const char *device_name ) { assert( port_no <= OFPP_MAX ); assert( device_name != NULL ); if ( datapath_is_running() && !lock_pipeline() ) { return ERROR_LOCK; } OFDPE ret = add_ether_device_as_switch_port( device_name, port_no ); if ( ret != OFDPE_SUCCESS ) { error( "Failed to add an Ethernet port as a switch port ( ret = %d, port_no = %u, device_name = %s ).", ret, port_no, device_name ); } if ( datapath_is_running() && !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
OFDPE replace_meter_entry( const uint16_t flags, const uint32_t meter_id, const list_element *bands ) { assert( table != NULL ); if ( meter_id == 0 || ( meter_id > OFPM_MAX && meter_id != OFPM_CONTROLLER )) { return ERROR_OFDPE_METER_MOD_FAILED_INVALID_METER; } meter_entry *entry = alloc_meter_entry( flags, meter_id, bands ); if ( entry == NULL ) { return ERROR_INVALID_PARAMETER; } OFDPE ret = OFDPE_SUCCESS; if ( !lock_pipeline() ) { free_meter_entry( entry ); return ERROR_LOCK; } meter_entry *old_entry = lookup_meter_entry( entry->meter_id ); if ( NULL == old_entry ) { ret = ERROR_OFDPE_METER_MOD_FAILED_UNKNOWN_METER; free_meter_entry( entry ); } else { entry->ref_count = old_entry->ref_count; entry->packet_count = old_entry->packet_count; entry->byte_count = old_entry->byte_count; entry->estimated_rate = old_entry->estimated_rate; entry->created_at = old_entry->created_at; entry->meter_at = old_entry->meter_at; delete_element( &table->entries, old_entry ); append_to_tail( &table->entries, entry ); free_meter_entry( old_entry ); } if ( !unlock_pipeline() ) { free_meter_entry( entry ); return ERROR_UNLOCK; } return ret; }
OFDPE update_group_entry( const uint32_t group_id, const uint8_t type, bucket_list *buckets ) { assert( table != NULL ); if ( !valid_group_id( group_id ) ) { return ERROR_OFDPE_GROUP_MOD_FAILED_INVALID_GROUP; } if ( !valid_group_type( type ) ) { return ERROR_OFDPE_GROUP_MOD_FAILED_INVALID_GROUP; } if ( !lock_pipeline() ) { return ERROR_LOCK; } OFDPE ret = OFDPE_SUCCESS; group_entry *entry = lookup_group_entry( group_id ); if ( entry == NULL ) { ret = ERROR_OFDPE_GROUP_MOD_FAILED_UNKNOWN_GROUP; } if ( ret == OFDPE_SUCCESS ) { ret = validate_buckets( entry->type, buckets ); } if ( ret == OFDPE_SUCCESS ) { if ( entry->buckets != NULL ) { delete_action_bucket_list( entry->buckets ); } entry->type = type; entry->buckets = buckets; } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
OFDPE delete_meter_entry( const uint32_t meter_id ) { assert( table != NULL ); if ( meter_id == 0 && meter_id <= OFPM_MAX && meter_id != OFPM_CONTROLLER && meter_id != OFPM_ALL ) { return ERROR_OFDPE_METER_MOD_FAILED_INVALID_METER; } OFDPE ret = OFDPE_SUCCESS; if ( !lock_pipeline() ) { return ERROR_LOCK; } if ( meter_id == OFPM_ALL ) { delete_flow_entries_by_meter_id( meter_id ); for ( list_element *e = table->entries; e != NULL; ) { list_element *next = e->next; meter_entry *entry = e->data; if ( entry->meter_id > 0 && entry->meter_id <= OFPM_MAX ) { // virtual meters won't be deleted by OFPM_ALL delete_element( &table->entries, entry ); free_meter_entry( entry ); } e = next; } } else { meter_entry *old_entry = lookup_meter_entry( meter_id ); if ( NULL == old_entry ) { ret = ERROR_OFDPE_METER_MOD_FAILED_UNKNOWN_METER; } else { if ( old_entry->ref_count > 0 ) { delete_flow_entries_by_meter_id( meter_id ); } delete_element( &table->entries, old_entry ); free_meter_entry( old_entry ); } } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
OFDPE delete_group_entry( const uint32_t group_id ) { assert( table != NULL ); if ( !valid_group_id( group_id ) ) { return ERROR_OFDPE_GROUP_MOD_FAILED_INVALID_GROUP; } if ( !lock_pipeline() ) { return ERROR_LOCK; } if ( group_id != OFPG_ALL ) { group_entry *entry = lookup_group_entry( group_id ); if ( entry != NULL ) { delete_element( &table->entries, entry ); delete_flow_entries_by_group_id( entry->group_id ); free_group_entry( entry ); } } else { for ( list_element *element = table->entries; element != NULL; element = element->next ) { if ( element->data == NULL ) { continue; } group_entry *entry = element->data; delete_flow_entries_by_group_id( entry->group_id ); free_group_entry( entry ); } delete_list( table->entries ); create_list( &table->entries ); } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }
list_element * lookup_flow_entries( const uint8_t table_id, const match *match ) { assert( valid_table_id( table_id ) || table_id == FLOW_TABLE_ALL ); if ( !lock_pipeline() ) { return NULL; } list_element *list = NULL; if ( table_id != FLOW_TABLE_ALL ) { list = lookup_flow_entries_with_table_id( table_id, match, 0, false, true ); } else { list = lookup_flow_entries_from_all_tables( match, 0, false, true ); } if ( !unlock_pipeline() ) { delete_list( list ); return NULL; } return list; }
OFDPE add_flow_entry( const uint8_t table_id, flow_entry *entry, const uint16_t flags ) { if ( !valid_table_id( table_id ) ) { return ERROR_OFDPE_FLOW_MOD_FAILED_BAD_TABLE_ID; } if ( entry == NULL ) { return ERROR_INVALID_PARAMETER; } if ( !lock_pipeline() ) { return ERROR_LOCK; } flow_table *table = get_flow_table( table_id ); if ( table == NULL ) { return ERROR_OFDPE_FLOW_MOD_FAILED_BAD_TABLE_ID; } if ( table->features.max_entries <= get_active_count( table_id ) ) { return ERROR_OFDPE_FLOW_MOD_FAILED_TABLE_FULL; } OFDPE ret = validate_instruction_set( entry->instructions, table->features.metadata_write ); if ( ret == OFDPE_SUCCESS ) { entry->table_id = table_id; ret = insert_flow_entry( table, entry, flags ); if ( ret == OFDPE_SUCCESS ) { increment_reference_counters_in_groups( entry->instructions ); } } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
OFDPE update_or_add_flow_entry( const uint8_t table_id, const match *key, const uint64_t cookie, const uint64_t cookie_mask, const uint16_t priority, const uint16_t idle_timeout, const uint16_t hard_timeout, const uint16_t flags, const bool strict, instruction_set *instructions ) { if ( !valid_table_id( table_id ) ) { return ERROR_OFDPE_FLOW_MOD_FAILED_BAD_TABLE_ID; } if ( key == NULL ) { error( "Invalid match ( %p ).", key ); return ERROR_INVALID_PARAMETER; } if ( instructions == NULL ) { error( "Invalid instruction set ( %p ).", instructions ); return ERROR_INVALID_PARAMETER; } if ( !lock_pipeline() ) { return ERROR_LOCK; } flow_table *table = get_flow_table( table_id ); assert( table != NULL ); OFDPE ret = validate_instruction_set( instructions, table->features.metadata_write ); if ( ret != OFDPE_SUCCESS ) { error( "Invalid instruction set ( ret = %#x, instructions = %p ).", ret, instructions ); unlock_pipeline(); return ret; } list_element *list = lookup_flow_entries_with_table_id( table_id, key, priority, strict, false ); if ( list != NULL ) { update_flow_entries_in_list( list, cookie, cookie_mask, flags, instructions ); delete_list( list ); } else { match *duplicated_match = duplicate_match( key ); instruction_set *duplicated_instructions = duplicate_instructions( instructions ); flow_entry *entry = alloc_flow_entry( duplicated_match, duplicated_instructions, priority, idle_timeout, hard_timeout, flags, cookie ); if ( entry != NULL ) { ret = add_flow_entry( table_id, entry, flags ); if ( ret != OFDPE_SUCCESS ) { error( "Failed to add flow entry ( table_id = %#x, entry = %p, flags = %#x ).", table_id, entry, flags ); } } else { delete_match( duplicated_match ); delete_instructions( duplicated_instructions ); ret = OFDPE_FAILED; } } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
OFDPE get_group_stats( const uint32_t group_id, group_stats **stats, uint32_t *n_groups ) { assert( table != NULL ); assert( stats != NULL ); assert( n_groups != NULL ); if ( !valid_group_id( group_id ) && group_id != OFPG_ALL ) { return ERROR_OFDPE_BAD_REQUEST_BAD_TABLE_ID; } if ( !lock_pipeline() ) { return ERROR_LOCK; } OFDPE ret = OFDPE_SUCCESS; list_element *groups = NULL; create_list( &groups ); *n_groups = 0; if ( group_id != OFPG_ALL ) { group_entry *entry = lookup_group_entry( group_id ); if ( entry == NULL ) { if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return ERROR_OFDPE_BAD_REQUEST_BAD_TABLE_ID; } append_to_tail( &groups, entry ); ( *n_groups )++; } else { for ( list_element *e = table->entries; e != NULL; e = e->next ) { if ( e->data == NULL ) { continue; } group_entry *entry = e->data; append_to_tail( &groups, entry ); ( *n_groups )++; } } *stats = NULL; if ( *n_groups > 0 ) { *stats = xmalloc( sizeof( group_stats ) * ( *n_groups ) ); memset( *stats, 0, sizeof( group_stats ) * ( *n_groups ) ); } group_stats *stat = *stats; for ( list_element *e = groups; e != NULL; e = e->next ) { assert( e->data != NULL ); group_entry *entry = e->data; stat->group_id = entry->group_id; stat->ref_count = entry->ref_count; stat->packet_count = entry->packet_count; stat->byte_count = entry->byte_count; struct timespec now = { 0, 0 }; time_now( &now ); struct timespec diff = { 0, 0 }; timespec_diff( entry->created_at, now, &diff ); stat->duration_sec = ( uint32_t ) diff.tv_sec; stat->duration_nsec = ( uint32_t ) diff.tv_nsec; create_list( &stat->bucket_stats ); for ( dlist_element *b = get_first_element( entry->buckets ); b != NULL; b = b->next ) { if ( b->data == NULL ) { continue; } bucket *bucket = b->data; bucket_counter *counter = xmalloc( sizeof( bucket_counter ) ); memset( counter, 0, sizeof( bucket_counter ) ); counter->packet_count = bucket->packet_count; counter->byte_count = bucket->byte_count; append_to_tail( &stat->bucket_stats, counter ); } stat++; } if ( groups != NULL ) { delete_list( groups ); } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return ret; }
OFDPE get_flow_stats( const uint8_t table_id, const match *match, const uint64_t cookie, const uint64_t cookie_mask, const uint32_t out_port, const uint32_t out_group, flow_stats **stats, uint32_t *n_entries ) { assert( valid_table_id( table_id ) || table_id == FLOW_TABLE_ALL ); assert( stats != NULL ); assert( n_entries != NULL ); if ( !lock_pipeline() ) { return ERROR_LOCK; } list_element *list = NULL; if ( table_id != FLOW_TABLE_ALL ) { list = lookup_flow_entries_with_table_id( table_id, match, 0, false, false ); } else { list = lookup_flow_entries_from_all_tables( match, 0, false, false ); } *n_entries = 0; list_element *entries = NULL; create_list( &entries ); for ( list_element *e = list; e != NULL; e = e->next ) { flow_entry *entry = e->data; assert( entry != NULL ); bool matched = true; if ( out_port != OFPP_ANY ) { if ( !instructions_have_output_port( entry->instructions, out_port ) ) { matched = false; } } if ( out_group != OFPG_ANY ) { if ( !instructions_have_output_group( entry->instructions, out_group ) ) { matched = false; } } if ( matched && cookie_mask != 0 ) { if ( ( entry->cookie & cookie_mask ) != ( cookie & cookie_mask ) ) { matched = false; } } if ( matched ) { ( *n_entries )++; append_to_tail( &entries, entry ); } } if ( list != NULL ) { delete_list( list ); } *stats = NULL; if ( *n_entries > 0 ) { *stats = xmalloc( sizeof( flow_stats ) * ( *n_entries ) ); memset( *stats, 0, sizeof( flow_stats ) * ( *n_entries ) ); } struct timespec now = { 0, 0 }; time_now( &now ); struct timespec diff = { 0, 0 }; flow_stats *stat = *stats; for ( list_element *e = entries; e != NULL; e = e->next ) { flow_entry *entry = e->data; stat->table_id = entry->table_id; timespec_diff( entry->created_at, now, &diff ); stat->duration_sec = ( uint32_t ) diff.tv_sec; stat->duration_nsec = ( uint32_t ) diff.tv_nsec; stat->priority = entry->priority; stat->idle_timeout = entry->idle_timeout; stat->hard_timeout = entry->hard_timeout; stat->flags = entry->flags; stat->cookie = entry->cookie; stat->packet_count = entry->packet_count; stat->byte_count = entry->byte_count; stat->match = *entry->match; stat->instructions = *entry->instructions; stat++; } if ( entries != NULL ) { delete_list( entries ); } if ( !unlock_pipeline() ) { return ERROR_UNLOCK; } return OFDPE_SUCCESS; }