fix
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses.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.c
23  * @brief ats service address management
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_ats_service.h"
29 #include "gnunet-service-ats.h"
30 #include "gnunet-service-ats_addresses.h"
31 #include "gnunet-service-ats_performance.h"
32 #include "gnunet-service-ats_scheduling.h"
33 #include "gnunet-service-ats_reservations.h"
34 #if HAVE_LIBGLPK
35 #include "gnunet-service-ats_addresses_mlp.h"
36 #endif
37 #include "gnunet-service-ats_addresses_simplistic.h"
38
39
40 /**
41  * Available ressource assignment modes
42  */
43 enum ATS_Mode
44 {
45   /*
46    * Simplistic mode:
47    *
48    * Assign each peer an equal amount of bandwidth (bw)
49    *
50    * bw_per_peer = bw_total / #active addresses
51    */
52   MODE_SIMPLISTIC,
53
54   /*
55    * MLP mode:
56    *
57    * Solve ressource assignment as an optimization problem
58    * Uses an mixed integer programming solver
59    */
60   MODE_MLP
61 };
62
63 /**
64  * Handle for ATS address component
65  */
66 struct GAS_Addresses_Suggestion_Requests
67 {
68   struct GAS_Addresses_Suggestion_Requests *next;
69   struct GAS_Addresses_Suggestion_Requests *prev;
70
71   struct GNUNET_PeerIdentity id;
72 };
73
74 /**
75  * Handle for ATS address component
76  */
77 struct GAS_Addresses_Handle
78 {
79   /**
80    * A multihashmap to store all addresses
81    */
82   struct GNUNET_CONTAINER_MultiHashMap *addresses;
83
84   /**
85    * Configure WAN quota in
86    */
87   unsigned long long wan_quota_in;
88
89   /**
90    * Configure WAN quota out
91    */
92   unsigned long long wan_quota_out;
93
94   /**
95    * Is ATS addresses running
96    */
97   int running;
98
99   /**
100    * Configured ATS solver
101    */
102   int ats_mode;
103
104   /**
105    *  Solver handle
106    */
107   void *solver;
108
109   /**
110    * Address suggestion requests DLL head
111    */
112   struct GAS_Addresses_Suggestion_Requests *r_head;
113
114   /**
115    * Address suggestion requests DLL tail
116    */
117   struct GAS_Addresses_Suggestion_Requests *r_tail;
118
119   /* Solver functions */
120
121   /**
122    * Initialize solver
123    */
124   GAS_solver_init s_init;
125
126   /**
127    * Update address in solver
128    */
129   GAS_solver_address_update s_update;
130
131   /**
132    * Get address from solver
133    */
134   GAS_solver_get_preferred_address s_get;
135
136   /**
137    * Delete address in solver
138    */
139   GAS_solver_address_delete s_del;
140
141   /**
142    * Change preference for quality in solver
143    */
144   GAS_solver_address_change_preference s_pref;
145
146   /**
147    * Shutdown solver
148    */
149   GAS_solver_done s_done;
150 };
151
152
153 /**
154  * Temporary handle
155  */
156 struct GAS_Addresses_Handle *handle;
157
158
159 static unsigned int
160 assemble_ats_information (const struct ATS_Address *aa,  struct GNUNET_ATS_Information **dest)
161 {
162   unsigned int ats_count = GNUNET_ATS_PropertyCount - 1;
163   struct GNUNET_ATS_Information *ats = GNUNET_malloc (ats_count * sizeof (struct GNUNET_ATS_Information));
164   (*dest) = ats;
165
166   ats[0].type = ntohl(GNUNET_ATS_UTILIZATION_UP);
167   ats[0].value = aa->atsp_utilization_out.value__;
168   ats[1].type = ntohl(GNUNET_ATS_UTILIZATION_DOWN);
169   ats[1].value = aa->atsp_utilization_in.value__;
170   ats[2].type = ntohl(GNUNET_ATS_NETWORK_TYPE);
171   ats[2].value = ntohl(aa->atsp_network_type);
172   ats[3].type = ntohl(GNUNET_ATS_QUALITY_NET_DELAY);
173   ats[3].value = ntohl(aa->atsp_latency.rel_value);
174   ats[4].type = ntohl(GNUNET_ATS_QUALITY_NET_DISTANCE);
175   ats[4].value = ntohl(aa->atsp_distance);
176   ats[5].type = ntohl(GNUNET_ATS_COST_WAN);
177   ats[5].value = ntohl (aa->atsp_cost_wan);
178   ats[6].type = ntohl(GNUNET_ATS_COST_LAN);
179   ats[6].value = ntohl (aa->atsp_cost_lan);
180   ats[7].type = ntohl(GNUNET_ATS_COST_WLAN);
181   ats[7].value = ntohl (aa->atsp_cost_wlan);
182   return ats_count;
183 }
184
185 /**
186  * Free the given address
187  * @param addr address to destroy
188  */
189 static void
190 free_address (struct ATS_Address *addr)
191 {
192   GNUNET_free_non_null (addr->ats);
193   GNUNET_free (addr->plugin);
194   GNUNET_free (addr);
195 }
196
197 /**
198  * Create a ATS_address with the given information
199  * @param peer peer
200  * @param plugin_name plugin
201  * @param plugin_addr address
202  * @param plugin_addr_len address length
203  * @param session_id session
204  * @return the ATS_Address
205  */
206 static struct ATS_Address *
207 create_address (const struct GNUNET_PeerIdentity *peer,
208                 const char *plugin_name,
209                 const void *plugin_addr, size_t plugin_addr_len,
210                 uint32_t session_id)
211 {
212   struct ATS_Address *aa = NULL;
213
214   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
215   aa->peer = *peer;
216   aa->addr_len = plugin_addr_len;
217   aa->addr = &aa[1];
218   memcpy (&aa[1], plugin_addr, plugin_addr_len);
219   aa->plugin = GNUNET_strdup (plugin_name);
220   aa->session_id = session_id;
221   return aa;
222 }
223
224
225 /**
226  * Destroy the given address.
227  *
228  * @param addr address to destroy
229  * @return GNUNET_YES if bandwidth allocations should be recalcualted
230  */
231 static int
232 destroy_address (struct ATS_Address *addr)
233 {
234   int ret;
235
236   ret = GNUNET_NO;
237   GNUNET_assert (GNUNET_YES ==
238                  GNUNET_CONTAINER_multihashmap_remove (handle->addresses,
239                                                        &addr->peer.hashPubKey,
240                                                        addr));
241
242   handle->s_del (handle->solver, handle->addresses, addr);
243   free_address (addr);
244   return ret;
245 }
246
247
248 struct CompareAddressContext
249 {
250   const struct ATS_Address *search;
251
252   /* exact_address != NULL if address and session is equal */
253   struct ATS_Address *exact_address;
254   /* exact_address != NULL if address and session is 0 */
255   struct ATS_Address *base_address;
256 };
257
258
259 static int
260 compare_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
261 {
262   struct CompareAddressContext *cac = cls;
263   struct ATS_Address *aa = value;
264
265   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Comparing peer %4s: address length %u session %u <-> address length %u session %u\n",
266       GNUNET_h2s (key),
267       aa->addr_len, aa->session_id,
268       cac->search->addr_len, cac->search->session_id);
269
270   /* Find an matching exact address:
271    *
272    * Compare by:
273    * aa->addr_len == cac->search->addr_len
274    * aa->plugin == cac->search->plugin
275    * aa->addr == cac->search->addr
276    * aa->session == cac->search->session
277    *
278    * return as exact address
279    */
280   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
281   {
282       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
283         cac->exact_address = aa;
284   }
285
286   /* Find an matching base address:
287    *
288    * Properties:
289    *
290    * aa->session_id == 0
291    *
292    * Compare by:
293    * aa->addr_len == cac->search->addr_len
294    * aa->plugin == cac->search->plugin
295    * aa->addr == cac->search->addr
296    *
297    * return as base address
298    */
299   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
300   {
301       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == 0))
302         cac->base_address = aa;
303   }
304
305   /* Find an matching exact address based on session:
306    *
307    * Properties:
308    *
309    * cac->search->addr_len == 0
310    *
311    * Compare by:
312    * aa->plugin == cac->search->plugin
313    * aa->session_id == cac->search->session_id
314    *
315    * return as exact address
316    */
317   if (0 == cac->search->addr_len)
318   {
319       if ((0 == strcmp (aa->plugin, cac->search->plugin)) && (aa->session_id == cac->search->session_id))
320         cac->exact_address = aa;
321   }
322
323   if (cac->exact_address == NULL)
324     return GNUNET_YES; /* Continue iteration to find exact address */
325   else
326     return GNUNET_NO; /* Stop iteration since we have an exact address */
327 }
328
329
330 /**
331  * Find an existing equivalent address record.
332  * Compares by peer identity and network address OR by session ID
333  * (one of the two must match).
334  *
335  * @param peer peer to lookup addresses for
336  * @param addr existing address record
337  * @return existing address record, NULL for none
338  */
339 struct ATS_Address *
340 find_address (const struct GNUNET_PeerIdentity *peer,
341               const struct ATS_Address *addr)
342 {
343   struct CompareAddressContext cac;
344
345   cac.exact_address = NULL;
346   cac.base_address = NULL;
347   cac.search = addr;
348   GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses, &peer->hashPubKey,
349                                               &compare_address_it, &cac);
350
351 #if 0
352   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353               "Found exact address: %s           base address: %s\n",
354               (cac.exact_address != NULL) ? "YES" : "NO",
355               (cac.base_address != NULL) ? "YES" : "NO");
356 #endif
357   if (cac.exact_address == NULL)
358     return cac.base_address;
359   return cac.exact_address;
360 }
361
362
363 static struct ATS_Address *
364 lookup_address (const struct GNUNET_PeerIdentity *peer,
365                 const char *plugin_name, const void *plugin_addr,
366                 size_t plugin_addr_len, uint32_t session_id,
367                 const struct GNUNET_ATS_Information *atsi,
368                 uint32_t atsi_count)
369 {
370   struct ATS_Address *aa;
371   struct ATS_Address *old;
372
373   aa = create_address (peer,
374                        plugin_name,
375                        plugin_addr, plugin_addr_len,
376                        session_id);
377
378   aa->mlp_information = NULL;
379   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
380   aa->ats_count = atsi_count;
381   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
382
383   /* Get existing address or address with session == 0 */
384   old = find_address (peer, aa);
385   free_address (aa);
386   if (old == NULL)
387   {
388     return NULL;
389   }
390   else if (old->session_id != session_id)
391   {
392     return NULL;
393   }
394   return old;
395 }
396
397
398 #if 0
399 static int
400 compare_address_session_it (void *cls, const struct GNUNET_HashCode * key, void *value)
401 {
402   struct CompareAddressContext *cac = cls;
403   struct ATS_Address *aa = value;
404
405   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
406   {
407       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
408       {
409        cac->exact_address = aa;
410         return GNUNET_NO;
411       }
412   }
413   return GNUNET_YES;
414 }
415
416
417 /**
418  * Find an existing equivalent address record.
419  * Compares by peer identity and network address AND by session ID
420  * (one of the two must match).
421  *
422  * @param peer peer to lookup addresses for
423  * @param addr existing address record
424  * @return existing address record, NULL for none
425  */
426 static struct ATS_Address *
427 find_exact_address (const struct GNUNET_PeerIdentity *peer,
428               const struct ATS_Address *addr)
429 {
430   struct CompareAddressContext cac;
431
432   cac.exact_address = NULL;
433   cac.search = addr;
434   GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses, &peer->hashPubKey,
435                                               &compare_address_session_it, &cac);
436   return cac.exact_address;
437 }
438 #endif
439
440 void
441 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
442                       const char *plugin_name, const void *plugin_addr,
443                       size_t plugin_addr_len, uint32_t session_id,
444                       const struct GNUNET_ATS_Information *atsi,
445                       uint32_t atsi_count)
446 {
447   struct ATS_Address *aa;
448   struct ATS_Address *old;
449
450   if (GNUNET_NO == handle->running)
451     return;
452
453   GNUNET_assert (NULL != handle->addresses);
454
455   aa = create_address (peer,
456                        plugin_name,
457                        plugin_addr, plugin_addr_len,
458                        session_id);
459   aa->mlp_information = NULL;
460   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
461   aa->ats_count = atsi_count;
462   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
463
464   /* Get existing address or address with session == 0 */
465   old = find_address (peer, aa);
466   if (old == NULL)
467   {
468     /* We have a new address */
469     GNUNET_assert (GNUNET_OK ==
470                    GNUNET_CONTAINER_multihashmap_put (handle->addresses,
471                                                       &peer->hashPubKey, aa,
472                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
473     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' session id %u, %p\n",
474                 GNUNET_i2s (peer), session_id, aa);
475     /* Tell solver about update */
476     handle->s_update (handle->solver, handle->addresses, aa);
477     return;
478   }
479
480   if (old->session_id != 0)
481   {
482       /* This address and session is already existing */
483       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
484                 "Added already existing address for peer `%s' `%s' %p with new session %u\n",
485                 GNUNET_i2s (peer), plugin_name, session_id);
486       GNUNET_break (0);
487       return;
488   }
489
490   /* We have an address without an session, update this address */
491   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
492             "Updated existing address for peer `%s' %p with new session %u\n",
493             GNUNET_i2s (peer), old, session_id);
494   GNUNET_free_non_null (old->ats);
495   old->session_id = session_id;
496   old->ats = NULL;
497   old->ats_count = 0;
498   old->ats = aa->ats;
499   old->ats_count = aa->ats_count;
500   GNUNET_free (aa->plugin);
501   GNUNET_free (aa);
502   handle->s_update (handle->solver, handle->addresses, old);
503 }
504
505
506 void
507 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
508                       const char *plugin_name, const void *plugin_addr,
509                       size_t plugin_addr_len, uint32_t session_id,
510                       const struct GNUNET_ATS_Information *atsi,
511                       uint32_t atsi_count)
512 {
513   struct ATS_Address *old;
514   uint32_t i;
515
516   if (GNUNET_NO == handle->running)
517     return;
518
519   GNUNET_assert (NULL != handle->addresses);
520
521   /* Get existing address */
522   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
523                        session_id, atsi, atsi_count);
524   if (old == NULL)
525   {
526     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to update unknown address for peer `%s' `%s' session id %u\n",
527                 GNUNET_i2s (peer), plugin_name, session_id);
528     GNUNET_break (0);
529     return;
530   }
531
532   for (i = 0; i < atsi_count; i++)
533     switch (ntohl (atsi[i].type))
534     {
535     case GNUNET_ATS_UTILIZATION_UP:
536       old->atsp_utilization_out.value__ = atsi[i].value;
537       break;
538     case GNUNET_ATS_UTILIZATION_DOWN:
539       old->atsp_utilization_in.value__ = atsi[i].value;
540       break;
541     case GNUNET_ATS_QUALITY_NET_DELAY:
542       old->atsp_latency.rel_value = ntohl (atsi[i].value);
543       break;
544     case GNUNET_ATS_QUALITY_NET_DISTANCE:
545       old->atsp_distance = ntohl (atsi[i].value);
546       break;
547     case GNUNET_ATS_COST_WAN:
548       old->atsp_cost_wan = ntohl (atsi[i].value);
549       break;
550     case GNUNET_ATS_COST_LAN:
551       old->atsp_cost_lan = ntohl (atsi[i].value);
552       break;
553     case GNUNET_ATS_COST_WLAN:
554       old->atsp_cost_wlan = ntohl (atsi[i].value);
555       break;
556     case GNUNET_ATS_NETWORK_TYPE:
557       old->atsp_network_type = ntohl (atsi[i].value);
558       break;
559
560     default:
561       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
562                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
563       GNUNET_break (0);
564       break;
565     }
566   /* Tell solver about update */
567   handle->s_update (handle->solver, handle->addresses, old);
568 }
569
570
571 /**
572  * Delete an address
573  *
574  * If session != 0, just the session is deleted, the address itself still exists
575  * If session == 0, remove full address
576  * If session == 0 and addrlen == 0, destroy inbound address
577  *
578  * @param cls unused
579  * @param key unused
580  * @param value the 'struct ATS_Address'
581  * @return GNUNET_OK (continue to iterate)
582  */
583 static int
584 destroy_by_session_id (void *cls, const struct GNUNET_HashCode * key, void *value)
585 {
586   const struct ATS_Address *info = cls;
587   struct ATS_Address *aa = value;
588
589   GNUNET_assert (0 ==
590                  memcmp (&aa->peer, &info->peer,
591                          sizeof (struct GNUNET_PeerIdentity)));
592   /* session == 0, remove full address  */
593   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
594       (aa->addr_len == info->addr_len) &&
595       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
596   {
597
598     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
599                 "Deleting address for peer `%s': `%s' %u\n",
600                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
601
602     destroy_address (aa);
603       // FIXME if (GNUNET_YES == destroy_address (aa))recalculate_assigned_bw ();
604     return GNUNET_OK;
605   }
606   /* session != 0, just remove session */
607   if (aa->session_id != info->session_id)
608     return GNUNET_OK;           /* irrelevant */
609   if (aa->session_id != 0)
610     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
611   /* session died */
612   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
613               "Deleting session for peer `%s': `%s' %u\n",
614               GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
615   aa->session_id = 0;
616
617   if (GNUNET_YES == aa->active)
618   {
619     aa->active = GNUNET_NO;
620     //FIXME recalculate_assigned_bw ();
621   }
622
623   /* session == 0 and addrlen == 0 : destroy address */
624   if (aa->addr_len == 0)
625   {
626     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627                 "Deleting session and address for peer `%s': `%s' %u\n",
628                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
629     (void) destroy_address (aa);
630   }
631   else
632   {
633     /* session was set to 0, update address */
634 #if HAVE_LIBGLPK
635   if (handle->ats_mode == MODE_MLP)
636     GAS_mlp_address_update (handle->solver, handle->addresses, aa);
637 #endif
638   }
639
640   return GNUNET_OK;
641 }
642
643
644 void
645 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
646                        const char *plugin_name, const void *plugin_addr,
647                        size_t plugin_addr_len, uint32_t session_id)
648 {
649   struct ATS_Address *aa;
650   struct ATS_Address *old;
651
652   if (GNUNET_NO == handle->running)
653     return;
654
655   /* Get existing address */
656   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
657                        session_id, NULL, 0);
658   if (old == NULL)
659   {
660     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tried to destroy unknown address for peer `%s' `%s' session id %u\n",
661                 GNUNET_i2s (peer), plugin_name, session_id);
662     return;
663   }
664
665   GNUNET_break (0 < strlen (plugin_name));
666   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
667   GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses, &peer->hashPubKey,
668                                               &destroy_by_session_id, aa);
669   free_address (aa);
670 }
671
672
673 int
674 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
675                       const char *plugin_name, const void *plugin_addr,
676                       size_t plugin_addr_len, uint32_t session_id, int in_use)
677 {
678 #if DEBUG_ATS
679   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
680               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
681               GNUNET_i2s (peer), in_use);
682 #endif
683
684   struct ATS_Address *old;
685
686   if (GNUNET_NO == handle->running)
687     return GNUNET_SYSERR;
688
689   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id, NULL, 0);
690   if (NULL == old)
691   {
692     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
693                 "Trying to set unknown address `%s', %s %u %s \n",
694                 GNUNET_i2s (peer),
695                 plugin_name, session_id,
696                 (GNUNET_NO == in_use) ? "NO" : "YES");
697     GNUNET_break (0);
698     return GNUNET_SYSERR;
699   }
700   if (old->used == in_use)
701   {
702     GNUNET_break (0);
703     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
704                 "Address in use called multiple times for peer `%s': %s -> %s \n",
705                 GNUNET_i2s (peer),
706                 (GNUNET_NO == old->used) ? "NO" : "YES",
707                 (GNUNET_NO == in_use) ? "NO" : "YES");
708     return GNUNET_SYSERR;
709   }
710   old->used = in_use;
711
712   /* Tell solver about update */
713   handle->s_update (handle->solver, handle->addresses, old);
714
715   return GNUNET_OK;
716 }
717
718
719 /**
720  * Cancel address suggestions for a peer
721  *
722  * @param peer the respective peer
723  */
724 void
725 GAS_addresses_request_address_cancel (const struct GNUNET_PeerIdentity *peer)
726 {
727   struct GAS_Addresses_Suggestion_Requests *cur = handle->r_head;
728
729   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
730               "Received request: `%s' for peer %s\n", "request_address_cancel", GNUNET_i2s (peer));
731
732   while (NULL != cur)
733   {
734       if (0 == memcmp (peer, &cur->id, sizeof (cur->id)))
735         break; /* found */
736       cur = cur->next;
737   }
738
739   if (NULL == cur)
740   {
741       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
742                   "No address requests pending for peer `%s', cannot remove!\n", GNUNET_i2s (peer));
743       return;
744   }
745
746   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747               "Removed request pending for peer `%s\n", GNUNET_i2s (peer));
748   GNUNET_CONTAINER_DLL_remove (handle->r_head, handle->r_tail, cur);
749   GNUNET_free (cur);
750 }
751
752
753 /**
754  * Add an address suggestions for a peer
755  *
756  * @param peer the respective peer
757  */
758 void
759 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
760 {
761   struct GAS_Addresses_Suggestion_Requests *cur = handle->r_head;
762   const struct ATS_Address *aa;
763   struct GNUNET_ATS_Information *ats;
764   unsigned int ats_count;
765
766   if (GNUNET_NO == handle->running)
767     return;
768   while (NULL != cur)
769   {
770       if (0 == memcmp (peer, &cur->id, sizeof (cur->id)))
771         break; /* already suggesting */
772       cur = cur->next;
773   }
774   if (NULL == cur)
775   {
776       cur = GNUNET_malloc (sizeof (struct GAS_Addresses_Suggestion_Requests));
777       cur->id = (*peer);
778       GNUNET_CONTAINER_DLL_insert (handle->r_head, handle->r_tail, cur);
779   }
780
781   /* Get prefered address from solver */
782   aa = handle->s_get (handle->solver, handle->addresses, peer);
783   if (NULL == aa)
784     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
785                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
786   else
787   {
788     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
789                 "Suggesting address %p for peer `%s'\n", aa, GNUNET_i2s (peer));
790
791     ats_count = assemble_ats_information (aa, &ats);
792     GAS_scheduling_transmit_address_suggestion (peer,
793                                                 aa->plugin,
794                                                 aa->addr, aa->addr_len,
795                                                 aa->session_id,
796                                                 ats, ats_count,
797                                                 aa->assigned_bw_out,
798                                                 aa->assigned_bw_in);
799     GNUNET_free (ats);
800   }
801 }
802
803
804 static int
805 reset_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
806 {
807   struct ATS_Address *aa = value;
808
809   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
810               "Resetting interval for peer `%s' address %p from %llu to 0\n",
811               GNUNET_i2s (&aa->peer), aa, aa->block_interval);
812
813   aa->blocked_until = GNUNET_TIME_UNIT_ZERO_ABS;
814   aa->block_interval = GNUNET_TIME_UNIT_ZERO;
815   return GNUNET_OK;
816 }
817
818
819 void
820 GAS_addresses_handle_backoff_reset (const struct GNUNET_PeerIdentity *peer)
821 {
822   GNUNET_break (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses,
823                                               &peer->hashPubKey,
824                                               &reset_address_it,
825                                               NULL));
826 }
827
828
829 void
830 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
831                                  enum GNUNET_ATS_PreferenceKind kind,
832                                  float score)
833 {
834   if (GNUNET_NO == handle->running)
835     return;
836
837   /* Tell solver about update */
838   handle->s_pref (handle->solver, peer, kind, score);
839 }
840
841 static unsigned int
842 load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned long long *out_dest, unsigned long long *in_dest, int dest_length)
843 {
844   int quotas[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType;
845   char * entry_in = NULL;
846   char * entry_out = NULL;
847   char * quota_out_str;
848   char * quota_in_str;
849   int c;
850
851   for (c = 0; (c < GNUNET_ATS_NetworkTypeCount) && (c < dest_length); c++)
852   {
853     in_dest[c] = 0;
854     out_dest[c] = 0;
855     switch (quotas[c]) {
856       case GNUNET_ATS_NET_UNSPECIFIED:
857         entry_out = "UNSPECIFIED_QUOTA_OUT";
858         entry_in = "UNSPECIFIED_QUOTA_IN";
859         break;
860       case GNUNET_ATS_NET_LOOPBACK:
861         entry_out = "LOOPBACK_QUOTA_OUT";
862         entry_in = "LOOPBACK_QUOTA_IN";
863         break;
864       case GNUNET_ATS_NET_LAN:
865         entry_out = "LAN_QUOTA_OUT";
866         entry_in = "LAN_QUOTA_IN";
867         break;
868       case GNUNET_ATS_NET_WAN:
869         entry_out = "WAN_QUOTA_OUT";
870         entry_in = "WAN_QUOTA_IN";
871         break;
872       case GNUNET_ATS_NET_WLAN:
873         entry_out = "WLAN_QUOTA_OUT";
874         entry_in = "WLAN_QUOTA_IN";
875         break;
876       default:
877         break;
878     }
879
880     if ((entry_in == NULL) || (entry_out == NULL))
881       continue;
882
883     /* quota out */
884     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_out, &quota_out_str))
885     {
886       if (0 == strcmp(quota_out_str, BIG_M_STRING) ||
887           (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str, &out_dest[c])))
888         out_dest[c] = UINT32_MAX;
889
890       GNUNET_free (quota_out_str);
891       quota_out_str = NULL;
892     }
893     else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c])
894       out_dest[c] = UINT32_MAX;
895     else
896       out_dest[c] = UINT32_MAX;
897
898     /* quota in */
899     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_in, &quota_in_str))
900     {
901       if (0 == strcmp(quota_in_str, BIG_M_STRING) ||
902           (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, &in_dest[c])))
903         in_dest[c] = UINT32_MAX;
904
905       GNUNET_free (quota_in_str);
906       quota_in_str = NULL;
907     }
908     else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c])
909     {
910       in_dest[c] = UINT32_MAX;
911     }
912     else
913     {
914         in_dest[c] = UINT32_MAX;
915     }
916     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded quota: %s %u, %s %u\n", entry_in, in_dest[c], entry_out, out_dest[c]);
917
918   }
919   return GNUNET_ATS_NetworkTypeCount;
920 }
921
922
923
924 /**
925  * Initialize address subsystem.
926  *
927  * @param cfg configuration to use
928  * @param stats the statistics handle to use
929  */
930 struct GAS_Addresses_Handle *
931 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
932                     const struct GNUNET_STATISTICS_Handle *stats)
933 {
934   struct GAS_Addresses_Handle *ah;
935   int quotas[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType;
936   unsigned long long  quotas_in[GNUNET_ATS_NetworkTypeCount];
937   unsigned long long  quotas_out[GNUNET_ATS_NetworkTypeCount];
938   int quota_count;
939   char *mode_str;
940   int c;
941
942   ah = GNUNET_malloc (sizeof (struct GAS_Addresses_Handle));
943   handle = ah;
944   handle->running = GNUNET_NO;
945
946   /* Initialize the addresses database */
947   ah->addresses = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
948   GNUNET_assert (NULL != ah->addresses);
949
950   /* Figure out configured solution method */
951   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "MODE", &mode_str))
952   {
953       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No ressource assignment method configured, using simplistic approch\n");
954       ah->ats_mode = MODE_SIMPLISTIC;
955   }
956   else
957   {
958       for (c = 0; c < strlen (mode_str); c++)
959         mode_str[c] = toupper (mode_str[c]);
960       if (0 == strcmp (mode_str, "SIMPLISTIC"))
961       {
962           ah->ats_mode = MODE_SIMPLISTIC;
963       }
964       else if (0 == strcmp (mode_str, "MLP"))
965       {
966           ah->ats_mode = MODE_MLP;
967 #if !HAVE_LIBGLPK
968           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Assignment method `%s' configured, but GLPK is not availabe, please install \n", mode_str);
969           ah->ats_mode = MODE_SIMPLISTIC;
970 #endif
971       }
972       else
973       {
974           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid ressource assignment method `%s' configured, using simplistic approch\n", mode_str);
975           ah->ats_mode = MODE_SIMPLISTIC;
976       }
977       GNUNET_free (mode_str);
978   }
979   /* Start configured solution method */
980   switch (ah->ats_mode)
981   {
982     case MODE_MLP:
983       /* Init the MLP solver with default values */
984 #if HAVE_LIBGLPK
985       ah->ats_mode = MODE_MLP;
986       ah->s_init = &GAS_mlp_init;
987       ah->s_update = &GAS_mlp_address_update;
988       ah->s_get = &GAS_mlp_get_preferred_address;
989       ah->s_pref = &GAS_mlp_address_change_preference;
990       ah->s_del =  &GAS_mlp_address_delete;
991       ah->s_done = &GAS_mlp_done;
992 #else
993       GNUNET_free (ah);
994       return NULL;
995 #endif
996       break;
997     case MODE_SIMPLISTIC:
998       /* Init the simplistic solver with default values */
999       ah->ats_mode = MODE_SIMPLISTIC;
1000       ah->s_init = &GAS_simplistic_init;
1001       ah->s_update = &GAS_simplistic_address_update;
1002       ah->s_get = &GAS_simplistic_get_preferred_address;
1003       ah->s_pref = &GAS_simplistic_address_change_preference;
1004       ah->s_del  = &GAS_simplistic_address_delete;
1005       ah->s_done = &GAS_simplistic_done;
1006       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "SIMPLISTIC");
1007       break;
1008     default:
1009       return NULL;
1010       break;
1011   }
1012
1013   GNUNET_assert (NULL != ah->s_init);
1014   GNUNET_assert (NULL != ah->s_update);
1015   GNUNET_assert (NULL != ah->s_get);
1016   GNUNET_assert (NULL != ah->s_pref);
1017   GNUNET_assert (NULL != ah->s_del);
1018   GNUNET_assert (NULL != ah->s_done);
1019
1020   quota_count = load_quotas(cfg, quotas_in, quotas_out, GNUNET_ATS_NetworkTypeCount);
1021
1022   ah->solver = ah->s_init (cfg, stats, quotas, quotas_in, quotas_out, quota_count);
1023   if (NULL == ah->solver)
1024   {
1025     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize solver!\n");
1026     GNUNET_free (ah);
1027     return NULL;
1028   }
1029
1030   /* up and running */
1031   ah->running = GNUNET_YES;
1032   return ah;
1033 }
1034
1035
1036 /**
1037  * Free memory of address.
1038  *
1039  * @param cls NULL
1040  * @param key peer identity (unused)
1041  * @param value the 'struct ATS_Address' to free
1042  * @return GNUNET_OK (continue to iterate)
1043  */
1044 static int
1045 free_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1046 {
1047   struct ATS_Address *aa = value;
1048
1049   destroy_address (aa);
1050   return GNUNET_OK;
1051 }
1052
1053
1054 void
1055 GAS_addresses_destroy_all ()
1056 {
1057   if (GNUNET_NO == handle->running)
1058     return;
1059
1060   if (handle->addresses != NULL)
1061     GNUNET_CONTAINER_multihashmap_iterate (handle->addresses, &free_address_it, NULL);
1062 }
1063
1064
1065 /**
1066  * Shutdown address subsystem.
1067  */
1068 void
1069 GAS_addresses_done (struct GAS_Addresses_Handle *handle)
1070 {
1071   struct GAS_Addresses_Suggestion_Requests *cur;
1072
1073   GNUNET_assert (NULL != handle);
1074   GAS_addresses_destroy_all ();
1075   handle->running = GNUNET_NO;
1076   GNUNET_CONTAINER_multihashmap_destroy (handle->addresses);
1077   handle->addresses = NULL;
1078   while (NULL != (cur = handle->r_head))
1079   {
1080       GNUNET_CONTAINER_DLL_remove (handle->r_head, handle->r_tail, cur);
1081       GNUNET_free (cur);
1082   }
1083
1084   GNUNET_free (handle);
1085   /* Stop configured solution method */
1086
1087 }
1088
1089 struct PeerIteratorContext
1090 {
1091   GNUNET_ATS_Peer_Iterator it;
1092   void *it_cls;
1093   struct GNUNET_CONTAINER_MultiHashMap *peers_returned;
1094 };
1095
1096 static int
1097 peer_it (void *cls,
1098          const struct GNUNET_HashCode * key,
1099          void *value)
1100 {
1101   struct PeerIteratorContext *ip_ctx = cls;
1102   struct GNUNET_PeerIdentity tmp;
1103
1104   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(ip_ctx->peers_returned, key))
1105   {
1106       GNUNET_CONTAINER_multihashmap_put(ip_ctx->peers_returned, key, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1107       tmp.hashPubKey = (*key);
1108       ip_ctx->it (ip_ctx->it_cls, &tmp);
1109   }
1110
1111   return GNUNET_OK;
1112 }
1113
1114 /**
1115  * Return all peers currently known to ATS
1116  *
1117  * @param p_it the iterator to call for every peer, callbach with id == NULL
1118  *        when done
1119  * @param p_it_cls the closure for the iterator
1120  */
1121 void
1122 GAS_addresses_iterate_peers (GNUNET_ATS_Peer_Iterator p_it, void *p_it_cls)
1123 {
1124   struct PeerIteratorContext ip_ctx;
1125   unsigned int size;
1126
1127   if (NULL == p_it)
1128       return;
1129   GNUNET_assert (NULL != handle->addresses);
1130
1131   size = GNUNET_CONTAINER_multihashmap_size(handle->addresses);
1132   if (0 != size)
1133   {
1134     ip_ctx.it = p_it;
1135     ip_ctx.it_cls = p_it_cls;
1136     ip_ctx.peers_returned = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_NO);
1137     GNUNET_CONTAINER_multihashmap_iterate (handle->addresses, &peer_it, &ip_ctx);
1138     GNUNET_CONTAINER_multihashmap_destroy (ip_ctx.peers_returned);
1139   }
1140   p_it (p_it_cls, NULL);
1141 }
1142
1143 struct PeerInfoIteratorContext
1144 {
1145   GNUNET_ATS_PeerInfo_Iterator it;
1146   void *it_cls;
1147 };
1148
1149
1150 static int 
1151 peerinfo_it (void *cls,
1152              const struct GNUNET_HashCode * key,
1153              void *value)
1154 {
1155   struct PeerInfoIteratorContext *pi_ctx = cls;
1156   struct ATS_Address *addr = (struct ATS_Address *)  value;
1157   struct GNUNET_ATS_Information *ats;
1158   uint32_t ats_count;
1159
1160   if (NULL != pi_ctx->it)
1161   {
1162     ats_count = assemble_ats_information (addr, &ats);
1163
1164     pi_ctx->it (pi_ctx->it_cls,
1165                 &addr->peer,
1166                 addr->plugin,
1167                 addr->addr, addr->addr_len,
1168                 addr->active,
1169                 ats, ats_count,
1170                 addr->assigned_bw_out,
1171                 addr->assigned_bw_in);
1172     GNUNET_free (ats);
1173   }
1174   return GNUNET_YES;
1175 }
1176
1177
1178 /**
1179  * Return all peers currently known to ATS
1180  *
1181  * @param peer the respective peer
1182  * @param pi_it the iterator to call for every peer
1183  * @param pi_it_cls the closure for the iterator
1184  */
1185 void
1186 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer, GNUNET_ATS_PeerInfo_Iterator pi_it, void *pi_it_cls)
1187 {
1188   struct PeerInfoIteratorContext pi_ctx;
1189   struct GNUNET_BANDWIDTH_Value32NBO zero_bw;
1190   GNUNET_assert (NULL != peer);
1191   GNUNET_assert (NULL != handle->addresses);
1192   if (NULL == pi_it)
1193     return; /* does not make sense without callback */
1194
1195   zero_bw = GNUNET_BANDWIDTH_value_init (0);
1196   pi_ctx.it = pi_it;
1197   pi_ctx.it_cls = pi_it_cls;
1198
1199   GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses, &peer->hashPubKey, &peerinfo_it, &pi_ctx);
1200
1201   if (NULL != pi_it)
1202     pi_it (pi_it_cls, NULL, NULL, NULL, 0, GNUNET_NO, NULL, 0, zero_bw, zero_bw);
1203
1204 }
1205
1206
1207 /* end of gnunet-service-ats_addresses.c */