2 This file is part of GNUnet.
3 (C) 2011-2015 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file ats/gnunet-service-ats_performance.c
23 * @brief ats service, interaction with 'performance' API
24 * @author Matthias Wachs
25 * @author Christian Grothoff
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"
35 * We keep clients that are interested in performance in a linked list.
37 struct PerformanceClient
40 * Next in doubly-linked list.
42 struct PerformanceClient *next;
45 * Previous in doubly-linked list.
47 struct PerformanceClient *prev;
50 * Actual handle to the client.
52 struct GNUNET_SERVER_Client *client;
55 * Options for the client.
63 * Head of linked list of all clients to this service.
65 static struct PerformanceClient *pc_head;
68 * Tail of linked list of all clients to this service.
70 static struct PerformanceClient *pc_tail;
74 * Context for sending messages to performance clients.
76 static struct GNUNET_SERVER_NotificationContext *nc;
80 * Find the performance client associated with the given handle.
82 * @param client server handle
83 * @return internal handle
85 static struct PerformanceClient *
86 find_client (struct GNUNET_SERVER_Client *client)
88 struct PerformanceClient *pc;
90 for (pc = pc_head; pc != NULL; pc = pc->next)
91 if (pc->client == client)
97 * Unregister a client (which may have been a performance client,
98 * but this is not assured).
100 * @param client handle of the (now dead) client
103 GAS_performance_remove_client (struct GNUNET_SERVER_Client *client)
105 struct PerformanceClient *pc;
107 pc = find_client (client);
110 GNUNET_CONTAINER_DLL_remove (pc_head, pc_tail, pc);
116 * Transmit the given performance information to all performance
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
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,
140 const struct GNUNET_ATS_Information *atsi,
142 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
143 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
146 struct PeerInformationMessage *msg;
147 size_t plugin_name_length = strlen (plugin_name) + 1;
149 sizeof (struct PeerInformationMessage) +
150 atsi_count * sizeof (struct GNUNET_ATS_Information) + plugin_addr_len +
152 char buf[msize] GNUNET_ALIGN;
153 struct GNUNET_ATS_Information *atsp;
156 GNUNET_assert (NULL != pc);
157 if (NULL == find_client (pc->client))
158 return; /* Client disconnected */
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);
168 msg->ats_count = htonl (atsi_count);
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,
188 * Transmit the given performance information to all performance
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
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,
210 const struct GNUNET_ATS_Information *atsi,
212 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
213 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
215 struct PerformanceClient *pc;
217 for (pc = pc_head; pc != NULL; pc = pc->next)
218 if (pc->flag == START_FLAG_PERFORMANCE_WITH_PIC)
220 GAS_performance_notify_client (pc,
227 bandwidth_out, bandwidth_in);
229 GNUNET_STATISTICS_update (GSA_stats,
230 "# performance updates given to clients", 1,
237 * Iterator for called from #GAS_addresses_get_peer_info()
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
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,
257 const struct GNUNET_ATS_Information *atsi,
259 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
260 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
262 struct PerformanceClient *pc = cls;
264 GNUNET_assert (NULL != pc);
267 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268 "Callback for peer `%s' plugin `%s' BW out %u, BW in %u \n",
271 (unsigned int) ntohl (bandwidth_out.value__),
272 (unsigned int) ntohl (bandwidth_in.value__));
273 GAS_performance_notify_client (pc,
286 * Register a new performance client.
288 * @param client handle of the new client
289 * @param flag flag specifying the type of the client
292 GAS_performance_add_client (struct GNUNET_SERVER_Client *client,
295 struct PerformanceClient *pc;
297 GNUNET_break (NULL == find_client (client));
298 pc = GNUNET_new (struct PerformanceClient);
302 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303 "Adding performance client %s PIC\n",
304 (flag == START_FLAG_PERFORMANCE_WITH_PIC) ? "with" : "without");
306 GNUNET_SERVER_notification_context_add (nc,
308 GNUNET_CONTAINER_DLL_insert (pc_head,
311 GAS_addresses_get_peer_info (NULL,
318 * Information we need for the callbacks to return a list of addresses
319 * back to the client.
321 struct AddressIteration
324 * Actual handle to the client.
326 struct PerformanceClient *pc;
329 * Are we sending all addresses, or only those that are active?
334 * Which ID should be included in the response?
342 * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
343 * given address details to the client identified in @a ai.
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
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,
363 const struct GNUNET_ATS_Information *atsi,
365 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
366 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
369 struct GNUNET_ATS_Information *atsp;
370 struct PeerInformationMessage *msg;
372 size_t plugin_name_length;
375 if (NULL != plugin_name)
376 plugin_name_length = strlen (plugin_name) + 1;
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;
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);
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,
417 * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
418 * information to be passed back to the client.
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
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,
438 const struct GNUNET_ATS_Information *atsi,
440 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
441 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
443 struct AddressIteration *ai = cls;
446 (NULL == plugin_name) &&
447 (NULL == plugin_addr) )
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450 "Address iteration done for one peer\n");
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",
458 (unsigned int) ntohl (bandwidth_out.value__),
459 (unsigned int) ntohl (bandwidth_in.value__));
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))
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",
471 (unsigned int) ntohl (bandwidth_out.value__),
472 (unsigned int) ntohl (bandwidth_in.value__));
473 transmit_req_addr (ai,
476 plugin_addr, plugin_addr_len,
487 * Handle 'address list request' messages from clients.
489 * @param cls unused, NULL
490 * @param client client that sent the request
491 * @param message the request message
494 GAS_handle_request_address_list (void *cls,
495 struct GNUNET_SERVER_Client *client,
496 const struct GNUNET_MessageHeader *message)
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;
504 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
505 "Received `%s' message\n",
506 "ADDRESSLIST_REQUEST");
507 if (NULL == (pc = find_client(client)))
512 alrm = (const struct AddressListRequestMessage *) message;
513 ai.all = ntohl (alrm->all);
514 ai.id = ntohl (alrm->id);
517 memset (&allzeros, '\0', sizeof (struct GNUNET_PeerIdentity));
518 bandwidth_zero.value__ = htonl (0);
519 if (0 == memcmp (&alrm->peer,
521 sizeof (struct GNUNET_PeerIdentity)))
523 /* Return addresses for all peers */
524 GAS_addresses_get_peer_info (NULL,
525 &req_addr_peerinfo_it,
530 /* Return addresses for a specific peer */
531 GAS_addresses_get_peer_info (&alrm->peer,
532 &req_addr_peerinfo_it,
535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
536 "Finished handling `%s' message\n",
537 "ADDRESSLIST_REQUEST");
538 transmit_req_addr (&ai,
544 GNUNET_SERVER_receive_done (client,
550 * Handle 'reservation request' messages from clients.
552 * @param cls unused, NULL
553 * @param client client that sent the request
554 * @param message the request message
557 GAS_handle_reservation_request (void *cls,
558 struct GNUNET_SERVER_Client *client,
559 const struct GNUNET_MessageHeader *message)
561 const struct ReservationRequestMessage *msg =
562 (const struct ReservationRequestMessage *) message;
563 struct ReservationResultMessage result;
565 struct GNUNET_TIME_Relative res_delay;
567 if (NULL == find_client (client))
569 /* missing start message! */
571 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
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)
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,
589 GNUNET_SERVER_notification_context_unicast (nc, client, &result.header,
591 GNUNET_SERVER_receive_done (client, GNUNET_OK);
598 * Initialize performance subsystem.
600 * @param server handle to our server
601 * @param addresses the address handle to use
604 GAS_performance_init (struct GNUNET_SERVER_Handle *server)
606 nc = GNUNET_SERVER_notification_context_create (server, 128);
611 * Shutdown performance subsystem.
614 GAS_performance_done ()
616 GNUNET_SERVER_notification_context_destroy (nc);
620 /* end of gnunet-service-ats_performance.c */