add address test
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses.c
index 2d3abefac149f74096cea116295082c21c483239..9388a66b4caca10779ab4841a0310c28daad527b 100644 (file)
 #include "gnunet_ats_service.h"
 #include "gnunet-service-ats.h"
 #include "gnunet-service-ats_addresses.h"
+#include "gnunet-service-ats_normalization.h"
 #include "gnunet-service-ats_performance.h"
 #include "gnunet-service-ats_scheduling.h"
 #include "gnunet-service-ats_reservations.h"
 #if HAVE_LIBGLPK
-#include "gnunet-service-ats_addresses_mlp.h"
+#include "gnunet-service-ats-solver_mlp.h"
 #endif
-#include "gnunet-service-ats_addresses_simplistic.h"
+#include "gnunet-service-ats-solver_proportional.h"
+#include "gnunet-service-ats-solver_ril.h"
 
 /**
  * NOTE: Do not change this documentation. This documentation is based on
  *    it as a value_number. If no configuration value is found it will assign
  *    GNUNET_ATS_DefaultBandwidth. The most important step is to load the
  *    configured solver using configuration "[ats]:MODE". Current solvers are
- *    MODE_SIMPLISTIC, MODE_MLP. Interaction is done using a solver API
+ *    MODE_PROPORTIONAL, MODE_MLP. Interaction is done using a solver API
  *
  *     1.4 Solver API
  *
 enum ATS_Mode
 {
   /*
-   * Simplistic mode:
+   * proportional mode:
    *
    * Assign each peer an equal amount of bandwidth (bw)
    *
    * bw_per_peer = bw_total / #active addresses
    */
-  MODE_SIMPLISTIC,
+  MODE_PROPORTIONAL,
 
   /*
    * MLP mode:
@@ -249,7 +251,14 @@ enum ATS_Mode
    * Solve ressource assignment as an optimization problem
    * Uses an mixed integer programming solver
    */
-  MODE_MLP
+  MODE_MLP,
+
+  /*
+   * Reinforcement Learning mode:
+   *
+   * Solve resource assignment using a learning agent
+   */
+  MODE_RIL
 };
 
 
@@ -337,10 +346,14 @@ struct GAS_Addresses_Handle
    */
   GAS_solver_address_add s_add;
 
-  /**
-   * Update address in solver
-   */
-  GAS_solver_address_update s_update;
+
+  GAS_solver_address_property_changed s_address_update_property;
+
+  GAS_solver_address_session_changed s_address_update_session;
+
+  GAS_solver_address_inuse_changed s_address_update_inuse;
+
+  GAS_solver_address_network_changed s_address_update_network;
 
   /**
    * Get address from solver
@@ -358,10 +371,25 @@ struct GAS_Addresses_Handle
   GAS_solver_address_delete s_del;
 
   /**
-   * Change preference for quality in solver
+   * Change relative preference for quality in solver
    */
   GAS_solver_address_change_preference s_pref;
 
+  /**
+   * Give feedback about the current assignment
+   */
+  GAS_solver_address_feedback_preference s_feedback;
+
+  /**
+   * Start a bulk operation
+   */
+  GAS_solver_bulk_start s_bulk_start;
+
+  /**
+   * Bulk operation done
+   */
+  GAS_solver_bulk_stop s_bulk_stop;
+
   /**
    * Shutdown solver
    */
@@ -370,132 +398,132 @@ struct GAS_Addresses_Handle
 
 
 /**
- * Assemble ATS information from address
+ * Disassemble ATS information and update performance information in address
  *
- * @param aa source address
- * @param dest destination
- * @return number of elements
- */
-static unsigned int
-assemble_ats_information (const struct ATS_Address *aa,  struct GNUNET_ATS_Information **dest)
-{
-  unsigned int ats_count = GNUNET_ATS_PropertyCount - 1;
-  struct GNUNET_ATS_Information *ats;
-
-  ats = GNUNET_malloc (ats_count * sizeof (struct GNUNET_ATS_Information));
-  (*dest) = ats;
-
-  ats[0].type = ntohl(GNUNET_ATS_UTILIZATION_UP);
-  ats[0].value = aa->atsp_utilization_out.value__;
-  ats[1].type = ntohl(GNUNET_ATS_UTILIZATION_DOWN);
-  ats[1].value = aa->atsp_utilization_in.value__;
-  ats[2].type = ntohl(GNUNET_ATS_NETWORK_TYPE);
-  ats[2].value = ntohl(aa->atsp_network_type);
-  ats[3].type = ntohl(GNUNET_ATS_QUALITY_NET_DELAY);
-  ats[3].value = ntohl(aa->atsp_latency.rel_value);
-  ats[4].type = ntohl(GNUNET_ATS_QUALITY_NET_DISTANCE);
-  ats[4].value = ntohl(aa->atsp_distance);
-  ats[5].type = ntohl(GNUNET_ATS_COST_WAN);
-  ats[5].value = ntohl (aa->atsp_cost_wan);
-  ats[6].type = ntohl(GNUNET_ATS_COST_LAN);
-  ats[6].value = ntohl (aa->atsp_cost_lan);
-  ats[7].type = ntohl(GNUNET_ATS_COST_WLAN);
-  ats[7].value = ntohl (aa->atsp_cost_wlan);
-  return ats_count;
-}
-
-
-/**
- * Disassemble ATS information and update address
+ * Updates existing information and adds new information
  *
- * @param src source ATS information
- * @param ats_count number of ATS information
  * @param dest destination address
+ * @param update source ATS information
+ * @param update_count number of ATS information
+ * @param delta_dest ats performance information which were updated
+ *                             including previous value
+ * @param delta_count number of ATS information in the delta
  * @return GNUNET_YES if address was address updated, GNUNET_NO otherwise
  */
 static unsigned int
