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