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