-disassemble_ats_information (const struct GNUNET_ATS_Information *src,
-                             uint32_t ats_count,
-                             struct ATS_Address *dest)
+disassemble_ats_information (struct ATS_Address *dest,
+                             const struct GNUNET_ATS_Information *update,
+                             uint32_t update_count,
+                             struct GNUNET_ATS_Information **delta_dest,
+                             uint32_t *delta_count)
 {
-  int i;
-  int change = GNUNET_NO;
-  int res = 0;
-  for (i = 0; i < ats_count; i++)
-    switch (ntohl (src[i].type))
+
+  int c1;
+  int c2;
+  int found;
+  int change;
+
+  struct GNUNET_ATS_Information add_atsi[update_count];
+  struct GNUNET_ATS_Information delta_atsi[update_count];
+  struct GNUNET_ATS_Information *tmp_atsi;
+  uint32_t add_atsi_count;
+  uint32_t delta_atsi_count;
+
+  change = GNUNET_NO;
+  add_atsi_count = 0;
+  delta_atsi_count = 0;
+
+  if (0 == update_count)
+       return GNUNET_NO;
+
+  if (NULL == dest->atsi)
+  {
+    /* Create performance information */
+    dest->atsi = GNUNET_malloc (update_count * sizeof (struct GNUNET_ATS_Information));
+    dest->atsi_count = update_count;
+    memcpy (dest->atsi, update, update_count * sizeof (struct GNUNET_ATS_Information));
+    (*delta_dest) = GNUNET_malloc (update_count * sizeof (struct GNUNET_ATS_Information));
+    for (c1 = 0; c1 < update_count; c1 ++)
     {
-    case GNUNET_ATS_UTILIZATION_UP:
-       if (dest->atsp_utilization_out.value__ != src[i].value)
-               change = GNUNET_YES;
-      dest->atsp_utilization_out.value__ = src[i].value;
-      res ++;
-      break;
-    case GNUNET_ATS_UTILIZATION_DOWN:
-       if (dest->atsp_utilization_in.value__ != src[i].value)
-               change = GNUNET_YES;
-      dest->atsp_utilization_in.value__ = src[i].value;
-      res ++;
-      break;
-    case GNUNET_ATS_QUALITY_NET_DELAY:
-       if (dest->atsp_latency.rel_value  != src[i].value)
-               change = GNUNET_YES;
-      dest->atsp_latency.rel_value = ntohl (src[i].value);
-      res ++;
-      break;
-    case GNUNET_ATS_QUALITY_NET_DISTANCE:
-       if (dest->atsp_distance!= src[i].value)
-               change = GNUNET_YES;
-      dest->atsp_distance = ntohl (src[i].value);
-      res ++;
-      break;
-    case GNUNET_ATS_COST_WAN:
-       if (dest->atsp_cost_wan != src[i].value)
-               change = GNUNET_YES;
-      dest->atsp_cost_wan = ntohl (src[i].value);
-      res ++;
-      break;
-    case GNUNET_ATS_COST_LAN:
-       if (dest->atsp_cost_lan != src[i].value)
-               change = GNUNET_YES;
-      dest->atsp_cost_lan = ntohl (src[i].value);
-      res ++;
-      break;
-    case GNUNET_ATS_COST_WLAN:
-       if (dest->atsp_cost_wlan != src[i].value)
-               change = GNUNET_YES;
-      dest->atsp_cost_wlan = ntohl (src[i].value);
-      res ++;
-      break;
-    case GNUNET_ATS_NETWORK_TYPE:
-       if (dest->atsp_network_type != src[i].value)
-               change = GNUNET_YES;
-      dest->atsp_network_type = ntohl (src[i].value);
-      res ++;
-      break;
-    case GNUNET_ATS_ARRAY_TERMINATOR:
-      break;
-    default:
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Received unsupported ATS type %u\n", ntohl (src[i].type));
-      GNUNET_break (0);
-      break;
+      (*delta_dest)[c1].type = update[c1].type;
+      (*delta_dest)[c1].value = htonl(GNUNET_ATS_VALUE_UNDEFINED);
+    }
+    (*delta_count) = update_count;
+    return GNUNET_YES;
+  }
+
+  for (c1 = 0; c1 < update_count; c1++)
+  {
+    /* Update existing performance information */
+    found = GNUNET_NO;
+    for (c2 = 0; c2 < dest->atsi_count; c2++)
+    {
+      if (update[c1].type == dest->atsi[c2].type)
+      {
+        if (update[c1].value != dest->atsi[c2].value)
+        {
+          /* Save previous value in delta */
+          delta_atsi[delta_atsi_count] = dest->atsi[c2];
+          delta_atsi_count ++;
+          /* Set new value */
+          dest->atsi[c2].value = update[c1].value;
+          change = GNUNET_YES;
+        }
+        found = GNUNET_YES;
+        break;
+      }
     }
+    if (GNUNET_NO == found)
+    {
+      add_atsi[add_atsi_count] = update[c1];
+      add_atsi_count ++;
+      delta_atsi[delta_atsi_count].type = update[c1].type;
+      delta_atsi[delta_atsi_count].value = htonl (GNUNET_ATS_VALUE_UNDEFINED);
+      delta_atsi_count ++;
+    }
+  }
+
+  if (add_atsi_count > 0)
+  {
+    /* Extend ats performance information */
+
+    tmp_atsi = GNUNET_malloc ((dest->atsi_count + add_atsi_count) *
+                              (sizeof (struct GNUNET_ATS_Information)));
+    memcpy (tmp_atsi, dest->atsi, dest->atsi_count * sizeof (struct GNUNET_ATS_Information));
+    memcpy (&tmp_atsi[dest->atsi_count], add_atsi, add_atsi_count * sizeof (struct GNUNET_ATS_Information));
+    GNUNET_free (dest->atsi);
+    dest->atsi = tmp_atsi;
+    dest->atsi_count = dest->atsi_count + add_atsi_count;
+    change = GNUNET_YES;
+  }
+
+  if (delta_atsi_count > 0)
+  {
+    /* Copy delta */
+    (*delta_dest) = GNUNET_malloc (delta_atsi_count * sizeof (struct GNUNET_ATS_Information));
+    memcpy ((*delta_dest), delta_atsi, delta_atsi_count * sizeof (struct GNUNET_ATS_Information));
+    (*delta_count) = delta_atsi_count;
+  }
+
   return change;
 }
 
 /**
  * Free the given address
+ *
  * @param addr address to destroy
  */
 static void
 free_address (struct ATS_Address *addr)
 {
   GNUNET_free (addr->plugin);
+  GNUNET_free_non_null (addr->atsi);
   GNUNET_free (addr);
 }
 
 /**
  * Create a ATS_address with the given information
+ *
  * @param peer peer
  * @param plugin_name plugin
  * @param plugin_addr address
@@ -510,6 +538,8 @@ create_address (const struct GNUNET_PeerIdentity *peer,
                 uint32_t session_id)
 {
   struct ATS_Address *aa = NULL;
+  int c1;
+  int c2;
 
   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
   aa->peer = *peer;
@@ -521,31 +551,19 @@ create_address (const struct GNUNET_PeerIdentity *peer,
   aa->active = GNUNET_NO;
   aa->used = GNUNET_NO;
   aa->solver_information = NULL;
+  aa->atsi = NULL;
+  aa->atsi_count = 0;
   aa->assigned_bw_in = GNUNET_BANDWIDTH_value_init(0);
   aa->assigned_bw_out = GNUNET_BANDWIDTH_value_init(0);
-  return aa;
-}
 
+  for (c1 = 0; c1 < GNUNET_ATS_QualityPropertiesCount; c1 ++)
+  {
+       aa->atsin[c1].avg_queue_index = 0;
+       for (c2 = 0; c2 < GAS_normalization_queue_length; c2++)
+               aa->atsin[c1].atsi_abs[c2] = GNUNET_ATS_VALUE_UNDEFINED;
+  }
 
-/**
- * Destroy the given address.
- *
- * @param handle the address handle
- * @param addr address to destroy
- * @return GNUNET_YES if bandwidth allocations should be recalcualted
- */
-static int
-destroy_address (struct GAS_Addresses_Handle *handle, struct ATS_Address *addr)
-{
-  int ret;
-
-  ret = GNUNET_NO;
-  GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multihashmap_remove (handle->addresses,
-                                                       &addr->peer.hashPubKey,
-                                                       addr));
-  free_address (addr);
-  return ret;
+  return aa;
 }
 
 
@@ -559,6 +577,14 @@ struct CompareAddressContext
   struct ATS_Address *base_address;
 };
 
+/**
+ * Comapre addresses
+ *
+ * @param cls a CompareAddressContext containin the source address
+ * @param key peer id
+ * @param value the address to compare with
+ * @return GNUNET_YES to continue, GNUNET_NO if address is founce
+ */
 
 static int
 compare_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
