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