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