@@ -655,15 +681,25 @@ find_equivalent_address (struct GAS_Addresses_Handle *handle,
 }
 
 
+/**
+ * Find the exact address
+ *
+ * @param handle the address handle to use
+ * @param peer peer
+ * @param plugin_name transport plugin name
+ * @param plugin_addr plugin address
+ * @param plugin_addr_len length of the plugin address
+ * @param session_id session id, can be 0
+ * @return an ATS_address or NULL
+ */
+
 static struct ATS_Address *
-lookup_address (struct GAS_Addresses_Handle *handle,
+find_exact_address (struct GAS_Addresses_Handle *handle,
                 const struct GNUNET_PeerIdentity *peer,
                 const char *plugin_name,
                 const void *plugin_addr,
                 size_t plugin_addr_len,
-                uint32_t session_id,
-                const struct GNUNET_ATS_Information *atsi,
-                uint32_t atsi_count)
+                uint32_t session_id)
 {
   struct ATS_Address *aa;
   struct ATS_Address *ea;
@@ -677,17 +713,37 @@ lookup_address (struct GAS_Addresses_Handle *handle,
   ea = find_equivalent_address (handle, peer, aa);
   free_address (aa);
   if (ea == NULL)
-  {
     return NULL;
-  }
   else if (ea->session_id != session_id)
-  {
     return NULL;
-  }
   return ea;
 }
 
 
+/**
+ * Extract an ATS performance info from an address
+ *
+ * @param address the address
+ * @param type the type to extract in HBO
+ * @return the value in HBO or GNUNET_ATS_VALUE_UNDEFINED in HBO if value does not exist
+ */
+static int
+get_performance_info (struct ATS_Address *address, uint32_t type)
+{
+       int c1;
+       GNUNET_assert (NULL != address);
+
+       if ((NULL == address->atsi) || (0 == address->atsi_count))
+                       return GNUNET_ATS_VALUE_UNDEFINED;
+
+       for (c1 = 0; c1 < address->atsi_count; c1++)
+       {
+                       if (ntohl(address->atsi[c1].type) == type)
+                               return ntohl(address->atsi[c1].value);
+       }
+       return GNUNET_ATS_VALUE_UNDEFINED;
+}
+
 
 /**
  * Add a new address for a peer.
@@ -704,16 +760,20 @@ lookup_address (struct GAS_Addresses_Handle *handle,
 void
 GAS_addresses_add (struct GAS_Addresses_Handle *handle,
                    const struct GNUNET_PeerIdentity *peer,
-                   const char *plugin_name, const void *plugin_addr,
-                   size_t plugin_addr_len, uint32_t session_id,
+                   const char *plugin_name,
+                   const void *plugin_addr,
+                   size_t plugin_addr_len,
+                   uint32_t session_id,
                    const struct GNUNET_ATS_Information *atsi,
                    uint32_t atsi_count)
 {
-  struct ATS_Address *aa;
-  struct ATS_Address *ea;
-  struct GNUNET_ATS_Information *ats_new;
-  uint32_t ats_count_new;
-  unsigned int ats_res;
+  struct ATS_Address *new_address;
+  struct ATS_Address *existing_address;
+  struct GNUNET_ATS_Information *atsi_delta;
+  uint32_t atsi_delta_count;
+  uint32_t addr_net;
+  uint32_t previous_session;
+  int c1;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Received `%s' for peer `%s'\n",
@@ -725,77 +785,122 @@ GAS_addresses_add (struct GAS_Addresses_Handle *handle,
 
   GNUNET_assert (NULL != handle->addresses);
 
-  aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len,
+  new_address = create_address (peer, plugin_name, plugin_addr, plugin_addr_len,
                        session_id);
-
-  if (atsi_count != (ats_res = disassemble_ats_information(atsi, atsi_count, aa)))
-  {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "While adding address: had %u ATS elements to add, could only add %u\n",
-                atsi_count, ats_res);
-  }
+  atsi_delta = NULL;
+  disassemble_ats_information (new_address, atsi, atsi_count, &atsi_delta, &atsi_delta_count);
+  GNUNET_free_non_null (atsi_delta);
+  addr_net = get_performance_info (new_address, GNUNET_ATS_NETWORK_TYPE);
+  if (GNUNET_ATS_VALUE_UNDEFINED == addr_net)
+               addr_net = GNUNET_ATS_NET_UNSPECIFIED;
 
   /* Get existing address or address with session == 0 */
-  ea = find_equivalent_address (handle, peer, aa);
-  if (ea == NULL)
+  existing_address = find_equivalent_address (handle, peer, new_address);
+  if (existing_address == NULL)
   {
-    /* We have a new address */
+    /* Add a new address */
     GNUNET_assert (GNUNET_OK ==
                    GNUNET_CONTAINER_multihashmap_put (handle->addresses,
-                                                      &peer->hashPubKey, aa,
-                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' session id %u, %p\n",
-                GNUNET_i2s (peer), session_id, aa);
+                      &peer->hashPubKey, new_address,
+                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Adding new address %p for peer `%s', length %u, session id %u, %s\n",
+               new_address, GNUNET_i2s (peer), plugin_addr_len, session_id, GNUNET_ATS_print_network_type(addr_net));
+
     /* Tell solver about new address */
-    handle->s_add (handle->solver, handle->addresses, aa);
+    handle->s_add (handle->solver, new_address, addr_net);
+
+    handle->s_bulk_start (handle->solver);
+    GAS_normalization_normalize_property (handle->addresses, new_address, atsi, atsi_count);
+    handle->s_bulk_stop (handle->solver);
+
     /* Notify performance clients about new address */
-    ats_count_new = assemble_ats_information (aa, &ats_new);
-    GAS_performance_notify_all_clients (&aa->peer,
-        aa->plugin,
-        aa->addr, aa->addr_len,
-        aa->session_id,
-        ats_new, ats_count_new,
-        aa->assigned_bw_out,
-        aa->assigned_bw_in);
-    GNUNET_free (ats_new);
+    GAS_performance_notify_all_clients (&new_address->peer,
+        new_address->plugin,
+        new_address->addr, new_address->addr_len,
+        new_address->session_id,
+        new_address->atsi, new_address->atsi_count,
+        new_address->assigned_bw_out,
+        new_address->assigned_bw_in);
     return;
   }
-  GNUNET_free (aa->plugin);
-  GNUNET_free (aa);
 
-  if (ea->session_id != 0)
+  /* We have an existing address we can use, clean up new */
+  GNUNET_free (new_address->plugin);
+  GNUNET_free_non_null (new_address->atsi);
+  GNUNET_free (new_address);
+  new_address = NULL;
+
+  if (0 != existing_address->session_id)
   {
-      /* This address with the same session is already existing
-       * Should not happen */
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Added already existing address for peer `%s' `%s' %p with new session %u\n",
-                GNUNET_i2s (peer), plugin_name, session_id);
+      /* Should not happen */
       GNUNET_break (0);
       return;
   }
 
-  /* We have an address without an session, update this address */
-
-  /* Notify solver about update with atsi information and session */
-  handle->s_update (handle->solver, handle->addresses, ea, session_id, ea->used, atsi, atsi_count);
+  addr_net = get_performance_info (existing_address, GNUNET_ATS_NETWORK_TYPE);
+  if (GNUNET_ATS_VALUE_UNDEFINED == addr_net)
+               addr_net = GNUNET_ATS_NET_UNSPECIFIED;
 
-  /* Do the update */
-  ea->session_id = session_id;
-  if (GNUNET_YES == disassemble_ats_information(atsi, atsi_count, ea))
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+           "Found existing address for peer `%s' %p with new session %u in network %s\n",
+           GNUNET_i2s (peer),
+           existing_address,
+           session_id,
+           GNUNET_ATS_print_network_type (addr_net));
+  /* We have an address without an session, update this address */
+  atsi_delta = NULL;
+  atsi_delta_count = 0;
+  if (GNUNET_YES == disassemble_ats_information (existing_address, atsi, atsi_count, &atsi_delta, &atsi_delta_count))
   {
-               ats_count_new = assemble_ats_information (aa, &ats_new);
-               GAS_performance_notify_all_clients (&aa->peer,
-                               aa->plugin,
-                               aa->addr, aa->addr_len,
-                               aa->session_id,
-                               ats_new, ats_count_new,
-                               aa->assigned_bw_out,
-                               aa->assigned_bw_in);
-               GNUNET_free (ats_new);
+    /* Notify performance clients about properties */
+    GAS_performance_notify_all_clients (&existing_address->peer,
+                existing_address->plugin,
+                existing_address->addr, existing_address->addr_len,
+                existing_address->session_id,
+                existing_address->atsi, existing_address->atsi_count,
+                existing_address->assigned_bw_out,
+                existing_address->assigned_bw_in);
+
+    for (c1 = 0; c1 < atsi_delta_count; c1++)
+    {
+      if ((GNUNET_ATS_NETWORK_TYPE == ntohl (atsi_delta[c1].type)) &&
+              (addr_net != ntohl (atsi_delta[c1].value)))
+      {
+          /* Network type changed */
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Address for peer `%s' %p changed from network %s to %s\n",
+                  GNUNET_i2s (peer), existing_address,
+                  GNUNET_ATS_print_network_type (addr_net),
+                  GNUNET_ATS_print_network_type (ntohl (atsi_delta[c1].value)));
+          handle->s_address_update_network (handle->solver, existing_address,
+                  ntohl (atsi_delta[c1].value),
+                  get_performance_info (existing_address, GNUNET_ATS_NETWORK_TYPE));
+          addr_net = get_performance_info (existing_address, GNUNET_ATS_NETWORK_TYPE);
+      }
+    }
+    /* Notify solver about update with atsi information and session */
+    handle->s_bulk_start (handle->solver);
+    GAS_normalization_normalize_property (handle->addresses, existing_address, atsi, atsi_count);
+    handle->s_bulk_stop (handle->solver);
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-           "Updated existing address for peer `%s' %p with new session %u\n",
-           GNUNET_i2s (peer), ea, session_id);
+  GNUNET_free_non_null (atsi_delta);
+
+  /* Notify solver about new session */
+  if (existing_address->session_id == session_id)
+       return; /* possible, can both be 0 since address is revalidated */
+
+  previous_session = existing_address->session_id;
+  existing_address->session_id = session_id;
+  handle->s_address_update_session (handle->solver, existing_address,
+               previous_session, session_id);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+           "Updated existing address for peer `%s' %p length %u with new session %u in network %s\n",
+           GNUNET_i2s (peer),
+           existing_address,
+           existing_address->addr_len,
+           session_id,
+           GNUNET_ATS_print_network_type(addr_net));
 }
 
 
