- new test
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses_simplistic.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file ats/gnunet-service-ats_addresses_simplistic.h
23  * @brief ats simplistic ressource assignment
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet-service-ats_addresses.h"
30 #include "gnunet_statistics_service.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "ats-simplistic",__VA_ARGS__)
33
34 /**
35  * ATS simplistic solver
36  *
37  * Assigns in and outbound bandwidth equally for all addresses in specific
38  * network type (WAN, LAN) based on configured in and outbound quota for this
39  * network.
40  *
41  * For each peer only a single is selected and marked as "active" in the address
42  * struct.
43  *
44  * E.g.:
45  *
46  * You have the networks WAN and LAN and quotas
47  * WAN_TOTAL_IN, WAN_TOTAL_OUT
48  * LAN_TOTAL_IN, LAN_TOTAL_OUT
49  *
50  * If you have x addresses in the network segment LAN, the quotas are
51  * QUOTA_PER_ADDRESS = LAN_TOTAL_OUT / x
52  *
53  * Quotas are automatically recalculated and reported back when addresses are
54  * - requested
55  *
56  */
57
58
59 /**
60  * A handle for the simplistic solver
61  */
62 struct GAS_SIMPLISTIC_Handle
63 {
64   /**
65    * Statistics handle
66    */
67
68   struct GNUNET_STATISTICS_Handle *stats;
69
70   /**
71    * Total number of addresses for solver
72    */
73   unsigned int total_addresses;
74
75   /**
76    * Number of active addresses for solver
77    */
78   unsigned int active_addresses;
79
80   /**
81    * Networks array
82    */
83   struct Network *network_entries;
84
85   /**
86    * Number of networks
87    */
88   unsigned int networks;
89
90   /**
91    * Callback
92    */
93   GAS_bandwidth_changed_cb bw_changed;
94
95   /**
96    * Callback cls
97    */
98   void *bw_changed_cls;
99
100   struct PreferenceClient *pc_head;
101   struct PreferenceClient *pc_tail;
102 };
103
104 struct Network
105 {
106   /**
107    * ATS network type
108    */
109   unsigned int type;
110
111   /**
112    * Network description
113    */
114   char *desc;
115
116   /**
117    * Total inbound quota
118    *
119    */
120   unsigned long long total_quota_in;
121
122   /**
123    * Total outbound quota
124    *
125    */
126   unsigned long long total_quota_out;
127
128   /**
129    * Number of active addresses for this network
130    */
131   unsigned int active_addresses;
132
133   /**
134    * Number of total addresses for this network
135    */
136   unsigned int total_addresses;
137
138   /**
139    * String for statistics total addresses
140    */
141   char *stat_total;
142
143   /**
144    * String for statistics active addresses
145    */
146   char *stat_active;
147
148   struct AddressWrapper *head;
149   struct AddressWrapper *tail;
150 };
151
152 struct AddressWrapper
153 {
154   struct AddressWrapper *next;
155   struct AddressWrapper *prev;
156
157   struct ATS_Address *addr;
158 };
159
160
161 struct PreferencePeer
162 {
163   struct GNUNET_PeerIdentity id;
164
165   float prefs[GNUNET_ATS_PreferenceCount];
166 };
167
168 struct PreferenceClient
169 {
170   struct PreferenceClient *prev;
171   struct PreferenceClient *next;
172   void *client;
173
174   float prefs[GNUNET_ATS_PreferenceCount];
175
176   struct GNUNET_CONTAINER_MultiHashMap *peers;
177 };
178
179
180 /**
181  * Get the prefered address for a specific peer
182  *
183  * @param solver the solver handle
184  * @param addresses the address hashmap containing all addresses
185  * @param peer the identity of the peer
186  */
187 const struct ATS_Address *
188 GAS_simplistic_get_preferred_address (void *solver,
189                                struct GNUNET_CONTAINER_MultiHashMap * addresses,
190                                const struct GNUNET_PeerIdentity *peer);
191
192 /**
193  * Init the simplistic problem solving component
194  *
195  * Quotas:
196  * network[i] contains the network type as type GNUNET_ATS_NetworkType[i]
197  * out_quota[i] contains outbound quota for network type i
198  * in_quota[i] contains inbound quota for network type i
199  *
200  * Example
201  * network = {GNUNET_ATS_NET_UNSPECIFIED, GNUNET_ATS_NET_LOOPBACK, GNUNET_ATS_NET_LAN, GNUNET_ATS_NET_WAN, GNUNET_ATS_NET_WLAN}
202  * network[2]   == GNUNET_ATS_NET_LAN
203  * out_quota[2] == 65353
204  * in_quota[2]  == 65353
205  *
206  * @param cfg configuration handle
207  * @param stats the GNUNET_STATISTICS handle
208  * @param network array of GNUNET_ATS_NetworkType with length dest_length
209  * @param out_quota array of outbound quotas
210  * @param in_quota array of outbound quota
211  * @param dest_length array length for quota arrays
212  * @param bw_changed_cb callback for changed bandwidth amounts
213  * @param bw_changed_cb_cls cls for callback
214  * @return handle for the solver on success, NULL on fail
215  */
216 void *
217 GAS_simplistic_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
218                      const struct GNUNET_STATISTICS_Handle *stats,
219                      int *network,
220                      unsigned long long *out_quota,
221                      unsigned long long *in_quota,
222                      int dest_length,
223                      GAS_bandwidth_changed_cb bw_changed_cb,
224                      void *bw_changed_cb_cls)
225 {
226   int c;
227   struct GAS_SIMPLISTIC_Handle *s = GNUNET_malloc (sizeof (struct GAS_SIMPLISTIC_Handle));
228   struct Network * cur;
229   char * net_str[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkTypeString;
230
231
232   s->stats = (struct GNUNET_STATISTICS_Handle *) stats;
233   s->bw_changed = bw_changed_cb;
234   s->bw_changed_cls = bw_changed_cb_cls;
235   s->networks = dest_length;
236   s->network_entries = GNUNET_malloc (dest_length * sizeof (struct Network));
237   s->active_addresses = 0;
238   s->total_addresses = 0;
239
240   for (c = 0; c < dest_length; c++)
241   {
242       cur = &s->network_entries[c];
243       cur->total_addresses = 0;
244       cur->active_addresses = 0;
245       cur->type = network[c];
246       cur->total_quota_in = in_quota[c];
247       cur->total_quota_out = out_quota[c];
248       cur->desc = net_str[c];
249       GNUNET_asprintf (&cur->stat_total, "# ATS addresses %s total", cur->desc);
250       GNUNET_asprintf (&cur->stat_active, "# ATS active addresses %s total", cur->desc);
251   }
252   return s;
253 }
254
255
256 static int
257 delete_peers (void *cls,
258               const struct GNUNET_HashCode * key,
259               void *value)
260 {
261   struct GNUNET_CONTAINER_MultiHashMap *m = cls;
262   struct PreferencePeer *p = (struct PreferencePeer *) value;
263   GNUNET_CONTAINER_multihashmap_remove (m, &p->id.hashPubKey, p);
264   GNUNET_free (p);
265   return GNUNET_OK;
266 }
267
268 /**
269  * Shutdown the simplistic problem solving component
270  *
271  * @param solver the respective handle to shutdown
272  */
273 void
274 GAS_simplistic_done (void *solver)
275 {
276   struct GAS_SIMPLISTIC_Handle *s = solver;
277   struct PreferenceClient *pc;
278   struct PreferenceClient *next_pc;
279   struct AddressWrapper *cur;
280   struct AddressWrapper *next;
281   int c;
282   GNUNET_assert (s != NULL);
283
284   for (c = 0; c < s->networks; c++)
285   {
286       if (s->network_entries[c].total_addresses > 0)
287       {
288         LOG (GNUNET_ERROR_TYPE_ERROR,
289                     "Had %u addresses for network `%s' not deleted during shutdown\n",
290                     s->network_entries[c].total_addresses,
291                     s->network_entries[c].desc);
292         GNUNET_break (0);
293       }
294
295       if (s->network_entries[c].active_addresses > 0)
296       {
297         LOG (GNUNET_ERROR_TYPE_ERROR,
298                     "Had %u active addresses for network `%s' not deleted during shutdown\n",
299                     s->network_entries[c].active_addresses,
300                     s->network_entries[c].desc);
301         GNUNET_break (0);
302       }
303
304       next = s->network_entries[c].head;
305       while (NULL != (cur = next))
306       {
307           next = cur->next;
308           GNUNET_CONTAINER_DLL_remove (s->network_entries[c].head,
309                                        s->network_entries[c].tail,
310                                        cur);
311           GNUNET_free (cur);
312       }
313       GNUNET_free (s->network_entries[c].stat_total);
314       GNUNET_free (s->network_entries[c].stat_active);
315   }
316   if (s->total_addresses > 0)
317   {
318     LOG (GNUNET_ERROR_TYPE_ERROR,
319                 "Had %u addresses not deleted during shutdown\n",
320                 s->total_addresses);
321     GNUNET_break (0);
322   }
323   if (s->active_addresses > 0)
324   {
325     LOG (GNUNET_ERROR_TYPE_ERROR,
326                 "Had %u active addresses not deleted during shutdown\n",
327                 s->active_addresses);
328     GNUNET_break (0);
329   }
330   GNUNET_free (s->network_entries);
331
332   next_pc = s->pc_head;
333   while (NULL != (pc = next_pc))
334   {
335       next_pc = pc->next;
336       GNUNET_CONTAINER_DLL_remove (s->pc_head, s->pc_tail, pc);
337       GNUNET_CONTAINER_multihashmap_iterate (pc->peers, &delete_peers, pc->peers);
338       GNUNET_CONTAINER_multihashmap_destroy (pc->peers);
339       GNUNET_free (pc);
340   }
341   GNUNET_free (s);
342 }
343
344 static unsigned long long int
345 calculate_new_quota (unsigned long long int total,
346                      unsigned int addresses)
347 {
348   return (total / addresses);
349 }
350
351 /**
352  * Test if bandwidth is available in this network
353  *
354  * @param s the solver handle
355  * @param net the network type to update
356  * @return GNUNET_YES or GNUNET_NO
357  */
358
359 static int
360 bw_available_in_network (struct Network *net)
361 {
362   unsigned int na = net->active_addresses + 1;
363   uint32_t min_bw = ntohl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__);
364   if ((calculate_new_quota (net->total_quota_in, na) > min_bw) &&
365       (calculate_new_quota (net->total_quota_out, na) > min_bw))
366   {    
367     LOG (GNUNET_ERROR_TYPE_DEBUG,
368          "Enough bandwidth available for %u active addresses in network `%s'\n",
369          na,
370          net->desc);
371                                                                       
372     return GNUNET_YES;
373   }
374     LOG (GNUNET_ERROR_TYPE_DEBUG,
375          "Not enough bandwidth available for %u active addresses in network `%s'\n",
376          na,
377          net->desc);  
378   return GNUNET_NO;
379 }
380
381 /**
382  * Update the quotas for a network type
383  *
384  * @param s the solver handle
385  * @param net the network type to update
386  * @param address_except address excluded from notifcation, since we suggest
387  * this address
388  */
389 static void
390 update_quota_per_network (struct GAS_SIMPLISTIC_Handle *s,
391                           struct Network *net,
392                           struct ATS_Address *address_except)
393 {
394   unsigned long long quota_in = 0;
395   unsigned long long quota_out = 0;
396   struct AddressWrapper *cur;
397
398   LOG (GNUNET_ERROR_TYPE_DEBUG,
399               "Recalculate quota for network type `%s' for %u addresses (in/out): %llu/%llu \n",
400               net->desc, net->active_addresses, quota_in, quota_out);
401
402   if (net->active_addresses == 0)
403     return; /* no addresses to update */
404
405   quota_in = calculate_new_quota (net->total_quota_in, net->active_addresses);
406   quota_out = calculate_new_quota (net->total_quota_out, net->active_addresses);
407
408   LOG (GNUNET_ERROR_TYPE_DEBUG,
409               "New per address quota for network type `%s' for %u addresses (in/out): %llu/%llu \n",
410               net->desc, net->active_addresses, quota_in, quota_out);
411
412   cur = net->head;
413   while (NULL != cur)
414   {
415       /* Compare to current bandwidth assigned */
416       if ((quota_in != ntohl(cur->addr->assigned_bw_in.value__)) ||
417           (quota_out != ntohl(cur->addr->assigned_bw_out.value__)))
418       {
419         cur->addr->assigned_bw_in.value__ = htonl (quota_in);
420         cur->addr->assigned_bw_out.value__ = htonl (quota_out);
421         /* Notify on change */
422         if ((GNUNET_YES == cur->addr->active) && (cur->addr != address_except))
423           s->bw_changed (s->bw_changed_cls, cur->addr);
424       }
425       cur = cur->next;
426   }
427 }
428
429 static void
430 addresse_increment (struct GAS_SIMPLISTIC_Handle *s,
431                                 struct Network *net,
432                                 int total,
433                                 int active)
434 {
435   if (GNUNET_YES == total)
436   {
437       s->total_addresses ++;
438       net->total_addresses ++;
439       GNUNET_STATISTICS_update (s->stats, "# ATS addresses total", 1, GNUNET_NO);
440       GNUNET_STATISTICS_update (s->stats, net->stat_total, 1, GNUNET_NO);
441   }
442   if (GNUNET_YES == active)
443   {
444     net->active_addresses ++;
445     s->active_addresses ++;
446     GNUNET_STATISTICS_update (s->stats, "# ATS active addresses total", 1, GNUNET_NO);
447     GNUNET_STATISTICS_update (s->stats, net->stat_active, 1, GNUNET_NO);
448   }
449
450 }
451
452 static int
453 addresse_decrement (struct GAS_SIMPLISTIC_Handle *s,
454                     struct Network *net,
455                     int total,
456                     int active)
457 {
458   int res = GNUNET_OK;
459   if (GNUNET_YES == total)
460   {
461     if (s->total_addresses < 1)
462     {
463       GNUNET_break (0);
464       res = GNUNET_SYSERR;
465     }
466     else
467     {
468       s->total_addresses --;
469       GNUNET_STATISTICS_update (s->stats, "# ATS addresses total", -1, GNUNET_NO);
470     }
471     if (net->total_addresses < 1)
472     {
473       GNUNET_break (0);
474       res = GNUNET_SYSERR;
475     }
476     else
477     {
478       net->total_addresses --;
479       GNUNET_STATISTICS_update (s->stats, net->stat_total, -1, GNUNET_NO);
480     }
481   }
482
483   if (GNUNET_YES == active)
484   {
485     if (net->active_addresses < 1)
486     {
487       GNUNET_break (0);
488       res = GNUNET_SYSERR;
489     }
490     else
491     {
492       net->active_addresses --;
493       GNUNET_STATISTICS_update (s->stats, net->stat_active, -1, GNUNET_NO);
494     }
495     if (s->active_addresses < 1)
496     {
497       GNUNET_break (0);
498       res = GNUNET_SYSERR;
499     }
500     else
501     {
502       s->active_addresses --;
503       GNUNET_STATISTICS_update (s->stats, "# ATS addresses total", -1, GNUNET_NO);
504     }
505   }
506   return res;
507 }
508
509
510 /**
511  * Add a single address to the solve
512  *
513  * @param solver the solver Handle
514  * @param addresses the address hashmap containing all addresses
515  * @param address the address to add
516  */
517 void
518 GAS_simplistic_address_add (void *solver, struct GNUNET_CONTAINER_MultiHashMap * addresses, struct ATS_Address *address)
519 {
520   struct GAS_SIMPLISTIC_Handle *s = solver;
521   struct Network *net = NULL;
522   struct AddressWrapper *aw = NULL;
523   GNUNET_assert (NULL != s);
524   int c;
525   for (c = 0; c < s->networks; c++)
526   {
527       net = &s->network_entries[c];
528       if (address->atsp_network_type == net->type)
529           break;
530   }
531   if (NULL == net)
532   {
533     GNUNET_break (0);
534     return;
535   }
536
537   aw = GNUNET_malloc (sizeof (struct AddressWrapper));
538   aw->addr = address;
539   GNUNET_CONTAINER_DLL_insert (net->head, net->tail, aw);
540   addresse_increment (s, net, GNUNET_YES, GNUNET_NO);
541   aw->addr->solver_information = net;
542
543
544   LOG (GNUNET_ERROR_TYPE_DEBUG, "After adding address now total %u and active %u addresses in network `%s'\n",
545       net->total_addresses,
546       net->active_addresses,
547       net->desc);
548 }
549
550 /**
551  * Remove an address from the solver
552  *
553  * @param solver the solver handle
554  * @param addresses the address hashmap containing all addresses
555  * @param address the address to remove
556  * @param session_only delete only session not whole address
557  */
558 void
559 GAS_simplistic_address_delete (void *solver,
560     struct GNUNET_CONTAINER_MultiHashMap * addresses,
561     struct ATS_Address *address, int session_only)
562 {
563   struct GAS_SIMPLISTIC_Handle *s = solver;
564   struct Network *net;
565   struct AddressWrapper *aw;
566
567   /* Remove an adress completely, we have to:
568    * - Remove from specific network
569    * - Decrease number of total addresses
570    * - If active:
571    *   - decrease number of active addreses
572    *   - update quotas
573    */
574
575   net = (struct Network *) address->solver_information;
576
577   if (GNUNET_NO == session_only)
578   {
579     LOG (GNUNET_ERROR_TYPE_DEBUG, "Deleting %s address %p for peer `%s' from network `%s' (total: %u/ active: %u)\n",
580         (GNUNET_NO == address->active) ? "inactive" : "active",
581         address, GNUNET_i2s (&address->peer),
582         net->desc, net->total_addresses, net->active_addresses);
583
584     /* Remove address */
585     addresse_decrement (s, net, GNUNET_YES, GNUNET_NO);
586     for (aw = net->head; NULL != aw; aw = aw->next)
587     {
588         if (aw->addr == address)
589           break;
590     }
591     if (NULL == aw )
592     {
593         GNUNET_break (0);
594         return;
595     }
596     GNUNET_CONTAINER_DLL_remove (net->head, net->tail, aw);
597     GNUNET_free (aw);
598   }
599   else
600   {
601       /* Remove session only: remove if active and update */
602       LOG (GNUNET_ERROR_TYPE_DEBUG, "Deleting %s session %p for peer `%s' from network `%s' (total: %u/ active: %u)\n",
603           (GNUNET_NO == address->active) ? "inactive" : "active",
604           address, GNUNET_i2s (&address->peer),
605           net->desc, net->total_addresses, net->active_addresses);
606   }
607
608   if (GNUNET_YES == address->active)
609   {
610       /* Address was active, remove from network and update quotas*/
611       address->active = GNUNET_NO;
612       if (GNUNET_SYSERR == addresse_decrement (s, net, GNUNET_NO, GNUNET_YES))
613         GNUNET_break (0);
614       update_quota_per_network (s, net, NULL);
615   }
616   LOG (GNUNET_ERROR_TYPE_DEBUG, "After deleting address now total %u and active %u addresses in network `%s'\n",
617       net->total_addresses,
618       net->active_addresses,
619       net->desc);
620
621 }
622
623 static struct Network *
624 find_network (struct GAS_SIMPLISTIC_Handle *s, uint32_t type)
625 {
626   int c;
627   for (c = 0 ; c < s->networks; c++)
628   {
629       if (s->network_entries[c].type == type)
630         return &s->network_entries[c];
631   }
632   return NULL;
633 }
634
635 /**
636  * Updates a single address in the solve
637  *
638  * @param solver the solver Handle
639  * @param addresses the address hashmap containing all addresses
640  * @param address the update address
641  * @param session the new session (if changed otherwise current)
642  * @param in_use the new address in use state (if changed otherwise current)
643  * @param atsi the latest ATS information
644  * @param atsi_count the atsi count
645  */
646 void
647 GAS_simplistic_address_update (void *solver,
648                               struct GNUNET_CONTAINER_MultiHashMap *addresses,
649                               struct ATS_Address *address,
650                               uint32_t session,
651                               int in_use,
652                               const struct GNUNET_ATS_Information *atsi,
653                               uint32_t atsi_count)
654 {
655   struct ATS_Address *new;
656   struct GAS_SIMPLISTIC_Handle *s = (struct GAS_SIMPLISTIC_Handle *) solver;
657   int i;
658   uint32_t value;
659   uint32_t type;
660   int save_active = GNUNET_NO;
661   struct Network *new_net = NULL;
662   for (i = 0; i < atsi_count; i++)
663   {
664     type = ntohl (atsi[i].type);
665     value = ntohl (atsi[i].value);
666     switch (type)
667     {
668     case GNUNET_ATS_UTILIZATION_UP:
669       //if (address->atsp_utilization_out.value__ != atsi[i].value)
670
671       break;
672     case GNUNET_ATS_UTILIZATION_DOWN:
673       //if (address->atsp_utilization_in.value__ != atsi[i].value)
674
675       break;
676     case GNUNET_ATS_QUALITY_NET_DELAY:
677       //if (address->atsp_latency.rel_value != value)
678
679       break;
680     case GNUNET_ATS_QUALITY_NET_DISTANCE:
681       //if (address->atsp_distance != value)
682
683       break;
684     case GNUNET_ATS_COST_WAN:
685       //if (address->atsp_cost_wan != value)
686
687       break;
688     case GNUNET_ATS_COST_LAN:
689       //if (address->atsp_cost_lan != value)
690
691       break;
692     case GNUNET_ATS_COST_WLAN:
693       //if (address->atsp_cost_wlan != value)
694
695       break;
696     case GNUNET_ATS_NETWORK_TYPE:
697       if (address->atsp_network_type != value)
698       {
699
700         LOG (GNUNET_ERROR_TYPE_DEBUG, "Network type changed, moving %s address from `%s' to `%s'\n",
701             (GNUNET_YES == address->active) ? "active" : "inactive",
702             GNUNET_ATS_print_network_type(address->atsp_network_type),
703             GNUNET_ATS_print_network_type(value));
704
705         save_active = address->active;
706         /* remove from old network */
707         GAS_simplistic_address_delete (solver, addresses, address, GNUNET_NO);
708
709         /* set new network type */
710         address->atsp_network_type = value;
711         new_net = find_network (solver, value);
712         address->solver_information = new_net;
713         if (address->solver_information == NULL)
714         {
715             GNUNET_break (0);
716             address->atsp_network_type = GNUNET_ATS_NET_UNSPECIFIED;
717             return;
718         }
719
720         /* restore active state, add to new network and update*/
721         address->active = save_active;
722         GAS_simplistic_address_add (solver, addresses, address);
723         if (GNUNET_YES == save_active)
724         {
725           /* check if bandwidth available*/
726           if (GNUNET_NO == (bw_available_in_network (new_net)))
727           {
728             /* Find new address to suggest since no bandwidth in network*/
729             new = (struct ATS_Address *) GAS_simplistic_get_preferred_address (s, addresses, &address->peer);
730             if (NULL != new)
731             {
732                 /* Have an alternative address to suggest */
733                 s->bw_changed  (s->bw_changed_cls, address);
734             }
735             else
736               continue;
737           }
738           addresse_increment (s, new_net, GNUNET_NO, GNUNET_YES);
739           update_quota_per_network (solver, new_net, NULL);
740         }
741       }
742       break;
743     case GNUNET_ATS_ARRAY_TERMINATOR:
744       break;
745     default:
746       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
747                   "Received unsupported ATS type %u\n", type);
748       GNUNET_break (0);
749       break;
750
751     }
752
753   }
754   if (address->session_id != session)
755   {
756       LOG (GNUNET_ERROR_TYPE_DEBUG,
757                   "Session changed from %u to %u\n", address->session_id, session);
758       address->session_id = session;
759   }
760   if (address->used != in_use)
761   {
762       LOG (GNUNET_ERROR_TYPE_DEBUG,
763                   "Usage changed from %u to %u\n", address->used, in_use);
764       address->used = in_use;
765   }
766
767 }
768
769
770
771 /**
772  * Find a "good" address to use for a peer.  If we already have an existing
773  * address, we stick to it.  Otherwise, we pick by lowest distance and then
774  * by lowest latency.
775  *
776  * @param cls the 'struct ATS_Address**' where we store the result
777  * @param key unused
778  * @param value another 'struct ATS_Address*' to consider using
779  * @return GNUNET_OK (continue to iterate)
780  */
781 static int
782 find_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
783 {
784   struct ATS_Address **previous_p = cls;
785   struct ATS_Address *current = (struct ATS_Address *) value;
786   struct ATS_Address *previous = *previous_p;
787   struct GNUNET_TIME_Absolute now;
788   struct Network *net = (struct Network *) current->solver_information;
789
790   now = GNUNET_TIME_absolute_get();
791
792   if (current->blocked_until.abs_value == GNUNET_TIME_absolute_max (now, current->blocked_until).abs_value)
793   {
794     /* This address is blocked for suggestion */
795     LOG (GNUNET_ERROR_TYPE_DEBUG,
796                 "Address %p blocked for suggestion for %llu ms \n",
797                 current,
798                 GNUNET_TIME_absolute_get_difference(now, current->blocked_until).rel_value);
799     return GNUNET_OK;
800   }
801
802   if (GNUNET_NO == bw_available_in_network (net))
803     return GNUNET_OK; /* There's no bandwidth available in this network */
804
805   if (NULL != previous)
806   {
807     if ((0 == strcmp (previous->plugin, "tcp")) &&
808         (0 == strcmp (current->plugin, "tcp")))
809     {
810       if ((0 != previous->addr_len) &&
811           (0 == current->addr_len))
812       {
813         /* saved address was an outbound address, but we have an inbound address */
814         *previous_p = current;
815         return GNUNET_OK;
816       }
817       if (0 == previous->addr_len)
818       {
819         /* saved address was an inbound address, so do not overwrite */
820         return GNUNET_OK;
821       }
822     }
823   }
824
825   if (NULL == previous)
826   {
827     *previous_p = current;
828     return GNUNET_OK;
829   }
830   if ((ntohl (previous->assigned_bw_in.value__) == 0) &&
831       (ntohl (current->assigned_bw_in.value__) > 0))
832   {
833     /* stick to existing connection */
834     *previous_p = current;
835     return GNUNET_OK;
836   }
837   if (previous->atsp_distance > current->atsp_distance)
838   {
839     /* user shorter distance */
840     *previous_p = current;
841     return GNUNET_OK;
842   }
843   if (previous->atsp_latency.rel_value > current->atsp_latency.rel_value)
844   {
845     /* user lower latency */
846     *previous_p = current;
847     return GNUNET_OK;
848   }
849   /* don't care */
850   return GNUNET_OK;
851 }
852
853 static int
854 find_active_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
855 {
856   struct ATS_Address * dest = (struct ATS_Address *) (*(struct ATS_Address **)cls);
857   struct ATS_Address * aa = (struct ATS_Address *) value;
858
859   if (GNUNET_YES == aa->active)
860   {
861       if (dest != NULL)
862       {
863           /* should never happen */
864           LOG (GNUNET_ERROR_TYPE_ERROR, "Multiple active addresses for peer `%s'\n", GNUNET_i2s (&aa->peer));
865           GNUNET_break (0);
866           return GNUNET_NO;
867       }
868       dest = aa;
869   }
870   return GNUNET_OK;
871 }
872
873 static struct ATS_Address *
874 find_active_address (void *solver,
875                      struct GNUNET_CONTAINER_MultiHashMap * addresses,
876                      const struct GNUNET_PeerIdentity *peer)
877 {
878   struct ATS_Address * dest = NULL;
879
880   GNUNET_CONTAINER_multihashmap_get_multiple(addresses,
881        &peer->hashPubKey,
882        &find_active_address_it, &dest);
883   return dest;
884 }
885
886 /**
887  * Get the prefered address for a specific peer
888  *
889  * @param solver the solver handle
890  * @param addresses the address hashmap containing all addresses
891  * @param peer the identity of the peer
892  */
893 const struct ATS_Address *
894 GAS_simplistic_get_preferred_address (void *solver,
895                                struct GNUNET_CONTAINER_MultiHashMap * addresses,
896                                const struct GNUNET_PeerIdentity *peer)
897 {
898   struct GAS_SIMPLISTIC_Handle *s = solver;
899   struct Network *net_prev;
900   struct Network *net_cur;
901   struct ATS_Address *cur;
902   struct ATS_Address *prev;
903
904   GNUNET_assert (s != NULL);
905   cur = NULL;
906   /* Get address with: stick to current address, lower distance, lower latency */
907   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
908                                               &find_address_it, &cur);
909   if (NULL == cur)
910   {
911     LOG (GNUNET_ERROR_TYPE_DEBUG, "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
912     return NULL;
913   }
914
915   LOG (GNUNET_ERROR_TYPE_DEBUG, "Suggesting %s address %p for peer `%s'\n",
916       (GNUNET_NO == cur->active) ? "inactive" : "active",
917       cur, GNUNET_i2s (peer));
918   net_cur = (struct Network *) cur->solver_information;
919   if (GNUNET_YES == cur->active)
920   {
921       /* This address was selected previously, so no need to update quotas */
922       return cur;
923   }
924
925   /* This address was not active, so we have to:
926    *
927    * - mark previous active address as not active
928    * - update quota for previous address network
929    * - update quota for this address network
930    */
931
932   prev = find_active_address (s, addresses, peer);
933   if (NULL != prev)
934   {
935       net_prev = (struct Network *) prev->solver_information;
936       prev->active = GNUNET_NO; /* No active any longer */
937       prev->assigned_bw_in = GNUNET_BANDWIDTH_value_init (0); /* no bw assigned */
938       prev->assigned_bw_out = GNUNET_BANDWIDTH_value_init (0); /* no bw assigned */
939       s->bw_changed (s->bw_changed_cls, prev); /* notify about bw change, REQUIRED? */
940       if (GNUNET_SYSERR == addresse_decrement (s, net_prev, GNUNET_NO, GNUNET_YES))
941         GNUNET_break (0);
942       update_quota_per_network (s, net_prev, NULL);
943   }
944
945   if (GNUNET_NO == (bw_available_in_network (cur->solver_information)))
946   {
947     GNUNET_break (0); /* This should never happen*/
948     return NULL;
949   }
950
951   cur->active = GNUNET_YES;
952   addresse_increment(s, net_cur, GNUNET_NO, GNUNET_YES);
953   update_quota_per_network (s, net_cur, cur);
954
955   return cur;
956 }
957
958
959 static double
960 update_pref_value (struct PreferenceClient *cur, int kind, float score)
961 {
962   float res = (cur->prefs[kind] + score) / 2; /* change update algorithm here */;
963   LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p changes preference for peer type %s from %f to %f\n",
964                                 cur->client,
965                                 GNUNET_ATS_print_preference_type (kind),
966                                 cur->prefs[kind], res);
967   return res;
968 }
969
970 /**
971  * Changes the preferences for a peer in the problem
972  *
973  * @param solver the solver handle
974  * @param client the client with this preference
975  * @param peer the peer to change the preference for
976  * @param kind the kind to change the preference
977  * @param score the score
978  */
979 void
980 GAS_simplistic_address_change_preference (void *solver,
981                                    void *client,
982                                    const struct GNUNET_PeerIdentity *peer,
983                                    enum GNUNET_ATS_PreferenceKind kind,
984                                    float score)
985 {
986   struct GAS_SIMPLISTIC_Handle *s = solver;
987   struct PreferenceClient *cur;
988   struct PreferencePeer *p;
989   int i;
990
991   GNUNET_assert (NULL != solver);
992   GNUNET_assert (NULL != client);
993   GNUNET_assert (NULL != peer);
994
995   LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p changes preference for peer `%s' %s %f\n",
996                                 client,
997                                 GNUNET_i2s (peer),
998                                 GNUNET_ATS_print_preference_type (kind),
999                                 score);
1000
1001   if (kind >= GNUNET_ATS_PreferenceCount)
1002   {
1003       GNUNET_break (0);
1004       return;
1005   }
1006
1007   for (cur = s->pc_head; NULL != cur; cur = cur->next)
1008   {
1009       if (client == cur->client)
1010         break;
1011   }
1012   if (NULL == cur)
1013   {
1014     cur = GNUNET_malloc (sizeof (struct PreferenceClient));
1015     cur->client = client;
1016     cur->peers = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
1017     GNUNET_CONTAINER_DLL_insert (s->pc_head, s->pc_tail, cur);
1018   }
1019
1020   p = GNUNET_CONTAINER_multihashmap_get (cur->peers, &peer->hashPubKey);
1021   if (NULL == p)
1022   {
1023       p = GNUNET_malloc (sizeof (struct PreferencePeer));
1024       p->id = (*peer);
1025       for (i = 0; i < GNUNET_ATS_PreferenceCount; i++)
1026         p->prefs[i] = 1;
1027       GNUNET_CONTAINER_multihashmap_put (cur->peers,
1028                                          &peer->hashPubKey,
1029                                          p,
1030                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1031   }
1032
1033   switch (kind) {
1034     case GNUNET_ATS_PREFERENCE_BANDWIDTH:
1035     case GNUNET_ATS_PREFERENCE_LATENCY:
1036       p->prefs[kind] = update_pref_value (cur, kind, score);
1037       break;
1038     case GNUNET_ATS_PREFERENCE_END:
1039       break;
1040     default:
1041       break;
1042   }
1043
1044 }
1045
1046 /* end of gnunet-service-ats_addresses_simplistic.c */