2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file transport/plugin_transport_wlan.c
23 * @brief transport plugin for wlan
24 * @author David Brodski
28 #include "gnunet_protocols.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_statistics_service.h"
31 #include "gnunet_transport_service.h"
32 #include "plugin_transport.h"
33 #include "plugin_transport_wlan.h"
34 #include "gnunet_common.h"
36 #define PROTOCOL_PREFIX "wlan"
39 * Max size of packet from helper
43 #define DEBUG_wlan GNUNET_NO
46 * After how long do we expire an address that we
47 * learned from another peer if it is not reconfirmed
50 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
53 * Initial handshake message for a session.
58 * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME.
60 struct GNUNET_MessageHeader header;
63 * Identity of the node connecting (TCP client)
65 struct GNUNET_PeerIdentity clientIdentity;
70 * Encapsulation of all of the state of the plugin.
77 struct GNUNET_TRANSPORT_PluginEnvironment *env;
80 * List of open sessions.
82 struct Session *sessions;
85 * encapsulation to the local wlan server prog
88 struct GNUNET_SERVER_MessageStreamTokenizer * consoltoken;
92 * stdout pipe handle for the gnunet-wlan-helper process
94 struct GNUNET_DISK_PipeHandle *server_stdout;
97 * stdout file handle for the gnunet-wlan-helper process
99 const struct GNUNET_DISK_FileHandle *server_stdout_handle;
102 * stdin pipe handle for the gnunet-wlan-helper process
104 struct GNUNET_DISK_PipeHandle *server_stdin;
107 * stdin file handle for the gnunet-wlan-helper process
109 const struct GNUNET_DISK_FileHandle *server_stdin_handle;
112 * ID of select gnunet-nat-server std read task
114 GNUNET_SCHEDULER_TaskIdentifier server_read_task;
117 * ID of select gnunet-nat-server std read task
119 GNUNET_SCHEDULER_TaskIdentifier server_write_task;
122 * The process id of the server process (if behind NAT)
127 * The interface of the wlan card given to us by the user.
132 * The mac_address of the wlan card given to us by the helper.
137 * Sessions currently pending for transmission
138 * to this peer, if any.
140 struct Sessionqueue * pending_Sessions;
143 * Sessions currently pending for transmission
144 * to this peer, if any.
146 struct Sessionqueue * all_Sessions;
153 struct Sessionqueue * next;
154 struct Sessionqueue * prev;
155 struct Session * content;
159 * Session handle for connections.
167 struct SessionHeader header;
170 * Pointer to the global plugin struct.
172 struct Plugin *plugin;
175 * Messages currently pending for transmission
176 * to this peer, if any.
178 struct PendingMessage *pending_messages_head;
181 * Messages currently pending for transmission
182 * to this peer, if any.
184 struct PendingMessage *pending_messages_tail;
187 * To whom are we talking to (set to our identity
188 * if we are still waiting for the welcome message)
190 struct GNUNET_PeerIdentity target;
193 * encapsulation of the data
195 struct GNUNET_SERVER_MessageStreamTokenizer * datatoken;
203 * Address of the other peer (either based on our 'connect'
204 * call or on our 'accept' call).
209 * Last activity on this connection. Used to select preferred
212 struct GNUNET_TIME_Absolute last_activity;
215 * number of message, to distinguish between the messages
217 uint16_t message_num_in;
220 * number of message, to distinguish between the messages
222 uint16_t message_num_out;
228 * Information kept for each message that is yet to
231 struct PendingMessage
235 * This is a doubly-linked list.
237 struct PendingMessage *next;
240 * This is a doubly-linked list.
242 struct PendingMessage *prev;
245 * The pending message
250 * Continuation function to call once the message
251 * has been sent. Can be NULL if there is no
252 * continuation to call.
254 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
257 * Cls for transmit_cont
259 void * transmit_cont_cls;
262 * Timeout value for the pending message.
264 struct GNUNET_TIME_Absolute timeout;
267 * Size of the message
272 * pos in the message, for fragmentation/segmentation
279 * Header for messages which need fragmentation
284 struct GNUNET_MessageHeader header;
287 * checksum/error correction
289 uint32_t crc GNUNET_PACKED;
292 * To whom are we talking to (set to our identity
293 * if we are still waiting for the welcome message)
295 struct GNUNET_PeerIdentity target;
297 // followed by payload
302 * Header for messages which need fragmentation
304 struct FragmentationHeader
307 struct GNUNET_MessageHeader header;
310 * To whom are we talking to (set to our identity
311 * if we are still waiting for the welcome message)
313 // struct GNUNET_PeerIdentity target GNUNET_PACKED;
316 * ID of message, to distinguish between the messages, picked randomly.
318 uint32_t message_id GNUNET_PACKED;
321 * Offset or number of this fragment, for fragmentation/segmentation (design choice, TBD)
323 uint16_t fragment_off_or_num GNUNET_PACKED;
326 * CRC of fragment (for error checking)
328 uint16_t message_crc GNUNET_PACKED;
332 * // 0x1 ack => Use two different message types in header.type! (FRAG_MESSAGE; FRAG_ACK)
333 * // 0x2 has data (not only ack)
334 * // 0x4 last fragment of message
337 // uint32_t flags GNUNET_PACKED;
340 * checksum/error correction
342 // uint32_t crc GNUNET_PACKED;
344 // followed by payload unless ACK
348 enum { ACK_FRAGMENT = 1, DATA_FRAGMENT = 2, LAST_FRAGMENT = 4, NEW_MESSAGE = 8 };
350 int getRadiotapHeader (struct RadiotapHeader * Header);
351 int getWlanHeader (struct IeeeHeader * Header);
352 static int wlan_plugin_address_suggested (void *cls,
356 * get Session from address
360 //TODO add other possibilities to find the right session (are there other?)
361 static struct Session *
362 get_Session (struct Plugin *plugin,
365 struct Sessionqueue * queue = plugin->all_Sessions;
366 struct Sessionqueue * lastitem = NULL;
368 while (queue != NULL){
369 // content is never NULL
370 GNUNET_assert (queue->content == NULL);
371 char * addr2 = queue->content->addr;
372 if (memcmp(addr, addr2, 6) == 0)
375 return queue->content;
382 queue = GNUNET_malloc (sizeof (struct Sessionqueue));
384 if (plugin->all_Sessions == NULL){
386 plugin->all_Sessions = queue;
388 lastitem->next = queue;
389 queue->prev = lastitem;
392 queue->content = GNUNET_malloc (sizeof (struct Session));
393 queue->content->plugin = plugin;
394 memcpy(queue->content->addr, addr, 6);
397 struct WelcomeMessage welcome;
398 struct PendingMessage *pm;
399 pm = GNUNET_malloc (sizeof (struct PendingMessage) + sizeof (struct WelcomeMessage));
400 pm->msg = (const char*) &pm[1];
401 pm->message_size = sizeof (struct WelcomeMessage);
402 welcome.header.size = htons (sizeof (struct WelcomeMessage));
403 welcome.header.type = htons (GNUNET_MESSAGE_TYPE_WLAN_ADVERTISEMENT);
404 welcome.clientIdentity = *plugin->env->my_identity;
405 memcpy (&pm[1], &welcome, sizeof (welcome));
406 pm->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
407 GNUNET_CONTAINER_DLL_insert ((queue->content)->pending_messages_head,
408 (queue->content)->pending_messages_tail,
411 return queue->content;
416 * Queue the session to send data
420 queue_Session (struct Plugin *plugin,
421 struct Session * session)
423 struct Sessionqueue * queue = plugin->pending_Sessions;
424 struct Sessionqueue * lastitem = NULL;
426 while (queue != NULL){
427 // content is never NULL
428 GNUNET_assert (queue->content == NULL);
429 // is session already in queue?
430 if (session == queue->content){
438 // Session is not in the queue
440 queue = GNUNET_malloc (sizeof (struct Sessionqueue));
441 if (plugin->pending_Sessions == NULL){
443 plugin->pending_Sessions = queue;
445 lastitem->next = queue;
446 queue->prev = lastitem;
449 queue->content = session;
454 * Function called to when wlan helper is ready to get some data
457 * @param GNUNET_SCHEDULER_TaskContext
461 do_transmit (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
463 struct Plugin * plugin = cls;
466 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
469 struct Session * session;
470 struct Sessionqueue * queue;
471 struct PendingMessage * pm;
472 struct IeeeHeader * wlanheader;
473 struct RadiotapHeader * radioHeader;
474 struct GNUNET_MessageHeader * msgheader;
477 queue = plugin->pending_Sessions;
479 //check if the are some pending sessions/messages ...
480 GNUNET_assert(queue != NULL);
482 session = queue->content;
483 GNUNET_assert(session != NULL);
485 pm = session->pending_messages_head;
486 GNUNET_assert(pm != NULL);
488 //check if msg is valid to send
489 if (GNUNET_TIME_absolute_get_remaining(pm->timeout).value > 0){
490 // fixme split msg if to large
492 //increment one, this is a new message
493 session->message_num_out ++;
494 // fixme peer id is needed in each packet
495 size = pm->message_size + sizeof(struct RadiotapHeader)
496 + sizeof(struct IeeeHeader) + sizeof(struct GNUNET_MessageHeader)
497 + sizeof(struct FragmentationHeader);
498 msgheader = GNUNET_malloc(size);
499 msgheader->size = pm->message_size + sizeof(struct RadiotapHeader) + sizeof(struct IeeeHeader);
500 msgheader->type = GNUNET_MESSAGE_TYPE_WLAN_HELPER_DATA;
502 radioHeader = (struct RadiotapHeader*) &msgheader[1];
503 getRadiotapHeader(radioHeader);
505 wlanheader = (struct IeeeHeader *) &radioHeader[1];
506 getWlanHeader(wlanheader);
508 bytes = GNUNET_DISK_file_write(plugin->server_stdin_handle, msgheader, size);
511 GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
512 session->pending_messages_tail,
527 //plugin->server_read_task =
528 //GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
529 // GNUNET_TIME_UNIT_FOREVER_REL,
530 // plugin->server_stdout_handle, &wlan_plugin_helper_read, plugin);
535 * If we have pending messages, ask the server to
536 * transmit them (schedule the respective tasks, etc.)
538 * @param Plugin env to get everything needed
541 process_pending_messages (struct Plugin * plugin)
543 struct Sessionqueue * queue;
544 struct Session * session;
546 if (plugin->pending_Sessions == NULL)
549 queue = plugin->pending_Sessions;
550 //contet should not be empty
551 GNUNET_assert(queue->content != NULL);
553 session = queue->content;
554 //pending sessions should have some msg
555 GNUNET_assert(session->pending_messages_head != NULL);
557 // GNUNET_TIME_UNIT_FOREVER_REL is needed to clean up old msg
558 plugin->server_write_task
559 = GNUNET_SCHEDULER_add_write_file(plugin->env->sched,
560 GNUNET_TIME_UNIT_FOREVER_REL,
561 plugin->server_stdin_handle,
569 * Function that can be used by the transport service to transmit
570 * a message using the plugin.
573 * @param target who should receive this message
574 * @param priority how important is the message
575 * @param msgbuf the message to transmit
576 * @param msgbuf_size number of bytes in 'msgbuf'
577 * @param timeout when should we time out
578 * @param session which session must be used (or NULL for "any")
579 * @param addr the address to use (can be NULL if the plugin
580 * is "on its own" (i.e. re-use existing TCP connection))
581 * @param addrlen length of the address in bytes
582 * @param force_address GNUNET_YES if the plugin MUST use the given address,
583 * otherwise the plugin may use other addresses or
584 * existing connections (if available)
585 * @param cont continuation to call once the message has
586 * been transmitted (or if the transport is ready
587 * for the next transmission call; or if the
588 * peer disconnected...)
589 * @param cont_cls closure for cont
590 * @return number of bytes used (on the physical network, with overheads);
591 * -1 on hard errors (i.e. address invalid); 0 is a legal value
592 * and does NOT mean that the message was not transmitted (DV)
595 wlan_plugin_send (void *cls,
596 const struct GNUNET_PeerIdentity * target,
599 unsigned int priority,
600 struct GNUNET_TIME_Relative timeout,
601 struct Session *session,
605 GNUNET_TRANSPORT_TransmitContinuation cont,
608 struct Plugin * plugin = cls;
609 struct PendingMessage * newmsg = NULL;
611 //check if msglen > 0
612 GNUNET_assert(msgbuf_size > 0);
614 //get session if needed
615 if (session == NULL) {
616 if ( wlan_plugin_address_suggested(plugin , addr, addrlen) == GNUNET_OK){
617 session = get_Session(plugin, addr);
619 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
620 _("Wlan Address len %d is wrong\n"),
626 //TODO target "problem" not solved
627 session->target = *target;
630 //first queue session
631 queue_Session(plugin, session);
633 //queue message in session
634 newmsg = GNUNET_malloc(sizeof(struct PendingMessage) + msgbuf_size);
635 newmsg->msg = (const char*) &newmsg[1];
636 //copy msg to buffer, not fragmented / segmented yet
637 memcpy(&newmsg[1], msgbuf, msgbuf_size);
638 newmsg->transmit_cont = cont;
639 newmsg->transmit_cont_cls = cont_cls;
640 newmsg->timeout = GNUNET_TIME_relative_to_absolute(timeout);
641 newmsg->message_pos = 0;
642 newmsg->message_size = msgbuf_size;
645 //check if queue is empty
646 struct PendingMessage * tailmsg;
647 tailmsg = session->pending_messages_tail;
649 //new tail is the new msg
650 session->pending_messages_tail = newmsg;
651 newmsg->prev = tailmsg;
653 //test if tail was not NULL (queue is empty)
654 if (tailmsg == NULL){
655 // head should be NULL too
656 GNUNET_assert(session->pending_messages_head == NULL);
658 session->pending_messages_head = newmsg;
661 //next at the tail should be NULL
662 GNUNET_assert(tailmsg->next == NULL);
665 tailmsg->next = newmsg;
668 process_pending_messages(plugin);
671 //FIXME not the correct size
679 * Function that can be used to force the plugin to disconnect
680 * from the given peer and cancel all previous transmissions
681 * (and their continuation).
684 * @param target peer from which to disconnect
687 wlan_plugin_disconnect (void *cls,
688 const struct GNUNET_PeerIdentity *target)
690 // struct Plugin *plugin = cls;
696 * Convert the transports address to a nice, human-readable
700 * @param type name of the transport that generated the address
701 * @param addr one of the addresses of the host, NULL for the last address
702 * the specific address format depends on the transport
703 * @param addrlen length of the address
704 * @param numeric should (IP) addresses be displayed in numeric form?
705 * @param timeout after how long should we give up?
706 * @param asc function to call on each string
707 * @param asc_cls closure for asc
710 wlan_plugin_address_pretty_printer (void *cls,
715 struct GNUNET_TIME_Relative timeout,
716 GNUNET_TRANSPORT_AddressStringCallback
720 const unsigned char * input;
722 GNUNET_assert(cls !=NULL);
725 /* invalid address (MAC addresses have 6 bytes) */
730 input = (const unsigned char*) addr;
731 GNUNET_snprintf (ret,
733 "%s Mac-Adress %X:%X:%X:%X:%X:%X",
735 input[0], input[1], input[2], input[3], input[4], input[5]);
742 * Another peer has suggested an address for this
743 * peer and transport plugin. Check that this could be a valid
744 * address. If so, consider adding it to the list
748 * @param addr pointer to the address
749 * @param addrlen length of addr
750 * @return GNUNET_OK if this is a plausible address for this peer
756 wlan_plugin_address_suggested (void *cls,
760 /* struct Plugin *plugin = cls; */
762 /* check if the address is plausible; if so,
763 add it to our list! */
765 GNUNET_assert(cls !=NULL);
767 //Mac Adress has 6 bytes
769 /* TODO check for bad addresses like milticast, broadcast, etc */
772 return GNUNET_SYSERR;
775 return GNUNET_SYSERR;
780 * Function called for a quick conversion of the binary address to
781 * a numeric address. Note that the caller must not free the
782 * address and that the next call to this function is allowed
783 * to override the address again.
786 * @param addr binary address
787 * @param addrlen length of the address
788 * @return string representing the same address
791 wlan_plugin_address_to_string (void *cls,
796 const unsigned char * input;
798 GNUNET_assert(cls !=NULL);
801 /* invalid address (MAC addresses have 6 bytes) */
805 input = (const unsigned char*) addr;
806 GNUNET_snprintf (ret,
808 "%s Mac-Adress %X:%X:%X:%X:%X:%X",
810 input[0], input[1], input[2], input[3], input[4], input[5]);
811 return GNUNET_strdup (ret);
817 * Function used for to process the data from the suid process
820 wlan_process_helper (void *cls,
822 const struct GNUNET_MessageHeader *hdr)
824 struct Plugin *plugin = cls;
825 if (hdr->type == GNUNET_MESSAGE_TYPE_WLAN_HELPER_DATA){
827 } else if (hdr->type == GNUNET_MESSAGE_TYPE_WLAN_ADVERTISEMENT){
829 } else if (hdr->type == GNUNET_MESSAGE_TYPE_WLAN_HELPER_CONTROL){
832 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Notifying transport of address %s\n", wlan_plugin_address_to_string(cls, plugin->mac_address, hdr->size));
833 plugin->env->notify_address (plugin->env->cls,
835 &plugin->mac_address, sizeof(plugin->mac_address),
836 GNUNET_TIME_UNIT_FOREVER_REL);
838 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Wrong wlan mac address %s\n", plugin->mac_address);
849 wlan_plugin_helper_read (void *cls,
850 const struct GNUNET_SCHEDULER_TaskContext *tc)
852 struct Plugin *plugin = cls;
853 char mybuf[WLAN_MTU];
856 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
858 bytes = GNUNET_DISK_file_read (plugin->server_stdout_handle,
859 mybuf, sizeof(mybuf));
863 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864 _("Finished reading from wlan-helper stdout with code: %d\n"), bytes);
868 GNUNET_SERVER_mst_receive(plugin->consoltoken, NULL,
869 mybuf, bytes, 0, GNUNET_NO);
875 * Start the gnunet-wlan-helper process.
877 * @param plugin the transport plugin
879 * @return GNUNET_YES if process was started, GNUNET_SYSERR on error
882 wlan_transport_start_wlan_helper (struct Plugin *plugin)
885 plugin->server_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_NO, GNUNET_YES);
886 if (plugin->server_stdout == NULL)
887 return GNUNET_SYSERR;
889 plugin->server_stdin = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO);
890 if (plugin->server_stdin == NULL)
891 return GNUNET_SYSERR;
894 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
895 "Starting gnunet-wlan-helper process cmd: %s %s\n", "gnunet-wlan-helper", plugin->interface);
897 /* Start the server process */
898 plugin->server_pid = GNUNET_OS_start_process(plugin->server_stdin, plugin->server_stdout, "gnunet-transport-wlan-helper", "gnunet-transport-wlan-helper", plugin->interface, NULL);
899 if (plugin->server_pid == GNUNET_SYSERR)
902 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
903 "Failed to start gnunet-wlan-helper process\n");
905 return GNUNET_SYSERR;
907 /* Close the write end of the read pipe */
908 GNUNET_DISK_pipe_close_end(plugin->server_stdout, GNUNET_DISK_PIPE_END_WRITE);
910 /* Close the read end of the write pipe */
911 GNUNET_DISK_pipe_close_end(plugin->server_stdin, GNUNET_DISK_PIPE_END_READ);
913 plugin->server_stdout_handle = GNUNET_DISK_pipe_handle(plugin->server_stdout, GNUNET_DISK_PIPE_END_READ);
914 plugin->server_stdin_handle = GNUNET_DISK_pipe_handle(plugin->server_stdin, GNUNET_DISK_PIPE_END_WRITE);
916 plugin->server_read_task =
917 GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
918 GNUNET_TIME_UNIT_FOREVER_REL,
919 plugin->server_stdout_handle, &wlan_plugin_helper_read, plugin);
929 * Entry point for the plugin.
931 * @param cls closure, the 'struct GNUNET_TRANSPORT_PluginEnvironment*'
932 * @return the 'struct GNUNET_TRANSPORT_PluginFunctions*' or NULL on error
935 gnunet_plugin_transport_wlan_init (void *cls)
937 struct GNUNET_SERVICE_Context *service;
938 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
939 struct GNUNET_TRANSPORT_PluginFunctions *api;
940 struct Plugin *plugin;
942 GNUNET_assert(cls !=NULL);
944 service = GNUNET_SERVICE_start ("transport-wlan", env->sched, env->cfg);
945 if (service == NULL){
946 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
947 _("Failed to start service for `%s' transport plugin.\n"),
952 plugin = GNUNET_malloc (sizeof (struct Plugin));
955 wlan_transport_start_wlan_helper(plugin);
956 plugin->consoltoken = GNUNET_SERVER_mst_create(&wlan_process_helper,plugin);
958 //plugin->all_Sessions = GNUNET_malloc (sizeof (struct Sessionqueue));
959 //plugin->pending_Sessions = GNUNET_malloc (sizeof (struct Sessionqueue));
961 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
963 api->send = &wlan_plugin_send;
964 api->disconnect = &wlan_plugin_disconnect;
965 api->address_pretty_printer = &wlan_plugin_address_pretty_printer;
966 api->check_address = &wlan_plugin_address_suggested;
967 api->address_to_string = &wlan_plugin_address_to_string;
974 * Exit point from the plugin.
977 gnunet_plugin_transport_wlan_done (void *cls)
979 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
980 struct Plugin *plugin = api->cls;
982 GNUNET_assert(cls !=NULL);
984 GNUNET_free (plugin);
989 /* end of plugin_transport_wlan.c */