static void delete_flow_entries_in_list( list_element *entries, const uint64_t cookie, const uint64_t cookie_mask, const uint32_t out_port, const uint32_t out_group, const uint8_t reason ) { for ( list_element *e = entries; e != NULL; e = e->next ) { flow_entry *entry = e->data; assert( entry != NULL ); bool to_be_deleted = true; if ( cookie_mask != 0 ) { if ( ( entry->cookie & cookie_mask ) != ( cookie & cookie_mask ) ) { to_be_deleted = false; } } if ( to_be_deleted && out_port != OFPP_ANY ) { if ( !instructions_have_output_port( entry->instructions, out_port ) ) { to_be_deleted = false; } } if ( to_be_deleted && out_group != OFPG_ANY ) { if ( !instructions_have_output_group( entry->instructions, out_group ) ) { to_be_deleted = false; } } if ( !to_be_deleted ) { continue; } flow_table *table = get_flow_table( entry->table_id ); assert( table != NULL ); delete_flow_entry_from_table( table, entry, reason, true ); } }
OFDPE finalize_flow_table( const uint8_t table_id ) { if ( !valid_table_id( table_id ) ) { error( "Invalid flow table id ( %#x ).", table_id ); return OFDPE_FAILED; } flow_table *table = get_flow_table( table_id ); if ( table == NULL ) { return OFDPE_FAILED; } delete_timer_event_safe( age_flow_entries, &table->features.table_id ); for ( list_element *e = table->entries; e != NULL; e = e->next ) { flow_entry *entry = e->data; if ( entry != NULL ) { free_flow_entry( entry ); } } delete_list( table->entries ); memset( table, 0, sizeof( flow_table ) ); table->initialized = false; return OFDPE_SUCCESS; }
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(); }
static uint64_t get_matched_count( const uint8_t table_id ) { assert( valid_table_id( table_id ) ); flow_table *table = get_flow_table( table_id ); if ( table == NULL ) { return 0; } return table->counters.matched_count; }
static uint64_t increment_lookup_count( const uint8_t table_id ) { assert( valid_table_id( table_id ) ); flow_table *table = get_flow_table( table_id ); if ( table == NULL ) { return 0; } return ++table->counters.lookup_count; }
static uint32_t get_active_count( const uint8_t table_id ) { assert( valid_table_id( table_id ) ); flow_table *table = get_flow_table( table_id ); if ( table == NULL ) { return 0; } return table->counters.active_count; }
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; }
static list_element * lookup_flow_entries_with_table_id( const uint8_t table_id, const match *match, const uint16_t priority, const bool strict, const bool update_counters ) { assert( valid_table_id( table_id ) ); if ( get_logging_level() >= LOG_DEBUG ) { debug( "Looking up flow entries ( table_id = %#x, match = %p, priority = %u, strict = %s, update_counters = %s ).", table_id, match, priority, strict ? "true" : "false", update_counters ? "true" : "false" ); if ( match != NULL ) { dump_match( match, debug ); } } flow_table *table = get_flow_table( table_id ); if ( table == NULL ) { return NULL; } if ( update_counters ) { increment_lookup_count( table_id ); } list_element *head = NULL; create_list( &head ); for ( list_element *e = table->entries; e != NULL; e = e->next ) { flow_entry *entry = e->data; assert( entry != NULL ); if ( strict ) { if ( entry->priority < priority ) { break; } if ( priority == entry->priority && compare_match_strict( match, entry->match ) ) { if ( update_counters ) { increment_matched_count( table_id ); } append_to_tail( &head, entry ); break; } } else { if ( compare_match( match, entry->match ) ) { if ( update_counters ) { increment_matched_count( table_id ); } append_to_tail( &head, entry ); } } } return head; }
static OFDPE insert_flow_entry( flow_table *table, flow_entry *entry, const uint16_t flags ) { assert( table != NULL ); assert( entry != NULL ); list_element *element = table->entries; while( element != NULL ) { list_element *next = element->next; flow_entry *e = element->data; assert( e != NULL ); if ( e->priority < entry->priority ) { break; } if ( e->priority == entry->priority ) { if ( e->table_miss && !entry->table_miss ) { break; } if ( ( flags & OFPFF_CHECK_OVERLAP ) != 0 && compare_match( e->match, entry->match ) ) { return ERROR_OFDPE_FLOW_MOD_FAILED_OVERLAP; } if ( compare_match_strict( e->match, entry->match ) ) { if ( ( flags & OFPFF_RESET_COUNTS ) != 0 ) { entry->byte_count = e->byte_count; entry->packet_count = e->packet_count; } flow_table *table = get_flow_table( e->table_id ); assert( table != NULL ); delete_flow_entry_from_table( table, e, 0, false ); } } element = next; } if ( element == NULL ) { // tail append_to_tail( &table->entries, entry ); } else if ( element == table->entries ) { // head insert_in_front( &table->entries, entry ); } else { // insert before insert_before( &table->entries, element->data, entry ); } increment_active_count( table->features.table_id ); return OFDPE_SUCCESS; }
OFDPE get_flow_table_config( const uint8_t table_id, uint32_t *config ) { assert( valid_table_id( table_id ) ); assert( config != NULL ); flow_table *table = get_flow_table( table_id ); if ( table == NULL ) { return OFDPE_FAILED; } *config = table->features.config; return OFDPE_SUCCESS; }
OFDPE get_flow_table_features( const uint8_t table_id, flow_table_features *stats ) { if ( stats == NULL ) { return ERROR_INVALID_PARAMETER; } flow_table *table = get_flow_table( table_id ); if ( table == NULL ) { return ERROR_OFDPE_BAD_REQUEST_BAD_TABLE_ID; } memcpy( stats, &( table->features ), sizeof( flow_table_features ) ); return OFDPE_SUCCESS; }
static void foreach_flow_entry( const uint8_t table_id, flow_entry_handler callback, void *user_data ) { assert( valid_table_id( table_id ) ); assert( callback != NULL ); flow_table *table = get_flow_table( table_id ); assert( table != NULL ); list_element *e = table->entries; while ( e != NULL ) { list_element *next = e->next; // Current element may be deleted inside the callback function. flow_entry *entry = e->data; assert( entry != NULL ); callback( entry, user_data ); e = next; } }
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; }