Exemple #1
0
static void
test_delete_entry() {
  table = create_hash( compare_string, hash_string );

  insert_hash_entry( table, alpha, alpha );
  insert_hash_entry( table, bravo, bravo );
  insert_hash_entry( table, charlie, charlie );

  delete_hash_entry( table, bravo );
  assert_string_equal( lookup_hash_entry( table, alpha ), "alpha" );
  assert_true( lookup_hash_entry( table, bravo ) == NULL );
  assert_string_equal( lookup_hash_entry( table, charlie ), "charlie" );

  delete_hash( table );
}
static bool
add_transaction( uint64_t datapath_id, uint32_t vni, int operation,
                 overlay_operation_completed_handler callback, void *user_data ) {
  debug( "Adding a transaction record ( datapath_id = %#" PRIx64 ", vni = %#x, operation = %#x, callback = %p, user_data = %p ).",
         datapath_id, vni, operation, callback, user_data );

  if ( !valid_vni( vni ) ) {
    return false;
  }

  transaction_entry *entry = xmalloc( sizeof( transaction_entry ) );
  memset( entry, 0, sizeof( transaction_entry ) );
  entry->datapath_id = datapath_id;
  entry->vni = vni;
  entry->operation = operation;
  entry->port_wait_count = 0;
  entry->n_ongoing_http_requests = 0;
  entry->n_failed_http_requests = 0;
  entry->callback = callback;
  entry->user_data = user_data;

  entry = insert_hash_entry( transactions, entry, entry );
  if ( entry != NULL ) {
    warn( "Duplicated transaction record found ( datapath_id = %#" PRIx64 ", vni = %#x, operation = %#x, callback = %p, user_data = %p ).",
           datapath_id, vni, operation, callback, user_data );
    xfree( entry );
  }
  debug( "A transaction record is added ( datapath_id = %#" PRIx64 ", vni = %#x, operation = %#x, callback = %p, user_data = %p ).",
         datapath_id, vni, operation, callback, user_data );

  return true;
}
static void
handle_desc_stats_reply( uint64_t datapath_id, uint32_t transaction_id,
                        uint16_t type, uint16_t flags, const buffer *data,
                       void *user_data ) {
  UNUSED( transaction_id );
  UNUSED( type );
  UNUSED( flags );
  show_desc *show_desc = user_data;

  if ( data->length < SIZE_OF_OFP_DESC ) {
    error( "invalid data" );

    stop_trema();
    return;
  }
  desc_entry *entry = xmalloc( sizeof( desc_entry ) );
  entry->datapath_id = datapath_id;
#ifdef TREMA_EDGE
  create_list( &entry->port_desc );
#endif
  memcpy( &entry->desc_stats, data->data, SIZE_OF_OFP_DESC );
  insert_hash_entry( show_desc->db, &entry->datapath_id, entry );

#ifdef TREMA_EDGE
  buffer *port_desc_request = create_port_desc_multipart_request( get_transaction_id(), 0 );
  send_openflow_message( datapath_id, port_desc_request );
  free_buffer( port_desc_request );
#else
  buffer *features_request = create_features_request( get_transaction_id() );
  send_openflow_message( datapath_id, features_request );
  free_buffer( features_request );
#endif
}
Exemple #4
0
uint32_t
insert_xid_entry( uint32_t original_xid, char *service_name ) {
  xid_entry_t *new_entry;

  debug( "Inserting xid entry ( original_xid = %#lx, service_name = %s ).",
         original_xid, service_name );

  if ( xid_table.next_index >= XID_MAX_ENTRIES ) {
    xid_table.next_index = 0;
  }

  if ( xid_table.entries[ xid_table.next_index ] != NULL ) {
    delete_xid_entry( xid_table.entries[ xid_table.next_index ] );
  }

  new_entry = allocate_xid_entry( original_xid, service_name, xid_table.next_index );
  xid_entry_t *old = insert_hash_entry( xid_table.hash, &new_entry->xid, new_entry );
  if ( old != NULL ) {
    xid_table.entries[ old->index ] = NULL;
    free_xid_entry( old );
  }
  xid_table.entries[ xid_table.next_index ] = new_entry;
  xid_table.next_index++;

  return new_entry->xid;
}
Exemple #5
0
/**
 * Adds a parameter to the Statistics database/table, stats of which are to be tracked.
 * @param key Identifier for the parameter of which stats are required
 * @return bool True if the entry was successfully added, else False in case entry already exists
 */
