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