17f030a6cadfb1d832a5250047415309218c5035
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2015 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-service-ats_addresses.h"
29 #include "gnunet-service-ats_performance.h"
30 #include "gnunet-service-ats_normalization.h"
31 #include "gnunet-service-ats_plugins.h"
32
33
34 /**
35  * A multihashmap to store all addresses
36  */
37 struct GNUNET_CONTAINER_MultiPeerMap *GSA_addresses;
38
39
40 /**
41  * Update statistic on number of addresses.
42  */
43 static void
44 update_addresses_stat ()
45 {
46   GNUNET_STATISTICS_set (GSA_stats,
47                          "# addresses",
48                          GNUNET_CONTAINER_multipeermap_size (GSA_addresses),
49                          GNUNET_NO);
50 }
51
52
53 /**
54  * Free the given address
55  *
56  * @param addr address to destroy
57  */
58 static void
59 free_address (struct ATS_Address *addr)
60 {
61   GNUNET_CONTAINER_multipeermap_remove (GSA_addresses,
62                                         &addr->peer,
63                                         addr);
64   update_addresses_stat ();
65   GAS_plugin_delete_address (addr);
66   GAS_performance_notify_all_clients (&addr->peer,
67                                       addr->plugin,
68                                       addr->addr,
69                                       addr->addr_len,
70                                       GNUNET_NO,
71                                       NULL,
72                                       GNUNET_BANDWIDTH_ZERO,
73                                       GNUNET_BANDWIDTH_ZERO);
74   GNUNET_free (addr->plugin);
75   GNUNET_free (addr);
76 }
77
78
79 /**
80  * Initialize @a norm.  Sets all historic values to undefined.
81  *
82  * @param norm normalization data to initialize
83  */
84 static void
85 init_norm (struct GAS_NormalizationInfo *norm)
86 {
87   unsigned int c;
88
89   for (c = 0; c < GAS_normalization_queue_length; c++)
90     norm->atsi_abs[c] = UINT64_MAX;
91 }
92
93
94 /**
95  * Create a ATS_address with the given information
96  *
97  * @param peer peer
98  * @param plugin_name plugin
99  * @param plugin_addr address
100  * @param plugin_addr_len address length
101  * @param local_address_info additional local info for the address
102  * @param session_id session identifier, can never be 0
103  * @return the ATS_Address
104  */
105 static struct ATS_Address *
106 create_address (const struct GNUNET_PeerIdentity *peer,
107                 const char *plugin_name,
108                 const void *plugin_addr,
109                 size_t plugin_addr_len,
110                 uint32_t local_address_info,
111                 uint32_t session_id)
112 {
113   struct ATS_Address *aa;
114
115   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
116   aa->peer = *peer;
117   aa->addr_len = plugin_addr_len;
118   aa->addr = &aa[1];
119   memcpy (&aa[1],
120           plugin_addr,
121           plugin_addr_len);
122   aa->plugin = GNUNET_strdup (plugin_name);
123   aa->session_id = session_id;
124   aa->local_address_info = local_address_info;
125   init_norm (&aa->norm_delay);
126   init_norm (&aa->norm_distance);
127   init_norm (&aa->norm_utilization_in);
128   init_norm (&aa->norm_utilization_out);
129   return aa;
130 }
131
132
133 /**
134  * Closure for #find_address_cb()
135  */
136 struct FindAddressContext
137 {
138   /**
139    * Session Id to look for.
140    */
141   uint32_t session_id;
142
143   /**
144    * Where to store matching address result.
145    */
146   struct ATS_Address *exact_address;
147
148 };
149
150
151 /**
152  * Find session matching given session ID.
153  *
154  * @param cls a `struct FindAddressContext`
155  * @param key peer id
156  * @param value the address to compare with
157  * @return #GNUNET_YES to continue, #GNUNET_NO if address is found
158  */
159 static int
160 find_address_cb (void *cls,
161                  const struct GNUNET_PeerIdentity *key,
162                  void *value)
163 {
164   struct FindAddressContext *fac = cls;
165   struct ATS_Address *aa = value;
166
167   if (aa->session_id == fac->session_id)
168   {
169     fac->exact_address = aa;
170     return GNUNET_NO;
171   }
172   return GNUNET_YES;
173 }
174
175
176 /**
177  * Find the exact address
178  *
179  * @param peer peer
180  * @param session_id session id, can never be 0
181  * @return an ATS_address or NULL
182  */
183 static struct ATS_Address *
184 find_exact_address (const struct GNUNET_PeerIdentity *peer,
185                     uint32_t session_id)
186 {
187   struct FindAddressContext fac;
188
189   fac.exact_address = NULL;
190   fac.session_id = session_id;
191   GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
192                                               peer,
193                                               &find_address_cb, &fac);
194   return fac.exact_address;
195 }
196
197
198 /**
199  * Add a new address for a peer.
200  *
201  * @param peer peer
202  * @param plugin_name transport plugin name
203  * @param plugin_addr plugin address
204  * @param plugin_addr_len length of the plugin address in @a plugin_addr
205  * @param local_address_info the local address for the address
206  * @param session_id session id, can be 0
207  * @param prop performance information for this address
208  */
209 void
210 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
211                    const char *plugin_name,
212                    const void *plugin_addr,
213                    size_t plugin_addr_len,
214                    uint32_t local_address_info,
215                    uint32_t session_id,
216                    const struct GNUNET_ATS_Properties *prop)
217 {
218   struct ATS_Address *new_address;
219
220   if (NULL != find_exact_address (peer,
221                                   session_id))
222   {
223     GNUNET_break (0);
224     return;
225   }
226   new_address = create_address (peer,
227                                 plugin_name,
228                                 plugin_addr,
229                                 plugin_addr_len,
230                                 local_address_info,
231                                 session_id);
232   /* Add a new address */
233   new_address->properties = *prop;
234   new_address->t_added = GNUNET_TIME_absolute_get();
235   new_address->t_last_activity = GNUNET_TIME_absolute_get();
236   GNUNET_assert(GNUNET_OK ==
237                 GNUNET_CONTAINER_multipeermap_put (GSA_addresses,
238                                                    peer,
239                                                    new_address,
240                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
241   update_addresses_stat ();
242   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
243               "Adding new address for peer `%s' slot %u\n",
244               GNUNET_i2s (peer),
245               session_id);
246   /* Tell solver about new address */
247   GAS_plugin_solver_lock ();
248   GAS_plugin_new_address (new_address);
249   GAS_normalization_update_property (new_address); // FIXME: needed?
250   GAS_plugin_solver_unlock ();
251   /* Notify performance clients about new address */
252   GAS_performance_notify_all_clients (&new_address->peer,
253                                       new_address->plugin,
254                                       new_address->addr,
255                                       new_address->addr_len,
256                                       new_address->active,
257                                       &new_address->properties,
258                                       GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_out),
259                                       GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_in));
260 }
261
262
263 /**
264  * Update an address with new performance information for a peer.
265  *
266  * @param peer peer
267  * @param session_id session id, never 0
268  * @param prop performance information for this address
269  */
270 void
271 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
272                       uint32_t session_id,
273                       const struct GNUNET_ATS_Properties *prop)
274 {
275   struct ATS_Address *aa;
276
277   /* Get existing address */
278   aa = find_exact_address (peer,
279                            session_id);
280   if (NULL == aa)
281   {
282     GNUNET_break (0);
283     return;
284   }
285   if (NULL == aa->solver_information)
286   {
287     GNUNET_break (0);
288     return;
289   }
290   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
291               "Received ADDRESS_UPDATE for peer `%s' slot %u\n",
292               GNUNET_i2s (peer),
293               (unsigned int) session_id);
294
295   /* Update address */
296   aa->t_last_activity = GNUNET_TIME_absolute_get();
297   aa->properties = *prop;
298   /* Notify performance clients about updated address */
299   GAS_performance_notify_all_clients (&aa->peer,
300                                       aa->plugin,
301                                       aa->addr,
302                                       aa->addr_len,
303                                       aa->active,
304                                       prop,
305                                       GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
306                                       GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
307
308   GAS_normalization_update_property (aa);
309 }
310
311
312 /**
313  * Remove an address for a peer.
314  *
315  * @param peer peer
316  * @param session_id session id, can never be 0
317  */
318 void
319 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
320                        uint32_t session_id)
321 {
322   struct ATS_Address *ea;
323
324   /* Get existing address */
325   ea = find_exact_address (peer,
326                            session_id);
327   if (NULL == ea)
328   {
329     GNUNET_break (0);
330     return;
331   }
332   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333               "Received ADDRESS_DESTROYED for peer `%s' session %u\n",
334               GNUNET_i2s (peer),
335               session_id);
336   free_address (ea);
337 }
338
339
340 /**
341  * Initialize address subsystem. The addresses subsystem manages the addresses
342  * known and current performance information. It has a solver component
343  * responsible for the resource allocation. It tells the solver about changes
344  * and receives updates when the solver changes the resource allocation.
345  *
346  * @param server handle to our server
347  */
348 void
349 GAS_addresses_init (struct GNUNET_SERVER_Handle *server)
350 {
351   GSA_addresses = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
352   update_addresses_stat ();
353 }
354
355
356 /**
357  * Destroy all addresses iterator
358  *
359  * @param cls NULL
360  * @param key peer identity (unused)
361  * @param value the 'struct ATS_Address' to free
362  * @return #GNUNET_OK (continue to iterate)
363  */
364 static int
365 destroy_all_address_it (void *cls,
366                         const struct GNUNET_PeerIdentity *key,
367                         void *value)
368 {
369   struct ATS_Address *aa = value;
370
371   free_address (aa);
372   return GNUNET_OK;
373 }
374
375
376 /**
377  * Remove all addresses
378  */
379 void
380 GAS_addresses_destroy_all ()
381 {
382   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
383               "Destroying all addresses\n");
384   if (0 ==
385       GNUNET_CONTAINER_multipeermap_size (GSA_addresses))
386     return;
387   GAS_plugin_solver_lock ();
388   GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
389                                          &destroy_all_address_it,
390                                          NULL);
391   GAS_plugin_solver_unlock ();
392 }
393
394
395 /**
396  * Shutdown address subsystem.
397  */
398 void
399 GAS_addresses_done ()
400 {
401   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
402              "Shutting down addresses\n");
403   GAS_addresses_destroy_all ();
404   GNUNET_CONTAINER_multipeermap_destroy (GSA_addresses);
405   GSA_addresses = NULL;
406 }
407
408
409 /**
410  * Closure for #peerinfo_it().
411  */
412 struct PeerInfoIteratorContext
413 {
414   /**
415    * Function to call for each address.
416    */
417   GNUNET_ATS_PeerInfo_Iterator it;
418
419   /**
420    * Closure for @e it.
421    */
422   void *it_cls;
423 };
424
425
426 /**
427  * Iterator to iterate over a peer's addresses
428  *
429  * @param cls a `struct PeerInfoIteratorContext`
430  * @param key the peer id
431  * @param value the `struct ATS_address`
432  * @return #GNUNET_OK to continue
433  */
434 static int
435 peerinfo_it (void *cls,
436              const struct GNUNET_PeerIdentity *key,
437              void *value)
438 {
439   struct PeerInfoIteratorContext *pi_ctx = cls;
440   struct ATS_Address *addr = value;
441
442   pi_ctx->it (pi_ctx->it_cls,
443               &addr->peer,
444               addr->plugin,
445               addr->addr,
446               addr->addr_len,
447               addr->active,
448               &addr->properties,
449               GNUNET_BANDWIDTH_value_init (addr->assigned_bw_out),
450               GNUNET_BANDWIDTH_value_init (addr->assigned_bw_in));
451   return GNUNET_OK;
452 }
453
454
455 /**
456  * Return information all peers currently known to ATS
457  *
458  * @param peer the respective peer, NULL for 'all' peers
459  * @param pi_it the iterator to call for every peer
460  * @param pi_it_cls the closure for @a pi_it
461  */
462 void
463 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer,
464                              GNUNET_ATS_PeerInfo_Iterator pi_it,
465                              void *pi_it_cls)
466 {
467   struct PeerInfoIteratorContext pi_ctx;
468
469   if (NULL == pi_it)
470   {
471     /* does not make sense without callback */
472     GNUNET_break (0);
473     return;
474   }
475   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476               "Returning information for %s from a total of %u known addresses\n",
477               (NULL == peer)
478               ? "all peers"
479               : GNUNET_i2s (peer),
480               (unsigned int) GNUNET_CONTAINER_multipeermap_size (GSA_addresses));
481   pi_ctx.it = pi_it;
482   pi_ctx.it_cls = pi_it_cls;
483   if (NULL == peer)
484     GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
485                                            &peerinfo_it,
486                                            &pi_ctx);
487   else
488     GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
489                                                 peer,
490                                                 &peerinfo_it, &pi_ctx);
491   pi_it (pi_it_cls,
492          NULL, NULL, NULL, 0,
493          GNUNET_NO,
494          NULL,
495          GNUNET_BANDWIDTH_ZERO,
496          GNUNET_BANDWIDTH_ZERO);
497 }
498
499
500 /**
501  * Information we need for the callbacks to return a list of addresses
502  * back to the client.
503  */
504 struct AddressIteration
505 {
506   /**
507    * Actual handle to the client.
508    */
509   struct GNUNET_SERVER_Client *client;
510
511   /**
512    * Are we sending all addresses, or only those that are active?
513    */
514   int all;
515
516   /**
517    * Which ID should be included in the response?
518    */
519   uint32_t id;
520
521 };
522
523
524 /**
525  * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
526  * given address details to the client identified in @a ai.
527  *
528  * @param ai our address information context (identifies the client)
529  * @param id the peer id this address is for
530  * @param plugin_name name of the plugin that supports this address
531  * @param plugin_addr address
532  * @param plugin_addr_len length of @a plugin_addr
533  * @param active #GNUNET_YES if this address is actively used
534  * @param prop performance information
535  * @param bandwidth_out current outbound bandwidth assigned to address
536  * @param bandwidth_in current inbound bandwidth assigned to address
537  */
538 static void
539 transmit_req_addr (struct AddressIteration *ai,
540                    const struct GNUNET_PeerIdentity *id,
541                    const char *plugin_name,
542                    const void *plugin_addr,
543                    size_t plugin_addr_len,
544                    int active,
545                    const struct GNUNET_ATS_Properties *prop,
546                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
547                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
548
549 {
550   struct PeerInformationMessage *msg;
551   char *addrp;
552   size_t plugin_name_length;
553   size_t msize;
554   struct GNUNET_SERVER_NotificationContext *nc;
555
556   if (NULL != plugin_name)
557     plugin_name_length = strlen (plugin_name) + 1;
558   else
559     plugin_name_length = 0;
560   msize = sizeof (struct PeerInformationMessage) +
561           plugin_addr_len + plugin_name_length;
562   char buf[msize] GNUNET_ALIGN;
563
564   GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
565   msg = (struct PeerInformationMessage *) buf;
566   msg->header.size = htons (msize);
567   msg->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE);
568   msg->id = htonl (ai->id);
569   if (NULL != id)
570     msg->peer = *id;
571   else
572     memset (&msg->peer, '\0', sizeof (struct GNUNET_PeerIdentity));
573   msg->address_length = htons (plugin_addr_len);
574   msg->address_active = ntohl (active);
575   msg->plugin_name_length = htons (plugin_name_length);
576   msg->bandwidth_out = bandwidth_out;
577   msg->bandwidth_in = bandwidth_in;
578   GNUNET_ATS_properties_hton (&msg->properties,
579                               prop);
580   addrp = (char *) &msg[1];
581   if (NULL != plugin_addr)
582     memcpy (addrp, plugin_addr, plugin_addr_len);
583   if (NULL != plugin_name)
584     strcpy (&addrp[plugin_addr_len], plugin_name);
585   nc = *GNUNET_SERVER_client_get_user_context (ai->client,
586                                                struct GNUNET_SERVER_NotificationContext *);
587   if (NULL == nc)
588   {
589     GNUNET_break (0);
590     return;
591   }
592   GNUNET_SERVER_notification_context_unicast (nc,
593                                               ai->client,
594                                               &msg->header,
595                                               GNUNET_NO);
596 }
597
598
599 /**
600  * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
601  * information to be passed back to the client.
602  *
603  * @param cls closure with our `struct AddressIteration *`
604  * @param id the peer id
605  * @param plugin_name plugin name
606  * @param plugin_addr address
607  * @param plugin_addr_len length of @a plugin_addr
608  * @param active is address actively used
609  * @param prop performance information
610  * @param bandwidth_out current outbound bandwidth assigned to address
611  * @param bandwidth_in current inbound bandwidth assigned to address
612  */
613 static void
614 req_addr_peerinfo_it (void *cls,
615                       const struct GNUNET_PeerIdentity *id,
616                       const char *plugin_name,
617                       const void *plugin_addr,
618                       size_t plugin_addr_len,
619                       int active,
620                       const struct GNUNET_ATS_Properties *prop,
621                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
622                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
623 {
624   struct AddressIteration *ai = cls;
625
626   if ( (NULL == id) &&
627        (NULL == plugin_name) &&
628        (NULL == plugin_addr) )
629   {
630     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
631                 "Address iteration done for one peer\n");
632     return;
633   }
634   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
635               "Callback for %s peer `%s' plugin `%s' BW out %u, BW in %u\n",
636               (active == GNUNET_YES) ? "ACTIVE" : "INACTIVE",
637               GNUNET_i2s (id),
638               plugin_name,
639               (unsigned int) ntohl (bandwidth_out.value__),
640               (unsigned int) ntohl (bandwidth_in.value__));
641   /* Transmit result (either if address is active, or if
642      client wanted all addresses) */
643   if ( (GNUNET_YES != ai->all) &&
644        (GNUNET_YES != active))
645     return;
646   transmit_req_addr (ai,
647                      id,
648                      plugin_name,
649                      plugin_addr, plugin_addr_len,
650                      active,
651                      prop,
652                      bandwidth_out,
653                      bandwidth_in);
654 }
655
656
657 /**
658  * Handle 'address list request' messages from clients.
659  *
660  * @param cls unused, NULL
661  * @param client client that sent the request
662  * @param message the request message
663  */
664 void
665 GAS_handle_request_address_list (void *cls,
666                                  struct GNUNET_SERVER_Client *client,
667                                  const struct GNUNET_MessageHeader *message)
668 {
669   struct AddressIteration ai;
670   const struct AddressListRequestMessage *alrm;
671   struct GNUNET_PeerIdentity allzeros;
672
673   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
674               "Received ADDRESSLIST_REQUEST message\n");
675   alrm = (const struct AddressListRequestMessage *) message;
676   ai.all = ntohl (alrm->all);
677   ai.id = ntohl (alrm->id);
678   ai.client = client;
679
680   memset (&allzeros,
681           '\0',
682           sizeof (struct GNUNET_PeerIdentity));
683   if (0 == memcmp (&alrm->peer,
684                    &allzeros,
685                    sizeof (struct GNUNET_PeerIdentity)))
686   {
687     /* Return addresses for all peers */
688     GAS_addresses_get_peer_info (NULL,
689                                  &req_addr_peerinfo_it,
690                                  &ai);
691   }
692   else
693   {
694     /* Return addresses for a specific peer */
695     GAS_addresses_get_peer_info (&alrm->peer,
696                                  &req_addr_peerinfo_it,
697                                  &ai);
698   }
699   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700               "Finished handling `%s' message\n",
701               "ADDRESSLIST_REQUEST");
702   transmit_req_addr (&ai,
703                      NULL, NULL, NULL,
704                      0, GNUNET_NO,
705                      NULL,
706                      GNUNET_BANDWIDTH_ZERO,
707                      GNUNET_BANDWIDTH_ZERO);
708   GNUNET_SERVER_receive_done (client,
709                               GNUNET_OK);
710 }
711
712
713
714 /* end of gnunet-service-ats_addresses.c */