2 This file is part of GNUnet
3 Copyright (C) 2002--2014 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @file dv/plugin_transport_dv.c
21 * @brief DV transport service, takes incoming DV requests and deals with
23 * @author Nathan Evans
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_dv_service.h"
31 #include "gnunet_transport_service.h"
32 #include "gnunet_transport_plugin.h"
36 #define LOG(kind,...) GNUNET_log_from (kind, "transport-dv",__VA_ARGS__)
40 * Encapsulation of all of the state of the plugin.
46 * Session handle for connections.
48 struct GNUNET_ATS_Session
51 * Pointer to the global plugin struct.
53 struct Plugin *plugin;
56 * Address we use for the other peer.
58 struct GNUNET_HELLO_Address *address;
61 * To whom are we talking to.
63 struct GNUNET_PeerIdentity sender;
66 * Number of bytes waiting for transmission to this peer.
69 unsigned long long bytes_in_queue;
72 * Number of messages waiting for transmission to this peer.
75 unsigned int msgs_in_queue;
78 * Current distance to the given peer.
83 * Current network the next hop peer is located in
85 enum GNUNET_NetworkType network;
88 * Does the transport service know about this session (and we thus
89 * need to call `session_end` when it is released?)
97 * Encapsulation of all of the state of the plugin.
104 struct GNUNET_TRANSPORT_PluginEnvironment *env;
107 * Hash map of sessions (active and inactive).
109 struct GNUNET_CONTAINER_MultiPeerMap *sessions;
112 * Copy of the handler array where the closures are
113 * set to this struct's instance.
115 struct GNUNET_SERVER_MessageHandler *handlers;
118 * Handle to the DV service
120 struct GNUNET_DV_ServiceHandle *dvh;
123 * Tokenizer for boxed messages.
125 struct GNUNET_SERVER_MessageStreamTokenizer *mst;
128 * Function to call about session status changes.
130 GNUNET_TRANSPORT_SessionInfoCallback sic;
133 * Closure for @e sic.
140 * If a session monitor is attached, notify it about the new
143 * @param plugin our plugin
144 * @param session session that changed state
145 * @param state new state of the session
148 notify_session_monitor (struct Plugin *plugin,
149 struct GNUNET_ATS_Session *session,
150 enum GNUNET_TRANSPORT_SessionState state)
152 struct GNUNET_TRANSPORT_SessionInfo info;
154 if (NULL == plugin->sic)
156 memset (&info, 0, sizeof (info));
158 info.is_inbound = GNUNET_SYSERR; /* hard to say */
159 info.num_msg_pending = session->msgs_in_queue;
160 info.num_bytes_pending = session->bytes_in_queue;
161 /* info.receive_delay remains zero as this is not supported by DV
162 (cannot selectively not receive from 'core') */
163 info.session_timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
164 info.address = session->address;
165 plugin->sic (plugin->sic_cls,
172 * Notify transport service about the change in distance.
174 * @param session session where the distance changed
177 notify_distance_change (struct GNUNET_ATS_Session *session)
179 struct Plugin *plugin = session->plugin;
181 if (GNUNET_YES != session->active)
183 plugin->env->update_address_distance (plugin->env->cls,
190 * Function called by MST on each message from the box.
192 * @param cls closure with the `struct Plugin *`
193 * @param client identification of the client (with the 'struct GNUNET_ATS_Session')
194 * @param message the actual message
195 * @return #GNUNET_OK on success
200 const struct GNUNET_MessageHeader *message)
202 struct Plugin *plugin = cls;
203 struct GNUNET_ATS_Session *session = client;
205 session->active = GNUNET_YES;
206 LOG (GNUNET_ERROR_TYPE_DEBUG,
207 "Delivering message of type %u with %u bytes from peer `%s'\n",
208 ntohs (message->type),
209 ntohs (message->size),
210 GNUNET_i2s (&session->sender));
211 plugin->env->receive (plugin->env->cls,
215 plugin->env->update_address_distance (plugin->env->cls,
223 * Handler for messages received from the DV service.
225 * @param cls closure with the plugin
226 * @param sender sender of the message
227 * @param distance how far did the message travel
228 * @param msg actual message payload
231 handle_dv_message_received (void *cls,
232 const struct GNUNET_PeerIdentity *sender,
234 const struct GNUNET_MessageHeader *msg)
236 struct Plugin *plugin = cls;
237 struct GNUNET_ATS_Session *session;
239 LOG (GNUNET_ERROR_TYPE_DEBUG,
240 "Received DV_MESSAGE_RECEIVED message for peer `%s': new distance %u\n",
243 session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
250 if (GNUNET_MESSAGE_TYPE_DV_BOX == ntohs (msg->type))
252 /* need to unbox using MST */
253 LOG (GNUNET_ERROR_TYPE_DEBUG,
254 "Unboxing DV message using MST\n");
255 GNUNET_SERVER_mst_receive (plugin->mst,
257 (const char *) &msg[1],
258 ntohs (msg->size) - sizeof (struct GNUNET_MessageHeader),
263 session->active = GNUNET_YES;
264 LOG (GNUNET_ERROR_TYPE_DEBUG,
265 "Delivering message of type %u with %u bytes from peer `%s'\n",
268 GNUNET_i2s (sender));
269 plugin->env->receive (plugin->env->cls,
273 plugin->env->update_address_distance (plugin->env->cls,
280 * Function called if DV starts to be able to talk to a peer.
282 * @param cls closure with `struct Plugin *`
283 * @param peer newly connected peer
284 * @param distance distance to the peer
285 * @param network the network the next hop is located in
288 handle_dv_connect (void *cls,
289 const struct GNUNET_PeerIdentity *peer,
291 enum GNUNET_NetworkType network)
293 struct Plugin *plugin = cls;
294 struct GNUNET_ATS_Session *session;
296 GNUNET_break (GNUNET_NT_UNSPECIFIED != network);
298 * This requires transport plugin to be linked to libgnunetats.
299 * If you remove it, also remove libgnunetats linkage from Makefile.am
301 LOG (GNUNET_ERROR_TYPE_DEBUG,
302 "Received DV_CONNECT message for peer `%s' with next hop in network %s\n",
304 GNUNET_NT_to_string (network));
306 session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
311 session->distance = distance;
312 notify_distance_change (session);
313 return; /* nothing to do */
316 session = GNUNET_new (struct GNUNET_ATS_Session);
317 session->address = GNUNET_HELLO_address_allocate (peer, "dv",
319 GNUNET_HELLO_ADDRESS_INFO_NONE);
320 session->sender = *peer;
321 session->plugin = plugin;
322 session->distance = distance;
323 session->network = network;
324 GNUNET_assert(GNUNET_YES ==
325 GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
326 &session->sender, session,
327 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
329 LOG (GNUNET_ERROR_TYPE_DEBUG,
330 "Creating new DV session %p for peer `%s' at distance %u\n",
335 session->active = GNUNET_YES;
336 plugin->env->session_start (plugin->env->cls,
340 plugin->env->update_address_distance (plugin->env->cls,
344 notify_session_monitor (session->plugin,
346 GNUNET_TRANSPORT_SS_UP);
351 * Function called if DV distance to a peer is changed.
353 * @param cls closure with `struct Plugin *`
354 * @param peer connected peer
355 * @param distance new distance to the peer
356 * @param network network type used for the connection
359 handle_dv_distance_changed (void *cls,
360 const struct GNUNET_PeerIdentity *peer,
362 enum GNUNET_NetworkType network)
364 struct Plugin *plugin = cls;
365 struct GNUNET_ATS_Session *session;
367 GNUNET_break (GNUNET_NT_UNSPECIFIED != network);
368 LOG (GNUNET_ERROR_TYPE_DEBUG,
369 "Received `%s' message for peer `%s': new distance %u\n",
370 "DV_DISTANCE_CHANGED",
373 session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
378 handle_dv_connect (plugin, peer, distance, network);
381 session->distance = distance;
382 notify_distance_change (session);
387 * Release session object and clean up associated resources.
389 * @param session session to clean up
392 free_session (struct GNUNET_ATS_Session *session)
394 struct Plugin *plugin = session->plugin;
396 GNUNET_assert (GNUNET_YES ==
397 GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
401 LOG (GNUNET_ERROR_TYPE_DEBUG,
402 "Freeing session %p for peer `%s'\n",
404 GNUNET_i2s (&session->sender));
405 if (GNUNET_YES == session->active)
407 notify_session_monitor (session->plugin,
409 GNUNET_TRANSPORT_SS_DONE);
410 plugin->env->session_end (plugin->env->cls,
413 session->active = GNUNET_NO;
415 GNUNET_HELLO_address_free (session->address);
416 GNUNET_free (session);
421 * Function called if DV is no longer able to talk to a peer.
423 * @param cls closure with `struct Plugin *`
424 * @param peer peer that disconnected
427 handle_dv_disconnect (void *cls,
428 const struct GNUNET_PeerIdentity *peer)
430 struct Plugin *plugin = cls;
431 struct GNUNET_ATS_Session *session;
433 LOG (GNUNET_ERROR_TYPE_DEBUG,
434 "Received `%s' message for peer `%s'\n",
437 session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
440 return; /* nothing to do */
441 free_session (session);
446 * Function that can be used by the transport service to transmit
447 * a message using the plugin.
450 * @param session the session used
451 * @param priority how important is the message
452 * @param msgbuf the message to transmit
453 * @param msgbuf_size number of bytes in 'msgbuf'
454 * @param timeout when should we time out
455 * @param cont continuation to call once the message has
456 * been transmitted (or if the transport is ready
457 * for the next transmission call; or if the
458 * peer disconnected...)
459 * @param cont_cls closure for @a cont
460 * @return number of bytes used (on the physical network, with overheads);
461 * -1 on hard errors (i.e. address invalid); 0 is a legal value
462 * and does NOT mean that the message was not transmitted (DV)
465 dv_plugin_send (void *cls,
466 struct GNUNET_ATS_Session *session,
469 unsigned int priority,
470 struct GNUNET_TIME_Relative timeout,
471 GNUNET_TRANSPORT_TransmitContinuation cont,
474 struct Plugin *plugin = cls;
475 const struct GNUNET_MessageHeader *msg;
476 struct GNUNET_MessageHeader *box;
479 msg = (const struct GNUNET_MessageHeader *) msgbuf;
480 if (ntohs (msg->size) != msgbuf_size)
483 LOG (GNUNET_ERROR_TYPE_DEBUG,
484 "Boxing DV message\n");
485 box = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + msgbuf_size);
486 box->type = htons (GNUNET_MESSAGE_TYPE_DV_BOX);
487 box->size = htons (sizeof (struct GNUNET_MessageHeader) + msgbuf_size);
488 GNUNET_memcpy (&box[1], msgbuf, msgbuf_size);
491 GNUNET_DV_send (plugin->dvh,
498 GNUNET_free_non_null (box);
504 * Function that can be used to force the plugin to disconnect
505 * from the given peer and cancel all previous transmissions
506 * (and their continuations).
508 * @param cls closure with the `struct Plugin *`
509 * @param target peer from which to disconnect
512 dv_plugin_disconnect_peer (void *cls,
513 const struct GNUNET_PeerIdentity *target)
515 struct Plugin *plugin = cls;
516 struct GNUNET_ATS_Session *session;
518 session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
521 return; /* nothing to do */
522 session->active = GNUNET_NO;
527 * Function that can be used to force the plugin to disconnect
528 * from the given peer and cancel all previous transmissions
529 * (and their continuations).
531 * @param cls closure with the `struct Plugin *`
532 * @param session which session to disconnect
536 dv_plugin_disconnect_session (void *cls,
537 struct GNUNET_ATS_Session *session)
539 session->active = GNUNET_NO;
545 * Convert the transports address to a nice, human-readable
549 * @param type name of the transport that generated the address
550 * @param addr one of the addresses of the host, NULL for the last address
551 * the specific address format depends on the transport
552 * @param addrlen length of the address
553 * @param numeric should (IP) addresses be displayed in numeric form?
554 * @param timeout after how long should we give up?
555 * @param asc function to call on each string
556 * @param asc_cls closure for @a asc
559 dv_plugin_address_pretty_printer (void *cls,
564 struct GNUNET_TIME_Relative timeout,
565 GNUNET_TRANSPORT_AddressStringCallback asc,
568 if ( (0 == addrlen) &&
569 (0 == strcmp (type, "dv")) )
584 * Convert the DV address to a pretty string.
587 * @param addr the (hopefully) DV address
588 * @param addrlen the length of the @a addr
589 * @return string representing the DV address
592 dv_plugin_address_to_string (void *cls,
598 GNUNET_break (0); /* malformed */
606 * Another peer has suggested an address for this peer and transport
607 * plugin. Check that this could be a valid address. This function
608 * is not expected to 'validate' the address in the sense of trying to
609 * connect to it but simply to see if the binary format is technically
610 * legal for establishing a connection to this peer (and make sure that
611 * the address really corresponds to our network connection/settings
612 * and not some potential man-in-the-middle).
615 * @param addr pointer to the address
616 * @param addrlen length of @a addr
617 * @return #GNUNET_OK if this is a plausible address for this peer
618 * and transport, #GNUNET_SYSERR if not
622 dv_plugin_check_address (void *cls,
627 return GNUNET_SYSERR;
633 * Create a new session to transmit data to the target
634 * This session will used to send data to this peer and the plugin will
635 * notify us by calling the env->session_end function
637 * @param cls the plugin
638 * @param address the address
639 * @return the session if the address is valid, NULL otherwise
641 static struct GNUNET_ATS_Session *
642 dv_get_session (void *cls,
643 const struct GNUNET_HELLO_Address *address)
645 struct Plugin *plugin = cls;
646 struct GNUNET_ATS_Session *session;
648 if (0 != address->address_length)
650 session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
653 return NULL; /* not valid right now */
654 session->active = GNUNET_YES;
660 * Function called to convert a string address to
663 * @param cls closure ('struct Plugin*')
664 * @param addr string address
665 * @param addrlen length of the @a addr including \0 termination
666 * @param buf location to store the buffer
667 * If the function returns #GNUNET_SYSERR, its contents are undefined.
668 * @param added length of created address
669 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
672 dv_plugin_string_to_address (void *cls,
678 if ( (addrlen == 3) &&
679 (0 == strcmp ("dv", addr)) )
684 return GNUNET_SYSERR;
689 * Function that will be called whenever the transport service wants to
690 * notify the plugin that a session is still active and in use and
691 * therefore the session timeout for this session has to be updated
693 * @param cls closure (`struct Plugin *`)
694 * @param peer which peer was the session for
695 * @param session which session is being updated
698 dv_plugin_update_session_timeout (void *cls,
699 const struct GNUNET_PeerIdentity *peer,
700 struct GNUNET_ATS_Session *session)
702 /* DV currently doesn't time out like "normal" plugins,
703 so it should be safe to do nothing, right?
704 (or should we add an internal timeout?) */
709 * Function to obtain the network type for a session
710 * FIXME: we should probably look at the network type
711 * used by the next hop here. Or find some other way
712 * to properly allow ATS-DV resource allocation.
714 * @param cls closure (`struct Plugin *`)
715 * @param session the session
716 * @return the network type
718 static enum GNUNET_NetworkType
719 dv_get_network (void *cls,
720 struct GNUNET_ATS_Session *session)
722 GNUNET_assert (NULL != session);
723 return session->network;
728 * Function obtain the network type for an address.
730 * @param cls closure (`struct Plugin *`)
731 * @param address the address
732 * @return the network type
734 static enum GNUNET_NetworkType
735 dv_plugin_get_network_for_address (void *cls,
736 const struct GNUNET_HELLO_Address *address)
738 return GNUNET_NT_WAN; /* FOR NOW */
743 * Function that is called to get the keepalive factor.
744 * #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to
745 * calculate the interval between keepalive packets.
747 * @param cls closure with the `struct Plugin`
748 * @return keepalive factor
751 dv_plugin_query_keepalive_factor (void *cls)
758 * Return information about the given session to the
761 * @param cls the `struct Plugin` with the monitor callback (`sic`)
762 * @param peer peer we send information about
763 * @param value our `struct GNUNET_ATS_Session` to send information about
764 * @return #GNUNET_OK (continue to iterate)
767 send_session_info_iter (void *cls,
768 const struct GNUNET_PeerIdentity *peer,
771 struct Plugin *plugin = cls;
772 struct GNUNET_ATS_Session *session = value;
774 if (GNUNET_YES != session->active)
776 notify_session_monitor (plugin,
778 GNUNET_TRANSPORT_SS_UP);
784 * Begin monitoring sessions of a plugin. There can only
785 * be one active monitor per plugin (i.e. if there are
786 * multiple monitors, the transport service needs to
787 * multiplex the generated events over all of them).
789 * @param cls closure of the plugin
790 * @param sic callback to invoke, NULL to disable monitor;
791 * plugin will being by iterating over all active
792 * sessions immediately and then enter monitor mode
793 * @param sic_cls closure for @a sic
796 dv_plugin_setup_monitor (void *cls,
797 GNUNET_TRANSPORT_SessionInfoCallback sic,
800 struct Plugin *plugin = cls;
803 plugin->sic_cls = sic_cls;
806 GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
807 &send_session_info_iter,
809 /* signal end of first iteration */
810 sic (sic_cls, NULL, NULL);
816 * Entry point for the plugin.
818 * @param cls closure with the plugin environment
822 libgnunet_plugin_transport_dv_init (void *cls)
824 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
825 struct GNUNET_TRANSPORT_PluginFunctions *api;
826 struct Plugin *plugin;
828 plugin = GNUNET_new (struct Plugin);
830 plugin->sessions = GNUNET_CONTAINER_multipeermap_create (1024 * 8, GNUNET_YES);
831 plugin->mst = GNUNET_SERVER_mst_create (&unbox_cb,
833 plugin->dvh = GNUNET_DV_service_connect (env->cfg,
836 &handle_dv_distance_changed,
837 &handle_dv_disconnect,
838 &handle_dv_message_received);
839 if (NULL == plugin->dvh)
841 GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
842 GNUNET_SERVER_mst_destroy (plugin->mst);
843 GNUNET_free (plugin);
846 api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
848 api->send = &dv_plugin_send;
849 api->disconnect_peer = &dv_plugin_disconnect_peer;
850 api->disconnect_session = &dv_plugin_disconnect_session;
851 api->address_pretty_printer = &dv_plugin_address_pretty_printer;
852 api->check_address = &dv_plugin_check_address;
853 api->address_to_string = &dv_plugin_address_to_string;
854 api->string_to_address = &dv_plugin_string_to_address;
855 api->query_keepalive_factor = &dv_plugin_query_keepalive_factor;
856 api->get_session = &dv_get_session;
857 api->get_network = &dv_get_network;
858 api->get_network_for_address = &dv_plugin_get_network_for_address;
859 api->update_session_timeout = &dv_plugin_update_session_timeout;
860 api->setup_monitor = &dv_plugin_setup_monitor;
866 * Function called to free a session.
870 * @param value session to free
871 * @return #GNUNET_OK (continue to iterate)
874 free_session_iterator (void *cls,
875 const struct GNUNET_PeerIdentity *key,
878 struct GNUNET_ATS_Session *session = value;
880 free_session (session);
886 * Exit point from the plugin.
888 * @param cls plugin API
892 libgnunet_plugin_transport_dv_done (void *cls)
894 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
895 struct Plugin *plugin = api->cls;
897 GNUNET_DV_service_disconnect (plugin->dvh);
898 GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
899 &free_session_iterator,
901 GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
902 GNUNET_SERVER_mst_destroy (plugin->mst);
903 GNUNET_free (plugin);
908 /* end of plugin_transport_dv.c */