@@ -823,8 +928,10 @@ GAS_addresses_update (struct GAS_Addresses_Handle *handle,
                       uint32_t atsi_count)
 {
   struct ATS_Address *aa;
-  struct GNUNET_ATS_Information *ats_new;
-  uint32_t ats_count_new;
+  struct GNUNET_ATS_Information *atsi_delta;
+  uint32_t atsi_delta_count;
+  uint32_t prev_session;
+  int c1;
 
   if (GNUNET_NO == handle->running)
     return;
@@ -832,8 +939,8 @@ GAS_addresses_update (struct GAS_Addresses_Handle *handle,
   GNUNET_assert (NULL != handle->addresses);
 
   /* Get existing address */
-  aa = lookup_address (handle, peer, plugin_name, plugin_addr, plugin_addr_len,
-                       session_id, atsi, atsi_count);
+  aa = find_exact_address (handle, peer, plugin_name,
+        plugin_addr, plugin_addr_len, session_id);
   if (aa == NULL)
   {
     /* GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to update unknown address for peer `%s' `%s' session id %u\n", */
@@ -842,28 +949,57 @@ GAS_addresses_update (struct GAS_Addresses_Handle *handle,
     return;
   }
 
+  if (NULL == aa->solver_information)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to update unknown address for peer `%s' `%s' session id %u\n",
+                 GNUNET_i2s (peer), plugin_name, session_id);
+    return;
+  }
+
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Received `%s' for peer `%s' address \n",
                 "ADDRESS UPDATE",
                 GNUNET_i2s (peer), aa);
 
-  /* Tell solver about update */
-  handle->s_update (handle->solver, handle->addresses, aa, session_id, aa->used, atsi, atsi_count);
-
   /* Update address */
-  if (GNUNET_YES == disassemble_ats_information (atsi, atsi_count, aa))
+  if (session_id != aa->session_id)
+  {
+       /* Session changed */
+    prev_session = aa->session_id;
+    aa->session_id = session_id;
+    handle->s_address_update_session (handle->solver, aa, prev_session, aa->session_id);
+  }
+
+  atsi_delta = NULL;
+  atsi_delta_count = 0;
+  if (GNUNET_YES == disassemble_ats_information (aa, atsi, atsi_count, &atsi_delta, &atsi_delta_count))
   {
-               ats_count_new = assemble_ats_information (aa, &ats_new);
-               /* Notify performance clients about updated address */
-               GAS_performance_notify_all_clients (&aa->peer,
-                               aa->plugin,
-                               aa->addr, aa->addr_len,
-                               aa->session_id,
-                               ats_new, ats_count_new,
-                               aa->assigned_bw_out,
-                               aa->assigned_bw_in);
-               GNUNET_free (ats_new);
+    /* ATS properties changed */
+    for (c1 = 0; c1 < atsi_delta_count; c1++)
+    {
+        if (GNUNET_ATS_NETWORK_TYPE == ntohl (atsi_delta[c1].type))
+        {
+            /* Network type changed */
+            handle->s_address_update_network (handle->solver, aa,
+                      ntohl (atsi_delta[c1].value),
+                      get_performance_info (aa, GNUNET_ATS_NETWORK_TYPE));
+        }
+    }
+
+    /* Notify performance clients about updated address */
+    GAS_performance_notify_all_clients (&aa->peer,
+                    aa->plugin,
+                    aa->addr, aa->addr_len,
+                    aa->session_id,
+                    aa->atsi, aa->atsi_count,
+                    aa->assigned_bw_out,
+                    aa->assigned_bw_in);
+
+    handle->s_bulk_start (handle->solver);
+    GAS_normalization_normalize_property (handle->addresses, aa, atsi, atsi_count);
+    handle->s_bulk_stop (handle->solver);
   }