bool
add_stat_entry( const char *key ) {
  assert( key != NULL );

  pthread_mutex_lock( &stats_table_mutex );

  stat_entry *entry = lookup_hash_entry( stats, key );

  if ( entry != NULL ) {
    error( "Statistic entry for %s already exists.", key );
    pthread_mutex_unlock( &stats_table_mutex );
    return false;
  }

  entry = xmalloc( sizeof( stat_entry ) );
  entry->value = 0;
  strncpy( entry->key, key, STAT_KEY_LENGTH );
  entry->key[ STAT_KEY_LENGTH - 1 ] = '\0';

  insert_hash_entry( stats, entry->key, entry );

  pthread_mutex_unlock( &stats_table_mutex );

  return true;
}
Exemple #6
0
static void
test_insert_twice_overwrites_old_value() {
  char *prev;
  char key[] = "key";
  table = create_hash( compare_string, hash_string );

  char old_value[] = "old value";
  char new_value[] = "new value";

  insert_hash_entry( table, key, old_value );
  prev = insert_hash_entry( table, key, new_value );

  assert_string_equal( lookup_hash_entry( table, key ), "new value" );
  assert_string_equal( prev, "old value" );

  delete_hash( table );
}
Exemple #7
0
static void
test_map() {
  table = create_hash( compare_string, hash_string );

  char key[] = "key";
  insert_hash_entry( table, key, alpha );
  insert_hash_entry( table, key, bravo );
  insert_hash_entry( table, key, charlie );

  map_hash( table, key, append_back, NULL );

  assert_string_equal( abc0[ 0 ], "charlie" );
  assert_string_equal( abc0[ 1 ], "bravo" );
  assert_string_equal( abc0[ 2 ], "alpha" );

  delete_hash( table );
}
Exemple #8
0
static void
test_foreach() {
  table = create_hash( compare_string, hash_string );
  insert_hash_entry( table, alpha, alpha );
  insert_hash_entry( table, bravo, bravo );
  insert_hash_entry( table, charlie, charlie );
  delete_hash_entry( table, bravo );
  delete_hash_entry( table, charlie );

  foreach_hash( table, append_back_foreach, NULL );

  assert_string_equal( abc[ 0 ], "alpha" );
  assert_true( abc[ 1 ] == NULL );
  assert_true( abc[ 2 ] == NULL );

  delete_hash( table );
}
Exemple #9
0
static void
test_iterator() {
  table = create_hash( compare_string, hash_string );

  char one[] = "one";
  insert_hash_entry( table, one, ( void * ) 1 );

  char two[] = "two";
  insert_hash_entry( table, two, ( void * ) 2 );

  char three[] = "three";
  insert_hash_entry( table, three, ( void * ) 3 );

  char four[] = "four";
  insert_hash_entry( table, four, ( void * ) 4 );

  char five[] = "five";
  insert_hash_entry( table, five, ( void * ) 5 );

  char six[] = "six";
  insert_hash_entry( table, six, ( void * ) 6 );

  char seven[] = "seven";
  insert_hash_entry( table, seven, ( void * ) 7 );

  char eight[] = "eight";
  insert_hash_entry( table, eight, ( void * ) 8 );

  char nine[] = "nine";
  insert_hash_entry( table, nine, ( void * ) 9 );

  char ten[] = "ten";
  insert_hash_entry( table, ten, ( void * ) 10 );

  int sum = 0;
  hash_iterator iter;
  hash_entry *e;
  init_hash_iterator( table, &iter );
  while ( ( e = iterate_hash_next( &iter ) ) != NULL ) {
    sum += ( int ) ( uintptr_t ) e->value;
    delete_hash_entry( table, e->key );
  }
  assert_true( sum == 55 );

  delete_hash( table );
}
Exemple #10
0
static void
test_insert_and_lookup() {
  table = create_hash( compare_string, hash_string );

  insert_hash_entry( table, alpha, alpha );
  assert_string_equal( lookup_hash_entry( table, alpha ), "alpha" );

  delete_hash( table );
}
static bool
add_transaction_entry( transaction_entry *entry ) {
  debug( "Adding a transaction entry ( entry = %p, transaction_db = %p ).", entry, transaction_db );

  assert( entry != NULL );
  assert( transaction_db != NULL );
  assert( transaction_db->xid != NULL );
  assert( transaction_db->barrier_xid != NULL );

  bool ret = get_monotonic_time( &entry->expires_at );
  if ( !ret ) {
    return false;
  }
  ADD_TIMESPEC( &entry->expires_at, &TRANSACTION_TIMEOUT, &entry->expires_at );

  dump_transaction_entry( debug, entry );

  if ( lookup_transaction_entry_by_xid( entry->xid ) == NULL ) {
    void *duplicated = insert_hash_entry( transaction_db->xid, &entry->xid, entry );
    assert( duplicated == NULL );
  }
  else {
    error( "Duplicated transaction entry found ( entry = %p, xid = %#x ).", entry, entry->xid );
    dump_transaction_entry( error, entry );
    return false;
  }

  if ( lookup_transaction_entry_by_barrier_xid( entry->barrier_xid ) == NULL ) {
    void *duplicated = insert_hash_entry( transaction_db->barrier_xid, &entry->barrier_xid, entry );
    assert( duplicated == NULL );
  }
  else {
    error( "Duplicated transaction entry found ( entry = %p, barrier_xid = %#x ).", entry, entry->barrier_xid );
    dump_transaction_entry( error, entry );
    delete_transaction_entry_by_xid( entry->xid );
    return false;
  }

  debug( "A transaction is added." );
  dump_transaction_entry( debug, entry );

  return true;
}
void
insert_datapath_entry( hash_table *db, uint64_t datapath_id, void *user_data, void delete_user_data( void *user_data ) ) {
    datapath_entry *entry = allocate_datapath_entry( datapath_id, user_data, delete_user_data );
    debug( "insert datapath: %#" PRIx64 " ( entry = %p )", datapath_id, entry );
    datapath_entry *old_datapath_entry = insert_hash_entry( db, &entry->id, entry );
    if ( old_datapath_entry != NULL ) {
      warn( "duplicated datapath ( datapath_id = %#" PRIx64 ", entry = %p )", datapath_id, old_datapath_entry );
      free_datapath_entry( old_datapath_entry );
    }
}
Exemple #13
0
static void
test_insert_and_lookup_by_atom_hash() {
  key mykey = { "alpha" };
  table = create_hash( compare_atom, hash_atom );

  insert_hash_entry( table, &mykey, alpha );
  assert_string_equal( lookup_hash_entry( table, &mykey ), "alpha" );

  delete_hash( table );
}
static void
handle_switch_ready( uint64_t datapath_id, void *switch_db ) {
  known_switch *sw = lookup_hash_entry( switch_db, &datapath_id );
  if ( sw == NULL ) {
    sw = new_switch( datapath_id );
    insert_hash_entry( switch_db, &sw->datapath_id, sw );
  }
  else {
    refresh( sw );
  }
}
static void
learn( hash_table *forwarding_db, uint16_t port_no, uint8_t *mac ) {
  forwarding_entry *entry = lookup_hash_entry( forwarding_db, mac );

  if ( entry == NULL ) {
    entry = xmalloc( sizeof( forwarding_entry ) );
    memcpy( entry->mac, mac, sizeof( entry->mac ) );
    insert_hash_entry( forwarding_db, entry->mac, entry );
  }
  entry->port_no = port_no;
  entry->last_update = now();
}
Exemple #16
0
void
learn_fdb( hash_table *db, uint8_t *mac, uint16_t port_number ) {
  fdb *entry = lookup_hash_entry( db, mac );
  if ( entry == NULL ) {
    entry = xmalloc( sizeof( fdb ) );
    memcpy( entry->mac, mac, ETH_ADDRLEN );
    entry->port_number = port_number;
    insert_hash_entry( db, entry->mac, entry );
  }
  else {
    entry->port_number = port_number;
  }
}
all_sw_tx*
_insert_tx( size_t n_dpids, struct event_forward_operation_to_all_request_param *param ) {
  all_sw_tx *tx = xcalloc( 1, sizeof(all_sw_tx) );
  tx->txid = get_txid();
  tx->request_param = param;
  tx->tx_result = EFI_OPERATION_SUCCEEDED;
  tx->waiting_dpid = create_hash_with_size( compare_datapath_id,
                                            hash_datapath_id,
                                            ( unsigned ) n_dpids );
  void *dupe_tx = insert_hash_entry( efi_tx_table, &tx->txid, tx );
  assert( dupe_tx == NULL );
  return tx;
}
Exemple #18
0
void
add_counter( hash_table *db, uint8_t *mac, uint64_t packet_count, uint64_t byte_count ) {
  counter *entry = lookup_hash_entry( db, mac );
  if ( entry == NULL ) {
    entry = xmalloc( sizeof( counter ) );
    memcpy( entry->mac, mac, ETH_ADDRLEN );
    entry->packet_count = 0;
    entry->byte_count = 0;
    insert_hash_entry( db, entry->mac, entry );
  }
  entry->packet_count += packet_count;
  entry->byte_count += byte_count;
}
Exemple #19
0
static void
learn( hash_table *forwarding_db, struct key new_key, uint16_t port_no ) {
  forwarding_entry *entry = lookup_hash_entry( forwarding_db, &new_key );

  if ( entry == NULL ) {
    entry = xmalloc( sizeof( forwarding_entry ) );
    memcpy( entry->key.mac, new_key.mac, OFP_ETH_ALEN );
    entry->key.datapath_id = new_key.datapath_id;
    insert_hash_entry( forwarding_db, &entry->key, entry );
  }
  entry->port_no = port_no;
  entry->last_update = now();
}
Exemple #20
0
bool
update_fdb( hash_table *fdb, const uint8_t mac[ OFP_ETH_ALEN ], uint64_t dpid, uint16_t port ) {
    assert( fdb != NULL );
    assert( mac != NULL );
    assert( port != 0 );

    fdb_entry *entry = lookup_hash_entry( fdb, mac );

    debug( "Updating fdb ( mac = %02x:%02x:%02x:%02x:%02x:%02x, dpid = %#" PRIx64 ", port = %u ).",
           mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ], dpid, port );

    if ( entry != NULL ) {
        if ( ( entry->dpid == dpid ) && ( entry->port == port ) ) {
            entry->updated_at = time( NULL );

            return true;
        }

        if ( entry->created_at + HOST_MOVE_GUARD_SEC < time( NULL ) ) {
            // Poisoning when the terminal moves
            poison( entry->dpid, mac );

            entry->dpid = dpid;
            entry->port = port;
            entry->created_at = time( NULL );
            entry->updated_at = entry->created_at;

            return true;
        }

        warn( "Failed to update fdb because host move detected in %d sec "
              "( mac = %02x:%02x:%02x:%02x:%02x:%02x, "
              "dpid = %#" PRIx64 " -> %#" PRIx64 ", port = %u -> %u ).",
              HOST_MOVE_GUARD_SEC,
              mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ],
              entry->dpid, dpid, entry->port, port );

        return false;
    }

    entry = xmalloc( sizeof( fdb_entry ) );
    entry->dpid = dpid;
    memcpy( entry->mac, mac, OFP_ETH_ALEN );
    entry->port = port;
    entry->created_at = time( NULL );
    entry->updated_at = entry->created_at;
    insert_hash_entry( fdb, entry->mac, entry );

    return true;
}
Exemple #21
0
static void
test_multiple_inserts_and_deletes_then_iterate() {
  table = create_hash( compare_string, hash_string );

  char one[] = "one";
  insert_hash_entry( table, one, ( void * ) 1 );
  delete_hash_entry( table, one );
  insert_hash_entry( table, one, ( void * ) 1 );
  delete_hash_entry( table, one );
  insert_hash_entry( table, one, ( void * ) 1 );

  int sum = 0;
  hash_iterator iter;
  hash_entry *e;
  init_hash_iterator( table, &iter );
  while ( ( e = iterate_hash_next( &iter ) ) != NULL ) {
    sum += ( int ) ( uintptr_t ) e->value;
    delete_hash_entry( table, e->key );
  }
  assert_true( sum == 1 );

  delete_hash( table );
}
Exemple #22
0
void
insert_dpid_entry( uint64_t *dpid ) {
  assert( dpid_table != NULL );
  assert( dpid != NULL );

  if ( lookup_hash_entry( dpid_table, dpid ) != NULL ) {
    warn( "datapath %#" PRIx64 " is already registered.", *dpid );
    return;
  }

  uint64_t *new_entry = xmalloc( sizeof ( uint64_t ) );
  *new_entry = *dpid;
  insert_hash_entry( dpid_table, new_entry, new_entry );
}
Exemple #23
0
void
insert_match_entry( struct ofp_match *ofp_match, uint16_t priority, const char *service_name, const char *entry_name ) {
  match_entry *new_entry, *entry;
  list_element *list;

  pthread_mutex_lock( match_table_head.mutex );

  new_entry = allocate_match_entry( ofp_match, priority, service_name, entry_name );

  if ( !ofp_match->wildcards ) {
    entry = lookup_hash_entry( match_table_head.exact_table, ofp_match );
    if ( entry != NULL ) {
      warn( "insert entry exits" );
      free_match_entry( new_entry );
      pthread_mutex_unlock( match_table_head.mutex );
      return;
    }
    insert_hash_entry( match_table_head.exact_table, &new_entry->ofp_match, new_entry );
    pthread_mutex_unlock( match_table_head.mutex );
    return;
  }

  // wildcard flags are set
  for ( list = match_table_head.wildcard_table; list != NULL; list = list->next ) {
    entry = list->data;
    if ( entry->priority <= new_entry->priority ) {
      break;
    }
  }
  if ( list == NULL ) {
    // wildcard_table is null or tail
    append_to_tail( &match_table_head.wildcard_table, new_entry );
    pthread_mutex_unlock( match_table_head.mutex );
    return;
  }
  if ( list == match_table_head.wildcard_table ) {
    // head
    insert_in_front( &match_table_head.wildcard_table, new_entry );
    pthread_mutex_unlock( match_table_head.mutex );
    return;
  }

  // insert brefore
  insert_before( &match_table_head.wildcard_table, list->data, new_entry );
  pthread_mutex_unlock( match_table_head.mutex );
}
Exemple #24
0
static bool
insert_exact_match_entry( hash_table *exact_table, struct ofp_match *match, void *data ) {
  assert( exact_table != NULL );
  assert( match != NULL );

  match_entry *entry = lookup_hash_entry( exact_table, match );
  if ( entry != NULL ) {
    char match_string[ MATCH_STRING_LENGTH ];
    match_to_string( match, match_string, sizeof( match_string ) );
    warn( "exact match entry already exists ( match = [%s] )", match_string );
    return false;
  }
  match_entry *new_entry = allocate_match_entry( match, 0 /* dummy priority */, data );
  match_entry *conflict_entry = insert_hash_entry( exact_table, &new_entry->match, new_entry );
  assert( conflict_entry == NULL );
  return true;
}
void
print_with_graph_easy_format( void *param, size_t entries, const topology_link_status *s ) {
  size_t i;

  UNUSED( param );

  debug( "topology: entries %d", entries );

  hash_table *link_hash = create_hash( compare_link, hash_link );
  // show_topology graph-easy | graph-easy
  // Graph-Easy:
  //   http://search.cpan.org/~shlomif/Graph-Easy/bin/graph-easy
  printf("#! /usr/bin/env graph-easy\n" );

  for ( i = 0; i < entries; i++ ) {
    if ( s[ i ].status != TD_LINK_UP ) {
      continue;
    }
    topology_link_status r;
    r.from_dpid = s[ i ].to_dpid;
    r.from_portno = s[ i ].to_portno;
    r.to_dpid = s[ i ].from_dpid;
    r.to_portno = s[ i ].from_portno;

    topology_link_status *e = lookup_hash_entry( link_hash, &r );
    if ( e != NULL ) {
      delete_hash_entry( link_hash, e );
      print_link_status( e, true );
    } else {
      insert_hash_entry( link_hash, ( void * ) ( intptr_t ) &s[ i ], ( void *) ( intptr_t ) &s[ i ] );
    }
  }
  hash_iterator iter;
  init_hash_iterator( link_hash, &iter );
  hash_entry *e;
  while ( ( e = iterate_hash_next( &iter ) ) != NULL ) {
    topology_link_status *le = e->value;
    print_link_status( le, false );
  }

  delete_hash( link_hash );

  stop_trema();
}
Exemple #26
0
switch_port *
add_switch_port( const char *interface, uint32_t port_no,
                 const size_t max_send_queue, const size_t max_recv_queue ) {
  assert( interface != NULL );
  assert( switch_ports != NULL );

  switch_port *port = ( switch_port * ) xmalloc( sizeof( switch_port ) );
  memset( port, 0, sizeof( switch_port ) );
  port->port_no = port_no;
  port->device = create_ether_device( interface, max_send_queue, max_recv_queue );
  if ( port->device == NULL ) {
    xfree( port );
    return NULL;
  }

  insert_hash_entry( switch_ports, &port->port_no, port );

  return port;
}
Exemple #27
0
static void grow_hash_table(struct hash_table *table)
{
	unsigned int i;
	unsigned int old_size = table->size, new_size;
	struct hash_table_entry *old_array = table->array, *new_array;
	new_size = alloc_nr(old_size);
	new_array = xzmalloc(sizeof(struct hash_table_entry) * new_size);
	table->size = new_size;
	table->array = new_array;
	table->nr = 0;
	for (i = 0; i < old_size; i++) {
		unsigned int hash = old_array[i].hash;
		void *ptr = old_array[i].ptr;
		if (ptr)
			insert_hash_entry(hash, ptr, table);
	}
	if (old_array)
		xfree(old_array);
}
static void
add_host( const uint8_t *mac, const uint32_t ip, const uint64_t dpid, const uint16_t port ) {
  host_entry *entry;
  struct in_addr addr;

  addr.s_addr = htonl( ip );

  entry = lookup_host( ip );

  if ( entry != NULL ) {
    debug( "Updating a host entry (mac = %02x:%02x:%02x:%02x:%02x:%02x, "
           "ip = %s, dpid = %#" PRIx64 ", port = %u).",
           mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ],
           inet_ntoa( addr ), dpid, port );

    memcpy( entry->mac, mac, ETH_ADDRLEN );
    entry->dpid = dpid;
    entry->port = port;
    entry->updated_at = time( NULL );

    return;
  }

  debug( "Adding a host entry (mac = %02x:%02x:%02x:%02x:%02x:%02x, "
         "ip = %s, dpid = %#" PRIx64 ", port = %u).",
         mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ],
         inet_ntoa( addr ), dpid, port );

  entry = xmalloc( sizeof( host_entry ) );

  memcpy( entry->mac, mac, ETH_ADDRLEN );
  entry->ip = ip;
  entry->dpid = dpid;
  entry->port = port;
  entry->updated_at = time( NULL );

  insert_hash_entry( host_db, &entry->ip, entry );

  add_host_route( entry->ip );
}
Exemple #29
0
static bool
add_http_transaction( http_transaction *transaction, CURL *handle ) {
  debug( "Adding a HTTP transaction to database ( transaction = %p, handle = %p ).",
         transaction, handle );

  assert( transaction != NULL );
  assert( handle != NULL );

  transaction->handle = handle;

  assert( curl_handle != NULL );
  CURLMcode ret = curl_multi_add_handle( curl_handle, transaction->handle );
  if ( ret != CURLM_OK && ret != CURLM_CALL_MULTI_PERFORM ) {
    error( "Failed to add an easy handle to a multi handle ( curl_handle = %p, handle = %p, error = %s ).",
           curl_handle, transaction->handle, curl_multi_strerror( ret ) );
    return false;
  }
  ret = curl_multi_perform( curl_handle, &curl_running );
  if ( ret != CURLM_OK && ret != CURLM_CALL_MULTI_PERFORM ) {
    error( "Failed to run a multi handle ( curl_handle = %p, error = %s ).", curl_handle, curl_multi_strerror( ret ) );
    ret = curl_multi_remove_handle( curl_handle, transaction->handle );
    if ( ret != CURLM_OK  && ret != CURLM_CALL_MULTI_PERFORM ) {
      error( "Failed to remove an easy handle from a multi handle ( curl_handle = %p, handle = %p, error = %s ).",
             curl_handle, transaction->handle, curl_multi_strerror( ret ) );             
    }
    return false;
  }
  assert( transactions != NULL );

  void *duplicated = insert_hash_entry( transactions, transaction->handle, transaction );
  if ( duplicated != NULL ) {
    // FIXME: handle duplication properly
    warn( "Duplicated HTTP transaction found ( %p ).", duplicated );
    free_http_transaction( duplicated );
  }

  debug( "A HTTP transaction is added into database ( transaction = %p, handle = %p ).", transaction, handle );

  return true;
}
void
_dispatch_to_all_switch( uint64_t *dpids, size_t n_dpids, void *user_data ) {
  struct event_forward_operation_to_all_request_param *param = user_data;

  all_sw_tx *tx = _insert_tx( n_dpids, param );
  info( "txid %#x Start dispatching to switches", tx->txid );
  debug( "dispatching to %zd switches", n_dpids );
  // copy dpid hash to transaction.
  for ( size_t i = 0 ; i < n_dpids ; ++i ) {
    uint64_t *dpid = xmalloc( sizeof( uint64_t ) );
    *dpid = dpids[i];
    uint64_t *dupe_dpid = insert_hash_entry( tx->waiting_dpid, dpid, dpid );
    if ( dupe_dpid == NULL ) {
      struct txinfo *txinfo = xcalloc( 1, sizeof( struct txinfo ) );
      txinfo->dpid = *dpid;
      txinfo->txid = tx->txid;
      bool send_ok;

      if ( param->add ) {
        send_ok = add_switch_event_forward_entry( *dpid, param->type, param->service_name, _switch_response_handler, txinfo );
      }
      else {
        send_ok = delete_switch_event_forward_entry( *dpid, param->type, param->service_name, _switch_response_handler, txinfo );
      }

      if ( !send_ok ) {
        tx->tx_result = EFI_OPERATION_FAILED;
        warn( "txid %#x Failed to send request to switch %#" PRIx64 ".", tx->txid, *dpid );
        xfree( delete_hash_entry( tx->waiting_dpid, dpid ) );
        dpid = NULL;
        xfree( txinfo );
        continue;
      }

      struct itimerspec interval;
      interval.it_interval.tv_sec = 0;
      interval.it_interval.tv_nsec = 0;
      interval.it_value.tv_sec = 5; // FIXME make this configurable?
      interval.it_value.tv_nsec = 0;
      bool set_ok = add_timer_event_callback( &interval, _switch_response_timeout, txinfo );
      if ( !set_ok ) {
        tx->tx_result = EFI_OPERATION_FAILED;
        warn( "txid %#x Failed to set timeout timer for switch %#" PRIx64 ".", tx->txid, *dpid );
        xfree( delete_hash_entry( tx->waiting_dpid, dpid ) );
        dpid = NULL;
        // txinfo will be freed by _switch_response_handler
        continue;
      }
    }
    else {
      warn( "Duplicate dpid returned %#." PRIx64, *dupe_dpid );
      xfree( dupe_dpid );
    }
  }

  if ( n_dpids == 0 || tx->tx_result == EFI_OPERATION_FAILED ) {
    if ( n_dpids == 0 ) {
      info( "txid %#x completed. No switches found.", tx->txid );
    }
    else if ( tx->tx_result == EFI_OPERATION_FAILED ) {
      info( "txid %#x completed with failure.", tx->txid );
    }
    if ( param->callback != NULL ) {
      param->callback( tx->tx_result, param->user_data );
    }
    // remove and cleanup tx
    delete_hash_entry( efi_tx_table, &tx->txid );
    xfree_all_sw_tx( tx );
  }
}