get_address_latency also does not use session
[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 main for gnunet-service-transport
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_hello_lib.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_transport_service.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_ats_service.h"
33 #include "gnunet-service-transport.h"
34 #include "gnunet-service-transport_ats.h"
35 #include "gnunet-service-transport_blacklist.h"
36 #include "gnunet-service-transport_clients.h"
37 #include "gnunet-service-transport_hello.h"
38 #include "gnunet-service-transport_neighbours.h"
39 #include "gnunet-service-transport_plugins.h"
40 #include "gnunet-service-transport_validation.h"
41 #include "gnunet-service-transport_manipulation.h"
42 #include "transport.h"
43
44 /**
45  * Information we need for an asynchronous session kill.
46  */
47 struct SessionKiller
48 {
49   /**
50    * Kept in a DLL.
51    */
52   struct SessionKiller *next;
53
54   /**
55    * Kept in a DLL.
56    */
57   struct SessionKiller *prev;
58
59   /**
60    * Session to kill.
61    */
62   struct Session *session;
63
64   /**
65    * Plugin for the session.
66    */
67   struct GNUNET_TRANSPORT_PluginFunctions *plugin;
68
69   /**
70    * The kill task.
71    */
72   struct GNUNET_SCHEDULER_Task * task;
73 };
74
75
76 struct BlacklistCheckContext
77 {
78   struct BlacklistCheckContext *prev;
79
80   struct BlacklistCheckContext *next;
81
82   struct GST_BlacklistCheck *blc;
83
84   struct GNUNET_HELLO_Address *address;
85
86   struct Session *session;
87
88   struct GNUNET_MessageHeader *msg;
89
90 };
91
92 /* globals */
93
94 /**
95  * Statistics handle.
96  */
97 struct GNUNET_STATISTICS_Handle *GST_stats;
98
99 /**
100  * Configuration handle.
101  */
102 const struct GNUNET_CONFIGURATION_Handle *GST_cfg;
103
104 /**
105  * Configuration handle.
106  */
107 struct GNUNET_PeerIdentity GST_my_identity;
108
109 /**
110  * Handle to peerinfo service.
111  */
112 struct GNUNET_PEERINFO_Handle *GST_peerinfo;
113
114 /**
115  * Handle to our service's server.
116  */
117 static struct GNUNET_SERVER_Handle *GST_server;
118
119 /**
120  * Our private key.
121  */
122 struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key;
123
124 /**
125  * ATS handle.
126  */
127 struct GNUNET_ATS_SchedulingHandle *GST_ats;
128
129 /**
130  * Hello address expiration
131  */
132 struct GNUNET_TIME_Relative hello_expiration;
133
134 /**
135  * DEBUGGING connection counter
136  */
137 static int connections;
138
139 /**
140  * Head of DLL of asynchronous tasks to kill sessions.
141  */
142 static struct SessionKiller *sk_head;
143
144 /**
145  * Tail of DLL of asynchronous tasks to kill sessions.
146  */
147 static struct SessionKiller *sk_tail;
148
149 /**
150  * FIXME
151  */
152 struct BlacklistCheckContext *bc_head;
153
154 /**
155  * FIXME
156  */
157 struct BlacklistCheckContext *bc_tail;
158
159
160 /**
161  * Transmit our HELLO message to the given (connected) neighbour.
162  *
163  * @param cls the 'HELLO' message
164  * @param peer identity of the peer
165  * @param address the address
166  * @param state current state this peer is in
167  * @param state_timeout timeout for the current state of the peer
168  * @param bandwidth_in inbound quota in NBO
169  * @param bandwidth_out outbound quota in NBO
170  */
171 static void
172 transmit_our_hello (void *cls,
173                     const struct GNUNET_PeerIdentity *peer,
174                     const struct GNUNET_HELLO_Address *address,
175                     enum GNUNET_TRANSPORT_PeerState state,
176                     struct GNUNET_TIME_Absolute state_timeout,
177                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
178                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
179 {
180   const struct GNUNET_MessageHeader *hello = cls;
181
182   if (GNUNET_NO == GST_neighbours_test_connected (peer))
183     return;
184
185   GST_neighbours_send (peer,
186                        hello,
187                        ntohs (hello->size),
188                        hello_expiration,
189                        NULL, NULL);
190 }
191
192
193 /**
194  * My HELLO has changed. Tell everyone who should know.
195  *
196  * @param cls unused
197  * @param hello new HELLO
198  */
199 static void
200 process_hello_update (void *cls, const struct GNUNET_MessageHeader *hello)
201 {
202   GST_clients_broadcast (hello, GNUNET_NO);
203   GST_neighbours_iterate (&transmit_our_hello, (void *) hello);
204 }
205
206
207 /**
208  * We received some payload.  Prepare to pass it on to our clients.
209  *
210  * @param address address and (claimed) identity of the other peer
211  * @param session identifier used for this session (NULL for plugins
212  *                that do not offer bi-directional communication to the sender
213  *                using the same "connection")
214  * @param message the message to process
215  * @return how long the plugin should wait until receiving more data
216  */
217 static struct GNUNET_TIME_Relative
218 process_payload (const struct GNUNET_HELLO_Address *address,
219                  struct Session *session,
220                  const struct GNUNET_MessageHeader *message)
221 {
222   struct GNUNET_TIME_Relative ret;
223   int do_forward;
224   struct InboundMessage *im;
225   size_t msg_size = ntohs (message->size);
226   size_t size = sizeof(struct InboundMessage) + msg_size;
227   char buf[size] GNUNET_ALIGN;
228
229   do_forward = GNUNET_SYSERR;
230   ret = GST_neighbours_calculate_receive_delay (&address->peer,
231                                                 msg_size,
232                                                 &do_forward);
233   if (! GST_neighbours_test_connected (&address->peer))
234   {
235     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
236                 "Discarded %u bytes type %u payload from peer `%s'\n",
237                 msg_size,
238                 ntohs (message->type),
239                 GNUNET_i2s (&address->peer));
240     GNUNET_STATISTICS_update (GST_stats, gettext_noop
241                               ("# bytes payload discarded due to not connected peer"),
242                               msg_size,
243                               GNUNET_NO);
244     return ret;
245   }
246
247   if (GNUNET_YES != do_forward)
248     return ret;
249   im = (struct InboundMessage *) buf;
250   im->header.size = htons (size);
251   im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
252   im->peer = address->peer;
253   memcpy (&im[1], message, ntohs (message->size));
254   GST_clients_broadcast (&im->header, GNUNET_YES);
255   return ret;
256 }
257
258
259 /**
260  * Task to asynchronously terminate a session.
261  *
262  * @param cls the `struct SessionKiller` with the information for the kill
263  * @param tc scheduler context
264  */
265 static void
266 kill_session_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
267 {
268   struct SessionKiller *sk = cls;
269
270   sk->task = NULL;
271   GNUNET_CONTAINER_DLL_remove(sk_head, sk_tail, sk);
272   sk->plugin->disconnect_session (sk->plugin->cls, sk->session);
273   GNUNET_free(sk);
274 }
275
276
277 /**
278  * FIXME.  Also, consider moving the "bc_*" logic into
279  * blacklist.h?
280  */
281 static void
282 cancel_pending_blacklist_checks (const struct GNUNET_HELLO_Address *address,
283                                  struct Session *session)
284 {
285   struct BlacklistCheckContext *blctx;
286   struct BlacklistCheckContext *next;
287
288   next = bc_head;
289   for (blctx = next; NULL != blctx; blctx = next)
290   {
291     next = blctx->next;
292     if ( (NULL != blctx->address) &&
293          (0 == GNUNET_HELLO_address_cmp(blctx->address, address)) &&
294          (blctx->session == session))
295     {
296       GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, blctx);
297       if (NULL != blctx->blc)
298       {
299         GST_blacklist_test_cancel (blctx->blc);
300         blctx->blc = NULL;
301       }
302       GNUNET_HELLO_address_free (blctx->address);
303       GNUNET_free_non_null (blctx->msg);
304       GNUNET_free (blctx);
305     }
306   }
307 }
308
309
310 /**
311  * Force plugin to terminate session due to communication
312  * issue.
313  *
314  * @param plugin_name name of the plugin
315  * @param session session to termiante
316  */
317 static void
318 kill_session (const char *plugin_name,
319               struct Session *session)
320 {
321   struct GNUNET_TRANSPORT_PluginFunctions *plugin;
322   struct SessionKiller *sk;
323
324   for (sk = sk_head; NULL != sk; sk = sk->next)
325     if (sk->session == session)
326       return;
327   plugin = GST_plugins_find (plugin_name);
328   if (NULL == plugin)
329   {
330     GNUNET_break(0);
331     return;
332   }
333   /* need to issue disconnect asynchronously */
334   sk = GNUNET_new (struct SessionKiller);
335   sk->session = session;
336   sk->plugin = plugin;
337   sk->task = GNUNET_SCHEDULER_add_now (&kill_session_task, sk);
338   GNUNET_CONTAINER_DLL_insert (sk_head,
339                                sk_tail,
340                                sk);
341 }
342
343
344 /**
345  * Black list check result for try_connect call
346  * If connection to the peer is allowed request adddress and ???
347  *
348  * @param cls blc_ctx bl context
349  * @param peer the peer
350  * @param result the result
351  */
352 static void
353 connect_bl_check_cont (void *cls,
354                        const struct GNUNET_PeerIdentity *peer,
355                        int result)
356 {
357   struct BlacklistCheckContext *blctx = cls;
358
359   GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, blctx);
360   blctx->blc = NULL;
361
362   if (GNUNET_OK == result)
363   {
364     /* Blacklist allows to speak to this peer, forward SYN to neighbours  */
365     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
366                 "Received SYN message from peer `%s' with `%s' %p\n",
367                 GNUNET_i2s (peer),
368                 GST_plugins_a2s (blctx->address),
369                 blctx->session);
370
371     if (GNUNET_OK !=
372         GST_neighbours_handle_session_syn (blctx->msg,
373                                            &blctx->address->peer))
374     {
375       cancel_pending_blacklist_checks (blctx->address, blctx->session);
376       kill_session (blctx->address->transport_name, blctx->session);
377     }
378   }
379   else
380   {
381     /* Blacklist denies to speak to this peer */
382
383     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
384         "Discarding SYN message from `%s' due to denied blacklist check\n",
385         GNUNET_i2s (peer));
386     cancel_pending_blacklist_checks (blctx->address, blctx->session);
387     kill_session (blctx->address->transport_name, blctx->session);
388   }
389
390   if (NULL != blctx->address)
391     GNUNET_HELLO_address_free (blctx->address);
392   GNUNET_free (blctx->msg);
393   GNUNET_free (blctx);
394 }
395
396
397 /**
398  * Function called by the transport for each received message.
399  *
400  * @param cls closure, const char* with the name of the plugin we received the message from
401  * @param address address and (claimed) identity of the other peer
402  * @param message the message, NULL if we only care about
403  *                learning about the delay until we should receive again
404  * @param session identifier used for this session (NULL for plugins
405  *                that do not offer bi-directional communication to the sender
406  *                using the same "connection")
407  * @return how long the plugin should wait until receiving more data
408  *         (plugins that do not support this, can ignore the return value)
409  */
410 struct GNUNET_TIME_Relative
411 GST_receive_callback (void *cls,
412                       const struct GNUNET_HELLO_Address *address,
413                       struct Session *session,
414                       const struct GNUNET_MessageHeader *message)
415 {
416   const char *plugin_name = cls;
417   struct GNUNET_TIME_Relative ret;
418   struct BlacklistCheckContext *blctx;
419   struct GST_BlacklistCheck *blc;
420   uint16_t type;
421
422   ret = GNUNET_TIME_UNIT_ZERO;
423   if (NULL == message)
424     goto end;
425   type = ntohs (message->type);
426   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427               "Received Message with type %u from peer `%s'\n",
428               type,
429               GNUNET_i2s (&address->peer));
430
431   GNUNET_STATISTICS_update (GST_stats,
432                             gettext_noop ("# bytes total received"),
433                             ntohs (message->size),
434                             GNUNET_NO);
435   GST_neighbours_notify_data_recv (address,
436                                    session,
437                                    message);
438   switch (type)
439   {
440   case GNUNET_MESSAGE_TYPE_HELLO_LEGACY:
441     /* Legacy HELLO message, discard  */
442     return ret;
443   case GNUNET_MESSAGE_TYPE_HELLO:
444     if (GNUNET_OK != GST_validation_handle_hello (message))
445     {
446       GNUNET_break_op (0);
447       cancel_pending_blacklist_checks (address,
448                                        session);
449     }
450     return ret;
451   case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
452     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453                 "Processing `%s' from `%s'\n", "PING",
454                 GST_plugins_a2s (address));
455     if (GNUNET_OK !=
456         GST_validation_handle_ping (&address->peer,
457                                     message,
458                                     address,
459                                     session))
460     {
461       cancel_pending_blacklist_checks (address,
462                                        session);
463       kill_session (plugin_name,
464                     session);
465     }
466     break;
467   case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
468     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
469                "Processing `%s' from `%s'\n", "PONG",
470                GST_plugins_a2s (address));
471     if (GNUNET_OK != GST_validation_handle_pong (&address->peer, message))
472     {
473       GNUNET_break_op(0);
474       cancel_pending_blacklist_checks (address, session);
475       kill_session (plugin_name, session);
476     }
477     break;
478   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN:
479     /* Do blacklist check if communication with this peer is allowed */
480     blctx = GNUNET_new (struct BlacklistCheckContext);
481     blctx->address = GNUNET_HELLO_address_copy (address);
482     blctx->session = session;
483     blctx->msg = GNUNET_malloc (ntohs(message->size));
484     memcpy (blctx->msg, message, ntohs(message->size));
485     GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, blctx);
486     if (NULL != (blc = GST_blacklist_test_allowed (&address->peer, NULL,
487                                                    &connect_bl_check_cont,
488                                                    blctx)))
489     {
490       blctx->blc = blc;
491     }
492     break;
493   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK:
494     if (GNUNET_OK !=
495         GST_neighbours_handle_session_syn_ack (message,
496                                                address,
497                                                session))
498     {
499       cancel_pending_blacklist_checks (address, session);
500       kill_session (plugin_name, session);
501     }
502     break;
503   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
504     if (GNUNET_OK !=
505         GST_neighbours_handle_session_ack (message,
506                                            address,
507                                            session))
508     {
509       GNUNET_break_op(0);
510       cancel_pending_blacklist_checks (address, session);
511       kill_session (plugin_name, session);
512     }
513     break;
514   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
515     GST_neighbours_handle_disconnect_message (&address->peer, message);
516     break;
517   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
518     GST_neighbours_keepalive (&address->peer, message);
519     break;
520   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE:
521     GST_neighbours_keepalive_response (&address->peer, message);
522     break;
523   default:
524     /* should be payload */
525     GNUNET_STATISTICS_update (GST_stats,
526                               gettext_noop ("# bytes payload received"),
527                               ntohs (message->size),
528                               GNUNET_NO);
529     GST_neighbours_notify_payload_recv (address,
530                                         session,
531                                         message);
532     ret = process_payload (address,
533                            session,
534                            message);
535     break;
536   }
537   end:
538   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
539               "Allowing receive from peer %s to continue in %s\n",
540               GNUNET_i2s (&address->peer),
541               GNUNET_STRINGS_relative_time_to_string (ret,
542                                                       GNUNET_YES));
543   return ret;
544 }
545
546
547 /**
548  * Function that will be called for each address the transport
549  * is aware that it might be reachable under.  Update our HELLO.
550  *
551  * @param cls name of the plugin (const char*)
552  * @param add_remove should the address added (YES) or removed (NO) from the
553  *                   set of valid addresses?
554  * @param address the address to add or remove
555  */
556 static void
557 plugin_env_address_change_notification (void *cls,
558                                         int add_remove,
559                                         const struct GNUNET_HELLO_Address *address)
560 {
561   static int addresses = 0;
562   struct GNUNET_STATISTICS_Handle *cfg = GST_stats;
563
564   if (GNUNET_YES == add_remove)
565   {
566     addresses ++;
567     GNUNET_STATISTICS_update (cfg, "# transport addresses", 1, GNUNET_NO);
568   }
569   else if (GNUNET_NO == add_remove)
570   {
571     if (0 == addresses)
572       GNUNET_break (0);
573     else
574     {
575       addresses --;
576       GNUNET_STATISTICS_update (cfg, "# transport addresses", -1, GNUNET_NO);
577     }
578   }
579
580   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
581               "Transport now has %u addresses to communicate\n",
582               addresses);
583   GST_hello_modify_addresses (add_remove, address);
584 }
585
586
587 /**
588  * Function that will be called whenever the plugin internally
589  * cleans up a session pointer and hence the service needs to
590  * discard all of those sessions as well.  Plugins that do not
591  * use sessions can simply omit calling this function and always
592  * use NULL wherever a session pointer is needed.  This function
593  * should be called BEFORE a potential "TransmitContinuation"
594  * from the "TransmitFunction".
595  *
596  * @param cls closure
597  * @param address which address was the session for
598  * @param session which session is being destoyed
599  */
600 static void
601 plugin_env_session_end (void *cls,
602                         const struct GNUNET_HELLO_Address *address,
603                         struct Session *session)
604 {
605   struct SessionKiller *sk;
606
607   if (NULL == address)
608   {
609     GNUNET_break (0);
610     return;
611   }
612   if (NULL == session)
613   {
614     GNUNET_break (0);
615     return;
616   }
617   GNUNET_assert (strlen (address->transport_name) > 0);
618
619   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
620               "Notification from plugin `%s' about terminated %s session %p from peer `%s' address `%s'\n",
621               address->transport_name,
622               GNUNET_HELLO_address_check_option (address,
623                                                  GNUNET_HELLO_ADDRESS_INFO_INBOUND) ? "inbound" : "outbound",
624               session,
625               GNUNET_i2s (&address->peer),
626               GST_plugins_a2s (address));
627
628   GST_neighbours_session_terminated (&address->peer, session);
629   GST_ats_del_session (address, session);
630   cancel_pending_blacklist_checks (address, session);
631
632   for (sk = sk_head; NULL != sk; sk = sk->next)
633   {
634     if (sk->session == session)
635     {
636       GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk);
637       GNUNET_SCHEDULER_cancel (sk->task);
638       GNUNET_free(sk);
639       break;
640     }
641   }
642 }
643
644
645 /**
646  * Function that will be called to figure if an address is an loopback,
647  * LAN, WAN etc. address
648  *
649  * @param cls closure
650  * @param addr binary address
651  * @param addrlen length of the @a addr
652  * @return type of the network @a addr belongs to
653  */
654 static enum GNUNET_ATS_Network_Type
655 plugin_env_address_to_type (void *cls,
656                             const struct sockaddr *addr,
657                             size_t addrlen)
658 {
659   if (NULL == GST_ats)
660   {
661     GNUNET_break(0);
662     return GNUNET_ATS_NET_UNSPECIFIED;
663   }
664   return GNUNET_ATS_address_get_type (GST_ats,
665                                       addr,
666                                       addrlen);
667 }
668
669
670 /**
671  * Function that will be called to update metrics for an address
672  *
673  * @param cls closure
674  * @param address address to update metrics for
675  * @param session the session
676  * @param ats the ats information to update
677  * @param ats_count the number of @a ats elements
678  */
679 static void
680 plugin_env_update_metrics (void *cls,
681                            const struct GNUNET_HELLO_Address *address,
682                            struct Session *session,
683                            const struct GNUNET_ATS_Information *ats,
684                            uint32_t ats_count)
685 {
686   GST_ats_update_metrics (address,
687                           session,
688                           ats, ats_count);
689 }
690
691
692 /**
693  * Black list check result from blacklist check triggered when a
694  * plugin gave us a new session in #plugin_env_session_start().  If
695  * connection to the peer is disallowed, kill the session.
696  *
697  * @param cls blc_ctx bl context
698  * @param peer the peer
699  * @param result the result
700  */
701 static void
702 plugin_env_session_start_bl_check_cont (void *cls,
703                                         const struct GNUNET_PeerIdentity *peer,
704                                         int result)
705 {
706   struct BlacklistCheckContext *blctx = cls;
707
708   GNUNET_CONTAINER_DLL_remove (bc_head,
709                                bc_tail,
710                                blctx);
711   blctx->blc = NULL;
712   if (GNUNET_OK != result)
713   {
714     cancel_pending_blacklist_checks (blctx->address,
715                                      blctx->session);
716     kill_session (blctx->address->transport_name,
717                   blctx->session);
718   }
719   GNUNET_HELLO_address_free (blctx->address);
720   GNUNET_free (blctx);
721 }
722
723
724 /**
725  * Plugin tells transport service about a new inbound session
726  *
727  * @param cls unused
728  * @param address the address
729  * @param session the new session
730  * @param ats ats information
731  * @param ats_count number of @a ats information
732  */
733 static void
734 plugin_env_session_start (void *cls,
735                           const struct GNUNET_HELLO_Address *address,
736                           struct Session *session,
737                           const struct GNUNET_ATS_Information *ats,
738                           uint32_t ats_count)
739 {
740   struct BlacklistCheckContext *blctx;
741   struct GST_BlacklistCheck *blc;
742
743   if (NULL == address)
744   {
745     GNUNET_break(0);
746     return;
747   }
748   if (NULL == session)
749   {
750     GNUNET_break(0);
751     return;
752   }
753   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
754               "Notification from plugin `%s' about new %s session %p from peer `%s' address `%s'\n",
755               address->transport_name,
756               GNUNET_HELLO_address_check_option (address,
757                                                  GNUNET_HELLO_ADDRESS_INFO_INBOUND) ? "inbound" : "outbound",
758               session,
759               GNUNET_i2s (&address->peer),
760               GST_plugins_a2s (address));
761   if ( (GNUNET_YES ==
762         GNUNET_HELLO_address_check_option (address,
763                                            GNUNET_HELLO_ADDRESS_INFO_INBOUND)) ||
764        (GNUNET_NO ==
765         GST_ats_is_known (address, NULL) ) )
766   {
767     /* inbound is always new, but outbound MAY already be known, but
768        for example for UNIX, we have symmetric connections and thus we
769        may not know the address yet; add if necessary! */
770     GST_ats_add_address (address,
771                          session,
772                          ats,
773                          ats_count);
774   }
775   else
776   {
777     GST_ats_new_session (address,
778                          session);
779     GST_ats_update_metrics (address,
780                             session,
781                             ats,
782                             ats_count);
783   }
784   /* Do blacklist check if communication with this peer is allowed */
785   blctx = GNUNET_new (struct BlacklistCheckContext);
786   blctx->address = GNUNET_HELLO_address_copy (address);
787   blctx->session = session;
788   GNUNET_CONTAINER_DLL_insert (bc_head,
789                                bc_tail,
790                                blctx);
791   if (NULL !=
792       (blc = GST_blacklist_test_allowed (&address->peer,
793                                          address->transport_name,
794                                          &plugin_env_session_start_bl_check_cont,
795                                          blctx)))
796   {
797     blctx->blc = blc;
798   }
799 }
800
801
802 /**
803  * Function called by ATS to notify the callee that the
804  * assigned bandwidth or address for a given peer was changed.  If the
805  * callback is called with address/bandwidth assignments of zero, the
806  * ATS disconnect function will still be called once the disconnect
807  * actually happened.
808  *
809  * @param cls closure
810  * @param peer the peer this address is intended for
811  * @param address address to use (for peer given in address)
812  * @param session session to use (if available)
813  * @param bandwidth_out assigned outbound bandwidth for the connection in NBO,
814  *      0 to disconnect from peer
815  * @param bandwidth_in assigned inbound bandwidth for the connection in NBO,
816  *      0 to disconnect from peer
817  * @param ats ATS information
818  * @param ats_count number of @a ats elements
819  */
820 static void
821 ats_request_address_change (void *cls,
822                             const struct GNUNET_PeerIdentity *peer,
823                             const struct GNUNET_HELLO_Address *address,
824                             struct Session *session,
825                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
826                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
827 {
828   uint32_t bw_in = ntohl (bandwidth_in.value__);
829   uint32_t bw_out = ntohl (bandwidth_out.value__);
830
831   /* ATS tells me to disconnect from peer */
832   if ((0 == bw_in) && (0 == bw_out))
833   {
834     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
835                 "ATS tells me to disconnect from peer `%s'\n",
836                 GNUNET_i2s (&address->peer));
837     GST_neighbours_force_disconnect (&address->peer);
838     return;
839   }
840
841   GST_neighbours_switch_to_address (address,
842                                     session,
843                                     bandwidth_in, bandwidth_out);
844 }
845
846
847 /**
848  * Function called to notify transport users that another
849  * peer connected to us.
850  *
851  * @param cls closure
852  * @param peer the peer that connected
853  * @param bandwidth_in inbound bandwidth in NBO
854  * @param bandwidth_out outbound bandwidth in NBO
855  */
856 static void
857 neighbours_connect_notification (void *cls,
858                                  const struct GNUNET_PeerIdentity *peer,
859                                  struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
860                                  struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
861 {
862   size_t len = sizeof(struct ConnectInfoMessage);
863   char buf[len] GNUNET_ALIGN;
864   struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf;
865
866   connections++;
867   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
868               "We are now connected to peer `%s' and %u peers in total\n",
869               GNUNET_i2s (peer),
870               connections);
871   connect_msg->header.size = htons (sizeof(buf));
872   connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
873   connect_msg->id = *peer;
874   connect_msg->quota_in = bandwidth_in;
875   connect_msg->quota_out = bandwidth_out;
876   GST_clients_broadcast (&connect_msg->header, GNUNET_NO);
877 }
878
879
880 /**
881  * Function called to notify transport users that another
882  * peer disconnected from us.
883  *
884  * @param cls closure
885  * @param peer the peer that disconnected
886  */
887 static void
888 neighbours_disconnect_notification (void *cls,
889                                     const struct GNUNET_PeerIdentity *peer)
890 {
891   struct DisconnectInfoMessage disconnect_msg;
892
893   connections--;
894   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
895               "Peer `%s' disconnected and we are connected to %u peers\n",
896               GNUNET_i2s (peer),
897               connections);
898
899   GST_manipulation_peer_disconnect (peer);
900   disconnect_msg.header.size = htons (sizeof(struct DisconnectInfoMessage));
901   disconnect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
902   disconnect_msg.reserved = htonl (0);
903   disconnect_msg.peer = *peer;
904   GST_clients_broadcast (&disconnect_msg.header, GNUNET_NO);
905 }
906
907
908 /**
909  * Function called to notify transport users that a neighbour peer changed its
910  * active address.
911  *
912  * @param cls closure
913  * @param peer identity of the peer
914  * @param address address possibly NULL if peer is not connected
915  * @param state current state this peer is in
916  * @param state_timeout timeout for the current state of the peer
917  * @param bandwidth_in bandwidth assigned inbound, 0 on disconnect
918  * @param bandwidth_out bandwidth assigned outbound, 0 on disconnect
919  */
920 static void
921 neighbours_changed_notification (void *cls,
922                                  const struct GNUNET_PeerIdentity *peer,
923                                  const struct GNUNET_HELLO_Address *address,
924                                  enum GNUNET_TRANSPORT_PeerState state,
925                                  struct GNUNET_TIME_Absolute state_timeout,
926                                  struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
927                                  struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
928 {
929   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
930               "Notifying about change for peer `%s' with address `%s' in state `%s' timing out at %s\n",
931               GNUNET_i2s (peer),
932               GST_plugins_a2s (address),
933               GNUNET_TRANSPORT_ps2s (state),
934               GNUNET_STRINGS_absolute_time_to_string (state_timeout));
935   /* FIXME: include bandwidth in notification! */
936   GST_clients_broadcast_peer_notification (peer,
937                                            address,
938                                            state,
939                                            state_timeout);
940 }
941
942
943 /**
944  * Function called when the service shuts down.  Unloads our plugins
945  * and cancels pending validations.
946  *
947  * @param cls closure, unused
948  * @param tc task context (unused)
949  */
950 static void
951 shutdown_task (void *cls,
952                const struct GNUNET_SCHEDULER_TaskContext *tc)
953 {
954   GST_neighbours_stop ();
955   GST_plugins_unload ();
956   GST_validation_stop ();
957   GST_ats_done ();
958   GNUNET_ATS_scheduling_done (GST_ats);
959   GST_ats = NULL;
960   GST_clients_stop ();
961   GST_blacklist_stop ();
962   GST_hello_stop ();
963   GST_manipulation_stop ();
964
965   if (NULL != GST_peerinfo)
966   {
967     GNUNET_PEERINFO_disconnect (GST_peerinfo);
968     GST_peerinfo = NULL;
969   }
970   if (NULL != GST_stats)
971   {
972     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
973     GST_stats = NULL;
974   }
975   if (NULL != GST_my_private_key)
976   {
977     GNUNET_free(GST_my_private_key);
978     GST_my_private_key = NULL;
979   }
980   GST_server = NULL;
981 }
982
983
984 /**
985  * Initiate transport service.
986  *
987  * @param cls closure
988  * @param server the initialized server
989  * @param c configuration to use
990  */
991 static void
992 run (void *cls,
993      struct GNUNET_SERVER_Handle *server,
994      const struct GNUNET_CONFIGURATION_Handle *c)
995 {
996   char *keyfile;
997   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
998   long long unsigned int max_fd_cfg;
999   int max_fd_rlimit;
1000   int max_fd;
1001   int friend_only;
1002
1003   /* setup globals */
1004   GST_cfg = c;
1005   if (GNUNET_OK
1006       != GNUNET_CONFIGURATION_get_value_filename (c, "PEER", "PRIVATE_KEY",
1007           &keyfile))
1008   {
1009     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1010         _("Transport service is lacking key configuration settings. Exiting.\n"));
1011     GNUNET_SCHEDULER_shutdown ();
1012     return;
1013   }
1014   if (GNUNET_OK !=
1015       GNUNET_CONFIGURATION_get_value_time (c,
1016                                            "transport",
1017                                            "HELLO_EXPIRATION",
1018                                            &hello_expiration))
1019   {
1020     hello_expiration = GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION;
1021   }
1022   GST_server = server;
1023   pk = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
1024   GNUNET_free (keyfile);
1025   GNUNET_assert (NULL != pk);
1026   GST_my_private_key = pk;
1027
1028   GST_stats = GNUNET_STATISTICS_create ("transport", GST_cfg);
1029   GST_peerinfo = GNUNET_PEERINFO_connect (GST_cfg);
1030   GNUNET_CRYPTO_eddsa_key_get_public (GST_my_private_key,
1031                                       &GST_my_identity.public_key);
1032   GNUNET_assert(NULL != GST_my_private_key);
1033
1034   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1035              "My identity is `%4s'\n",
1036              GNUNET_i2s_full (&GST_my_identity));
1037
1038   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1039                                 &shutdown_task,
1040                                 NULL);
1041   if (NULL == GST_peerinfo)
1042   {
1043     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1044         _("Could not access PEERINFO service.  Exiting.\n"));
1045     GNUNET_SCHEDULER_shutdown ();
1046     return;
1047   }
1048
1049   max_fd_rlimit = 0;
1050   max_fd_cfg = 0;
1051 #if HAVE_GETRLIMIT
1052   struct rlimit r_file;
1053   if (0 == getrlimit (RLIMIT_NOFILE, &r_file))
1054   {
1055     max_fd_rlimit = r_file.rlim_cur;
1056     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1057         "Maximum number of open files was: %u/%u\n",
1058         r_file.rlim_cur,
1059         r_file.rlim_max);
1060   }
1061   max_fd_rlimit = (9 * max_fd_rlimit) / 10; /* Keep 10% for rest of transport */
1062 #endif
1063   GNUNET_CONFIGURATION_get_value_number (GST_cfg,
1064                                          "transport",
1065                                          "MAX_FD",
1066                                          &max_fd_cfg);
1067
1068   if (max_fd_cfg > max_fd_rlimit)
1069     max_fd = max_fd_cfg;
1070   else
1071     max_fd = max_fd_rlimit;
1072   if (max_fd < DEFAULT_MAX_FDS)
1073     max_fd = DEFAULT_MAX_FDS;
1074
1075   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1076               "Limiting number of sockets to %u: validation %u, neighbors: %u\n",
1077              max_fd, (max_fd / 3), (max_fd / 3) * 2);
1078
1079   friend_only = GNUNET_CONFIGURATION_get_value_yesno (GST_cfg, "topology",
1080       "FRIENDS-ONLY");
1081   if (GNUNET_SYSERR == friend_only)
1082     friend_only = GNUNET_NO; /* According to topology defaults */
1083   /* start subsystems */
1084   GST_hello_start (friend_only, &process_hello_update, NULL );
1085   GNUNET_assert(NULL != GST_hello_get());
1086   GST_blacklist_start (GST_server, GST_cfg, &GST_my_identity);
1087   GST_ats = GNUNET_ATS_scheduling_init (GST_cfg,
1088                                         &ats_request_address_change,
1089                                         NULL);
1090   GST_ats_init ();
1091   GST_manipulation_init (GST_cfg);
1092   GST_plugins_load (&GST_manipulation_recv,
1093                     &GST_neighbours_register_quota_notification,
1094                     &GST_neighbours_unregister_quota_notification,
1095                     &plugin_env_address_change_notification,
1096                     &plugin_env_session_start,
1097                     &plugin_env_session_end,
1098                     &plugin_env_address_to_type,
1099                     &plugin_env_update_metrics);
1100   GST_neighbours_start (NULL,
1101                         &neighbours_connect_notification,
1102                         &neighbours_disconnect_notification,
1103                         &neighbours_changed_notification,
1104                         (max_fd / 3) * 2);
1105   GST_clients_start (GST_server);
1106   GST_validation_start ((max_fd / 3));
1107 }
1108
1109
1110 /**
1111  * The main function for the transport service.
1112  *
1113  * @param argc number of arguments from the command line
1114  * @param argv command line arguments
1115  * @return 0 ok, 1 on error
1116  */
1117 int
1118 main (int argc, char * const *argv)
1119 {
1120   return
1121       (GNUNET_OK
1122           == GNUNET_SERVICE_run (argc, argv, "transport",
1123               GNUNET_SERVICE_OPTION_NONE, &run, NULL )) ? 0 : 1;
1124 }
1125
1126 /* end of file gnunet-service-transport.c */