d0225635bbc699575be9d928504d6a889fc888f7
[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         return; /* already suggesting */
1023       cur = cur->next;
1024   }
1025
1026   cur = GNUNET_malloc (sizeof (struct GAS_Addresses_Suggestion_Requests));
1027   cur->id = (*peer);
1028   GNUNET_CONTAINER_DLL_insert (handle->r_head, handle->r_tail, cur);
1029
1030   if (handle->ats_mode == MODE_SIMPLISTIC)
1031   {
1032     request_address_simple (peer);
1033   }
1034   if (handle->ats_mode == MODE_MLP)
1035   {
1036     request_address_mlp(peer);
1037   }
1038 }
1039
1040
1041 static int
1042 reset_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1043 {
1044   struct ATS_Address *aa = value;
1045
1046   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1047               "Resetting interval for peer `%s' address %p from %llu to 0\n", GNUNET_i2s (&aa->peer), aa, aa->block_interval);
1048
1049   aa->blocked_until = GNUNET_TIME_UNIT_ZERO_ABS;
1050   aa->block_interval = GNUNET_TIME_UNIT_ZERO;
1051   return GNUNET_OK;
1052 }
1053
1054
1055 void
1056 GAS_addresses_handle_backoff_reset (const struct GNUNET_PeerIdentity *peer)
1057 {
1058   GNUNET_break (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses,
1059                                               &peer->hashPubKey,
1060                                               &reset_address_it,
1061                                               NULL));
1062 }
1063
1064
1065 void
1066 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
1067                                  enum GNUNET_ATS_PreferenceKind kind,
1068                                  float score)
1069 {
1070   if (GNUNET_NO == handle->running)
1071     return;
1072
1073   /* Tell solver about update */
1074   handle->s_pref (handle->solver, peer, kind, score);
1075 }
1076
1077
1078
1079 /**
1080  * Initialize address subsystem.
1081  *
1082  * @param cfg configuration to use
1083  * @param stats the statistics handle to use
1084  */
1085 struct GAS_Addresses_Handle *
1086 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
1087                     const struct GNUNET_STATISTICS_Handle *stats)
1088 {
1089   struct GAS_Addresses_Handle *ah;
1090   char *quota_wan_in_str;
1091   char *quota_wan_out_str;
1092   char *mode_str;
1093   int c;
1094
1095   ah = GNUNET_malloc (sizeof (struct GAS_Addresses_Handle));
1096   handle = ah;
1097   handle->running = GNUNET_NO;
1098
1099   /* Initialize the system with configuration values */
1100   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_IN", &quota_wan_in_str))
1101   {
1102     if (0 == strcmp(quota_wan_in_str, "unlimited") ||
1103         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_in_str, &ah->wan_quota_in)))
1104       ah->wan_quota_in = (UINT32_MAX) /10;
1105
1106     GNUNET_free (quota_wan_in_str);
1107     quota_wan_in_str = NULL;
1108   }
1109   else
1110       ah->wan_quota_in = (UINT32_MAX) /10;
1111
1112   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(cfg, "ats", "WAN_QUOTA_OUT", &quota_wan_out_str))
1113   {
1114     if (0 == strcmp(quota_wan_out_str, "unlimited") ||
1115         (GNUNET_SYSERR == GNUNET_STRINGS_fancy_size_to_bytes (quota_wan_out_str, &ah->wan_quota_out)))
1116       ah->wan_quota_out = (UINT32_MAX) /10;
1117
1118     GNUNET_free (quota_wan_out_str);
1119     quota_wan_out_str = NULL;
1120   }
1121   else
1122     ah->wan_quota_out = (UINT32_MAX) /10;
1123
1124   /* Initialize the addresses database */
1125   ah->addresses = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_NO);
1126   GNUNET_assert (NULL != ah->addresses);
1127
1128   /* Figure out configured solution method */
1129   if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "MODE", &mode_str))
1130   {
1131       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No ressource assignment method configured, using simplistic approch\n");
1132       ah->ats_mode = MODE_SIMPLISTIC;
1133   }
1134   else
1135   {
1136       for (c = 0; c < strlen (mode_str); c++)
1137         mode_str[c] = toupper (mode_str[c]);
1138       if (0 == strcmp (mode_str, "SIMPLISTIC"))
1139       {
1140           ah->ats_mode = MODE_SIMPLISTIC;
1141       }
1142       else if (0 == strcmp (mode_str, "MLP"))
1143       {
1144           ah->ats_mode = MODE_MLP;
1145 #if !HAVE_LIBGLPK
1146           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Assignment method `%s' configured, but GLPK is not availabe, please install \n", mode_str);
1147           ah->ats_mode = MODE_SIMPLISTIC;
1148 #endif
1149       }
1150       else
1151       {
1152           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid ressource assignment method `%s' configured, using simplistic approch\n", mode_str);
1153           ah->ats_mode = MODE_SIMPLISTIC;
1154       }
1155       GNUNET_free (mode_str);
1156   }
1157   /* Start configured solution method */
1158   switch (ah->ats_mode)
1159   {
1160     case MODE_MLP:
1161       /* Init the MLP solver with default values */
1162 #if HAVE_LIBGLPK
1163       ah->ats_mode = MODE_MLP;
1164       ah->s_init = &GAS_mlp_init;
1165       ah->s_update = &GAS_mlp_address_update;
1166       ah->s_get = &GAS_mlp_get_preferred_address;
1167       ah->s_pref = &GAS_mlp_address_change_preference;
1168       ah->s_del =  &GAS_mlp_address_delete;
1169       ah->s_done = &GAS_mlp_done;
1170 #else
1171       GNUNET_free (ah);
1172       return NULL;
1173 #endif
1174       break;
1175     case MODE_SIMPLISTIC:
1176       /* Init the simplistic solver with default values */
1177       ah->ats_mode = MODE_SIMPLISTIC;
1178       ah->s_init = &GAS_simplistic_init;
1179       ah->s_update = &GAS_simplistic_address_update;
1180       ah->s_get = &GAS_simplistic_get_preferred_address;
1181       ah->s_pref = &GAS_simplistic_address_change_preference;
1182       ah->s_del  = &GAS_simplistic_address_delete;
1183       ah->s_done = &GAS_simplistic_done;
1184       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ATS started in %s mode\n", "SIMPLISTIC");
1185       break;
1186     default:
1187       return NULL;
1188       break;
1189   }
1190
1191   GNUNET_assert (NULL != ah->s_init);
1192   GNUNET_assert (NULL != ah->s_update);
1193   GNUNET_assert (NULL != ah->s_get);
1194   GNUNET_assert (NULL != ah->s_pref);
1195   GNUNET_assert (NULL != ah->s_del);
1196   GNUNET_assert (NULL != ah->s_done);
1197
1198   ah->solver = ah->s_init (cfg, stats);
1199   if (NULL == ah->solver)
1200   {
1201     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize MLP solver!\n");
1202     GNUNET_free (ah);
1203     return NULL;
1204   }
1205
1206   /* up and running */
1207   ah->running = GNUNET_YES;
1208   return ah;
1209 }
1210
1211
1212 /**
1213  * Free memory of address.
1214  *
1215  * @param cls NULL
1216  * @param key peer identity (unused)
1217  * @param value the 'struct ATS_Address' to free
1218  * @return GNUNET_OK (continue to iterate)
1219  */
1220 static int
1221 free_address_it (void *cls, const struct GNUNET_HashCode * key, void *value)
1222 {
1223   struct ATS_Address *aa = value;
1224
1225   destroy_address (aa);
1226   return GNUNET_OK;
1227 }
1228
1229
1230 void
1231 GAS_addresses_destroy_all ()
1232 {
1233   if (GNUNET_NO == handle->running)
1234     return;
1235
1236   if (handle->addresses != NULL)
1237     GNUNET_CONTAINER_multihashmap_iterate (handle->addresses, &free_address_it, NULL);
1238   GNUNET_assert (handle->active_addr_count == 0);
1239 }
1240
1241
1242 /**
1243  * Shutdown address subsystem.
1244  */
1245 void
1246 GAS_addresses_done (struct GAS_Addresses_Handle *handle)
1247 {
1248   struct GAS_Addresses_Suggestion_Requests *cur;
1249
1250   GNUNET_assert (NULL != handle);
1251   GAS_addresses_destroy_all ();
1252   handle->running = GNUNET_NO;
1253   GNUNET_CONTAINER_multihashmap_destroy (handle->addresses);
1254   handle->addresses = NULL;
1255   while (NULL != (cur = handle->r_head))
1256   {
1257       GNUNET_CONTAINER_DLL_remove (handle->r_head, handle->r_tail, cur);
1258       GNUNET_free (cur);
1259   }
1260
1261   GNUNET_free (handle);
1262   /* Stop configured solution method */
1263
1264 }
1265
1266 struct PeerIteratorContext
1267 {
1268   GNUNET_ATS_Peer_Iterator it;
1269   void *it_cls;
1270   struct GNUNET_CONTAINER_MultiHashMap *peers_returned;
1271 };
1272
1273 static int
1274 peer_it (void *cls,
1275          const struct GNUNET_HashCode * key,
1276          void *value)
1277 {
1278   struct PeerIteratorContext *ip_ctx = cls;
1279   struct GNUNET_PeerIdentity tmp;
1280
1281   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(ip_ctx->peers_returned, key))
1282   {
1283       GNUNET_CONTAINER_multihashmap_put(ip_ctx->peers_returned, key, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1284       tmp.hashPubKey = (*key);
1285       ip_ctx->it (ip_ctx->it_cls, &tmp);
1286   }
1287
1288   return GNUNET_OK;
1289 }
1290
1291 /**
1292  * Return all peers currently known to ATS
1293  *
1294  * @param p_it the iterator to call for every peer, callbach with id == NULL
1295  *        when done
1296  * @param p_it_cls the closure for the iterator
1297  */
1298 void
1299 GAS_addresses_iterate_peers (GNUNET_ATS_Peer_Iterator p_it, void *p_it_cls)
1300 {
1301   struct PeerIteratorContext ip_ctx;
1302   unsigned int size;
1303
1304   if (NULL == p_it)
1305       return;
1306   GNUNET_assert (NULL != handle->addresses);
1307
1308   size = GNUNET_CONTAINER_multihashmap_size(handle->addresses);
1309   if (0 != size)
1310   {
1311     ip_ctx.it = p_it;
1312     ip_ctx.it_cls = p_it_cls;
1313     ip_ctx.peers_returned = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_NO);
1314     GNUNET_CONTAINER_multihashmap_iterate (handle->addresses, &peer_it, &ip_ctx);
1315     GNUNET_CONTAINER_multihashmap_destroy (ip_ctx.peers_returned);
1316   }
1317   p_it (p_it_cls, NULL);
1318 }
1319
1320 struct PeerInfoIteratorContext
1321 {
1322   GNUNET_ATS_PeerInfo_Iterator it;
1323   void *it_cls;
1324 };
1325
1326
1327 static int 
1328 peerinfo_it (void *cls,
1329              const struct GNUNET_HashCode * key,
1330              void *value)
1331 {
1332   struct PeerInfoIteratorContext *pi_ctx = cls;
1333   struct ATS_Address *addr = (struct ATS_Address *)  value;
1334   struct GNUNET_ATS_Information *ats;
1335   uint32_t ats_count;
1336
1337   if (NULL != pi_ctx->it)
1338   {
1339     ats_count = assemble_ats_information (addr, &ats);
1340
1341     pi_ctx->it (pi_ctx->it_cls,
1342                 &addr->peer,
1343                 addr->plugin,
1344                 addr->addr, addr->addr_len,
1345                 addr->active,
1346                 ats, ats_count,
1347                 addr->assigned_bw_out,
1348                 addr->assigned_bw_in);
1349     GNUNET_free (ats);
1350   }
1351   return GNUNET_YES;
1352 }
1353
1354
1355 /**
1356  * Return all peers currently known to ATS
1357  *
1358  * @param peer the respective peer
1359  * @param pi_it the iterator to call for every peer
1360  * @param pi_it_cls the closure for the iterator
1361  */
1362 void
1363 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer, GNUNET_ATS_PeerInfo_Iterator pi_it, void *pi_it_cls)
1364 {
1365   struct PeerInfoIteratorContext pi_ctx;
1366   struct GNUNET_BANDWIDTH_Value32NBO zero_bw;
1367   GNUNET_assert (NULL != peer);
1368   GNUNET_assert (NULL != handle->addresses);
1369   if (NULL == pi_it)
1370     return; /* does not make sense without callback */
1371
1372   zero_bw = GNUNET_BANDWIDTH_value_init (0);
1373   pi_ctx.it = pi_it;
1374   pi_ctx.it_cls = pi_it_cls;
1375
1376   GNUNET_CONTAINER_multihashmap_get_multiple (handle->addresses, &peer->hashPubKey, &peerinfo_it, &pi_ctx);
1377
1378   if (NULL != pi_it)
1379     pi_it (pi_it_cls, NULL, NULL, NULL, 0, GNUNET_NO, NULL, 0, zero_bw, zero_bw);
1380
1381 }
1382
1383
1384 /* end of gnunet-service-ats_addresses.c */