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