+  GNUNET_free_non_null (atsi_delta);
 }
 
 
@@ -913,13 +1049,16 @@ destroy_by_session_id (void *cls, const struct GNUNET_HashCode * key, void *valu
         (0 == memcmp (des->addr, aa->addr, aa->addr_len)))
     {
 
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Deleting full address for peer `%s' session %u %p\n",
                   GNUNET_i2s (&aa->peer), aa->session_id, aa);
 
       /* Notify solver about deletion */
-      handle->s_del (handle->solver, handle->addresses, aa, GNUNET_NO);
-      destroy_address (handle, aa);
+      GNUNET_assert (GNUNET_YES ==
+               GNUNET_CONTAINER_multihashmap_remove (handle->addresses,
+                       &aa->peer.hashPubKey, aa));
+      handle->s_del (handle->solver, aa, GNUNET_NO);
+      free_address (aa);
       dc->result = GNUNET_NO;
       return GNUNET_OK; /* Continue iteration */
     }
@@ -933,7 +1072,7 @@ destroy_by_session_id (void *cls, const struct GNUNET_HashCode * key, void *valu
     if ((aa->session_id != 0) &&
         (0 != strcmp (des->plugin, aa->plugin)))
     {
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                     "Different plugins during removal: `%s' vs `%s' \n",
                     des->plugin, aa->plugin);
         GNUNET_break (0);
@@ -943,25 +1082,29 @@ destroy_by_session_id (void *cls, const struct GNUNET_HashCode * key, void *valu
     if (aa->addr_len == 0)
     {
         /* Inbound connection died, delete full address */
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                     "Deleting inbound address for peer `%s': `%s' session %u\n",
                     GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
 
         /* Notify solver about deletion */
-        handle->s_del (handle->solver, handle->addresses, aa, GNUNET_NO);
-        destroy_address (handle, aa);
+        GNUNET_assert (GNUNET_YES ==
+                       GNUNET_CONTAINER_multihashmap_remove (handle->addresses,
+                       &aa->peer.hashPubKey, aa));
+        handle->s_del (handle->solver, aa, GNUNET_NO);
+        free_address (aa);
         dc->result = GNUNET_NO;
         return GNUNET_OK; /* Continue iteration */
     }
     else
     {
         /* Session died */
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                     "Deleting session for peer `%s': `%s' %u\n",
                     GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
         /* Notify solver to delete session */
-        handle->s_del (handle->solver, handle->addresses, aa, GNUNET_YES);
+        handle->s_del (handle->solver, aa, GNUNET_YES);
         aa->session_id = 0;
+        aa->active = GNUNET_NO;
         return GNUNET_OK;
     }
   }
@@ -989,19 +1132,12 @@ GAS_addresses_destroy (struct GAS_Addresses_Handle *handle,
 {
   struct ATS_Address *ea;
   struct DestroyContext dc;
-
   if (GNUNET_NO == handle->running)
     return;
 
   /* Get existing address */
-  ea = lookup_address (handle, peer, plugin_name, plugin_addr, plugin_addr_len,
-                       session_id, NULL, 0);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received `%s' for peer `%s' address %p session %u\n",
-              "ADDRESS DESTROY",
-              GNUNET_i2s (peer), ea, session_id);
-
+  ea = find_exact_address (handle, peer, plugin_name, plugin_addr,
+               plugin_addr_len, session_id);
   if (ea == NULL)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tried to destroy unknown address for peer `%s' `%s' session id %u\n",
@@ -1009,6 +1145,11 @@ GAS_addresses_destroy (struct GAS_Addresses_Handle *handle,
     return;
   }
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received `%s' for peer `%s' address %p session %u\n",
+              "ADDRESS DESTROY",
+              GNUNET_i2s (peer), ea, session_id);
+
   GNUNET_break (0 < strlen (plugin_name));
   dc.handle = handle;
   dc.aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
@@ -1048,7 +1189,6 @@ GAS_addresses_in_use (struct GAS_Addresses_Handle *handle,
                       int in_use)
 {
   struct ATS_Address *ea;
-
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Received `%s' for peer `%s'\n",
                 "ADDRESS IN USE",
@@ -1057,13 +1197,12 @@ GAS_addresses_in_use (struct GAS_Addresses_Handle *handle,
   if (GNUNET_NO == handle->running)
     return GNUNET_SYSERR;
 
-  ea = lookup_address (handle, peer, plugin_name,
-                        plugin_addr, plugin_addr_len,
-                        session_id, NULL, 0);
+  ea = find_exact_address (handle, peer, plugin_name,
+               plugin_addr, plugin_addr_len, session_id);
   if (NULL == ea)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Trying to set unknown address `%s', %s %u %s \n",
+                "Trying to set unknown address `%s' `%s' `%u' to %s \n",
                 GNUNET_i2s (peer),
                 plugin_name, session_id,
                 (GNUNET_NO == in_use) ? "NO" : "YES");
@@ -1082,9 +1221,8 @@ GAS_addresses_in_use (struct GAS_Addresses_Handle *handle,
   }
 
   /* Tell solver about update */
-  handle->s_update (handle->solver, handle->addresses, ea, session_id, in_use, NULL, 0);
   ea->used = in_use;
-
+  handle->s_address_update_inuse (handle->solver, ea, ea->used);
   return GNUNET_OK;
 }
 
@@ -1117,7 +1255,7 @@ GAS_addresses_request_address_cancel (struct GAS_Addresses_Handle *handle,
                   "No address requests pending for peer `%s', cannot remove!\n", GNUNET_i2s (peer));
       return;
   }
-  handle->s_get_stop (handle->solver, handle->addresses, peer);
+  handle->s_get_stop (handle->solver, peer);
   GAS_addresses_handle_backoff_reset (handle, peer);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Removed request pending for peer `%s\n", GNUNET_i2s (peer));
@@ -1125,6 +1263,23 @@ GAS_addresses_request_address_cancel (struct GAS_Addresses_Handle *handle,
   GNUNET_free (cur);
 }
 
+/*
+static int
+addrinfo_it (void *cls, const struct GNUNET_HashCode *key, void *value)
+{
+       static int count = 0;
+       struct GNUNET_PeerIdentity *id = cls;
+       struct ATS_Address *aa = value;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "[%u] Peer `%s' %s length %u session %u active %s\n",
+              count, GNUNET_i2s (id), aa->plugin, aa->addr_len, aa->session_id,
+              (GNUNET_YES == aa->active) ? "active" : "inactive");
+
+  count ++;
+       return GNUNET_OK;
+}
+*/
 
 /**
  * Request address suggestions for a peer
@@ -1138,10 +1293,8 @@ GAS_addresses_request_address (struct GAS_Addresses_Handle *handle,
 {
   struct GAS_Addresses_Suggestion_Requests *cur = handle->r_head;
   struct ATS_Address *aa;
-  struct GNUNET_ATS_Information *ats;
-  unsigned int ats_count;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received `%s' for peer `%s'\n",
               "REQUEST ADDRESS",
               GNUNET_i2s (peer));
@@ -1161,11 +1314,18 @@ GAS_addresses_request_address (struct GAS_Addresses_Handle *handle,
       GNUNET_CONTAINER_DLL_insert (handle->r_head, handle->r_tail, cur);
   }
 
+  /*
+   * Debuging information about addresses
+   *
+   * GNUNET_CONTAINER_multihashmap_get_multiple(handle->addresses,
+   *  &peer->hashPubKey, &addrinfo_it, (void *) peer);
+   */
+
   /* Get prefered address from solver */
