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