8125b8664c2e538b451a89545a58f09d6a176de7
[oweals/gnunet.git] / src / ats / gnunet-service-ats_scheduling.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_scheduling.c
23  * @brief ats service, interaction with 'scheduling' API
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_scheduling.h"
30 #include "ats.h"
31
32
33 /**
34  * We keep clients that are interested in scheduling in a linked list.
35  * This list typically has only one entry (for the
36  * gnunet-service-transport process); however, it is possible that
37  * there is more than one (at least briefly) because after a crash a
38  * new one may connect before we've been notified to clean up the old
39  * process.
40  */
41 struct SchedulingClient
42 {
43   /**
44    * Next in doubly-linked list.
45    */
46   struct SchedulingClient * next;
47
48   /**
49    * Previous in doubly-linked list.
50    */
51   struct SchedulingClient * prev;
52
53   /**
54    * Actual handle to the client.
55    */
56   struct GNUNET_SERVER_Client *client;
57
58 };
59
60
61 /**
62  * Head of linked list of all clients to this service.
63  */
64 static struct SchedulingClient *sc_head;
65
66 /**
67  * Tail of linked list of all clients to this service.
68  */
69 static struct SchedulingClient *sc_tail;
70
71 /**
72  * Context for sending messages to clients.
73  */
74 static struct GNUNET_SERVER_NotificationContext *nc;
75
76 static unsigned long long total_quota_in;
77
78 static unsigned long long total_quota_out;
79
80
81 /**
82  * Find the scheduling client associated with the given
83  * handle.
84  *
85  * @param client server handle
86  * @return internal handle
87  */
88 static struct SchedulingClient * 
89 find_client (struct GNUNET_SERVER_Client *client)
90 {
91   struct SchedulingClient * sc;
92
93   for (sc = sc_head; sc != NULL; sc = sc->next)
94     if (sc->client == client)
95       return sc;
96   return NULL;
97 }
98
99
100 /**
101  * Register a new scheduling client.
102  *
103  * @param client handle of the new client
104  */
105 void
106 GAS_scheduling_add_client (struct GNUNET_SERVER_Client *client)
107 {
108   struct SchedulingClient *sc;
109
110   GNUNET_break (NULL == find_client (client));
111   sc = GNUNET_malloc (sizeof (struct SchedulingClient));
112   sc->client = client;
113   GNUNET_SERVER_notification_context_add (nc, client);
114   GNUNET_SERVER_client_keep (client);
115   GNUNET_CONTAINER_DLL_insert(sc_head, sc_tail, sc);
116 }
117
118
119
120 /**
121  * Unregister a client (which may have been a scheduling client,
122  * but this is not assured).
123  *
124  * @param client handle of the (now dead) client
125  */
126 void
127 GAS_scheduling_remove_client (struct GNUNET_SERVER_Client *client)
128 {
129   struct SchedulingClient * sc;
130
131   sc = find_client (client);
132   if (NULL == sc)
133     return;
134   GNUNET_CONTAINER_DLL_remove (sc_head, sc_tail, sc);
135   GAS_address_client_disconnected (client);
136   GNUNET_SERVER_client_drop (client);
137   GNUNET_free (sc);
138 }
139
140
141 /**
142  * Transmit the given address suggestion and bandwidth update to all scheduling
143  * clients.
144  *
145  * @param peer peer for which this is an address suggestion
146  * @param plugin_name 0-termintated string specifying the transport plugin
147  * @param plugin_addr binary address for the plugin to use
148  * @param plugin_addr_len number of bytes in plugin_addr
149  * @param session_client which client gave us this session_id?
150  * @param session_id session ID to use for the given client (other clients will see 0)
151  * @param atsi performance data for the address
152  * @param atsi_count number of performance records in 'ats'
153  * @param bandwidth_out assigned outbound bandwidth
154  * @param bandwidth_in assigned inbound bandwidth
155  */
156 void
157 GAS_scheduling_transmit_address_suggestion (const struct GNUNET_PeerIdentity *peer,
158                                             const char *plugin_name,
159                                             const void *plugin_addr, size_t plugin_addr_len,
160                                             struct GNUNET_SERVER_Client *session_client,
161                                             uint32_t session_id,
162                                             const struct GNUNET_TRANSPORT_ATS_Information *atsi,
163                                             uint32_t atsi_count,                                
164                                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
165                                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
166 {
167   struct SchedulingClient *sc;
168   struct AddressSuggestionMessage *msg;
169   size_t plugin_name_length = strlen (plugin_name) + 1;
170   size_t msize = sizeof (struct AddressSuggestionMessage) + atsi_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information) 
171     + plugin_addr_len + plugin_name_length;
172   char buf[msize];
173   struct GNUNET_TRANSPORT_ATS_Information *atsp;
174   char *addrp;
175
176   GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
177   GNUNET_assert (atsi_count < GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_TRANSPORT_ATS_Information));
178   msg = (struct AddressSuggestionMessage*) buf;
179   msg->header.size = htons (msize);
180   msg->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION);
181   msg->ats_count = htonl (atsi_count);
182   msg->peer = *peer;
183   msg->address_length = htons (plugin_addr_len);
184   msg->plugin_name_length = htons (plugin_name_length);
185   /* session ID is set only if 'client' is the same... */
186   msg->bandwidth_out = bandwidth_out;
187   msg->bandwidth_in = bandwidth_in;
188   atsp = (struct GNUNET_TRANSPORT_ATS_Information* ) &msg[1];
189   memcpy (atsp, atsi, sizeof (struct GNUNET_TRANSPORT_ATS_Information) * atsi_count);
190   addrp = (char*) &atsp[atsi_count];
191   memcpy (addrp, plugin_addr, plugin_addr_len);
192   strcpy (&addrp[plugin_addr_len], plugin_name);
193   for (sc = sc_head; sc != NULL; sc = sc->next)
194   {
195     if (sc->client == session_client)
196       msg->session_id = htonl (session_id);
197     else
198       msg->session_id = htonl (0);
199     GNUNET_SERVER_notification_context_unicast (nc,
200                                                 sc->client,
201                                                 &msg->header,
202                                                 GNUNET_YES);
203   } 
204 }
205
206
207 /**
208  * Handle 'request address' messages from clients.
209  *
210  * @param cls unused, NULL
211  * @param client client that sent the request
212  * @param message the request message
213  */
214 void
215 GAS_handle_request_address (void *cls, struct GNUNET_SERVER_Client *client,
216                             const struct GNUNET_MessageHeader *message)
217
218 {
219   const struct RequestAddressMessage * msg = (const struct RequestAddressMessage *) message;
220
221   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "REQUEST_ADDRESS");
222   GNUNET_break (0 == ntohl (msg->reserved));
223   GAS_addresses_request_address (&msg->peer);
224   GNUNET_SERVER_receive_done (client, GNUNET_OK);
225 }
226
227
228 /**
229  * Handle 'address update' messages from clients.
230  *
231  * @param cls unused, NULL
232  * @param client client that sent the request
233  * @param message the request message
234  */
235 void
236 GAS_handle_address_update (void *cls, struct GNUNET_SERVER_Client *client,
237                       const struct GNUNET_MessageHeader *message)
238
239 {
240   const struct AddressUpdateMessage * m;
241   const struct GNUNET_TRANSPORT_ATS_Information *atsi;
242   const char *address;
243   const char *plugin_name;
244   uint16_t address_length;
245   uint16_t plugin_name_length;
246   uint32_t ats_count;
247   uint16_t size;
248
249   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
250               "Received `%s' message\n",
251               "ADDRESS_UPDATE");
252   size = ntohs (message->size);
253   if (size <= sizeof (struct AddressUpdateMessage))
254   {
255     GNUNET_break (0);
256     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
257     return;
258   }
259   m = (const struct AddressUpdateMessage*) message;
260   ats_count = ntohl (m->ats_count);
261   address_length = ntohs (m->address_length);
262   plugin_name_length = ntohs (m->plugin_name_length);  
263   atsi = (const struct GNUNET_TRANSPORT_ATS_Information*) &m[1];
264   address = (const char*) &atsi[ats_count];
265   if (plugin_name_length != 0)
266     plugin_name = &address[address_length];
267   else
268     plugin_name = "";
269   if ( (address_length +
270         plugin_name_length +
271         ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information) +
272         sizeof (struct AddressUpdateMessage) != ntohs (message->size))  ||
273        (ats_count > GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_TRANSPORT_ATS_Information)) ||
274        (plugin_name[plugin_name_length - 1] != '\0') )
275   {
276     GNUNET_break (0);
277     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
278     return;
279   }
280   GAS_address_update (&m->peer,
281                       plugin_name,
282                       address,
283                       address_length,
284                       client,
285                       ntohl (m->session_id),
286                       atsi,
287                       ats_count);
288   GNUNET_SERVER_receive_done (client, GNUNET_OK);
289 }
290
291
292 /**
293  * Handle 'address destroyed' messages from clients.
294  *
295  * @param cls unused, NULL
296  * @param client client that sent the request
297  * @param message the request message
298  */
299 void
300 GAS_handle_address_destroyed (void *cls, struct GNUNET_SERVER_Client *client,
301                               const struct GNUNET_MessageHeader *message)
302
303 {
304   const struct AddressDestroyedMessage * m;
305   const char *address;
306   const char *plugin_name;
307   uint16_t address_length;
308   uint16_t plugin_name_length;
309   uint16_t size;
310
311   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
312               "Received `%s' message of size %u %u\n",
313               "ADDRESS_DESTROYED", ntohs (message->size), sizeof (struct AddressDestroyedMessage));
314   size = ntohs (message->size);
315   if (size < sizeof (struct AddressDestroyedMessage))
316   {
317     GNUNET_break (0);
318     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
319     return;
320   }
321   m = (const struct AddressDestroyedMessage*) message;
322   GNUNET_break (0 == ntohl (m->reserved));
323   address_length = ntohs (m->address_length);
324   plugin_name_length = ntohs (m->plugin_name_length);  
325   address = (const char*) &m[1];
326   if (plugin_name_length != 0)
327     plugin_name = &address[address_length];
328   else
329     plugin_name = "";
330
331   if ( (address_length +
332         plugin_name_length +
333         sizeof (struct AddressDestroyedMessage) != ntohs (message->size)))
334   {
335     GNUNET_break (0);
336     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
337     return;
338   }
339
340   if (plugin_name_length != 0)
341     if (plugin_name[plugin_name_length - 1] != '\0')
342     {
343       GNUNET_break (0);
344       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
345       return;
346     }
347
348   GAS_address_destroyed (&m->peer,
349                          plugin_name,
350                          address,
351                          address_length,
352                          client,
353                          ntohl (m->session_id));
354   GNUNET_SERVER_receive_done (client, GNUNET_OK);
355 }
356
357
358 /**
359  * Initialize scheduling subsystem.
360  *
361  * @param server handle to our server
362  * @param cfg configuration to use
363  */
364 void
365 GAS_scheduling_init (struct GNUNET_SERVER_Handle *server,
366                      const struct GNUNET_CONFIGURATION_Handle *cfg)
367 {
368   GNUNET_assert (GNUNET_OK ==
369                  GNUNET_CONFIGURATION_get_value_number (cfg,
370                                                         "core",
371                                                         "TOTAL_QUOTA_IN",
372                                                         &total_quota_in));
373   GNUNET_assert (GNUNET_OK ==
374                  GNUNET_CONFIGURATION_get_value_number (cfg,
375                                                         "core",
376                                                         "TOTAL_QUOTA_OUT",
377                                                         &total_quota_out));
378   nc = GNUNET_SERVER_notification_context_create (server, 128);
379 }
380
381
382 /**
383  * Shutdown scheduling subsystem.
384  */
385 void
386 GAS_scheduling_done ()
387 {
388   GNUNET_SERVER_notification_context_destroy (nc);
389   nc = NULL;
390 }
391
392
393 /* end of gnunet-service-ats_scheduling.c */