-  aa = (struct ATS_Address *) handle->s_get (handle->solver, handle->addresses, peer);
+  aa = (struct ATS_Address *) handle->s_get (handle->solver, peer);
   if (NULL == aa)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
     return;
   }
@@ -1173,12 +1333,11 @@ GAS_addresses_request_address (struct GAS_Addresses_Handle *handle,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Suggesting address %p for peer `%s'\n", aa, GNUNET_i2s (peer));
 
-  ats_count = assemble_ats_information (aa, &ats);
   GAS_scheduling_transmit_address_suggestion (peer,
                                               aa->plugin,
                                               aa->addr, aa->addr_len,
                                               aa->session_id,
-                                              ats, ats_count,
+                                              aa->atsi, aa->atsi_count,
                                               aa->assigned_bw_out,
                                               aa->assigned_bw_in);
 
@@ -1188,13 +1347,19 @@ GAS_addresses_request_address (struct GAS_Addresses_Handle *handle,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
        "Address %p ready for suggestion, block interval now %llu \n",
        aa, aa->block_interval);
-
-  GNUNET_free (ats);
 }
 
 
+/**
+ * Iterator to reset address blocking
+ *
+ * @param cls not used
+ * @param key the peer
+ * @param value the address to reset
+ * @return GNUNET_OK to continue
+ */
 static int
-reset_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
+reset_address_it (void *cls, const struct GNUNET_HashCode *key, void *value)
 {
   struct ATS_Address *aa = value;
 
@@ -1233,6 +1398,83 @@ GAS_addresses_handle_backoff_reset (struct GAS_Addresses_Handle *handle,
 }
 
 
+/**
+ * The preference changed for a peer
+ *
+ * @param cls the address handle
+ * @param peer the peer
+ * @param kind the ATS kind
+ * @param pref_rel the new relative preference value
+ */
+static void
+normalized_preference_changed_cb (void *cls,
+                                  const struct GNUNET_PeerIdentity *peer,
+                                  enum GNUNET_ATS_PreferenceKind kind,
+                                  double pref_rel)
+{
+  GNUNET_assert (NULL != cls);
+  struct GAS_Addresses_Handle *handle = cls;
+
+  /* Tell solver about update */
+  handle->s_pref (handle->solver, peer, kind, pref_rel);
+}
+
+
+/**
+ * The relative value for a property changed
+ *
+ * @param cls the address handle
+ * @param address the peer
+ * @param type the ATS type
+ * @param prop_rel the new relative preference value
+ */
+static void
+normalized_property_changed_cb (void *cls,
+                                struct ATS_Address *address,
+                                uint32_t type,
+                                double prop_rel)
+{
+  struct GAS_Addresses_Handle *ah = (struct GAS_Addresses_Handle *) cls;
+  GNUNET_assert (NULL != ah);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        "Normalized property %s for peer `%s' changed to %.3f \n",
+        GNUNET_ATS_print_property_type (type),
+        GNUNET_i2s (&address->peer),
+        prop_rel);
+
+  ah->s_address_update_property (ah->solver, address, type, 0, prop_rel);
+}
+
+
+/**
+ * Function allowing the solver to obtain normalized preference
+ * values from solver
+ *
+ * @param cls unused
+ * @param id the peer to return the normalized properties for
+ * @return array of double values with |GNUNET_ATS_PreferenceCount| elements
+ */
+const double *
+get_preferences_cb (void *cls, const struct GNUNET_PeerIdentity *id)
+{
+       return GAS_normalization_get_preferences (id);
+}
+
+/**
+ * Function allowing the solver to obtain normalized property
+ * values for an address from solver
+ *
+ * @param cls unused
+ * @param address the address
+ * @return array of double values with |GNUNET_ATS_QualityPropertiesCount| elements
+ */
+const double *
+get_property_cb (void *cls, const struct ATS_Address *address)
+{
+       return GAS_normalization_get_properties ((struct ATS_Address *) address);
+}
+
 /**
  * Change the preference for a peer
  *
@@ -1240,19 +1482,19 @@ GAS_addresses_handle_backoff_reset (struct GAS_Addresses_Handle *handle,
  * @param client the client sending this request
  * @param peer the peer id
  * @param kind the preference kind to change
- * @param score the new preference score
+ * @param score_abs the new preference score
  */
 void
 GAS_addresses_change_preference (struct GAS_Addresses_Handle *handle,
                                  void *client,
                                  const struct GNUNET_PeerIdentity *peer,
                                  enum GNUNET_ATS_PreferenceKind kind,
-                                 float score)
+                                 float score_abs)
 {
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received `%s' for peer `%s' for client %p\n",
-              "CHANGE PREFERENCE",
-              GNUNET_i2s (peer), client);
+        "Received `%s' for peer `%s' for client %p\n",
+        "CHANGE PREFERENCE",
+        GNUNET_i2s (peer), client);
 
   if (GNUNET_NO == handle->running)
     return;
@@ -1267,8 +1509,50 @@ GAS_addresses_change_preference (struct GAS_Addresses_Handle *handle,
       return;
   }
 
-  /* Tell solver about update */
-  handle->s_pref (handle->solver, client, peer, kind, score);
+  handle->s_bulk_start (handle->solver);
+  /* Tell normalization about change, normalization will call callback if preference changed */
+  GAS_normalization_normalize_preference (client, peer, kind, score_abs);
+  handle->s_bulk_stop (handle->solver);
+}
+
+
+/**
+ * Change the preference for a peer
+ *
+ * @param handle the address handle
+ * @param application the client sending this request
+ * @param peer the peer id
+ * @param scope the time interval for this feedback: [now - scope .. now]
+ * @param kind the preference kind to change
+ * @param score_abs the new preference score
+ */
+void
+GAS_addresses_preference_feedback (struct GAS_Addresses_Handle *handle,
+                                  void *application,
+                                  const struct GNUNET_PeerIdentity *peer,
+                                  const struct GNUNET_TIME_Relative scope,
+                                  enum GNUNET_ATS_PreferenceKind kind,
+                                  float score_abs)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+        "Received `%s' for peer `%s' for client %p\n",
+        "PREFERENCE FEEDBACK",
+        GNUNET_i2s (peer), application);
+
+  if (GNUNET_NO == handle->running)
+    return;
+
+  if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->addresses,
+                                                          &peer->hashPubKey))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Received `%s' for unknown peer `%s' from client %p\n",
+                "PREFERENCE FEEDBACK",
+                GNUNET_i2s (peer), application);
+    return;
+  }
+
+  handle->s_feedback (handle->solver, application, peer, scope, kind, score_abs);
 }
 
 
