more code to get latency in ATSI working; not complete, also now generating a warning...
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,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 transport/gnunet-service-transport.c
23  * @brief
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_transport_service.h"
30 #include "gnunet_peerinfo_service.h"
31 #include "gnunet_ats_service.h"
32 #include "gnunet-service-transport.h"
33 #include "gnunet-service-transport_blacklist.h"
34 #include "gnunet-service-transport_clients.h"
35 #include "gnunet-service-transport_hello.h"
36 #include "gnunet-service-transport_neighbours.h"
37 #include "gnunet-service-transport_plugins.h"
38 #include "gnunet-service-transport_validation.h"
39 #include "transport.h"
40
41 /* globals */
42
43 /**
44  * Statistics handle.
45  */
46 struct GNUNET_STATISTICS_Handle *GST_stats;
47
48 /**
49  * Configuration handle.
50  */
51 const struct GNUNET_CONFIGURATION_Handle *GST_cfg;
52
53 /**
54  * Configuration handle.
55  */
56 struct GNUNET_PeerIdentity GST_my_identity;
57
58 /**
59  * Handle to peerinfo service.
60  */
61 struct GNUNET_PEERINFO_Handle *GST_peerinfo;
62
63 /**
64  * Our public key.
65  */
66 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded GST_my_public_key;
67
68 /**
69  * Our private key.
70  */
71 struct GNUNET_CRYPTO_RsaPrivateKey *GST_my_private_key;
72
73 /**
74  * ATS handle.
75  */
76 struct GNUNET_ATS_SchedulingHandle *GST_ats;
77
78
79 /**
80  * Transmit our HELLO message to the given (connected) neighbour.
81  *
82  * @param cls the 'HELLO' message
83  * @param target a connected neighbour
84  * @param ats performance information (unused)
85  * @param ats_count number of records in ats (unused)
86  * @param transport plugin
87  * @param addr address
88  * @param addrlen address length
89  */
90 static void
91 transmit_our_hello (void *cls, const struct GNUNET_PeerIdentity *target,
92                     const struct GNUNET_ATS_Information *ats,
93                     uint32_t ats_count, const char *transport, const void *addr,
94                     size_t addrlen)
95 {
96   const struct GNUNET_MessageHeader *hello = cls;
97
98   GST_neighbours_send (target, (const char *) hello, ntohs (hello->size),
99                        GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION, NULL, NULL);
100 }
101
102
103 /**
104  * My HELLO has changed. Tell everyone who should know.
105  *
106  * @param cls unused
107  * @param hello new HELLO
108  */
109 static void
110 process_hello_update (void *cls, const struct GNUNET_MessageHeader *hello)
111 {
112   GST_clients_broadcast (hello, GNUNET_NO);
113   GST_neighbours_iterate (&transmit_our_hello, (void *) hello);
114 }
115
116
117
118 /**
119  * We received some payload.  Prepare to pass it on to our clients.
120  *
121  * @param peer (claimed) identity of the other peer
122  * @param message the message, never NULL
123  * @param ats performance information
124  * @param ats_count number of records in ats
125  * @return how long the plugin should wait until receiving more data
126  */
127 static struct GNUNET_TIME_Relative
128 process_payload (const struct GNUNET_PeerIdentity *peer,
129                  const struct GNUNET_MessageHeader *message,
130                  const struct GNUNET_ATS_Information *ats, uint32_t ats_count)
131 {
132   struct GNUNET_TIME_Relative ret;
133   int do_forward;
134   struct InboundMessage *im;
135   size_t msg_size = ntohs (message->size);
136   size_t size =
137       sizeof (struct InboundMessage) + msg_size +
138     sizeof (struct GNUNET_ATS_Information) * (ats_count + 1);
139   char buf[size];
140   struct GNUNET_ATS_Information *ap;
141
142   ret = GNUNET_TIME_UNIT_ZERO;
143   do_forward = GNUNET_SYSERR;
144   ret = GST_neighbours_calculate_receive_delay (peer, msg_size, &do_forward);
145
146   if (!GST_neighbours_test_connected (peer))
147   {
148
149     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
150                 "Discarded %u bytes type %u payload from peer `%s'\n", msg_size,
151                 ntohs (message->type), GNUNET_i2s (peer));
152
153     GNUNET_STATISTICS_update (GST_stats,
154                               gettext_noop
155                               ("# bytes payload discarded due to not connected peer "),
156                               msg_size, GNUNET_NO);
157     return ret;
158   }
159
160   if (do_forward != GNUNET_YES)
161     return ret;
162   im = (struct InboundMessage *) buf;
163   im->header.size = htons (size);
164   im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
165   im->ats_count = htonl (ats_count + 1);
166   im->peer = *peer;
167   ap = (struct GNUNET_ATS_Information *) &im[1];
168   memcpy (ap, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
169   ap[ats_count].type = htonl (GNUNET_ATS_QUALITY_NET_DELAY);
170   ap[ats_count].value = htonl ((uint32_t) GST_neighbour_get_latency (peer).rel_value);
171   memcpy (&ap[ats_count + 1], message, ntohs (message->size));
172
173   GST_clients_broadcast (&im->header, GNUNET_YES);
174
175   return ret;
176 }
177
178
179 /**
180  * Function called by the transport for each received message.
181  * This function should also be called with "NULL" for the
182  * message to signal that the other peer disconnected.
183  *
184  * @param cls closure, const char* with the name of the plugin we received the message from
185  * @param peer (claimed) identity of the other peer
186  * @param message the message, NULL if we only care about
187  *                learning about the delay until we should receive again -- FIXME!
188  * @param ats performance information
189  * @param ats_count number of records in ats
190  * @param session identifier used for this session (NULL for plugins
191  *                that do not offer bi-directional communication to the sender
192  *                using the same "connection")
193  * @param sender_address binary address of the sender (if we established the
194  *                connection or are otherwise sure of it; should be NULL
195  *                for inbound TCP/UDP connections since it it not clear
196  *                that we could establish ourselves a connection to that
197  *                IP address and get the same system)
198  * @param sender_address_len number of bytes in sender_address
199  * @return how long the plugin should wait until receiving more data
200  *         (plugins that do not support this, can ignore the return value)
201  */
202 static struct GNUNET_TIME_Relative
203 plugin_env_receive_callback (void *cls, const struct GNUNET_PeerIdentity *peer,
204                              const struct GNUNET_MessageHeader *message,
205                              const struct GNUNET_ATS_Information *ats,
206                              uint32_t ats_count, struct Session *session,
207                              const char *sender_address,
208                              uint16_t sender_address_len)
209 {
210   const char *plugin_name = cls;
211   struct GNUNET_TIME_Relative ret;
212   uint16_t type;
213
214   ret = GNUNET_TIME_UNIT_ZERO;
215   if (NULL == message)
216     goto end;
217   type = ntohs (message->type);
218 #if DEBUG_TRANSPORT
219
220   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received Message with type %u\n", type);
221 #endif
222
223   switch (type)
224   {
225   case GNUNET_MESSAGE_TYPE_HELLO:
226     GST_validation_handle_hello (message);
227     return ret;
228   case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
229 #if DEBUG_TRANSPORT
230     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
231                 "Processing `%s' from `%s'\n", "PING",
232                 (sender_address != NULL) ? GST_plugins_a2s (plugin_name,
233                                                             sender_address,
234                                                             sender_address_len)
235                 : "<inbound>");
236 #endif
237     GST_validation_handle_ping (peer, message, plugin_name, session,
238                                 sender_address, sender_address_len);
239     break;
240   case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
241 #if DEBUG_TRANSPORT
242     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
243                 "Processing `%s' from `%s'\n", "PONG",
244                 (sender_address != NULL) ? GST_plugins_a2s (plugin_name,
245                                                             sender_address,
246                                                             sender_address_len)
247                 : "<inbound>");
248 #endif
249     GST_validation_handle_pong (peer, message);
250     break;
251   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT:
252     GST_neighbours_handle_connect (message, peer, plugin_name, sender_address,
253                                    sender_address_len, session, ats, ats_count);
254     break;
255   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK:
256     GST_neighbours_handle_connect_ack (message, peer, plugin_name,
257                                        sender_address, sender_address_len,
258                                        session, ats, ats_count);
259     break;
260   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
261     GST_neighbours_handle_ack (message, peer, plugin_name, sender_address,
262                                sender_address_len, session, ats, ats_count);
263     break;
264   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
265     GST_neighbours_handle_disconnect_message (peer, message);
266     break;
267   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
268     GST_neighbours_keepalive (peer);
269     break;
270   default:
271     /* should be payload */
272     ret = process_payload (peer, message, ats, ats_count);
273     break;
274   }
275 end:
276 #if 1
277   /* FIXME: this should not be needed, and not sure it's good to have it, but without
278    * this connections seem to go extra-slow */
279   GNUNET_ATS_address_update (GST_ats, peer, plugin_name, sender_address,
280                              sender_address_len, session, ats, ats_count);
281 #endif
282 #if DEBUG_TRANSPORT
283   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284               "Allowing receive from peer %s to continue in %llu ms\n",
285               GNUNET_i2s (peer), (unsigned long long) ret.rel_value);
286 #endif
287   return ret;
288 }
289
290
291 /**
292  * Function that will be called for each address the transport
293  * is aware that it might be reachable under.  Update our HELLO.
294  *
295  * @param cls name of the plugin (const char*)
296  * @param add_remove should the address added (YES) or removed (NO) from the
297  *                   set of valid addresses?
298  * @param addr one of the addresses of the host
299  *        the specific address format depends on the transport
300  * @param addrlen length of the address
301  */
302 static void
303 plugin_env_address_change_notification (void *cls, int add_remove,
304                                         const void *addr, size_t addrlen)
305 {
306   const char *plugin_name = cls;
307
308   GST_hello_modify_addresses (add_remove, plugin_name, addr, addrlen);
309 }
310
311
312 /**
313  * Function that will be called whenever the plugin internally
314  * cleans up a session pointer and hence the service needs to
315  * discard all of those sessions as well.  Plugins that do not
316  * use sessions can simply omit calling this function and always
317  * use NULL wherever a session pointer is needed.  This function
318  * should be called BEFORE a potential "TransmitContinuation"
319  * from the "TransmitFunction".
320  *
321  * @param cls closure
322  * @param peer which peer was the session for
323  * @param session which session is being destoyed
324  */
325 static void
326 plugin_env_session_end (void *cls, const struct GNUNET_PeerIdentity *peer,
327                         struct Session *session)
328 {
329 #if DEBUG_TRANSPORT
330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Session %X to peer `%s' ended \n",
331               session, GNUNET_i2s (peer));
332 #endif
333   if (NULL != session)
334     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
335                      "transport-ats",
336                      "Telling ATS to destroy session %p from peer %s\n",
337                      session, GNUNET_i2s (peer));
338   GNUNET_ATS_address_destroyed (GST_ats, peer, NULL, NULL, 0, session);
339   GST_neighbours_session_terminated (peer, session);
340 }
341
342
343 /**
344  * Function called by ATS to notify the callee that the
345  * assigned bandwidth or address for a given peer was changed.  If the
346  * callback is called with address/bandwidth assignments of zero, the
347  * ATS disconnect function will still be called once the disconnect
348  * actually happened.
349  *
350  * @param cls closure
351  * @param peer identity of the peer
352  * @param plugin_name name of the transport plugin, NULL to disconnect
353  * @param session session to use (if available)
354  * @param plugin_addr address to use (if available)
355  * @param plugin_addr_len number of bytes in addr
356  * @param bandwidth_out assigned outbound bandwidth for the connection, 0 to disconnect from peer
357  * @param bandwidth_in assigned inbound bandwidth for the connection, 0 to disconnect from peer
358  */
359 static void
360 ats_request_address_change (void *cls, const struct GNUNET_PeerIdentity *peer,
361                             const char *plugin_name, const void *plugin_addr,
362                             size_t plugin_addr_len, struct Session *session,
363                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
364                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
365                             const struct GNUNET_ATS_Information *ats,
366                             uint32_t ats_count)
367 {
368   uint32_t bw_in = ntohl (bandwidth_in.value__);
369   uint32_t bw_out = ntohl (bandwidth_out.value__);
370
371   /* ATS tells me to disconnect from peer */
372   if ((bw_in == 0) && (bw_out == 0))
373   {
374 #if DEBUG_TRANSPORT
375     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376                 "ATS tells me to disconnect from peer `%s'\n",
377                 GNUNET_i2s (peer));
378 #endif
379     GST_neighbours_force_disconnect (peer);
380     return;
381   }
382   /* will never return GNUNET_YES since connection is to be established */
383   GST_neighbours_switch_to_address_3way (peer, plugin_name, plugin_addr,
384                                          plugin_addr_len, session, ats,
385                                          ats_count, bandwidth_in,
386                                          bandwidth_out);
387 }
388
389
390 /**
391  * Function called to notify transport users that another
392  * peer connected to us.
393  *
394  * @param cls closure
395  * @param peer the peer that connected
396  * @param ats performance data
397  * @param ats_count number of entries in ats
398  */
399 static void
400 neighbours_connect_notification (void *cls,
401                                  const struct GNUNET_PeerIdentity *peer,
402                                  const struct GNUNET_ATS_Information *ats,
403                                  uint32_t ats_count)
404 {
405   size_t len =
406       sizeof (struct ConnectInfoMessage) +
407       ats_count * sizeof (struct GNUNET_ATS_Information);
408   char buf[len];
409   struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf;
410   struct GNUNET_ATS_Information *ap;
411
412   connect_msg->header.size = htons (sizeof (buf));
413   connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
414   connect_msg->ats_count = htonl (ats_count);
415   connect_msg->id = *peer;
416   ap = (struct GNUNET_ATS_Information *) &connect_msg[1];
417   memcpy (ap, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
418   GST_clients_broadcast (&connect_msg->header, GNUNET_NO);
419 }
420
421
422 /**
423  * Function called to notify transport users that another
424  * peer disconnected from us.
425  *
426  * @param cls closure
427  * @param peer the peer that disconnected
428  */
429 static void
430 neighbours_disconnect_notification (void *cls,
431                                     const struct GNUNET_PeerIdentity *peer)
432 {
433   struct DisconnectInfoMessage disconnect_msg;
434
435   disconnect_msg.header.size = htons (sizeof (struct DisconnectInfoMessage));
436   disconnect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
437   disconnect_msg.reserved = htonl (0);
438   disconnect_msg.peer = *peer;
439   GST_clients_broadcast (&disconnect_msg.header, GNUNET_NO);
440 }
441
442
443 /**
444  * Function called when the service shuts down.  Unloads our plugins
445  * and cancels pending validations.
446  *
447  * @param cls closure, unused
448  * @param tc task context (unused)
449  */
450 static void
451 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
452 {
453   GST_validation_stop ();
454   GST_neighbours_stop ();
455   GST_plugins_unload ();
456
457   GNUNET_ATS_scheduling_done (GST_ats);
458   GST_ats = NULL;
459   GST_clients_stop ();
460   GST_blacklist_stop ();
461   GST_hello_stop ();
462
463   if (GST_peerinfo != NULL)
464   {
465     GNUNET_PEERINFO_disconnect (GST_peerinfo);
466     GST_peerinfo = NULL;
467   }
468   if (GST_stats != NULL)
469   {
470     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
471     GST_stats = NULL;
472   }
473   if (GST_my_private_key != NULL)
474   {
475     GNUNET_CRYPTO_rsa_key_free (GST_my_private_key);
476     GST_my_private_key = NULL;
477   }
478 }
479
480
481 /**
482  * Initiate transport service.
483  *
484  * @param cls closure
485  * @param server the initialized server
486  * @param c configuration to use
487  */
488 static void
489 run (void *cls, struct GNUNET_SERVER_Handle *server,
490      const struct GNUNET_CONFIGURATION_Handle *c)
491 {
492   char *keyfile;
493
494   /* setup globals */
495   GST_cfg = c;
496   if (GNUNET_OK !=
497       GNUNET_CONFIGURATION_get_value_filename (c, "GNUNETD", "HOSTKEY",
498                                                &keyfile))
499   {
500     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
501                 _
502                 ("Transport service is lacking key configuration settings.  Exiting.\n"));
503     GNUNET_SCHEDULER_shutdown ();
504     return;
505   }
506   GST_my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
507   GNUNET_free (keyfile);
508   if (GST_my_private_key == NULL)
509   {
510     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
511                 _("Transport service could not access hostkey.  Exiting.\n"));
512     GNUNET_SCHEDULER_shutdown ();
513     return;
514   }
515   GST_stats = GNUNET_STATISTICS_create ("transport", c);
516   GST_peerinfo = GNUNET_PEERINFO_connect (c);
517   GNUNET_CRYPTO_rsa_key_get_public (GST_my_private_key, &GST_my_public_key);
518   GNUNET_CRYPTO_hash (&GST_my_public_key, sizeof (GST_my_public_key),
519                       &GST_my_identity.hashPubKey);
520   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
521                                 NULL);
522   if (GST_peerinfo == NULL)
523   {
524     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
525                 _("Could not access PEERINFO service.  Exiting.\n"));
526     GNUNET_SCHEDULER_shutdown ();
527     return;
528   }
529
530   /* start subsystems */
531   GST_hello_start (&process_hello_update, NULL);
532   GST_blacklist_start (server);
533   GST_plugins_load (&plugin_env_receive_callback,
534                     &plugin_env_address_change_notification,
535                     &plugin_env_session_end);
536   GST_ats =
537       GNUNET_ATS_scheduling_init (GST_cfg, &ats_request_address_change, NULL);
538   GST_neighbours_start (NULL, &neighbours_connect_notification,
539                         &neighbours_disconnect_notification);
540   GST_clients_start (server);
541   GST_validation_start ();
542 }
543
544
545 /**
546  * The main function for the transport service.
547  *
548  * @param argc number of arguments from the command line
549  * @param argv command line arguments
550  * @return 0 ok, 1 on error
551  */
552 int
553 main (int argc, char *const *argv)
554 {
555   return (GNUNET_OK ==
556           GNUNET_SERVICE_run (argc, argv, "transport",
557                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
558 }
559
560 /* end of file gnunet-service-transport-new.c */