changes
[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
506 /**
507  * Handle 'reservation request' messages from clients.
508  *
509  * @param cls unused, NULL
510  * @param client client that sent the request
511  * @param message the request message
512  */
513 void
514 GAS_handle_reservation_request (void *cls, struct GNUNET_SERVER_Client *client,
515                                 const struct GNUNET_MessageHeader *message)
516 {
517   const struct ReservationRequestMessage *msg =
518       (const struct ReservationRequestMessage *) message;
519   struct ReservationResultMessage result;
520   int32_t amount;
521   struct GNUNET_TIME_Relative res_delay;
522
523   if (NULL == find_client (client))
524   {
525     /* missing start message! */
526     GNUNET_break (0);
527     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
528     return;
529   }
530   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n",
531               "RESERVATION_REQUEST");
532   amount = (int32_t) ntohl (msg->amount);
533   res_delay = GAS_reservations_reserve (&msg->peer, amount);
534   if (res_delay.rel_value > 0)
535     amount = 0;
536   result.header.size = htons (sizeof (struct ReservationResultMessage));
537   result.header.type = htons (GNUNET_MESSAGE_TYPE_ATS_RESERVATION_RESULT);
538   result.amount = htonl (amount);
539   result.peer = msg->peer;
540   result.res_delay = GNUNET_TIME_relative_hton (res_delay);
541   GNUNET_STATISTICS_update (GSA_stats, "# reservation requests processed", 1,
542                             GNUNET_NO);
543   GNUNET_SERVER_notification_context_unicast (nc, client, &result.header,
544                                               GNUNET_NO);
545   GNUNET_SERVER_receive_done (client, GNUNET_OK);
546 }
547
548
549 /**
550  * Handle 'preference change' 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_preference_change (void *cls,
558                               struct GNUNET_SERVER_Client *client,
559                               const struct GNUNET_MessageHeader *message)
560 {
561   const struct ChangePreferenceMessage *msg;
562   const struct PreferenceInformation *pi;
563   uint16_t msize;
564   uint32_t nump;
565   uint32_t i;
566
567   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n",
568               "PREFERENCE_CHANGE");
569   msize = ntohs (message->size);
570   if (msize < sizeof (struct ChangePreferenceMessage))
571   {
572     GNUNET_break (0);
573     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
574     return;
575   }
576   msg = (const struct ChangePreferenceMessage *) message;
577   nump = ntohl (msg->num_preferences);
578   if (msize !=
579       sizeof (struct ChangePreferenceMessage) +
580       nump * sizeof (struct PreferenceInformation))
581   {
582     GNUNET_break (0);
583     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
584     return;
585   }
586   GNUNET_STATISTICS_update (GSA_stats, "# preference change requests processed",
587                             1, GNUNET_NO);
588   pi = (const struct PreferenceInformation *) &msg[1];
589   for (i = 0; i < nump; i++)
590     GAS_addresses_change_preference (GSA_addresses,
591                                      client,
592                                      &msg->peer,
593                                      (enum GNUNET_ATS_PreferenceKind)
594                                      ntohl (pi[i].preference_kind),
595                                      pi[i].preference_value);
596   GNUNET_SERVER_receive_done (client, GNUNET_OK);
597 }
598
599
600 /**
601  * Initialize performance subsystem.
602  *
603  * @param server handle to our server
604  * @param addresses the address handle to use
605  */
606 void
607 GAS_performance_init (struct GNUNET_SERVER_Handle *server,
608                       struct GAS_Addresses_Handle *addresses)
609 {
610   GSA_addresses = addresses;
611   nc = GNUNET_SERVER_notification_context_create (server, 128);
612 }
613
614
615 /**
616  * Shutdown performance subsystem.
617  */
618 void
619 GAS_performance_done ()
620 {
621   GNUNET_SERVER_notification_context_destroy (nc);
622   nc = NULL;
623 }
624
625 /* end of gnunet-service-ats_performance.c */