@@ -1282,7 +1566,10 @@ GAS_addresses_change_preference (struct GAS_Addresses_Handle *handle,
  * @return number of networks loaded
  */
 static unsigned int
-load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned long long *out_dest, unsigned long long *in_dest, int dest_length)
+load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg,
+            unsigned long long *out_dest,
+            unsigned long long *in_dest,
+            int dest_length)
 {
   char *network_str[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkTypeString;
   char * entry_in = NULL;
@@ -1374,28 +1661,30 @@ load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned long long *
 }
 
 
+/**
+ * Callback for solver to notify about assignment changes
+ *
+ * @param cls the GAS_Addresses_Handle
+ * @param address the address with changes
+ */
 static void
 bandwidth_changed_cb (void *cls, struct ATS_Address *address)
 {
   struct GAS_Addresses_Handle *handle = cls;
   struct GAS_Addresses_Suggestion_Requests *cur;
-  struct GNUNET_ATS_Information *ats;
-  unsigned int ats_count;
 
   GNUNET_assert (handle != NULL);
   GNUNET_assert (address != NULL);
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bandwidth assignment changed for peer %s \n", GNUNET_i2s(&address->peer));
-
-
-  ats_count = assemble_ats_information (address, &ats);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bandwidth assignment changed for peer %s \n",
+               GNUNET_i2s(&address->peer));
 
   /* Notify performance clients about changes to address */
   GAS_performance_notify_all_clients (&address->peer,
       address->plugin,
       address->addr, address->addr_len,
       address->session_id,
-      ats, ats_count,
+      address->atsi, address->atsi_count,
       address->assigned_bw_out,
       address->assigned_bw_in);
   cur = handle->r_head;
@@ -1407,24 +1696,35 @@ bandwidth_changed_cb (void *cls, struct ATS_Address *address)
   }
   if (NULL == cur)
   {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Nobody is interested in peer `%s' :(\n",GNUNET_i2s (&address->peer));
-      GNUNET_free (ats);
       return;
   }
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Sending bandwidth update for peer `%s'\n",GNUNET_i2s (&address->peer));
+  if ((0 == ntohl (address->assigned_bw_in.value__)) &&
+               (0 == ntohl (address->assigned_bw_out.value__)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Telling transport to disconnect peer `%s'\n",
+                GNUNET_i2s (&address->peer));
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Sending bandwidth update for peer `%s': %llu %llu\n",
+                GNUNET_i2s (&address->peer),
+                address->assigned_bw_out,
+                address->assigned_bw_out);
+  }
 
   /* *Notify scheduling clients about suggestion */
   GAS_scheduling_transmit_address_suggestion (&address->peer,
                                               address->plugin,
                                               address->addr, address->addr_len,
                                               address->session_id,
-                                              ats, ats_count,
+                                              address->atsi, address->atsi_count,
                                               address->assigned_bw_out,
                                               address->assigned_bw_in);
-  GNUNET_free (ats);
 }
 
 
@@ -1432,7 +1732,7 @@ bandwidth_changed_cb (void *cls, struct ATS_Address *address)
  * Initialize address subsystem. The addresses subsystem manages the addresses
  * known and current performance information. It has a solver component
  * responsible for the resource allocation. It tells the solver about changes
- * and receives updates when the solver changes the ressource allocation.
+ * and receives updates when the solver changes the resource allocation.
  *
  * @param cfg configuration to use
  * @param stats the statistics handle to use
@@ -1461,29 +1761,33 @@ GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
   /* Figure out configured solution method */
   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "MODE", &mode_str))
   {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No ressource assignment method configured, using simplistic approch\n");
-      ah->ats_mode = MODE_SIMPLISTIC;
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No resource assignment method configured, using proportional approach\n");
+      ah->ats_mode = MODE_PROPORTIONAL;
   }
   else
   {
       for (c = 0; c < strlen (mode_str); c++)
         mode_str[c] = toupper (mode_str[c]);
-      if (0 == strcmp (mode_str, "SIMPLISTIC"))
+      if (0 == strcmp (mode_str, "PROPORTIONAL"))
       {
-          ah->ats_mode = MODE_SIMPLISTIC;
+          ah->ats_mode = MODE_PROPORTIONAL;
       }
       else if (0 == strcmp (mode_str, "MLP"))
       {
           ah->ats_mode = MODE_MLP;
 #if !HAVE_LIBGLPK
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Assignment method `%s' configured, but GLPK is not availabe, please install \n", mode_str);
-          ah->ats_mode = MODE_SIMPLISTIC;
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Assignment method `%s' configured, but GLPK is not available, please install \n", mode_str);
+          ah->ats_mode = MODE_PROPORTIONAL;
 #endif
       }
+      else if (0 == strcmp (mode_str, "RIL"))
+      {
+         ah->ats_mode = MODE_RIL;
+      }
       else
       {
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid ressource assignment method `%s' configured, using simplistic approch\n", mode_str);
-          ah->ats_mode = MODE_SIMPLISTIC;
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid resource assignment method `%s' configured, using proportional approach\n", mode_str);
+          ah->ats_mode = MODE_PROPORTIONAL;
       }
       GNUNET_free (mode_str);
   }
@@ -1493,33 +1797,61 @@ GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
     case MODE_MLP:
       /* Init the MLP solver with default values */
 #if HAVE_LIBGLPK
-      ah->ats_mode = MODE_MLP;
       ah->s_init = &GAS_mlp_init;
       ah->s_add = &GAS_mlp_address_add;
-      ah->s_update = &GAS_mlp_address_update;
+      ah->s_address_update_property = &GAS_mlp_address_property_changed;
+      ah->s_address_update_session = &GAS_mlp_address_session_changed;
+      ah->s_address_update_inuse = &GAS_mlp_address_inuse_changed;
+      ah->s_address_update_network = &GAS_mlp_address_change_network;
       ah->s_get = &GAS_mlp_get_preferred_address;
       ah->s_get_stop = &GAS_mlp_stop_get_preferred_address;
       ah->s_pref = &GAS_mlp_address_change_preference;
+      ah->s_feedback = &GAS_mlp_address_preference_feedback;
       ah->s_del =  &GAS_mlp_address_delete;
+      ah->s_bulk_start = &GAS_mlp_bulk_start;
+      ah->s_bulk_stop = &GAS_mlp_bulk_stop;
       ah->s_done = &GAS_mlp_done;
 #else
       GNUNET_free (ah);
       return NULL;
 #endif
       break;
-    case MODE_SIMPLISTIC:
-      /* Init the simplistic solver with default values */
-      ah->ats_mode = MODE_SIMPLISTIC;
-      ah->s_init = &GAS_simplistic_init;
-      ah->s_add = &GAS_simplistic_address_add;
-      ah->s_update = &GAS_simplistic_address_update;
-      ah->s_get = &GAS_simplistic_get_preferred_address;
-      ah->s_get_stop = &GAS_simplistic_stop_get_preferred_address;
-      ah->s_pref = &GAS_simplistic_address_change_preference;
-      ah->s_del  = &GAS_simplistic_address_delete;
-      ah->s_done = &GAS_simplistic_done;
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "SIMPLISTIC");
+    case MODE_PROPORTIONAL:
+      /* Init the proportional solver with default values */
+      ah->s_init = &GAS_proportional_init;
+      ah->s_add = &GAS_proportional_address_add;
+      ah->s_address_update_property = &GAS_proportional_address_property_changed;
+      ah->s_address_update_session = &GAS_proportional_address_session_changed;
+      ah->s_address_update_inuse = &GAS_proportional_address_inuse_changed;
+      ah->s_address_update_network = &GAS_proportional_address_change_network;
+      ah->s_get = &GAS_proportional_get_preferred_address;
+      ah->s_get_stop = &GAS_proportional_stop_get_preferred_address;
+      ah->s_pref = &GAS_proportional_address_change_preference;
+      ah->s_feedback = &GAS_proportional_address_preference_feedback;
+      ah->s_del  = &GAS_proportional_address_delete;
+      ah->s_bulk_start = &GAS_proportional_bulk_start;
+      ah->s_bulk_stop = &GAS_proportional_bulk_stop;
+      ah->s_done = &GAS_proportional_done;
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "PROPORTIONAL");
       break;
