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