b1b9a4e9f917fd73fd12e7b2b420579c6cf66eb5
[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   }
488
489   /* We have a base address with out an session, update this address */
490   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491             "Updated existing address for peer `%s' %p with new session %u\n",
492             GNUNET_i2s (peer), old, session_id);
493   GNUNET_free_non_null (old->ats);
494   old->session_id = session_id;
495   old->ats = NULL;
496   old->ats_count = 0;
497   old->ats = aa->ats;
498   old->ats_count = aa->ats_count;
499   GNUNET_free (aa->plugin);
500   GNUNET_free (aa);
501   handle->s_update (handle->solver, handle->addresses, old);
502 }
503
504
505 void
506 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
507                       const char *plugin_name, const void *plugin_addr,
508                       size_t plugin_addr_len, uint32_t session_id,
509                       const struct GNUNET_ATS_Information *atsi,
510                       uint32_t atsi_count)
511 {
512   struct ATS_Address *old;
513   uint32_t i;
514
515   if (GNUNET_NO == handle->running)
516     return;
517
518   GNUNET_assert (NULL != handle->addresses);
519
520   /* Get existing address */
521   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
522                        session_id, atsi, atsi_count);
523   if (old == NULL)
524   {
525     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tried to update unknown address for peer `%s' `%s' session id %u\n",
526                 GNUNET_i2s (peer), plugin_name, session_id);
527     GNUNET_break (0);
528     return;
529   }
530
531   for (i = 0; i < atsi_count; i++)
532     switch (ntohl (atsi[i].type))
533     {
534     case GNUNET_ATS_UTILIZATION_UP:
535       old->atsp_utilization_out.value__ = atsi[i].value;
536       break;
537     case GNUNET_ATS_UTILIZATION_DOWN:
538       old->atsp_utilization_in.value__ = atsi[i].value;
539       break;
540     case GNUNET_ATS_QUALITY_NET_DELAY:
541       old->atsp_latency.rel_value = ntohl (atsi[i].value);
542       break;
543     case GNUNET_ATS_QUALITY_NET_DISTANCE:
544       old->atsp_distance = ntohl (atsi[i].value);
545       break;
546     case GNUNET_ATS_COST_WAN:
547       old->atsp_cost_wan = ntohl (atsi[i].value);
548       break;
549     case GNUNET_ATS_COST_LAN:
550       old->atsp_cost_lan = ntohl (atsi[i].value);
551       break;
552     case GNUNET_ATS_COST_WLAN:
553       old->atsp_cost_wlan = ntohl (atsi[i].value);
554       break;
555     case GNUNET_ATS_NETWORK_TYPE:
556       old->atsp_network_type = ntohl (atsi[i].value);
557       break;
558
559     default:
560       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
561                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
562       GNUNET_break (0);
563       break;
564     }
565   /* Tell solver about update */
566   handle->s_update (handle->solver, handle->addresses, old);
567 }
568
569
570 /**
571  * Delete an address
572  *
573  * If session != 0, just the session is deleted, the address itself still exists
574  * If session == 0, remove full address
575  * If session == 0 and addrlen == 0, destroy inbound address
576  *
577  * @param cls unused
578  * @param key unused
579  * @param value the 'struct ATS_Address'
580  * @return GNUNET_OK (continue to iterate)
581  */
582 static int
583 destroy_by_session_id (void *cls, const struct GNUNET_HashCode * key, void *value)
584 {
585   const struct ATS_Address *info = cls;
586   struct ATS_Address *aa = value;
587
588   GNUNET_assert (0 ==
589                  memcmp (&aa->peer, &info->peer,
590                          sizeof (struct GNUNET_PeerIdentity)));
591   /* session == 0, remove full address  */
592   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
593       (aa->addr_len == info->addr_len) &&
594       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
595   {
596
597     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
598                 "Deleting address for peer `%s': `%s' %u\n",
599                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
600     /*FIXME
601     if (GNUNET_YES == destroy_address (aa))
602       recalculate_assigned_bw (); */
603     return GNUNET_OK;
604   }
605   /* session != 0, just remove session */
606   if (aa->session_id != info->session_id)
607     return GNUNET_OK;           /* irrelevant */
608   if (aa->session_id != 0)
609     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
610   /* session died */
611   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
612               "Deleting session for peer `%s': `%s' %u\n",
613               GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
614   aa->session_id = 0;
615
616   if (GNUNET_YES == aa->active)
617   {
618     aa->active = GNUNET_NO;
619     //FIXME recalculate_assigned_bw ();
620   }
621
622   /* session == 0 and addrlen == 0 : destroy address */
623   if (aa->addr_len == 0)
624   {
625     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626                 "Deleting session and address for peer `%s': `%s' %u\n",
627                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
628     (void) destroy_address (aa);
629   }
630   else
631   {
632     /* session was set to 0, update address */
633 #if HAVE_LIBGLPK
634   if (handle->ats_mode == MODE_MLP)
635     GAS_mlp_address_update (handle->solver, handle->addresses, aa);
636 #endif
637   }
638
639   return GNUNET_OK;
640 }
641
642
643 void
644 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
645                        const char *plugin_name, const void *plugin_addr,
646                        size_t plugin_addr_len, uint32_t session_id)
647 {
648   struct ATS_Address *aa;
649   struct ATS_Address *old;
650
651   if (GNUNET_NO == handle->running)
652     return;
653
654   /* Get existing address */
655   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len,
656                        session_id, NULL, 0);
657   if (old == NULL)
658   {
659     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tried to destroy unknown address for peer `%s' `%s' session id %u\n",
660                 GNUNET_i2s (peer), plugin_name, session_id);
661     return;
662   }
663
664
665   GNUNET_break (0 < strlen (plugin_name));
666   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
667
668   GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses, &peer->hashPubKey,
669                                               &destroy_by_session_id, aa);
670
671   free_address (aa);
672 }
673
674
675 int
676 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
677                       const char *plugin_name, const void *plugin_addr,
678                       size_t plugin_addr_len, uint32_t session_id, int in_use)
679 {
680 #if DEBUG_ATS
681   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
683               GNUNET_i2s (peer), in_use);
684 #endif
685
686   struct ATS_Address *old;
687
688   if (GNUNET_NO == handle->running)
689     return GNUNET_SYSERR;
690
691   old = lookup_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id, NULL, 0);
692   if (NULL == old)
693   {
694     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
695                 "Trying to set unknown address `%s', %s %u %s \n",
696                 GNUNET_i2s (peer),
697                 plugin_name, session_id,
698                 (GNUNET_NO == in_use) ? "NO" : "YES");
699     GNUNET_break (0);
700     return GNUNET_SYSERR;
701   }
702   if (old->used == in_use)
703   {
704     GNUNET_break (0);
705     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
706                 "Address in use called multiple times for peer `%s': %s -> %s \n",
707                 GNUNET_i2s (peer),
708                 (GNUNET_NO == old->used) ? "NO" : "YES",
709                 (GNUNET_NO == in_use) ? "NO" : "YES");
710     return GNUNET_SYSERR;
711   }
712   old->used = in_use;
713
714   /* Tell solver about update */
715   handle->s_update (handle->solver, handle->addresses, old);
716
717   return GNUNET_OK;
718 }
719
720
721 /**
722  * Cancel address suggestions for a peer
723  *
724  * @param peer the respective peer
725  */
726 void
727 GAS_addresses_request_address_cancel (const struct GNUNET_PeerIdentity *peer)
728 {
729   struct GAS_Addresses_Suggestion_Requests *cur = handle->r_head;
730   while (NULL != cur)
731   {
732       if (0 == memcmp (peer, &cur->id, sizeof (cur->id)))
733         break; /* found */
734       cur = cur->next;
735   }
736
737   if (NULL == cur)
738   {
739       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
740                   "No address requests pending for peer `%s', cannot remove!\n", GNUNET_i2s (peer));
741       return;
742   }
743   GNUNET_CONTAINER_DLL_remove (handle->r_head, handle->r_tail, cur);
744   GNUNET_free (cur);
745 }
746
747
748 /**
749  * Add an address suggestions for a peer
750  *
751  * @param peer the respective peer
752  */
753 void
754 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
755 {
756   struct GAS_Addresses_Suggestion_Requests *cur = handle->r_head;
757   const struct ATS_Address *aa;
758   struct GNUNET_ATS_Information *ats;
759   unsigned int ats_count;
760
761   if (GNUNET_NO == handle->running)
762     return;
763   while (NULL != cur)
764   {
765       if (0 == memcmp (peer, &cur->id, sizeof (cur->id)))
766         break; /* already suggesting */
767       cur = cur->next;
768   }
769   if (NULL == cur)
770   {
771       cur = GNUNET_malloc (sizeof (struct GAS_Addresses_Suggestion_Requests));
772       cur->id = (*peer);
773       GNUNET_CONTAINER_DLL_insert (handle->r_head, handle->r_tail, cur);
774   }
775
776   /* Get prefered address from solver */
777   aa = handle->s_get (handle->solver, handle->addresses, peer);
778   if (NULL == aa)
779     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
780                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
781   else
782     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
783                 "Suggesting address %p for peer `%s'\n", aa, GNUNET_i2s (peer));
784
785   ats_count = assemble_ats_information (aa, &ats);
786   GAS_scheduling_transmit_address_suggestion (peer,
787                                               aa->plugin,
788                                               aa->addr, aa->addr_len,
789                                               aa->session_id,
790                                               ats, ats_count,
791                                               aa->assigned_bw_out,
792                                               aa->assigned_bw_in);
793   GNUNET_free (ats);
794
795 }
796
797
798 static int
799 reset_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
800 {
801   struct ATS_Address *aa = value;
802
803   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804               "Resetting interval for peer `%s' address %p from %llu to 0\n",
805               GNUNET_i2s (&aa->peer), aa, aa->block_interval);
806
807   aa->blocked_until = GNUNET_TIME_UNIT_ZERO_ABS;
808   aa->block_interval = GNUNET_TIME_UNIT_ZERO;
809   return GNUNET_OK;
810 }
811
812
813 void
814 GAS_addresses_handle_backoff_reset (const struct GNUNET_PeerIdentity *peer)
815 {
816   GNUNET_break (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses,
817                                               &peer->hashPubKey,
818                                               &reset_address_it,
819                                               NULL));
820 }
821
822
823 void
824 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
825                                  enum GNUNET_ATS_PreferenceKind kind,
826                                  float score)
827 {
828   if (GNUNET_NO == handle->running)
829     return;
830
831   /* Tell solver about update */
832   handle->s_pref (handle->solver, peer, kind, score);
833 }
834
835 static unsigned int
836 load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned long long *out_dest, unsigned long long *in_dest, int dest_length)
837 {
838   int quotas[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType;
839   char * entry_in = NULL;
840   char * entry_out = NULL;
841   char * quota_out_str;
842   char * quota_in_str;
843   int c;
844
845   for (c = 0; (c < GNUNET_ATS_NetworkTypeCount) && (c < dest_length); c++)
846   {
847     in_dest[c] = 0;
848     out_dest[c] = 0;
849     switch (quotas[c]) {
850       case GNUNET_ATS_NET_UNSPECIFIED:
851         entry_out = "UNSPECIFIED_QUOTA_OUT";
852         entry_in = "UNSPECIFIED_QUOTA_IN";
853         break;
854       case GNUNET_ATS_NET_LOOPBACK:
855         entry_out = "LOOPBACK_QUOTA_OUT";
856         entry_in = "LOOPBACK_QUOTA_IN";
857         break;
858       case GNUNET_ATS_NET_LAN:
859         entry_out = "LAN_QUOTA_OUT";
860         entry_in = "LAN_QUOTA_IN";
861         break;
862       case GNUNET_ATS_NET_WAN:
863         entry_out = "WAN_QUOTA_OUT";
864         entry_in = "WAN_QUOTA_IN";
865         break;
866       case GNUNET_ATS_NET_WLAN:
867         entry_out = "WLAN_QUOTA_OUT";
868         entry_in = "WLAN_QUOTA_IN";
869         break;
870       default:
871         break;
872     }
873
874     if ((entry_in == NULL) || (entry_out == NULL))
875       continue;
876
877     /* quota out */
878     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_out, &quota_out_str))
879     {
880       if (0 == strcmp(quota_out_str, BIG_M_STRING) ||
881           (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str, &out_dest[c])))
882         out_dest[c] = UINT32_MAX;
883
884       GNUNET_free (quota_out_str);
885       quota_out_str = NULL;
886     }
887     else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c])
888       out_dest[c] = UINT32_MAX;
889     else
890       out_dest[c] = UINT32_MAX;
891
892     /* quota in */
893     if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", entry_in, &quota_in_str))
894     {
895       if (0 == strcmp(quota_in_str, BIG_M_STRING) ||
896           (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, &in_dest[c])))
897         in_dest[c] = UINT32_MAX;
898
899       GNUNET_free (quota_in_str);
900       quota_in_str = NULL;
901     }
902     else if (GNUNET_ATS_NET_UNSPECIFIED == quotas[c])
903     {
904       in_dest[c] = UINT32_MAX;
905     }
906     else
907     {
908         in_dest[c] = UINT32_MAX;
909     }
910     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded quota: %s %u, %s %u\n", entry_in, in_dest[c], entry_out, out_dest[c]);
911
912   }
913   return GNUNET_ATS_NetworkTypeCount;
914 }
915
916
917
918 /**
919  * Initialize address subsystem.
920  *
921  * @param cfg configuration to use
922  * @param stats the statistics handle to use
923  */
924 struct GAS_Addresses_Handle *
925 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
926                     const struct GNUNET_STATISTICS_Handle *stats)
927 {
928   struct GAS_Addresses_Handle *ah;
929   int quotas[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType;
930   unsigned long long  quotas_in[GNUNET_ATS_NetworkTypeCount];
931   unsigned long long  quotas_out[GNUNET_ATS_NetworkTypeCount];
932   int quota_count;
933   char *mode_str;
934   int c;
935
936   ah = GNUNET_malloc (sizeof (struct GAS_Addresses_Handle));
937   handle = ah;
938   handle->running = GNUNET_NO;
939
940   /* Initialize the addresses database */
941   ah->addresses = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
942   GNUNET_assert (NULL != ah->addresses);
943
944   /* Figure out configured solution method */
945   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "MODE", &mode_str))
946   {
947       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No ressource assignment method configured, using simplistic approch\n");
948       ah->ats_mode = MODE_SIMPLISTIC;
949   }
950   else
951   {
952       for (c = 0; c < strlen (mode_str); c++)
953         mode_str[c] = toupper (mode_str[c]);
954       if (0 == strcmp (mode_str, "SIMPLISTIC"))
955       {
956           ah->ats_mode = MODE_SIMPLISTIC;
957       }
958       else if (0 == strcmp (mode_str, "MLP"))
959       {
960           ah->ats_mode = MODE_MLP;
961 #if !HAVE_LIBGLPK
962           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Assignment method `%s' configured, but GLPK is not availabe, please install \n", mode_str);
963           ah->ats_mode = MODE_SIMPLISTIC;
964 #endif
965       }
966       else
967       {
968           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid ressource assignment method `%s' configured, using simplistic approch\n", mode_str);
969           ah->ats_mode = MODE_SIMPLISTIC;
970       }
971       GNUNET_free (mode_str);
972   }
973   /* Start configured solution method */
974   switch (ah->ats_mode)
975   {
976     case MODE_MLP:
977       /* Init the MLP solver with default values */
978 #if HAVE_LIBGLPK
979       ah->ats_mode = MODE_MLP;
980       ah->s_init = &GAS_mlp_init;
981       ah->s_update = &GAS_mlp_address_update;
982       ah->s_get = &GAS_mlp_get_preferred_address;
983       ah->s_pref = &GAS_mlp_address_change_preference;
984       ah->s_del =  &GAS_mlp_address_delete;
985       ah->s_done = &GAS_mlp_done;
986 #else
987       GNUNET_free (ah);
988       return NULL;
989 #endif
990       break;
991     case MODE_SIMPLISTIC:
992       /* Init the simplistic solver with default values */
993       ah->ats_mode = MODE_SIMPLISTIC;
994       ah->s_init = &GAS_simplistic_init;
995       ah->s_update = &GAS_simplistic_address_update;
996       ah->s_get = &GAS_simplistic_get_preferred_address;
997       ah->s_pref = &GAS_simplistic_address_change_preference;
998       ah->s_del  = &GAS_simplistic_address_delete;
999       ah->s_done = &GAS_simplistic_done;
1000       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "SIMPLISTIC");
1001       break;
1002     default:
1003       return NULL;
1004       break;
1005   }
1006
1007   GNUNET_assert (NULL != ah->s_init);
1008   GNUNET_assert (NULL != ah->s_update);
1009   GNUNET_assert (NULL != ah->s_get);
1010   GNUNET_assert (NULL != ah->s_pref);
1011   GNUNET_assert (NULL != ah->s_del);
1012   GNUNET_assert (NULL != ah->s_done);
1013
1014   quota_count = load_quotas(cfg, quotas_in, quotas_out, GNUNET_ATS_NetworkTypeCount);
1015
1016   ah->solver = ah->s_init (cfg, stats, quotas, quotas_in, quotas_out, quota_count);
1017   if (NULL == ah->solver)
1018   {
1019     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize solver!\n");
1020     GNUNET_free (ah);
1021     return NULL;
1022   }
1023
1024   /* up and running */
1025   ah->running = GNUNET_YES;
1026   return ah;
1027 }
1028
1029
1030 /**
1031  * Free memory of address.
1032  *
1033  * @param cls NULL
1034  * @param key peer identity (unused)
1035  * @param value the 'struct ATS_Address' to free
1036  * @return GNUNET_OK (continue to iterate)
1037  */
1038 static int
1039 free_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1040 {
1041   struct ATS_Address *aa = value;
1042
1043   destroy_address (aa);
1044   return GNUNET_OK;
1045 }
1046
1047
1048 void
1049 GAS_addresses_destroy_all ()
1050 {
1051   if (GNUNET_NO == handle->running)
1052     return;
1053
1054   if (handle->addresses != NULL)
1055     GNUNET_CONTAINER_multihashmap_iterate (handle->addresses, &free_address_it, NULL);
1056 }
1057
1058
1059 /**
1060  * Shutdown address subsystem.
1061  */
1062 void
1063 GAS_addresses_done (struct GAS_Addresses_Handle *handle)
1064 {
1065   struct GAS_Addresses_Suggestion_Requests *cur;
1066
1067   GNUNET_assert (NULL != handle);
1068   GAS_addresses_destroy_all ();
1069   handle->running = GNUNET_NO;
1070   GNUNET_CONTAINER_multihashmap_destroy (handle->addresses);
1071   handle->addresses = NULL;
1072   while (NULL != (cur = handle->r_head))
1073   {
1074       GNUNET_CONTAINER_DLL_remove (handle->r_head, handle->r_tail, cur);
1075       GNUNET_free (cur);
1076   }
1077
1078   GNUNET_free (handle);
1079   /* Stop configured solution method */
1080
1081 }
1082
1083 struct PeerIteratorContext
1084 {
1085   GNUNET_ATS_Peer_Iterator it;
1086   void *it_cls;
1087   struct GNUNET_CONTAINER_MultiHashMap *peers_returned;
1088 };
1089
1090 static int
1091 peer_it (void *cls,
1092          const struct GNUNET_HashCode * key,
1093          void *value)
1094 {
1095   struct PeerIteratorContext *ip_ctx = cls;
1096   struct GNUNET_PeerIdentity tmp;
1097
1098   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(ip_ctx->peers_returned, key))
1099   {
1100       GNUNET_CONTAINER_multihashmap_put(ip_ctx->peers_returned, key, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1101       tmp.hashPubKey = (*key);
1102       ip_ctx->it (ip_ctx->it_cls, &tmp);
1103   }
1104
1105   return GNUNET_OK;
1106 }
1107
1108 /**
1109  * Return all peers currently known to ATS
1110  *
1111  * @param p_it the iterator to call for every peer, callbach with id == NULL
1112  *        when done
1113  * @param p_it_cls the closure for the iterator
1114  */
1115 void
1116 GAS_addresses_iterate_peers (GNUNET_ATS_Peer_Iterator p_it, void *p_it_cls)
1117 {
1118   struct PeerIteratorContext ip_ctx;
1119   unsigned int size;
1120
1121   if (NULL == p_it)
1122       return;
1123   GNUNET_assert (NULL != handle->addresses);
1124
1125   size = GNUNET_CONTAINER_multihashmap_size(handle->addresses);
1126   if (0 != size)
1127   {
1128     ip_ctx.it = p_it;
1129     ip_ctx.it_cls = p_it_cls;
1130     ip_ctx.peers_returned = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_NO);
1131     GNUNET_CONTAINER_multihashmap_iterate (handle->addresses, &peer_it, &ip_ctx);
1132     GNUNET_CONTAINER_multihashmap_destroy (ip_ctx.peers_returned);
1133   }
1134   p_it (p_it_cls, NULL);
1135 }
1136
1137 struct PeerInfoIteratorContext
1138 {
1139   GNUNET_ATS_PeerInfo_Iterator it;
1140   void *it_cls;
1141 };
1142
1143
1144 static int 
1145 peerinfo_it (void *cls,
1146              const struct GNUNET_HashCode * key,
1147              void *value)
1148 {
1149   struct PeerInfoIteratorContext *pi_ctx = cls;
1150   struct ATS_Address *addr = (struct ATS_Address *)  value;
1151   struct GNUNET_ATS_Information *ats;
1152   uint32_t ats_count;
1153
1154   if (NULL != pi_ctx->it)
1155   {
1156     ats_count = assemble_ats_information (addr, &ats);
1157
1158     pi_ctx->it (pi_ctx->it_cls,
1159                 &addr->peer,
1160                 addr->plugin,
1161                 addr->addr, addr->addr_len,
1162                 addr->active,
1163                 ats, ats_count,
1164                 addr->assigned_bw_out,
1165                 addr->assigned_bw_in);
1166     GNUNET_free (ats);
1167   }
1168   return GNUNET_YES;
1169 }
1170
1171
1172 /**
1173  * Return all peers currently known to ATS
1174  *
1175  * @param peer the respective peer
1176  * @param pi_it the iterator to call for every peer
1177  * @param pi_it_cls the closure for the iterator
1178  */
1179 void
1180 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer, GNUNET_ATS_PeerInfo_Iterator pi_it, void *pi_it_cls)
1181 {
1182   struct PeerInfoIteratorContext pi_ctx;
1183   struct GNUNET_BANDWIDTH_Value32NBO zero_bw;
1184   GNUNET_assert (NULL != peer);
1185   GNUNET_assert (NULL != handle->addresses);
1186   if (NULL == pi_it)
1187     return; /* does not make sense without callback */
1188
1189   zero_bw = GNUNET_BANDWIDTH_value_init (0);
1190   pi_ctx.it = pi_it;
1191   pi_ctx.it_cls = pi_it_cls;
1192
1193   GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses, &peer->hashPubKey, &peerinfo_it, &pi_ctx);
1194
1195   if (NULL != pi_it)
1196     pi_it (pi_it_cls, NULL, NULL, NULL, 0, GNUNET_NO, NULL, 0, zero_bw, zero_bw);
1197
1198 }
1199
1200
1201 /* end of gnunet-service-ats_addresses.c */