+    case MODE_RIL:
+      /* Init the ril solver with default values */
+         ah->s_init = &GAS_ril_init;
+         ah->s_add = &GAS_ril_address_add;
+         ah->s_address_update_property = &GAS_ril_address_property_changed;
+         ah->s_address_update_session = &GAS_ril_address_session_changed;
+         ah->s_address_update_inuse = &GAS_ril_address_inuse_changed;
+         ah->s_address_update_network = &GAS_ril_address_change_network;
+         ah->s_get = &GAS_ril_get_preferred_address;
+         ah->s_get_stop = &GAS_ril_stop_get_preferred_address;
+         ah->s_pref = &GAS_ril_address_change_preference;
+         ah->s_feedback = &GAS_ril_address_preference_feedback;
+         ah->s_del  = &GAS_ril_address_delete;
+         ah->s_bulk_start = &GAS_ril_bulk_start;
+         ah->s_bulk_stop = &GAS_ril_bulk_stop;
+         ah->s_done = &GAS_ril_done;
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "RIL");
+         break;
     default:
       return NULL;
       break;
@@ -1527,19 +1859,31 @@ GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
 
   GNUNET_assert (NULL != ah->s_init);
   GNUNET_assert (NULL != ah->s_add);
-  GNUNET_assert (NULL != ah->s_update);
+  GNUNET_assert (NULL != ah->s_address_update_inuse);
+  GNUNET_assert (NULL != ah->s_address_update_property);
+  GNUNET_assert (NULL != ah->s_address_update_session);
+  GNUNET_assert (NULL != ah->s_address_update_network);
   GNUNET_assert (NULL != ah->s_get);
   GNUNET_assert (NULL != ah->s_get_stop);
   GNUNET_assert (NULL != ah->s_pref);
+  GNUNET_assert (NULL != ah->s_feedback);
   GNUNET_assert (NULL != ah->s_del);
   GNUNET_assert (NULL != ah->s_done);
+  GNUNET_assert (NULL != ah->s_bulk_start);
+  GNUNET_assert (NULL != ah->s_bulk_stop);
 
+  GAS_normalization_start (&normalized_preference_changed_cb, ah,
+                                                                                                &normalized_property_changed_cb, ah);
   quota_count = load_quotas(cfg, quotas_in, quotas_out, GNUNET_ATS_NetworkTypeCount);
 
-  ah->solver = ah->s_init (cfg, stats, quotas, quotas_in, quotas_out, quota_count, &bandwidth_changed_cb, ah);
+  ah->solver = ah->s_init (cfg, stats, ah->addresses,
+                 quotas, quotas_in, quotas_out, quota_count,
+                 &bandwidth_changed_cb, ah,
+                 &get_preferences_cb, NULL,
+                 &get_property_cb, NULL);
   if (NULL == ah->solver)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize solver!\n");
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to initialize solver!\n"));
     GNUNET_free (ah);
     return NULL;
   }
@@ -1551,7 +1895,7 @@ GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
 
 
 /**
- * Free memory of address.
+ * Destroy all addresses iterator
  *
  * @param cls NULL
  * @param key peer identity (unused)
@@ -1559,12 +1903,18 @@ GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
  * @return GNUNET_OK (continue to iterate)
  */
 static int
-free_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
+destroy_all_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
 {
   struct GAS_Addresses_Handle *handle = cls;
   struct ATS_Address *aa = value;
-  handle->s_del (handle->solver, handle->addresses, aa, GNUNET_NO);
-  destroy_address (handle, aa);
+
+  /* Remove */
+  GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (handle->addresses, key, value));
+  /* Notify */
+  handle->s_del (handle->solver, aa, GNUNET_NO);
+  /* Destroy */
+  free_address (aa);
+
   return GNUNET_OK;
 }
 
@@ -1580,12 +1930,12 @@ GAS_addresses_destroy_all (struct GAS_Addresses_Handle *handle)
   if (GNUNET_NO == handle->running)
     return;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received `%s'\n",
-              "DESTROY ALL");
-
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Destroying all addresses\n");
+  handle->s_bulk_start (handle->solver);
   if (handle->addresses != NULL)
-    GNUNET_CONTAINER_multihashmap_iterate (handle->addresses, &free_address_it, handle);
+    GNUNET_CONTAINER_multihashmap_iterate (handle->addresses, &destroy_all_address_it, handle);
+  handle->s_bulk_start (handle->solver);
 }
 
 
@@ -1599,7 +1949,7 @@ GAS_addresses_done (struct GAS_Addresses_Handle *handle)
 {
   struct GAS_Addresses_Suggestion_Requests *cur;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Shutting down addresses\n");
   GNUNET_assert (NULL != handle);
   GAS_addresses_destroy_all (handle);
@@ -1614,9 +1964,10 @@ GAS_addresses_done (struct GAS_Addresses_Handle *handle)
   handle->s_done (handle->solver);
   GNUNET_free (handle);
   /* Stop configured solution method */
-
+  GAS_normalization_stop ();
 }
 
+
 struct PeerIteratorContext
 {
   GNUNET_ATS_Peer_Iterator it;
@@ -1624,6 +1975,15 @@ struct PeerIteratorContext
   struct GNUNET_CONTAINER_MultiHashMap *peers_returned;
 };
 
+
+/**
+ * Iterator to iterate over all peers
+ *
+ * @param cls a PeerIteratorContext
+ * @param key the peer id
+ * @param value the ATS_address
+ * @return GNUNET_OK to continue
+ */
 static int
 peer_it (void *cls,
          const struct GNUNET_HashCode * key,
@@ -1682,6 +2042,14 @@ struct PeerInfoIteratorContext
 };
 
 
+/**
+ * Iterator to iterate over a peer's addresses
+ *
+ * @param cls a PeerInfoIteratorContext
+ * @param key the peer id
+ * @param value the ATS_address
+ * @return GNUNET_OK to continue
+ */
 static int 
 peerinfo_it (void *cls,
             const struct GNUNET_HashCode * key,
@@ -1689,22 +2057,17 @@ peerinfo_it (void *cls,
 {
   struct PeerInfoIteratorContext *pi_ctx = cls;
   struct ATS_Address *addr = (struct ATS_Address *)  value;
-  struct GNUNET_ATS_Information *ats;
-  uint32_t ats_count;
 
   if (NULL != pi_ctx->it)
   {
-    ats_count = assemble_ats_information (addr, &ats);
-
     pi_ctx->it (pi_ctx->it_cls,
                 &addr->peer,
                 addr->plugin,
                 addr->addr, addr->addr_len,
                 addr->active,
-                ats, ats_count,
+                addr->atsi, addr->atsi_count,
                 addr->assigned_bw_out,
                 addr->assigned_bw_in);
-    GNUNET_free (ats);
   }
   return GNUNET_YES;
 }