2 This file is part of GNUnet.
3 Copyright (C) 2010-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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @file transport/gnunet-service-transport.c
22 * @brief main for gnunet-service-transport
23 * @author Christian Grothoff
26 #include "gnunet_util_lib.h"
27 #include "gnunet_hello_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_ats.h"
34 #include "gnunet-service-transport_blacklist.h"
35 #include "gnunet-service-transport_clients.h"
36 #include "gnunet-service-transport_hello.h"
37 #include "gnunet-service-transport_neighbours.h"
38 #include "gnunet-service-transport_plugins.h"
39 #include "gnunet-service-transport_validation.h"
40 #include "gnunet-service-transport_manipulation.h"
41 #include "transport.h"
45 * Information we need for an asynchronous session kill.
47 struct GNUNET_ATS_SessionKiller
52 struct GNUNET_ATS_SessionKiller *next;
57 struct GNUNET_ATS_SessionKiller *prev;
62 struct GNUNET_ATS_Session *session;
65 * Plugin for the session.
67 struct GNUNET_TRANSPORT_PluginFunctions *plugin;
72 struct GNUNET_SCHEDULER_Task *task;
81 struct GNUNET_STATISTICS_Handle *GST_stats;
84 * Configuration handle.
86 const struct GNUNET_CONFIGURATION_Handle *GST_cfg;
89 * Configuration handle.
91 struct GNUNET_PeerIdentity GST_my_identity;
94 * Handle to peerinfo service.
96 struct GNUNET_PEERINFO_Handle *GST_peerinfo;
99 * Handle to our service's server.
101 static struct GNUNET_SERVER_Handle *GST_server;
106 struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key;
109 * ATS scheduling handle.
111 struct GNUNET_ATS_SchedulingHandle *GST_ats;
114 * ATS connectivity handle.
116 struct GNUNET_ATS_ConnectivityHandle *GST_ats_connect;
119 * Hello address expiration
121 struct GNUNET_TIME_Relative hello_expiration;
124 * Head of DLL of asynchronous tasks to kill sessions.
126 static struct GNUNET_ATS_SessionKiller *sk_head;
129 * Tail of DLL of asynchronous tasks to kill sessions.
131 static struct GNUNET_ATS_SessionKiller *sk_tail;
134 * Interface scanner determines our LAN address range(s).
136 struct GNUNET_ATS_InterfaceScanner *GST_is;
140 * Transmit our HELLO message to the given (connected) neighbour.
142 * @param cls the 'HELLO' message
143 * @param peer identity of the peer
144 * @param address the address
145 * @param state current state this peer is in
146 * @param state_timeout timeout for the current state of the peer
147 * @param bandwidth_in inbound quota in NBO
148 * @param bandwidth_out outbound quota in NBO
151 transmit_our_hello (void *cls,
152 const struct GNUNET_PeerIdentity *peer,
153 const struct GNUNET_HELLO_Address *address,
154 enum GNUNET_TRANSPORT_PeerState state,
155 struct GNUNET_TIME_Absolute state_timeout,
156 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
157 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
159 const struct GNUNET_MessageHeader *hello = cls;
164 sizeof (struct GNUNET_PeerIdentity)))
165 return; /* not to ourselves */
166 if (GNUNET_NO == GST_neighbours_test_connected (peer))
169 GST_neighbours_send (peer,
178 * My HELLO has changed. Tell everyone who should know.
181 * @param hello new HELLO
184 process_hello_update (void *cls,
185 const struct GNUNET_MessageHeader *hello)
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188 "Broadcasting HELLO to clients\n");
189 GST_clients_broadcast (hello, GNUNET_NO);
190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191 "Broadcasting HELLO to neighbours\n");
192 GST_neighbours_iterate (&transmit_our_hello,
198 * We received some payload. Prepare to pass it on to our clients.
200 * @param address address and (claimed) identity of the other peer
201 * @param session identifier used for this session (NULL for plugins
202 * that do not offer bi-directional communication to the sender
203 * using the same "connection")
204 * @param message the message to process
205 * @return how long the plugin should wait until receiving more data
207 static struct GNUNET_TIME_Relative
208 process_payload (const struct GNUNET_HELLO_Address *address,
209 struct GNUNET_ATS_Session *session,
210 const struct GNUNET_MessageHeader *message)
212 struct GNUNET_TIME_Relative ret;
214 struct InboundMessage *im;
215 size_t msg_size = ntohs (message->size);
216 size_t size = sizeof(struct InboundMessage) + msg_size;
217 char buf[size] GNUNET_ALIGN;
219 do_forward = GNUNET_SYSERR;
220 ret = GST_neighbours_calculate_receive_delay (&address->peer,
223 if (! GST_neighbours_test_connected (&address->peer))
225 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
226 "Discarded %u bytes type %u payload from peer `%s'\n",
228 ntohs (message->type),
229 GNUNET_i2s (&address->peer));
230 GNUNET_STATISTICS_update (GST_stats, gettext_noop
231 ("# bytes payload discarded due to not connected peer"),
237 if (GNUNET_YES != do_forward)
239 im = (struct InboundMessage *) buf;
240 im->header.size = htons (size);
241 im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
242 im->peer = address->peer;
243 memcpy (&im[1], message, ntohs (message->size));
244 GST_clients_broadcast (&im->header, GNUNET_YES);
250 * Task to asynchronously terminate a session.
252 * @param cls the `struct GNUNET_ATS_SessionKiller` with the information for the kill
253 * @param tc scheduler context
256 kill_session_task (void *cls,
257 const struct GNUNET_SCHEDULER_TaskContext *tc)
259 struct GNUNET_ATS_SessionKiller *sk = cls;
262 GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk);
263 sk->plugin->disconnect_session (sk->plugin->cls, sk->session);
269 * Force plugin to terminate session due to communication
272 * @param plugin_name name of the plugin
273 * @param session session to termiante
276 kill_session (const char *plugin_name,
277 struct GNUNET_ATS_Session *session)
279 struct GNUNET_TRANSPORT_PluginFunctions *plugin;
280 struct GNUNET_ATS_SessionKiller *sk;
282 for (sk = sk_head; NULL != sk; sk = sk->next)
283 if (sk->session == session)
285 plugin = GST_plugins_find (plugin_name);
291 /* need to issue disconnect asynchronously */
292 sk = GNUNET_new (struct GNUNET_ATS_SessionKiller);
293 sk->session = session;
295 sk->task = GNUNET_SCHEDULER_add_now (&kill_session_task, sk);
296 GNUNET_CONTAINER_DLL_insert (sk_head,
303 * Black list check result for try_connect call
304 * If connection to the peer is allowed request adddress and ???
306 * @param cls the message
307 * @param peer the peer
308 * @param address the address
309 * @param session the session
310 * @param result the result
313 connect_bl_check_cont (void *cls,
314 const struct GNUNET_PeerIdentity *peer,
315 const struct GNUNET_HELLO_Address *address,
316 struct GNUNET_ATS_Session *session,
319 struct GNUNET_MessageHeader *msg = cls;
321 if (GNUNET_OK == result)
323 /* Blacklist allows to speak to this peer, forward SYN to neighbours */
324 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
325 "Received SYN message from peer `%s' at `%s'\n",
327 GST_plugins_a2s (address));
329 GST_neighbours_handle_session_syn (msg,
332 GST_blacklist_abort_matching (address,
334 kill_session (address->transport_name,
341 if (GNUNET_SYSERR == result)
342 return; /* check was aborted, session destroyed */
343 /* Blacklist denies to speak to this peer */
344 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
345 "Discarding SYN message from `%s' due to denied blacklist check\n",
347 kill_session (address->transport_name,
353 * Function called by the transport for each received message.
355 * @param cls closure, const char* with the name of the plugin we received the message from
356 * @param address address and (claimed) identity of the other peer
357 * @param message the message, NULL if we only care about
358 * learning about the delay until we should receive again
359 * @param session identifier used for this session (NULL for plugins
360 * that do not offer bi-directional communication to the sender
361 * using the same "connection")
362 * @return how long the plugin should wait until receiving more data
363 * (plugins that do not support this, can ignore the return value)
365 struct GNUNET_TIME_Relative
366 GST_receive_callback (void *cls,
367 const struct GNUNET_HELLO_Address *address,
368 struct GNUNET_ATS_Session *session,
369 const struct GNUNET_MessageHeader *message)
371 const char *plugin_name = cls;
372 struct GNUNET_TIME_Relative ret;
375 ret = GNUNET_TIME_UNIT_ZERO;
378 type = ntohs (message->type);
379 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
380 "Received message with type %u from peer `%s'\n",
382 GNUNET_i2s (&address->peer));
384 GNUNET_STATISTICS_update (GST_stats,
385 gettext_noop ("# bytes total received"),
386 ntohs (message->size),
388 GST_neighbours_notify_data_recv (address,
392 case GNUNET_MESSAGE_TYPE_HELLO_LEGACY:
393 /* Legacy HELLO message, discard */
395 case GNUNET_MESSAGE_TYPE_HELLO:
396 if (GNUNET_OK != GST_validation_handle_hello (message))
399 GST_blacklist_abort_matching (address,
403 case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405 "Processing PING from `%s'\n",
406 GST_plugins_a2s (address));
408 GST_validation_handle_ping (&address->peer,
413 GST_blacklist_abort_matching (address,
415 kill_session (plugin_name,
419 case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
420 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
421 "Processing PONG from `%s'\n",
422 GST_plugins_a2s (address));
423 if (GNUNET_OK != GST_validation_handle_pong (&address->peer, message))
426 GST_blacklist_abort_matching (address,
428 kill_session (plugin_name, session);
431 case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN:
432 /* Do blacklist check if communication with this peer is allowed */
433 (void) GST_blacklist_test_allowed (&address->peer,
435 &connect_bl_check_cont,
436 GNUNET_copy_message (message),
440 case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK:
442 GST_neighbours_handle_session_syn_ack (message,
446 GST_blacklist_abort_matching (address, session);
447 kill_session (plugin_name, session);
450 case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
452 GST_neighbours_handle_session_ack (message,
457 GST_blacklist_abort_matching (address, session);
458 kill_session (plugin_name, session);
461 case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
462 GST_neighbours_handle_disconnect_message (&address->peer,
465 case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA:
466 GST_neighbours_handle_quota_message (&address->peer,
469 case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
470 GST_neighbours_keepalive (&address->peer,
473 case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE:
474 GST_neighbours_keepalive_response (&address->peer,
478 /* should be payload */
479 GNUNET_STATISTICS_update (GST_stats,
480 gettext_noop ("# bytes payload received"),
481 ntohs (message->size),
483 ret = process_payload (address,
489 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490 "Allowing receive from peer %s to continue in %s\n",
491 GNUNET_i2s (&address->peer),
492 GNUNET_STRINGS_relative_time_to_string (ret,
499 * Function that will be called for each address the transport
500 * is aware that it might be reachable under. Update our HELLO.
502 * @param cls name of the plugin (const char*)
503 * @param add_remove should the address added (YES) or removed (NO) from the
504 * set of valid addresses?
505 * @param address the address to add or remove
508 plugin_env_address_change_notification (void *cls,
510 const struct GNUNET_HELLO_Address *address)
512 static int addresses = 0;
513 struct GNUNET_STATISTICS_Handle *cfg = GST_stats;
515 if (GNUNET_YES == add_remove)
518 GNUNET_STATISTICS_update (cfg,
519 "# transport addresses",
523 else if (GNUNET_NO == add_remove)
532 GNUNET_STATISTICS_update (cfg,
533 "# transport addresses",
538 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
539 "Transport now has %u addresses to communicate\n",
541 GST_hello_modify_addresses (add_remove,
547 * Function that will be called whenever the plugin internally
548 * cleans up a session pointer and hence the service needs to
549 * discard all of those sessions as well. Plugins that do not
550 * use sessions can simply omit calling this function and always
551 * use NULL wherever a session pointer is needed. This function
552 * should be called BEFORE a potential "TransmitContinuation"
553 * from the "TransmitFunction".
556 * @param address which address was the session for
557 * @param session which session is being destoyed
560 plugin_env_session_end (void *cls,
561 const struct GNUNET_HELLO_Address *address,
562 struct GNUNET_ATS_Session *session)
564 struct GNUNET_ATS_SessionKiller *sk;
576 GNUNET_assert (strlen (address->transport_name) > 0);
578 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
579 "Notification from plugin about terminated session %p from peer `%s' address `%s'\n",
581 GNUNET_i2s (&address->peer),
582 GST_plugins_a2s (address));
584 GST_neighbours_session_terminated (&address->peer, session);
585 GST_ats_del_session (address,
587 GST_blacklist_abort_matching (address, session);
589 for (sk = sk_head; NULL != sk; sk = sk->next)
591 if (sk->session == session)
593 GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk);
594 GNUNET_SCHEDULER_cancel (sk->task);
603 * Black list check result from blacklist check triggered when a
604 * plugin gave us a new session in #plugin_env_session_start(). If
605 * connection to the peer is disallowed, kill the session.
608 * @param peer the peer
609 * @param address address associated with the request
610 * @param session session associated with the request
611 * @param result the result
614 plugin_env_session_start_bl_check_cont (void *cls,
615 const struct GNUNET_PeerIdentity *peer,
616 const struct GNUNET_HELLO_Address *address,
617 struct GNUNET_ATS_Session *session,
620 if (GNUNET_OK != result)
622 kill_session (address->transport_name,
627 GNUNET_HELLO_address_check_option (address,
628 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
630 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
631 "Informing verifier about inbound session's address `%s'\n",
632 GST_plugins_a2s (address));
633 GST_validation_handle_address (address);
639 * Plugin tells transport service about a new inbound session
642 * @param address the address
643 * @param session the new session
644 * @param scope network scope information
647 plugin_env_session_start (void *cls,
648 const struct GNUNET_HELLO_Address *address,
649 struct GNUNET_ATS_Session *session,
650 enum GNUNET_ATS_Network_Type scope)
652 struct GNUNET_ATS_Properties prop;
664 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
665 "Notification from plugin `%s' about new session from peer `%s' address `%s'\n",
666 address->transport_name,
667 GNUNET_i2s (&address->peer),
668 GST_plugins_a2s (address));
670 GNUNET_HELLO_address_check_option (address,
671 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
673 /* inbound is always new, but outbound MAY already be known, but
674 for example for UNIX, we have symmetric connections and thus we
675 may not know the address yet; add if necessary! */
676 /* FIXME: maybe change API here so we just pass scope? */
677 memset (&prop, 0, sizeof (prop));
678 GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != scope);
680 GST_ats_add_inbound_address (address,
684 /* Do blacklist check if communication with this peer is allowed */
685 (void) GST_blacklist_test_allowed (&address->peer,
686 address->transport_name,
687 &plugin_env_session_start_bl_check_cont,
695 * Function called by ATS to notify the callee that the
696 * assigned bandwidth or address for a given peer was changed. If the
697 * callback is called with address/bandwidth assignments of zero, the
698 * ATS disconnect function will still be called once the disconnect
702 * @param peer the peer this address is intended for
703 * @param address address to use (for peer given in address)
704 * @param session session to use (if available)
705 * @param bandwidth_out assigned outbound bandwidth for the connection in NBO,
706 * 0 to disconnect from peer
707 * @param bandwidth_in assigned inbound bandwidth for the connection in NBO,
708 * 0 to disconnect from peer
709 * @param ats ATS information
710 * @param ats_count number of @a ats elements
713 ats_request_address_change (void *cls,
714 const struct GNUNET_PeerIdentity *peer,
715 const struct GNUNET_HELLO_Address *address,
716 struct GNUNET_ATS_Session *session,
717 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
718 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
720 uint32_t bw_in = ntohl (bandwidth_in.value__);
721 uint32_t bw_out = ntohl (bandwidth_out.value__);
725 /* ATS service died, all suggestions become invalid!
726 (but we'll keep using the allocations for a little
727 while, to keep going while ATS restarts) */
728 /* FIXME: We should drop all
729 connections now, as ATS won't explicitly tell
730 us and be unaware of ongoing resource allocations! */
733 /* ATS tells me to disconnect from peer */
734 if ((0 == bw_in) && (0 == bw_out))
736 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
737 "ATS tells me to disconnect from peer `%s'\n",
739 GST_neighbours_force_disconnect (peer);
742 GNUNET_assert (NULL != address);
743 GNUNET_STATISTICS_update (GST_stats,
744 "# ATS suggestions received",
747 GST_neighbours_switch_to_address (address,
755 * Function called when the service shuts down. Unloads our plugins
756 * and cancels pending validations.
758 * @param cls closure, unused
759 * @param tc task context (unused)
762 shutdown_task (void *cls,
763 const struct GNUNET_SCHEDULER_TaskContext *tc)
765 GST_neighbours_stop ();
766 GST_plugins_unload ();
767 GST_validation_stop ();
769 GNUNET_ATS_scheduling_done (GST_ats);
771 GNUNET_ATS_connectivity_done (GST_ats_connect);
772 GST_ats_connect = NULL;
773 GNUNET_ATS_scanner_done (GST_is);
776 GST_blacklist_stop ();
778 GST_manipulation_stop ();
780 if (NULL != GST_peerinfo)
782 GNUNET_PEERINFO_disconnect (GST_peerinfo);
785 if (NULL != GST_stats)
787 GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
790 if (NULL != GST_my_private_key)
792 GNUNET_free(GST_my_private_key);
793 GST_my_private_key = NULL;
800 * Initiate transport service.
803 * @param server the initialized server
804 * @param c configuration to use
808 struct GNUNET_SERVER_Handle *server,
809 const struct GNUNET_CONFIGURATION_Handle *c)
812 struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
813 long long unsigned int max_fd_cfg;
821 GNUNET_CONFIGURATION_get_value_filename (c,
826 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
827 _("Transport service is lacking key configuration settings. Exiting.\n"));
828 GNUNET_SCHEDULER_shutdown ();
832 GNUNET_CONFIGURATION_get_value_time (c,
837 hello_expiration = GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION;
840 pk = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
841 GNUNET_free (keyfile);
842 GNUNET_assert (NULL != pk);
843 GST_my_private_key = pk;
845 GST_stats = GNUNET_STATISTICS_create ("transport", GST_cfg);
846 GST_peerinfo = GNUNET_PEERINFO_connect (GST_cfg);
847 GNUNET_CRYPTO_eddsa_key_get_public (GST_my_private_key,
848 &GST_my_identity.public_key);
849 GNUNET_assert(NULL != GST_my_private_key);
851 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
852 "My identity is `%4s'\n",
853 GNUNET_i2s_full (&GST_my_identity));
855 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
858 if (NULL == GST_peerinfo)
860 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
861 _("Could not access PEERINFO service. Exiting.\n"));
862 GNUNET_SCHEDULER_shutdown ();
869 struct rlimit r_file;
870 if (0 == getrlimit (RLIMIT_NOFILE, &r_file))
872 max_fd_rlimit = r_file.rlim_cur;
873 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
874 "Maximum number of open files was: %u/%u\n",
878 max_fd_rlimit = (9 * max_fd_rlimit) / 10; /* Keep 10% for rest of transport */
880 GNUNET_CONFIGURATION_get_value_number (GST_cfg,
885 if (max_fd_cfg > max_fd_rlimit)
888 max_fd = max_fd_rlimit;
889 if (max_fd < DEFAULT_MAX_FDS)
890 max_fd = DEFAULT_MAX_FDS;
892 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
893 "Limiting number of sockets to %u: validation %u, neighbors: %u\n",
894 max_fd, (max_fd / 3), (max_fd / 3) * 2);
896 friend_only = GNUNET_CONFIGURATION_get_value_yesno (GST_cfg,
899 if (GNUNET_SYSERR == friend_only)
900 friend_only = GNUNET_NO; /* According to topology defaults */
901 /* start subsystems */
902 GST_blacklist_start (GST_server,
905 GST_is = GNUNET_ATS_scanner_init ();
906 GST_ats_connect = GNUNET_ATS_connectivity_init (GST_cfg);
907 GST_ats = GNUNET_ATS_scheduling_init (GST_cfg,
908 &ats_request_address_change,
911 GST_manipulation_init ();
912 GST_plugins_load (&GST_manipulation_recv,
913 &plugin_env_address_change_notification,
914 &plugin_env_session_start,
915 &plugin_env_session_end);
916 GST_hello_start (friend_only,
917 &process_hello_update,
919 GST_neighbours_start ((max_fd / 3) * 2);
920 GST_clients_start (GST_server);
921 GST_validation_start ((max_fd / 3));
926 * The main function for the transport service.
928 * @param argc number of arguments from the command line
929 * @param argv command line arguments
930 * @return 0 ok, 1 on error
938 == GNUNET_SERVICE_run (argc, argv, "transport",
939 GNUNET_SERVICE_OPTION_NONE, &run, NULL )) ? 0 : 1;
942 /* end of file gnunet-service-transport.c */