big ATS refactoring, no serious semantic changes should stem from this
[oweals/gnunet.git] / src / ats / gnunet-service-ats_performance.c
1 /*
2      This file is part of GNUnet.
3      (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_performance.c
23  * @brief ats service, interaction with 'performance' API
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet-service-ats.h"
29 #include "gnunet-service-ats_addresses.h"
30 #include "gnunet-service-ats_performance.h"
31 #include "gnunet-service-ats_reservations.h"
32 #include "ats.h"
33
34 /**
35  * We keep clients that are interested in performance in a linked list.
36  */
37 struct PerformanceClient
38 {
39   /**
40    * Next in doubly-linked list.
41    */
42   struct PerformanceClient *next;
43
44   /**
45    * Previous in doubly-linked list.
46    */
47   struct PerformanceClient *prev;
48
49   /**
50    * Actual handle to the client.
51    */
52   struct GNUNET_SERVER_Client *client;
53
54   /**
55    * Options for the client.
56    */
57   enum StartFlag flag;
58
59 };
60
61
62 /**
63  * Head of linked list of all clients to this service.
64  */
65 static struct PerformanceClient *pc_head;
66
67 /**
68  * Tail of linked list of all clients to this service.
69  */
70 static struct PerformanceClient *pc_tail;
71
72
73 /**
74  * Context for sending messages to performance clients.
75  */
76 static struct GNUNET_SERVER_NotificationContext *nc;
77
78
79 /**
80  * Find the performance client associated with the given handle.
81  *
82  * @param client server handle
83  * @return internal handle
84  */
85 static struct PerformanceClient *
86 find_client (struct GNUNET_SERVER_Client *client)
87 {
88   struct PerformanceClient *pc;
89
90   for (pc = pc_head; pc != NULL; pc = pc->next)
91     if (pc->client == client)
92       return pc;
93   return NULL;
94 }
95
96 /**
97  * Unregister a client (which may have been a performance client,
98  * but this is not assured).
99  *
100  * @param client handle of the (now dead) client
101  */
102 void
103 GAS_performance_remove_client (struct GNUNET_SERVER_Client *client)
104 {
105   struct PerformanceClient *pc;
106
107   pc = find_client (client);
108   if (NULL == pc)
109     return;
110   GNUNET_CONTAINER_DLL_remove (pc_head, pc_tail, pc);
111   GNUNET_free (pc);
112 }
113
114
115 /**
116  * Transmit the given performance information to all performance
117  * clients.
118  *
119  * @param pc performance client to send to
120  * @param peer peer for which this is an address suggestion
121  * @param plugin_name 0-termintated string specifying the transport plugin
122  * @param plugin_addr binary address for the plugin to use
123  * @param plugin_addr_len number of bytes in plugin_addr
124  * @param active #GNUNET_YES if this address is actively used
125  *        to maintain a connection to a peer;
126  *        #GNUNET_NO if the address is not actively used;
127  *        #GNUNET_SYSERR if this address is no longer available for ATS
128  * @param atsi performance data for the address
129  * @param atsi_count number of performance records in @a atsi
130  * @param bandwidth_out assigned outbound bandwidth
131  * @param bandwidth_in assigned inbound bandwidth
132  */
133 void
134 GAS_performance_notify_client (struct PerformanceClient *pc,
135                                const struct GNUNET_PeerIdentity *peer,
136                                const char *plugin_name,
137                                const void *plugin_addr,
138                                size_t plugin_addr_len,
139                                int active,
140                                const struct GNUNET_ATS_Information *atsi,
141                                uint32_t atsi_count,
142                                struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
143                                struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
144 {
145
146   struct PeerInformationMessage *msg;
147   size_t plugin_name_length = strlen (plugin_name) + 1;
148   size_t msize =
149       sizeof (struct PeerInformationMessage) +
150       atsi_count * sizeof (struct GNUNET_ATS_Information) + plugin_addr_len +
151       plugin_name_length;
152   char buf[msize] GNUNET_ALIGN;
153   struct GNUNET_ATS_Information *atsp;
154   char *addrp;
155
156   GNUNET_assert (NULL != pc);
157   if (NULL == find_client (pc->client))
158     return; /* Client disconnected */
159
160   GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
161   GNUNET_assert (atsi_count <
162                  GNUNET_SERVER_MAX_MESSAGE_SIZE /
163                  sizeof (struct GNUNET_ATS_Information));
164   msg = (struct PeerInformationMessage *) buf;
165   msg->header.size = htons (msize);
166   msg->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_PEER_INFORMATION);
167   msg->id = htonl (0);
168   msg->ats_count = htonl (atsi_count);
169   msg->peer = *peer;
170   msg->address_length = htons (plugin_addr_len);
171   msg->address_active = ntohl ((uint32_t) active);
172   msg->plugin_name_length = htons (plugin_name_length);
173   msg->bandwidth_out = bandwidth_out;
174   msg->bandwidth_in = bandwidth_in;
175   atsp = (struct GNUNET_ATS_Information *) &msg[1];
176   memcpy (atsp, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count);
177   addrp = (char *) &atsp[atsi_count];
178   memcpy (addrp, plugin_addr, plugin_addr_len);
179   strcpy (&addrp[plugin_addr_len], plugin_name);
180   GNUNET_SERVER_notification_context_unicast (nc,
181                                               pc->client,
182                                               &msg->header,
183                                               GNUNET_YES);
184 }
185
186
187 /**
188  * Transmit the given performance information to all performance
189  * clients.
190  *
191  * @param peer peer for which this is an address suggestion
192  * @param plugin_name 0-termintated string specifying the transport plugin
193  * @param plugin_addr binary address for the plugin to use
194  * @param plugin_addr_len number of bytes in @a plugin_addr
195  * @param active #GNUNET_YES if this address is actively used
196  *        to maintain a connection to a peer;
197  *        #GNUNET_NO if the address is not actively used;
198  *        #GNUNET_SYSERR if this address is no longer available for ATS
199  * @param atsi performance data for the address
200  * @param atsi_count number of performance records in @a atsi
201  * @param bandwidth_out assigned outbound bandwidth
202  * @param bandwidth_in assigned inbound bandwidth
203  */
204 void
205 GAS_performance_notify_all_clients (const struct GNUNET_PeerIdentity *peer,
206                                     const char *plugin_name,
207                                     const void *plugin_addr,
208                                     size_t plugin_addr_len,
209                                     int active,
210                                     const struct GNUNET_ATS_Information *atsi,
211                                     uint32_t atsi_count,
212                                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
213                                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
214 {
215   struct PerformanceClient *pc;
216
217   for (pc = pc_head; pc != NULL; pc = pc->next)
218     if (pc->flag == START_FLAG_PERFORMANCE_WITH_PIC)
219     {
220         GAS_performance_notify_client (pc,
221                                        peer,
222                                        plugin_name,
223                                        plugin_addr,
224                                        plugin_addr_len,
225                                        active,
226                                        atsi, atsi_count,
227                                        bandwidth_out, bandwidth_in);
228     }
229   GNUNET_STATISTICS_update (GSA_stats,
230                             "# performance updates given to clients", 1,
231                             GNUNET_NO);
232 }
233
234
235
236 /**
237  * Iterator for called from #GAS_addresses_get_peer_info()
238  *
239  * @param cls closure with the `struct PerformanceClient *`
240  * @param id the peer id
241  * @param plugin_name plugin name
242  * @param plugin_addr address
243  * @param plugin_addr_len length of @a plugin_addr
244  * @param active is address actively used
245  * @param atsi ats performance information
246  * @param atsi_count number of ats performance elements in @a atsi
247  * @param bandwidth_out current outbound bandwidth assigned to address
248  * @param bandwidth_in current inbound bandwidth assigned to address
249  */
250 static void
251 peerinfo_it (void *cls,
252              const struct GNUNET_PeerIdentity *id,
253              const char *plugin_name,
254              const void *plugin_addr,
255              size_t plugin_addr_len,
256              int active,
257              const struct GNUNET_ATS_Information *atsi,
258              uint32_t atsi_count,
259              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
260              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
261 {
262   struct PerformanceClient *pc = cls;
263
264   GNUNET_assert (NULL != pc);
265   if (NULL == id)
266     return;
267   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268               "Callback for peer `%s' plugin `%s' BW out %u, BW in %u \n",
269               GNUNET_i2s (id),
270               plugin_name,
271               (unsigned int) ntohl (bandwidth_out.value__),
272               (unsigned int) ntohl (bandwidth_in.value__));
273   GAS_performance_notify_client (pc,
274                                  id,
275                                  plugin_name,
276                                  plugin_addr,
277                                  plugin_addr_len,
278                                  active,
279                                  atsi, atsi_count,
280                                  bandwidth_out,
281                                  bandwidth_in);
282 }
283
284
285 /**
286  * Register a new performance client.
287  *
288  * @param client handle of the new client
289  * @param flag flag specifying the type of the client
290  */
291 void
292 GAS_performance_add_client (struct GNUNET_SERVER_Client *client,
293                             enum StartFlag flag)
294 {
295   struct PerformanceClient *pc;
296
297   GNUNET_break (NULL == find_client (client));
298   pc = GNUNET_new (struct PerformanceClient);
299   pc->client = client;
300   pc->flag = flag;
301
302   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303               "Adding performance client %s PIC\n",
304               (flag == START_FLAG_PERFORMANCE_WITH_PIC) ? "with" : "without");
305
306   GNUNET_SERVER_notification_context_add (nc,
307                                           client);
308   GNUNET_CONTAINER_DLL_insert (pc_head,
309                                pc_tail,
310                                pc);
311   GAS_addresses_get_peer_info (NULL,
312                                &peerinfo_it,
313                                pc);
314 }
315
316
317 /**
318  * Information we need for the callbacks to return a list of addresses
319  * back to the client.
320  */
321 struct AddressIteration
322 {
323   /**
324    * Actual handle to the client.
325    */
326   struct PerformanceClient *pc;
327
328   /**
329    * Are we sending all addresses, or only those that are active?
330    */
331   int all;
332
333   /**
334    * Which ID should be included in the response?
335    */
336   uint32_t id;
337
338 };
339
340
341 /**
342  * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
343  * given address details to the client identified in @a ai.
344  *
345  * @param ai our address information context (identifies the client)
346  * @param id the peer id this address is for
347  * @param plugin_name name of the plugin that supports this address
348  * @param plugin_addr address
349  * @param plugin_addr_len length of @a plugin_addr
350  * @param active #GNUNET_YES if this address is actively used
351  * @param atsi ats performance information
352  * @param atsi_count number of ats performance elements in @a atsi
353  * @param bandwidth_out current outbound bandwidth assigned to address
354  * @param bandwidth_in current inbound bandwidth assigned to address
355  */
356 static void
357 transmit_req_addr (struct AddressIteration *ai,
358                    const struct GNUNET_PeerIdentity *id,
359                    const char *plugin_name,
360                    const void *plugin_addr,
361                    size_t plugin_addr_len,
362                    int active,
363                    const struct GNUNET_ATS_Information *atsi,
364                    uint32_t atsi_count,
365                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
366                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
367
368 {
369   struct GNUNET_ATS_Information *atsp;
370   struct PeerInformationMessage *msg;
371   char *addrp;
372   size_t plugin_name_length;
373   size_t msize;
374
375   if (NULL != plugin_name)
376     plugin_name_length = strlen (plugin_name) + 1;
377   else
378     plugin_name_length = 0;
379   msize = sizeof (struct PeerInformationMessage) +
380           atsi_count * sizeof (struct GNUNET_ATS_Information) +
381           plugin_addr_len + plugin_name_length;
382   char buf[msize] GNUNET_ALIGN;
383
384   GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
385   GNUNET_assert (atsi_count <
386                  GNUNET_SERVER_MAX_MESSAGE_SIZE /
387                  sizeof (struct GNUNET_ATS_Information));
388   msg = (struct PeerInformationMessage *) buf;
389   msg->header.size = htons (msize);
390   msg->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE);
391   msg->ats_count = htonl (atsi_count);
392   msg->id = htonl (ai->id);
393   if (NULL != id)
394     msg->peer = *id;
395   else
396     memset (&msg->peer, '\0', sizeof (struct GNUNET_PeerIdentity));
397   msg->address_length = htons (plugin_addr_len);
398   msg->address_active = ntohl (active);
399   msg->plugin_name_length = htons (plugin_name_length);
400   msg->bandwidth_out = bandwidth_out;
401   msg->bandwidth_in = bandwidth_in;
402   atsp = (struct GNUNET_ATS_Information *) &msg[1];
403   memcpy (atsp, atsi, sizeof (struct GNUNET_ATS_Information) * atsi_count);
404   addrp = (char *) &atsp[atsi_count];
405   if (NULL != plugin_addr)
406     memcpy (addrp, plugin_addr, plugin_addr_len);
407   if (NULL != plugin_name)
408     strcpy (&addrp[plugin_addr_len], plugin_name);
409   GNUNET_SERVER_notification_context_unicast (nc,
410                                               ai->pc->client,
411                                               &msg->header,
412                                               GNUNET_NO);
413 }
414
415
416 /**
417  * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
418  * information to be passed back to the client.
419  *
420  * @param cls closure with our `struct AddressIteration *`
421  * @param id the peer id
422  * @param plugin_name plugin name
423  * @param plugin_addr address
424  * @param plugin_addr_len length of @a plugin_addr
425  * @param active is address actively used
426  * @param atsi ats performance information
427  * @param atsi_count number of ats performance elements in @a atsi
428  * @param bandwidth_out current outbound bandwidth assigned to address
429  * @param bandwidth_in current inbound bandwidth assigned to address
430  */
431 static void
432 req_addr_peerinfo_it (void *cls,
433                       const struct GNUNET_PeerIdentity *id,
434                       const char *plugin_name,
435                       const void *plugin_addr,
436                       size_t plugin_addr_len,
437                       int active,
438                       const struct GNUNET_ATS_Information *atsi,
439                       uint32_t atsi_count,
440                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
441                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
442 {
443   struct AddressIteration *ai = cls;
444
445   if ( (NULL == id) &&
446        (NULL == plugin_name) &&
447        (NULL == plugin_addr) )
448   {
449     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450                 "Address iteration done for one peer\n");
451     return;
452   }
453   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
454               "Callback for %s peer `%s' plugin `%s' BW out %u, BW in %u\n",
455               (active == GNUNET_YES) ? "ACTIVE" : "INACTIVE",
456               GNUNET_i2s (id),
457               plugin_name,
458               (unsigned int) ntohl (bandwidth_out.value__),
459               (unsigned int) ntohl (bandwidth_in.value__));
460
461   /* Transmit result (either if address is active, or if
462      client wanted all addresses) */
463   if ( (GNUNET_YES == ai->all) ||
464        (GNUNET_YES == active))
465   {
466     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
467                 "Sending result for %s peer `%s' plugin `%s' BW out %u, BW in %u\n",
468                 (active == GNUNET_YES) ? "ACTIVE" : "INACTIVE",
469                 GNUNET_i2s (id),
470                 plugin_name,
471                 (unsigned int) ntohl (bandwidth_out.value__),
472                 (unsigned int) ntohl (bandwidth_in.value__));
473     transmit_req_addr (ai,
474                        id,
475                        plugin_name,
476                        plugin_addr, plugin_addr_len,
477                        active,
478                        atsi,
479                        atsi_count,
480                        bandwidth_out,
481                        bandwidth_in);
482   }
483 }
484
485
486 /**
487  * Handle 'address list request' messages from clients.
488  *
489  * @param cls unused, NULL
490  * @param client client that sent the request
491  * @param message the request message
492  */
493 void
494 GAS_handle_request_address_list (void *cls,
495                                  struct GNUNET_SERVER_Client *client,
496                                  const struct GNUNET_MessageHeader *message)
497 {
498   struct PerformanceClient *pc;
499   struct AddressIteration ai;
500   const struct AddressListRequestMessage *alrm;
501   struct GNUNET_PeerIdentity allzeros;
502   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_zero;
503
504   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
505               "Received `%s' message\n",
506               "ADDRESSLIST_REQUEST");
507   if (NULL == (pc = find_client(client)))
508   {
509     GNUNET_break (0);
510     return;
511   }
512   alrm = (const struct AddressListRequestMessage *) message;
513   ai.all = ntohl (alrm->all);
514   ai.id = ntohl (alrm->id);
515   ai.pc = pc;
516
517   memset (&allzeros, '\0', sizeof (struct GNUNET_PeerIdentity));
518   bandwidth_zero.value__ = htonl (0);
519   if (0 == memcmp (&alrm->peer,
520                    &allzeros,
521                    sizeof (struct GNUNET_PeerIdentity)))
522   {
523     /* Return addresses for all peers */
524     GAS_addresses_get_peer_info (NULL,
525                                  &req_addr_peerinfo_it,
526                                  &ai);
527   }
528   else
529   {
530     /* Return addresses for a specific peer */
531     GAS_addresses_get_peer_info (&alrm->peer,
532                                  &req_addr_peerinfo_it,
533                                  &ai);
534   }
535   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
536               "Finished handling `%s' message\n",
537               "ADDRESSLIST_REQUEST");
538   transmit_req_addr (&ai,
539                      NULL, NULL, NULL,
540                      0, GNUNET_NO,
541                      NULL, 0,
542                      bandwidth_zero,
543                      bandwidth_zero);
544   GNUNET_SERVER_receive_done (client,
545                               GNUNET_OK);
546 }
547
548
549 /**
550  * Handle 'reservation request' messages from clients.
551  *
552  * @param cls unused, NULL
553  * @param client client that sent the request
554  * @param message the request message
555  */
556 void
557 GAS_handle_reservation_request (void *cls,
558                                 struct GNUNET_SERVER_Client *client,
559                                 const struct GNUNET_MessageHeader *message)
560 {
561   const struct ReservationRequestMessage *msg =
562       (const struct ReservationRequestMessage *) message;
563   struct ReservationResultMessage result;
564   int32_t amount;
565   struct GNUNET_TIME_Relative res_delay;
566
567   if (NULL == find_client (client))
568   {
569     /* missing start message! */
570     GNUNET_break (0);
571     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
572     return;
573   }
574   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575               "Received `%s' message\n",
576               "RESERVATION_REQUEST");
577   amount = (int32_t) ntohl (msg->amount);
578   res_delay = GAS_reservations_reserve (&msg->peer, amount);
579   if (res_delay.rel_value_us > 0)
580     amount = 0;
581   result.header.size = htons (sizeof (struct ReservationResultMessage));
582   result.header.type = htons (GNUNET_MESSAGE_TYPE_ATS_RESERVATION_RESULT);
583   result.amount = htonl (amount);
584   result.peer = msg->peer;
585   result.res_delay = GNUNET_TIME_relative_hton (res_delay);
586   GNUNET_STATISTICS_update (GSA_stats,
587                             "# reservation requests processed", 1,
588                             GNUNET_NO);
589   GNUNET_SERVER_notification_context_unicast (nc, client, &result.header,
590                                               GNUNET_NO);
591   GNUNET_SERVER_receive_done (client, GNUNET_OK);
592 }
593
594
595
596
597 /**
598  * Initialize performance subsystem.
599  *
600  * @param server handle to our server
601  * @param addresses the address handle to use
602  */
603 void
604 GAS_performance_init (struct GNUNET_SERVER_Handle *server)
605 {
606   nc = GNUNET_SERVER_notification_context_create (server, 128);
607 }
608
609
610 /**
611  * Shutdown performance subsystem.
612  */
613 void
614 GAS_performance_done ()
615 {
616   GNUNET_SERVER_notification_context_destroy (nc);
617   nc = NULL;
618 }
619
620 /* end of gnunet-service-ats_performance.c */