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