fixing mantis 2098:
[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
38 #define VERBOSE GNUNET_EXTRA_LOGGING
39
40 enum ATS_Mode
41 {
42   /**
43    * Assign each peer an equal amount of bandwidth (bw)
44    *
45    * bw_per_peer = bw_total / #active addresses
46    */
47   SIMPLE,
48
49   /**
50    * Use MLP solver to assign bandwidth
51    */
52   MLP
53 };
54
55 static struct GNUNET_CONTAINER_MultiHashMap *addresses;
56
57 #if HAVE_LIBGLPK
58 static struct GAS_MLP_Handle *mlp;
59 #endif
60
61 static unsigned long long wan_quota_in;
62
63 static unsigned long long wan_quota_out;
64
65 static unsigned int active_addr_count;
66
67 static int ats_mode;
68
69
70 /**
71  * Update a bandwidth assignment for a peer.  This trivial method currently
72  * simply assigns the same share to all active connections.
73  *
74  * @param cls unused
75  * @param key unused
76  * @param value the 'struct ATS_Address'
77  * @return GNUNET_OK (continue to iterate)
78  */
79 static int
80 update_bw_it (void *cls, const GNUNET_HashCode * key, void *value)
81 {
82   struct ATS_Address *aa = value;
83
84
85   /* Simple method */
86   if (GNUNET_YES != aa->active)
87     return GNUNET_OK;
88   GNUNET_assert (active_addr_count > 0);
89   aa->assigned_bw_in.value__ = htonl (wan_quota_in / active_addr_count);
90   aa->assigned_bw_out.value__ = htonl (wan_quota_out / active_addr_count);
91   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New bandwidth for peer %s is %u/%u\n",
92               GNUNET_i2s (&aa->peer), ntohl (aa->assigned_bw_in.value__),
93               ntohl (aa->assigned_bw_out.value__));
94   GAS_scheduling_transmit_address_suggestion (&aa->peer, aa->plugin, aa->addr,
95                                               aa->addr_len, aa->session_id,
96                                               aa->ats, aa->ats_count,
97                                               aa->assigned_bw_out,
98                                               aa->assigned_bw_in);
99   GAS_reservations_set_bandwidth (&aa->peer, aa->assigned_bw_in);
100   GAS_performance_notify_clients (&aa->peer, aa->plugin, aa->addr, aa->addr_len,
101                                   aa->ats, aa->ats_count, aa->assigned_bw_out,
102                                   aa->assigned_bw_in);
103   return GNUNET_OK;
104 }
105
106
107 /**
108  * Some (significant) input changed, recalculate bandwidth assignment
109  * for all peers.
110  */
111 static void
112 recalculate_assigned_bw ()
113 {
114   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
115               "Recalculating bandwidth for all active connections\n");
116   GNUNET_STATISTICS_update (GSA_stats, "# bandwidth recalculations performed",
117                             1, GNUNET_NO);
118   GNUNET_STATISTICS_set (GSA_stats, "# active addresses", active_addr_count,
119                          GNUNET_NO);
120   GNUNET_CONTAINER_multihashmap_iterate (addresses, &update_bw_it, NULL);
121 }
122
123 /**
124  * Free the given address
125  * @param addr address to destroy
126  */
127 static void
128 free_address (struct ATS_Address *addr)
129 {
130   GNUNET_free_non_null (addr->ats);
131   GNUNET_free (addr->plugin);
132   GNUNET_free (addr);
133 }
134
135 /**
136  * Create a ATS_address with the given information
137  * @param peer peer
138  * @param plugin_name plugin
139  * @param plugin_addr address
140  * @param plugin_addr_len address length
141  * @param session_id session
142  * @return the ATS_Address
143  */
144 static struct ATS_Address *
145 create_address (const struct GNUNET_PeerIdentity *peer,
146                 const char *plugin_name,
147                 const void *plugin_addr, size_t plugin_addr_len,
148                 uint32_t session_id)
149 {
150   struct ATS_Address *aa = NULL;
151
152   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
153   aa->peer = *peer;
154   aa->addr_len = plugin_addr_len;
155   aa->addr = &aa[1];
156   memcpy (&aa[1], plugin_addr, plugin_addr_len);
157   aa->plugin = GNUNET_strdup (plugin_name);
158   aa->session_id = session_id;
159   aa->mlp_information = NULL;
160   aa->next = NULL;
161   aa->prev = NULL;
162   return aa;
163 }
164
165
166 /**
167  * Destroy the given address.
168  *
169  * @param addr address to destroy
170  * @return GNUNET_YES if bandwidth allocations should be recalcualted
171  */
172 static int
173 destroy_address (struct ATS_Address *addr)
174 {
175   int ret;
176
177   ret = GNUNET_NO;
178   GNUNET_assert (GNUNET_YES ==
179                  GNUNET_CONTAINER_multihashmap_remove (addresses,
180                                                        &addr->peer.hashPubKey,
181                                                        addr));
182
183 #if HAVE_LIBGLPK
184   if (ats_mode == MLP)
185     GAS_mlp_address_delete (mlp, addresses, addr);
186 #endif
187
188   if (GNUNET_YES == addr->active)
189   {
190     active_addr_count--;
191     addr->active = GNUNET_NO;
192     ret = GNUNET_YES;
193   }
194   free_address (addr);
195   return ret;
196 }
197
198
199 struct CompareAddressContext
200 {
201   const struct ATS_Address *search;
202
203   /* exact_address != NULL if address and session is equal */
204   struct ATS_Address *exact_address;
205   /* exact_address != NULL if address and session is 0 */
206   struct ATS_Address *base_address;
207 };
208
209
210 static int
211 compare_address_it (void *cls, const GNUNET_HashCode * key, void *value)
212 {
213   struct CompareAddressContext *cac = cls;
214   struct ATS_Address *aa = value;
215 /*
216   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
217               "Comparing to: %s %s %u session %u\n",
218               GNUNET_i2s (&aa->peer), aa->plugin, aa->addr_len, aa->session_id);
219
220 */
221   /* find an exact matching address: aa->addr == cac->search->addr && aa->session == cac->search->session */
222   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
223   {
224       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
225       {
226         cac->exact_address = aa;
227       }
228   }
229
230   /* find an matching address: aa->addr == cac->search->addr && aa->session == 0 */
231   /* this address can be used to be updated */
232   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
233   {
234       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == 0))
235       {
236         cac->base_address = aa;
237       }
238   }
239
240   if (cac->exact_address == NULL)
241     return GNUNET_YES;
242   else
243     return GNUNET_NO;
244 }
245
246
247 /**
248  * Find an existing equivalent address record.
249  * Compares by peer identity and network address OR by session ID
250  * (one of the two must match).
251  *
252  * @param peer peer to lookup addresses for
253  * @param addr existing address record
254  * @return existing address record, NULL for none
255  */
256 struct ATS_Address *
257 find_address (const struct GNUNET_PeerIdentity *peer,
258               const struct ATS_Address *addr)
259 {
260   struct CompareAddressContext cac;
261
262   cac.exact_address = NULL;
263   cac.base_address = NULL;
264   cac.search = addr;
265   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
266                                               &compare_address_it, &cac);
267
268 /*
269   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270               "exact address: %s           base address: %s\n",
271               (cac.exact_address != NULL) ? "YES" : "NO",
272               (cac.base_address != NULL) ? "YES" : "NO");
273 */
274   if (cac.exact_address == NULL)
275     return cac.base_address;
276   return cac.exact_address;
277 }
278
279
280 static int
281 compare_address_session_it (void *cls, const GNUNET_HashCode * key, void *value)
282 {
283   struct CompareAddressContext *cac = cls;
284   struct ATS_Address *aa = value;
285
286   if ((aa->addr_len == cac->search->addr_len) && (0 == strcmp (aa->plugin, cac->search->plugin)))
287   {
288       if ((0 == memcmp (aa->addr, cac->search->addr, aa->addr_len)) && (aa->session_id == cac->search->session_id))
289       {
290         cac->exact_address = aa;
291         return GNUNET_NO;
292       }
293   }
294   return GNUNET_YES;
295 }
296
297
298 /**
299  * Find an existing equivalent address record.
300  * Compares by peer identity and network address AND by session ID
301  * (one of the two must match).
302  *
303  * @param peer peer to lookup addresses for
304  * @param addr existing address record
305  * @return existing address record, NULL for none
306  */
307 struct ATS_Address *
308 find_exact_address (const struct GNUNET_PeerIdentity *peer,
309               const struct ATS_Address *addr)
310 {
311   struct CompareAddressContext cac;
312
313   cac.exact_address = NULL;
314   cac.search = addr;
315   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
316                                               &compare_address_session_it, &cac);
317   return cac.exact_address;
318 }
319
320
321 void
322 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
323                       const char *plugin_name, const void *plugin_addr,
324                       size_t plugin_addr_len, uint32_t session_id,
325                       const struct GNUNET_ATS_Information *atsi,
326                       uint32_t atsi_count)
327 {
328   struct ATS_Address *aa;
329   struct ATS_Address *old;
330   uint32_t i;
331
332   aa = create_address (peer,
333                        plugin_name,
334                        plugin_addr, plugin_addr_len,
335                        session_id);
336
337   aa->mlp_information = NULL;
338   aa->ats = GNUNET_malloc (atsi_count * sizeof (struct GNUNET_ATS_Information));
339   aa->ats_count = atsi_count;
340   memcpy (aa->ats, atsi, atsi_count * sizeof (struct GNUNET_ATS_Information));
341
342 #if DEBUG_ATS
343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Updating address for peer `%s' %u\n",
344               GNUNET_i2s (peer),
345               session_id);
346 #endif
347   /* Get existing address or address with session == 0 */
348   old = find_address (peer, aa);
349   if (old == NULL)
350   {
351     GNUNET_assert (GNUNET_OK ==
352                    GNUNET_CONTAINER_multihashmap_put (addresses,
353                                                       &peer->hashPubKey, aa,
354                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
355 #if DEBUG_ATS
356     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Added new address for peer `%s' %X\n",
357                 GNUNET_i2s (peer), aa);
358 #endif
359     old = aa;
360   }
361   else
362   {
363 #if DEBUG_ATS
364       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
365                 "Updated existing address for peer `%s' %p old session %u new session %u\n",
366                 GNUNET_i2s (peer), old,
367                 old->session_id, session_id);
368 #endif
369     GNUNET_free_non_null (old->ats);
370     old->session_id = session_id;
371     old->ats = NULL;
372     old->ats_count = 0;
373     old->ats = aa->ats;
374     old->ats_count = aa->ats_count;
375     GNUNET_free (aa->plugin);
376     GNUNET_free (aa);
377   }
378   for (i = 0; i < atsi_count; i++)
379     switch (ntohl (atsi[i].type))
380     {
381     case GNUNET_ATS_UTILIZATION_UP:
382       old->atsp_utilization_out.value__ = atsi[i].value;
383       break;
384     case GNUNET_ATS_UTILIZATION_DOWN:
385       old->atsp_utilization_in.value__ = atsi[i].value;
386       break;
387     case GNUNET_ATS_QUALITY_NET_DELAY:
388       old->atsp_latency.rel_value = ntohl (atsi[i].value);
389       break;
390     case GNUNET_ATS_QUALITY_NET_DISTANCE:
391       old->atsp_distance = ntohl (atsi[i].value);
392       break;
393     case GNUNET_ATS_COST_WAN:
394       old->atsp_cost_wan = ntohl (atsi[i].value);
395       break;
396     case GNUNET_ATS_COST_LAN:
397       old->atsp_cost_lan = ntohl (atsi[i].value);
398       break;
399     case GNUNET_ATS_COST_WLAN:
400       old->atsp_cost_wlan = ntohl (atsi[i].value);
401       break;
402     case GNUNET_ATS_NETWORK_TYPE:
403       old->atsp_network_type = ntohl (atsi[i].value);
404       break;
405
406     default:
407       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
408                   "Received unsupported ATS type %u\n", ntohl (atsi[i].type));
409       GNUNET_break (0);
410       break;
411     }
412 #if HAVE_LIBGLPK
413   if (ats_mode == MLP)
414     GAS_mlp_address_update (mlp, addresses, old);
415 #endif
416 }
417
418
419 /**
420  * Delete an address
421  *
422  * If session != 0, just the session is deleted, the address itself still exists
423  * If session == 0, remove full address
424  * If session == 0 and addrlen == 0, destroy inbound address
425  *
426  * @param cls unused
427  * @param key unused
428  * @param value the 'struct ATS_Address'
429  * @return GNUNET_OK (continue to iterate)
430  */
431 static int
432 destroy_by_session_id (void *cls, const GNUNET_HashCode * key, void *value)
433 {
434   const struct ATS_Address *info = cls;
435   struct ATS_Address *aa = value;
436
437   GNUNET_assert (0 ==
438                  memcmp (&aa->peer, &info->peer,
439                          sizeof (struct GNUNET_PeerIdentity)));
440   /* session == 0, remove full address  */
441   if ((info->session_id == 0) && (0 == strcmp (info->plugin, aa->plugin)) &&
442       (aa->addr_len == info->addr_len) &&
443       (0 == memcmp (info->addr, aa->addr, aa->addr_len)))
444   {
445 #if VERBOSE
446     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
447                 "Deleting address for peer `%s': `%s' %u\n",
448                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
449 #endif
450     if (GNUNET_YES == destroy_address (aa))
451       recalculate_assigned_bw ();
452     return GNUNET_OK;
453   }
454   /* session != 0, just remove session */
455   if (aa->session_id != info->session_id)
456     return GNUNET_OK;           /* irrelevant */
457   if (aa->session_id != 0)
458     GNUNET_break (0 == strcmp (info->plugin, aa->plugin));
459   /* session died */
460 #if VERBOSE
461   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
462               "Deleting session for peer `%s': `%s' %u\n",
463               GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
464 #endif
465   aa->session_id = 0;
466
467   if (GNUNET_YES == aa->active)
468   {
469     aa->active = GNUNET_NO;
470     active_addr_count--;
471     recalculate_assigned_bw ();
472   }
473
474   /* session == 0 and addrlen == 0 : destroy address */
475   if (aa->addr_len == 0)
476   {
477 #if VERBOSE
478     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
479                 "Deleting session and address for peer `%s': `%s' %u\n",
480                 GNUNET_i2s (&aa->peer), aa->plugin, aa->session_id);
481 #endif
482     (void) destroy_address (aa);
483   }
484   else
485   {
486     /* session was set to 0, update address */
487 #if HAVE_LIBGLPK
488   if (ats_mode == MLP)
489     GAS_mlp_address_update (mlp, addresses, aa);
490 #endif
491   }
492
493   return GNUNET_OK;
494 }
495
496 void
497 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
498                        const char *plugin_name, const void *plugin_addr,
499                        size_t plugin_addr_len, uint32_t session_id)
500 {
501   struct ATS_Address *aa;
502
503   GNUNET_break (0 < strlen (plugin_name));
504   aa = create_address (peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
505
506   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
507                                               &destroy_by_session_id, aa);
508
509   free_address (aa);
510 }
511
512
513 /**
514  * Find a "good" address to use for a peer.  If we already have an existing
515  * address, we stick to it.  Otherwise, we pick by lowest distance and then
516  * by lowest latency.
517  *
518  * @param cls the 'struct ATS_Address**' where we store the result
519  * @param key unused
520  * @param value another 'struct ATS_Address*' to consider using
521  * @return GNUNET_OK (continue to iterate)
522  */
523 static int
524 find_address_it (void *cls, const GNUNET_HashCode * key, void *value)
525 {
526   struct ATS_Address **ap = cls;
527   struct ATS_Address *aa = (struct ATS_Address *) value;
528   struct ATS_Address *ab = *ap;
529
530   if (NULL == ab)
531   {
532     *ap = aa;
533     return GNUNET_OK;
534   }
535   if ((ntohl (ab->assigned_bw_in.value__) == 0) &&
536       (ntohl (aa->assigned_bw_in.value__) > 0))
537   {
538     /* stick to existing connection */
539     *ap = aa;
540     return GNUNET_OK;
541   }
542   if (ab->atsp_distance > aa->atsp_distance)
543   {
544     /* user shorter distance */
545     *ap = aa;
546     return GNUNET_OK;
547   }
548   if (ab->atsp_latency.rel_value > aa->atsp_latency.rel_value)
549   {
550     /* user lower latency */
551     *ap = aa;
552     return GNUNET_OK;
553   }
554   /* don't care */
555   return GNUNET_OK;
556 }
557
558
559 void
560 GAS_addresses_in_use (const struct GNUNET_PeerIdentity *peer,
561                       const char *plugin_name, const void *plugin_addr,
562                       size_t plugin_addr_len, uint32_t session_id, int in_use)
563 {
564 #if DEBUG_ATS
565   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
566               "Received `%s' message for peer `%s': %i\n", "ADDRESS_IN_USE",
567               GNUNET_i2s (peer), in_use);
568 #endif
569
570   struct ATS_Address *aa;
571   struct ATS_Address *old;
572
573
574   aa = create_address(peer, plugin_name, plugin_addr, plugin_addr_len, session_id);
575   old = find_exact_address (peer, aa);
576   free_address (aa);
577
578   GNUNET_assert (old != NULL);
579   GNUNET_assert (old->used != in_use);
580   old->used = in_use;
581
582 #if HAVE_LIBGLPK
583   if (ats_mode == MLP)
584      GAS_mlp_address_update (mlp, addresses, old);
585 #endif
586 }
587
588 void
589 GAS_addresses_request_address (const struct GNUNET_PeerIdentity *peer)
590 {
591   struct ATS_Address *aa;
592
593   aa = NULL;
594   GNUNET_CONTAINER_multihashmap_get_multiple (addresses, &peer->hashPubKey,
595                                               &find_address_it, &aa);
596   if (aa == NULL)
597   {
598     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
599                 "Cannot suggest address for peer `%s'\n", GNUNET_i2s (peer));
600     return;
601   }
602   if (aa->active == GNUNET_NO)
603   {
604     aa->active = GNUNET_YES;
605     active_addr_count++;
606     recalculate_assigned_bw ();
607   }
608   else
609   {
610     /* just to be sure... */
611     GAS_scheduling_transmit_address_suggestion (peer, aa->plugin, aa->addr,
612                                                 aa->addr_len, aa->session_id,
613                                                 aa->ats, aa->ats_count,
614                                                 aa->assigned_bw_out,
615                                                 aa->assigned_bw_in);
616   }
617 }
618
619
620 // FIXME: this function should likely end up in the LP-subsystem and
621 // not with 'addresses' in the future...
622 void
623 GAS_addresses_change_preference (const struct GNUNET_PeerIdentity *peer,
624                                  enum GNUNET_ATS_PreferenceKind kind,
625                                  float score)
626 {
627 #if HAVE_LIBGLPK
628   if (ats_mode == MLP)
629     GAS_mlp_address_change_preference (mlp, peer, kind, score);
630 #endif
631 }
632
633
634
635 /**
636  * Initialize address subsystem.
637  *
638  * @param cfg configuration to use
639  * @param stats the statistics handle to use
640  */
641 void
642 GAS_addresses_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
643                     const struct GNUNET_STATISTICS_Handle *stats)
644 {
645   GNUNET_assert (GNUNET_OK ==
646                  GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
647                                                       "WAN_QUOTA_IN",
648                                                       &wan_quota_in));
649   GNUNET_assert (GNUNET_OK ==
650                  GNUNET_CONFIGURATION_get_value_size (cfg, "ats",
651                                                       "WAN_QUOTA_OUT",
652                                                       &wan_quota_out));
653
654   switch (GNUNET_CONFIGURATION_get_value_yesno (cfg, "ats", "MLP"))
655   {
656         /* MLP = YES */
657         case GNUNET_YES:
658 #if HAVE_LIBGLPK
659           ats_mode = MLP;
660           /* Init the MLP solver with default values */
661           mlp = GAS_mlp_init (cfg, stats, MLP_MAX_EXEC_DURATION, MLP_MAX_ITERATIONS);
662           break;
663 #else
664
665           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MLP mode was configured, but libglpk is not installed, switching to simple mode");
666           ats_mode = SIMPLE;
667           break;
668 #endif
669         /* MLP = NO */
670         case GNUNET_NO:
671                 ats_mode = SIMPLE;
672                 break;
673         /* No configuration value */
674         case GNUNET_SYSERR:
675                 ats_mode = SIMPLE;
676                 break;
677         default:
678                 break;
679   }
680
681   addresses = GNUNET_CONTAINER_multihashmap_create (128);
682 }
683
684
685 /**
686  * Free memory of address.
687  *
688  * @param cls NULL
689  * @param key peer identity (unused)
690  * @param value the 'struct ATS_Address' to free
691  * @return GNUNET_OK (continue to iterate)
692  */
693 static int
694 free_address_it (void *cls, const GNUNET_HashCode * key, void *value)
695 {
696   struct ATS_Address *aa = value;
697
698   destroy_address (aa);
699   return GNUNET_OK;
700 }
701
702
703 void
704 GAS_addresses_destroy_all ()
705 {
706   if (addresses != NULL)
707     GNUNET_CONTAINER_multihashmap_iterate (addresses, &free_address_it, NULL);
708   GNUNET_assert (active_addr_count == 0);
709 }
710
711
712 /**
713  * Shutdown address subsystem.
714  */
715 void
716 GAS_addresses_done ()
717 {
718   GAS_addresses_destroy_all ();
719   GNUNET_CONTAINER_multihashmap_destroy (addresses);
720   addresses = NULL;
721 #if HAVE_LIBGLPK
722   if (ats_mode == MLP)
723   {
724     GAS_mlp_done (mlp);
725   }
726 #endif
727
728 }
729
730
731 /* end of gnunet-service-ats_addresses.c */