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 #define DEBUG_wlan GNUNET_NO
42 * After how long do we expire an address that we
43 * learned from another peer if it is not reconfirmed
46 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
49 * Initial handshake message for a session.
54 * Type is GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME.
56 struct GNUNET_MessageHeader header;
59 * Identity of the node connecting (TCP client)
61 struct GNUNET_PeerIdentity clientIdentity;
66 * Encapsulation of all of the state of the plugin.
73 struct GNUNET_TRANSPORT_PluginEnvironment *env;
76 * List of open sessions.
78 struct Session *sessions;
81 * encapsulation to the local wlan server prog
84 struct GNUNET_SERVER_MessageStreamTokenizer * consoltoken;
88 * stdout pipe handle for the gnunet-wlan-helper process
90 struct GNUNET_DISK_PipeHandle *server_stdout;
93 * stdout file handle for the gnunet-wlan-helper process
95 const struct GNUNET_DISK_FileHandle *server_stdout_handle;
98 * stdin pipe handle for the gnunet-wlan-helper process
100 struct GNUNET_DISK_PipeHandle *server_stdin;
103 * stdin file handle for the gnunet-wlan-helper process
105 const struct GNUNET_DISK_FileHandle *server_stdin_handle;
108 * ID of select gnunet-nat-server std read task
110 GNUNET_SCHEDULER_TaskIdentifier server_read_task;
113 * ID of select gnunet-nat-server std read task
115 GNUNET_SCHEDULER_TaskIdentifier server_write_task;
118 * The process id of the server process (if behind NAT)
123 * The interface of the wlan card given to us by the user.
128 * The mac_address of the wlan card given to us by the helper.
133 * Sessions currently pending for transmission
134 * to this peer, if any.
136 struct Sessionqueue * pending_Sessions;
139 * Sessions currently pending for transmission
140 * to this peer, if any.
142 struct Sessionqueue * all_Sessions;
149 struct Sessionqueue * next;
150 struct Sessionqueue * prev;
151 struct Session * content;
155 * Session handle for connections.
163 struct SessionHeader header;
166 * Pointer to the global plugin struct.
168 struct Plugin *plugin;
171 * Messages currently pending for transmission
172 * to this peer, if any.
174 struct PendingMessage *pending_messages_head;
177 * Messages currently pending for transmission
178 * to this peer, if any.
180 struct PendingMessage *pending_messages_tail;
183 * To whom are we talking to (set to our identity
184 * if we are still waiting for the welcome message)
186 struct GNUNET_PeerIdentity * target;
189 * encapsulation of the data
192 struct GNUNET_SERVER_MessageStreamTokenizer * datatoken;
202 * Address of the other peer (either based on our 'connect'
203 * call or on our 'accept' call).
208 * Last activity on this connection. Used to select preferred
211 struct GNUNET_TIME_Absolute last_activity;
214 * number of message, to distinguish between the messages
217 uint16_t message_num_in;
220 * number of message, to distinguish between the messages
223 uint16_t message_num_out;
229 * Information kept for each message that is yet to
232 struct PendingMessage
236 * This is a doubly-linked list.
238 struct PendingMessage *next;
241 * This is a doubly-linked list.
243 struct PendingMessage *prev;
246 * The pending message
252 * Continuation function to call once the message
253 * has been sent. Can be NULL if there is no
254 * continuation to call.
256 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
259 * Cls for transmit_cont
262 void * transmit_cont_cls;
265 * Timeout value for the pending message.
267 struct GNUNET_TIME_Absolute timeout;
270 * Size of the message
275 * pos in the message, for fragmentation/segmentation
282 * Header for messages which need fragmentation
285 struct FragmentationHeader
289 * To whom are we talking to (set to our identity
290 * if we are still waiting for the welcome message)
292 struct GNUNET_PeerIdentity * target GNUNET_PACKED;
295 * number of message, to distinguish between the messages
298 uint16_t message_num GNUNET_PACKED;
301 * number of this fragment, for fragmentation/segmentation
303 uint16_t fregment_num GNUNET_PACKED;
307 * number of fregments in this message
309 uint16_t ack_message_num GNUNET_PACKED;
312 * number of this fragment, for fragmentation/segmentation
314 uint16_t ack_fregment_num GNUNET_PACKED;
319 * 0x2 has data (not only ack)
320 * 0x4 last fragment of message
324 uint32_t flags GNUNET_PACKED;
328 * checksum/error correction
331 uint32_t crc GNUNET_PACKED;
334 enum { ACK_FRAGMENT = 1, DATA_FRAGMENT = 2, LAST_FRAGMENT = 4, NEW_MESSAGE = 8 };
336 int getRadiotapHeader (struct RadiotapHeader * Header);
337 int getWlanHeader (struct IeeeHeader * Header);
338 static int wlan_plugin_address_suggested (void *cls,
342 * get Session from address
346 //TODO add other possibilities to find the right session (are there other?)
347 static struct Session *
348 get_Session (struct Plugin *plugin,
351 struct Sessionqueue * queue = plugin->all_Sessions;
352 struct Sessionqueue * lastitem = NULL;
354 while (queue != NULL){
355 // content is never NULL
356 GNUNET_assert (queue->content == NULL);
357 char * addr2 = (queue->content)->addr;
358 if (memcmp(addr, addr2, 6) == 0){
360 return queue->content;
367 queue = GNUNET_malloc (sizeof (struct Sessionqueue));
369 if (plugin->all_Sessions == NULL){
371 plugin->all_Sessions = queue;
373 lastitem->next = queue;
374 queue->prev = lastitem;
377 queue->content = GNUNET_malloc (sizeof (struct Session));
378 (queue->content)->plugin = plugin;
379 memcpy((queue->content)->addr, addr, 6);
382 struct WelcomeMessage welcome;
383 struct PendingMessage *pm;
384 pm = GNUNET_malloc (sizeof (struct PendingMessage) + sizeof (struct WelcomeMessage));
385 pm->msg = (const char*) &pm[1];
386 pm->message_size = sizeof (struct WelcomeMessage);
387 welcome.header.size = htons (sizeof (struct WelcomeMessage));
388 welcome.header.type = htons (GNUNET_MESSAGE_TYPE_WLAN_ADVERTISEMENT);
389 welcome.clientIdentity = *plugin->env->my_identity;
390 memcpy (&pm[1], &welcome, sizeof (welcome));
391 pm->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
392 GNUNET_CONTAINER_DLL_insert ((queue->content)->pending_messages_head,
393 (queue->content)->pending_messages_tail,
396 return queue->content;
401 * Queue the session to send data
405 queue_Session (struct Plugin *plugin,
406 struct Session * session)
408 struct Sessionqueue * queue = plugin->pending_Sessions;
409 struct Sessionqueue * lastitem = NULL;
411 while (queue != NULL){
412 // content is never NULL
413 GNUNET_assert (queue->content == NULL);
414 // is session already in queue?
415 if (session == queue->content){
423 // Session is not in the queue
425 queue = GNUNET_malloc (sizeof (struct Sessionqueue));
426 if (plugin->pending_Sessions == NULL){
428 plugin->pending_Sessions = queue;
430 lastitem->next = queue;
431 queue->prev = lastitem;
434 queue->content = session;
439 * Function called to when wlan helper is ready to get some data
442 * @param GNUNET_SCHEDULER_TaskContext
446 do_transmit (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
448 struct Plugin * plugin = cls;
452 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
455 struct Session * session;
456 struct Sessionqueue * queue;
457 struct PendingMessage * pm;
458 struct IeeeHeader * wlanheader;
459 struct RadiotapHeader * RadioHeader;
460 struct GNUNET_MessageHeader * msgheader;
463 queue = plugin->pending_Sessions;
465 //check if the are some pending sessions/messages ...
466 GNUNET_assert(queue != NULL);
468 session = queue->content;
469 GNUNET_assert(session != NULL);
471 pm = session->pending_messages_head;
472 GNUNET_assert(pm != NULL);
474 //check if msg is valid to send
475 if (GNUNET_TIME_absolute_get_remaining(pm->timeout).value > 0){
476 // fixme split msg if to large
478 //increment one, this is a new message
479 session->message_num_out ++;
480 // fixme peer id is needed in each packet
481 size = pm->message_size + sizeof(struct RadiotapHeader)
482 + sizeof(struct IeeeHeader) + sizeof(struct GNUNET_MessageHeader)
483 + sizeof(struct FragmentationHeader);
484 msg = GNUNET_malloc(size);
487 msgheader->size = pm->message_size + sizeof(struct RadiotapHeader) + sizeof(struct IeeeHeader);
488 msgheader->type = GNUNET_MESSAGE_TYPE_WLAN_HELPER_DATA;
490 RadioHeader = &msgheader[1];
491 getRadiotapHeader(RadioHeader);
493 wlanheader = &RadioHeader[1];
494 getWlanHeader(wlanheader);
496 bytes = GNUNET_DISK_file_write(plugin->server_stdin_handle, msg, size);
499 GNUNET_CONTAINER_DLL_remove (session->pending_messages_head,
500 session->pending_messages_tail,
502 GNUNET_free(pm->msg);
516 //plugin->server_read_task =
517 //GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
518 // GNUNET_TIME_UNIT_FOREVER_REL,
519 // plugin->server_stdout_handle, &wlan_plugin_helper_read, plugin);
524 * If we have pending messages, ask the server to
525 * transmit them (schedule the respective tasks, etc.)
527 * @param Plugin env to get everything needed
530 process_pending_messages (struct Plugin * plugin)
532 struct Sessionqueue * queue;
533 struct Session * session;
535 if (plugin->pending_Sessions == NULL)
538 queue = plugin->pending_Sessions;
539 //contet should not be empty
540 GNUNET_assert(queue->content != NULL);
542 session = queue->content;
543 //pending sessions should have some msg
544 GNUNET_assert(session->pending_messages_head != NULL);
546 // GNUNET_TIME_UNIT_FOREVER_REL is needed to clean up old msg
547 plugin->server_write_task
548 = GNUNET_SCHEDULER_add_write_file(plugin->env->sched,
549 GNUNET_TIME_UNIT_FOREVER_REL,
550 plugin->server_stdin_handle,
558 * Function that can be used by the transport service to transmit
559 * a message using the plugin.
562 * @param target who should receive this message
563 * @param priority how important is the message
564 * @param msgbuf the message to transmit
565 * @param msgbuf_size number of bytes in 'msgbuf'
566 * @param timeout when should we time out
567 * @param session which session must be used (or NULL for "any")
568 * @param addr the address to use (can be NULL if the plugin
569 * is "on its own" (i.e. re-use existing TCP connection))
570 * @param addrlen length of the address in bytes
571 * @param force_address GNUNET_YES if the plugin MUST use the given address,
572 * otherwise the plugin may use other addresses or
573 * existing connections (if available)
574 * @param cont continuation to call once the message has
575 * been transmitted (or if the transport is ready
576 * for the next transmission call; or if the
577 * peer disconnected...)
578 * @param cont_cls closure for cont
579 * @return number of bytes used (on the physical network, with overheads);
580 * -1 on hard errors (i.e. address invalid); 0 is a legal value
581 * and does NOT mean that the message was not transmitted (DV)
584 wlan_plugin_send (void *cls,
585 const struct GNUNET_PeerIdentity * target,
588 unsigned int priority,
589 struct GNUNET_TIME_Relative timeout,
590 struct Session *session,
594 GNUNET_TRANSPORT_TransmitContinuation cont,
600 struct Plugin * plugin = cls;
602 struct PendingMessage * newmsg = NULL;
604 //check if msglen > 0
605 GNUNET_assert(msgbuf_size > 0);
607 //get session if needed
608 if (session == NULL) {
609 if ( wlan_plugin_address_suggested(plugin , addr, addrlen) == GNUNET_OK){
610 session = get_Session(plugin, addr);
612 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
613 _("Wlan Address len %d is wrong\n"),
619 //TODO target "problem" not solved
620 session->target = target;
623 //first queue session
624 queue_Session(plugin, session);
626 //queue message in session
627 newmsg = GNUNET_malloc(sizeof(struct PendingMessage));
628 newmsg->msg = GNUNET_malloc(msgbuf_size);
629 //copy msg to buffer, not fragmented / segmented yet
630 memcpy(newmsg->msg, msgbuf, msgbuf_size);
631 newmsg->transmit_cont = cont;
632 newmsg->transmit_cont_cls = cont_cls;
633 newmsg->timeout = GNUNET_TIME_relative_to_absolute(timeout);
634 newmsg->message_pos = 0;
635 newmsg->message_size = msgbuf_size;
638 //check if queue is empty
639 struct PendingMessage * tailmsg;
640 tailmsg = session->pending_messages_tail;
642 //new tail is the new msg
643 session->pending_messages_tail = newmsg;
644 newmsg->prev = tailmsg;
646 //test if tail was not NULL (queue is empty)
647 if (tailmsg == NULL){
648 // head should be NULL too
649 GNUNET_assert(session->pending_messages_head == NULL);
651 session->pending_messages_head = newmsg;
654 //next at the tail should be NULL
655 GNUNET_assert(tailmsg->next == NULL);
658 tailmsg->next = newmsg;
661 process_pending_messages(plugin);
664 //FIXME not the correct size
672 * Function that can be used to force the plugin to disconnect
673 * from the given peer and cancel all previous transmissions
674 * (and their continuation).
677 * @param target peer from which to disconnect
680 wlan_plugin_disconnect (void *cls,
681 const struct GNUNET_PeerIdentity *target)
683 // struct Plugin *plugin = cls;
689 * Convert the transports address to a nice, human-readable
693 * @param type name of the transport that generated the address
694 * @param addr one of the addresses of the host, NULL for the last address
695 * the specific address format depends on the transport
696 * @param addrlen length of the address
697 * @param numeric should (IP) addresses be displayed in numeric form?
698 * @param timeout after how long should we give up?
699 * @param asc function to call on each string
700 * @param asc_cls closure for asc
703 wlan_plugin_address_pretty_printer (void *cls,
708 struct GNUNET_TIME_Relative timeout,
709 GNUNET_TRANSPORT_AddressStringCallback
713 const unsigned char * input;
715 GNUNET_assert(cls !=NULL);
718 /* invalid address (MAC addresses have 6 bytes) */
723 input = (const unsigned char*) addr;
724 GNUNET_snprintf (ret,
726 "%s Mac-Adress %X:%X:%X:%X:%X:%X",
728 input[0], input[1], input[2], input[3], input[4], input[5]);
735 * Another peer has suggested an address for this
736 * peer and transport plugin. Check that this could be a valid
737 * address. If so, consider adding it to the list
741 * @param addr pointer to the address
742 * @param addrlen length of addr
743 * @return GNUNET_OK if this is a plausible address for this peer
749 wlan_plugin_address_suggested (void *cls,
753 /* struct Plugin *plugin = cls; */
755 /* check if the address is plausible; if so,
756 add it to our list! */
758 GNUNET_assert(cls !=NULL);
760 //Mac Adress has 6 bytes
762 /* TODO check for bad addresses like milticast, broadcast, etc */
765 return GNUNET_SYSERR;
768 return GNUNET_SYSERR;
773 * Function called for a quick conversion of the binary address to
774 * a numeric address. Note that the caller must not free the
775 * address and that the next call to this function is allowed
776 * to override the address again.
779 * @param addr binary address
780 * @param addrlen length of the address
781 * @return string representing the same address
784 wlan_plugin_address_to_string (void *cls,
789 const unsigned char * input;
791 GNUNET_assert(cls !=NULL);
794 /* invalid address (MAC addresses have 6 bytes) */
798 input = (const unsigned char*) addr;
799 GNUNET_snprintf (ret,
801 "%s Mac-Adress %X:%X:%X:%X:%X:%X",
803 input[0], input[1], input[2], input[3], input[4], input[5]);
804 return GNUNET_strdup (ret);
810 * Function used for to process the data from the suid process
813 wlan_process_helper (void *cls,
815 const struct GNUNET_MessageHeader *hdr)
817 struct Plugin *plugin = cls;
818 if (hdr->type == GNUNET_MESSAGE_TYPE_WLAN_HELPER_DATA){
820 } else if (hdr->type == GNUNET_MESSAGE_TYPE_WLAN_ADVERTISEMENT){
822 } else if (hdr->type == GNUNET_MESSAGE_TYPE_WLAN_HELPER_CONTROL){
825 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Notifying transport of address %s\n", wlan_plugin_address_to_string(cls, plugin->mac_address, hdr->size));
826 plugin->env->notify_address (plugin->env->cls,
828 &plugin->mac_address, sizeof(plugin->mac_address),
829 GNUNET_TIME_UNIT_FOREVER_REL);
831 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Wrong wlan mac address %s\n", plugin->mac_address);
842 wlan_plugin_helper_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
844 struct Plugin *plugin = cls;
845 char mybuf[WLAN_MTU]; //max size of packet from helper
847 //memset(&mybuf, 0, sizeof(mybuf)); //?
849 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
852 bytes = GNUNET_DISK_file_read(plugin->server_stdout_handle, &mybuf, sizeof(mybuf));
857 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
858 _("Finished reading from wlan-helper stdout with code: %d\n"), bytes);
863 GNUNET_SERVER_mst_receive(plugin->consoltoken,NULL,&mybuf,bytes,0, GNUNET_NO);
869 * Start the gnunet-wlan-helper process.
871 * @param plugin the transport plugin
873 * @return GNUNET_YES if process was started, GNUNET_SYSERR on error
876 wlan_transport_start_wlan_helper(struct Plugin *plugin)
879 plugin->server_stdout = (GNUNET_YES, GNUNET_NO, GNUNET_YES);
880 if (plugin->server_stdout == NULL)
881 return GNUNET_SYSERR;
883 plugin->server_stdin = GNUNET_DISK_pipe(GNUNET_YES, GNUNET_YES, GNUNET_NO);
884 if (plugin->server_stdin == NULL)
885 return GNUNET_SYSERR;
888 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889 "Starting gnunet-wlan-helper process cmd: %s %s\n", "gnunet-wlan-helper", plugin->interface);
891 /* Start the server process */
892 plugin->server_pid = GNUNET_OS_start_process(plugin->server_stdin, plugin->server_stdout, "gnunet-transport-wlan-helper", "gnunet-transport-wlan-helper", plugin->interface, NULL);
893 if (plugin->server_pid == GNUNET_SYSERR)
896 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
897 "Failed to start gnunet-wlan-helper process\n");
899 return GNUNET_SYSERR;
901 /* Close the write end of the read pipe */
902 GNUNET_DISK_pipe_close_end(plugin->server_stdout, GNUNET_DISK_PIPE_END_WRITE);
904 /* Close the read end of the write pipe */
905 GNUNET_DISK_pipe_close_end(plugin->server_stdin, GNUNET_DISK_PIPE_END_READ);
907 plugin->server_stdout_handle = GNUNET_DISK_pipe_handle(plugin->server_stdout, GNUNET_DISK_PIPE_END_READ);
908 plugin->server_stdin_handle = GNUNET_DISK_pipe_handle(plugin->server_stdin, GNUNET_DISK_PIPE_END_WRITE);
910 plugin->server_read_task =
911 GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
912 GNUNET_TIME_UNIT_FOREVER_REL,
913 plugin->server_stdout_handle, &wlan_plugin_helper_read, plugin);
923 * Entry point for the plugin.
925 * @param cls closure, the 'struct GNUNET_TRANSPORT_PluginEnvironment*'
926 * @return the 'struct GNUNET_TRANSPORT_PluginFunctions*' or NULL on error
929 gnunet_plugin_transport_wlan_init (void *cls)
931 struct GNUNET_SERVICE_Context *service;
932 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
933 struct GNUNET_TRANSPORT_PluginFunctions *api;
934 struct Plugin *plugin;
936 GNUNET_assert(cls !=NULL);
938 service = GNUNET_SERVICE_start ("transport-wlan", env->sched, env->cfg);
939 if (service == NULL){
940 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
941 _("Failed to start service for `%s' transport plugin.\n"),
946 plugin = GNUNET_malloc (sizeof (struct Plugin));
949 wlan_transport_start_wlan_helper(plugin);
950 plugin->consoltoken = GNUNET_SERVER_mst_create(&wlan_process_helper,plugin);
952 //plugin->all_Sessions = GNUNET_malloc (sizeof (struct Sessionqueue));
953 //plugin->pending_Sessions = GNUNET_malloc (sizeof (struct Sessionqueue));
955 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
957 api->send = &wlan_plugin_send;
958 api->disconnect = &wlan_plugin_disconnect;
959 api->address_pretty_printer = &wlan_plugin_address_pretty_printer;
960 api->check_address = &wlan_plugin_address_suggested;
961 api->address_to_string = &wlan_plugin_address_to_string;
968 * Exit point from the plugin.
971 gnunet_plugin_transport_wlan_done (void *cls)
973 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
974 struct Plugin *plugin = api->cls;
976 GNUNET_assert(cls !=NULL);
978 GNUNET_free (plugin);
983 /* end of plugin_transport_wlan.c */