2 This file is part of GNUnet
3 (C) 2001, 2002, 2003, 2004, 2005, 2008 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 2, 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_udp.c
23 * @brief Implementation of the UDP transport service
24 * @author Christian Grothoff
28 #include "gnunet_util.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_transport.h"
31 #include "gnunet_stats_service.h"
32 #include "gnunet_upnp_service.h"
35 #define DEBUG_UDP GNUNET_YES
38 * The default maximum size of each outbound UDP message,
39 * optimal value for Ethernet (10 or 100 MBit).
41 #define MESSAGE_SIZE 1472
44 * Message-Packet header.
49 * size of the message, in bytes, including this header.
51 GNUNET_MessageHeader header;
54 * What is the identity of the sender (GNUNET_hash of public key)
56 GNUNET_PeerIdentity sender;
60 #define MY_TRANSPORT_NAME "UDP"
63 /* *********** globals ************* */
65 static int stat_bytesReceived;
67 static int stat_bytesSent;
69 static int stat_bytesDropped;
71 static int stat_udpConnected;
74 * thread that listens for inbound messages
76 static struct GNUNET_SelectHandle *selector;
79 * the socket that we transmit all data with
81 static struct GNUNET_SocketHandle *udp_sock;
83 static struct GNUNET_LoadMonitor *load_monitor;
87 * The socket of session has data waiting, process!
89 * This function may only be called if the tcplock is
90 * already held by the caller.
93 select_message_handler (void *mh_cls,
94 struct GNUNET_SelectHandle *sh,
95 struct GNUNET_SocketHandle *sock,
96 void *sock_ctx, const GNUNET_MessageHeader * msg)
99 GNUNET_TransportPacket *mp;
100 const UDPMessage *um;
102 len = ntohs (msg->size);
103 if (len <= sizeof (UDPMessage))
105 GNUNET_GE_LOG (coreAPI->ectx,
106 GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK,
107 _("Received malformed message via %s. Ignored.\n"),
109 return GNUNET_SYSERR;
111 um = (const UDPMessage *) msg;
112 mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
113 mp->msg = GNUNET_malloc (len - sizeof (UDPMessage));
114 memcpy (mp->msg, &um[1], len - sizeof (UDPMessage));
115 mp->sender = um->sender;
116 mp->size = len - sizeof (UDPMessage);
118 coreAPI->receive (mp);
120 stats->change (stat_bytesReceived, len);
125 select_accept_handler (void *ah_cls,
126 struct GNUNET_SelectHandle *sh,
127 struct GNUNET_SocketHandle *sock,
128 const void *addr, unsigned int addr_len)
130 static int nonnullpointer;
132 if (GNUNET_NO != is_rejected_tester (addr, addr_len))
134 return &nonnullpointer;
138 * Select has been forced to close a connection.
139 * Free the associated context.
142 select_close_handler (void *ch_cls,
143 struct GNUNET_SelectHandle *sh,
144 struct GNUNET_SocketHandle *sock, void *sock_ctx)
150 * Establish a connection to a remote node.
152 * @param hello the hello-Message for the target node
153 * @param tsessionPtr the session handle that is to be set
154 * @param may_reuse are we allowed to re-use an existing connection (ignored for UDP)
155 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
158 udp_connect (const GNUNET_MessageHello * hello,
159 GNUNET_TSession ** tsessionPtr, int may_reuse)
161 GNUNET_TSession *tsession;
163 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
164 memset (tsession, 0, sizeof (GNUNET_TSession));
165 tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello));
166 memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello));
167 tsession->ttype = myAPI.protocol_number;
168 tsession->peer = hello->senderIdentity;
169 *tsessionPtr = tsession;
171 stats->change (stat_udpConnected, 1);
176 * A (core) Session is to be associated with a transport session. The
177 * transport service may want to know in order to call back on the
178 * core if the connection is being closed.
180 * @param tsession the session handle passed along
181 * from the call to receive that was made by the transport
183 * @return GNUNET_OK if the session could be associated,
184 * GNUNET_SYSERR if not.
187 udp_associate (GNUNET_TSession * tsession)
189 return GNUNET_SYSERR; /* UDP connections can never be associated */
193 * Disconnect from a remote node.
195 * @param tsession the session that is closed
196 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
199 udp_disconnect (GNUNET_TSession * tsession)
201 if (tsession != NULL)
203 if (tsession->internal != NULL)
204 GNUNET_free (tsession->internal);
205 GNUNET_free (tsession);
207 stats->change (stat_udpConnected, -1);
213 * Shutdown the server process (stop receiving inbound traffic). Maybe
217 udp_transport_server_stop ()
219 GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL);
220 if (selector != NULL)
222 GNUNET_select_destroy (selector);
225 GNUNET_socket_destroy (udp_sock);
231 * Test if the transport would even try to send
232 * a message of the given size and importance
233 * for the given session.<br>
234 * This function is used to check if the core should
235 * even bother to construct (and encrypt) this kind
238 * @return GNUNET_YES if the transport would try (i.e. queue
239 * the message or call the OS to send),
240 * GNUNET_NO if the transport would just drop the message,
241 * GNUNET_SYSERR if the size/session is invalid
244 udp_test_would_try (GNUNET_TSession * tsession, unsigned int size,
247 const GNUNET_MessageHello *hello;
249 if (udp_sock == NULL)
250 return GNUNET_SYSERR;
253 GNUNET_GE_BREAK (coreAPI->ectx, 0);
254 return GNUNET_SYSERR;
256 if (size > myAPI.mtu)
258 GNUNET_GE_BREAK (coreAPI->ectx, 0);
259 return GNUNET_SYSERR;
261 hello = (const GNUNET_MessageHello *) tsession->internal;
263 return GNUNET_SYSERR;
268 * Create a UDP socket. If possible, use IPv6, otherwise
269 * try IPv4. Update available_protocols accordingly.
271 static struct GNUNET_NETWORK_Handle *
274 struct GNUNET_NETWORK_Handle *desc;
276 available_protocols = VERSION_AVAILABLE_NONE;
279 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", "DISABLE-IPV6",
282 desc = GNUNET_net_socket (PF_INET6, SOCK_DGRAM, 17);
286 desc = GNUNET_net_socket (PF_INET, SOCK_DGRAM, 17);
289 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
290 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
291 GNUNET_GE_BULK, "socket");
292 return GNUNET_SYSERR;
294 available_protocols = VERSION_AVAILABLE_IPV4;
298 available_protocols = VERSION_AVAILABLE_IPV6 | VERSION_AVAILABLE_IPV4;
304 * Send a message to the specified remote node.
306 * @param tsession the GNUNET_MessageHello identifying the remote node
307 * @param message what to send
308 * @param size the size of the message
309 * @param important is this message "important" to override typical transmit limits?
310 * @return GNUNET_SYSERR on error, GNUNET_OK on success
313 udp_send (GNUNET_TSession * tsession,
314 const void *message, const unsigned int size, int important)
316 const GNUNET_MessageHello *hello;
317 const HostAddress *haddr;
319 struct sockaddr_in serverAddrv4;
320 struct sockaddr_in6 serverAddrv6;
321 struct sockaddr *serverAddr;
323 unsigned short available;
328 GNUNET_GE_ASSERT (NULL, tsession != NULL);
329 if (udp_sock == NULL)
330 return GNUNET_SYSERR;
333 GNUNET_GE_BREAK (coreAPI->ectx, 0);
334 return GNUNET_SYSERR;
336 if (size > myAPI.mtu)
338 GNUNET_GE_BREAK (coreAPI->ectx, 0);
339 return GNUNET_SYSERR;
341 hello = (const GNUNET_MessageHello *) tsession->internal;
343 return GNUNET_SYSERR;
345 haddr = (const HostAddress *) &hello[1];
346 available = ntohs (haddr->availability) & available_protocols;
347 if (available == VERSION_AVAILABLE_NONE)
348 return GNUNET_SYSERR;
349 if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6))
351 if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0)
352 available = VERSION_AVAILABLE_IPV4;
354 available = VERSION_AVAILABLE_IPV6;
356 ssize = size + sizeof (UDPMessage);
357 mp = GNUNET_malloc (ssize);
358 mp->header.size = htons (ssize);
360 mp->sender = *(coreAPI->my_identity);
361 memcpy (&mp[1], message, size);
364 if ((available & VERSION_AVAILABLE_IPV4) > 0)
366 memset (&serverAddrv4, 0, sizeof (serverAddrv4));
367 #if HAVE_SOCKADDR_IN_SIN_LEN
368 serverAddrv4.sin_len = sizeof (serverAddrv4);
370 serverAddrv4.sin_family = AF_INET;
371 serverAddrv4.sin_port = haddr->port;
372 memcpy (&serverAddrv4.sin_addr, &haddr->ipv4, sizeof (struct in_addr));
373 addrlen = sizeof (serverAddrv4);
374 serverAddr = (struct sockaddr *) &serverAddrv4;
378 memset (&serverAddrv6, 0, sizeof (serverAddrv6));
379 #if HAVE_SOCKADDR_IN_SIN_LEN
380 serverAddrv6.sin6_len = sizeof (serverAddrv6);
382 serverAddrv6.sin6_family = AF_INET;
383 serverAddrv6.sin6_port = haddr->port;
384 memcpy (&serverAddrv6.sin6_addr, &haddr->ipv6,
385 sizeof (struct in6_addr));
386 addrlen = sizeof (serverAddrv6);
387 serverAddr = (struct sockaddr *) &serverAddrv6;
390 if (GNUNET_YES == GNUNET_socket_send_to (udp_sock,
391 GNUNET_NC_NONBLOCKING,
394 (const char *) serverAddr,
398 win_ols_sendto (udp_sock, mp, ssize, (const char *) serverAddr, addrlen);
399 if (sent != SOCKET_ERROR)
404 stats->change (stat_bytesSent, sent);
409 stats->change (stat_bytesDropped, ssize);
416 * Start the server process to receive inbound traffic.
418 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
421 udp_transport_server_start ()
423 struct sockaddr_in serverAddrv4;
424 struct sockaddr_in6 serverAddrv6;
425 struct sockaddr *serverAddr;
427 GNUNET_NETWORK_Handle *desc;
431 GNUNET_GE_ASSERT (coreAPI->ectx, selector == NULL);
432 /* initialize UDP network */
436 desc = udp_create_socket ();
438 return GNUNET_SYSERR;
439 if (GNUNET_net_setsockopt (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
441 GNUNET_GE_DIE_STRERROR (coreAPI->ectx,
442 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
443 GNUNET_GE_IMMEDIATE, "setsockopt");
444 return GNUNET_SYSERR;
446 if (available_protocols == VERSION_AVAILABLE_IPV4)
448 memset (&serverAddrv4, 0, sizeof (serverAddrv4));
449 #if HAVE_SOCKADDR_IN_SIN_LEN
450 serverAddrv4.sin_len = sizeof (serverAddrv4);
452 serverAddrv4.sin_family = AF_INET;
453 serverAddrv4.sin_addr.s_addr = INADDR_ANY;
454 serverAddrv4.sin_port = htons (port);
455 addrlen = sizeof (serverAddrv4);
456 serverAddr = (struct sockaddr *) &serverAddrv4;
460 memset (&serverAddrv6, 0, sizeof (serverAddrv6));
461 #if HAVE_SOCKADDR_IN_SIN_LEN
462 serverAddrv6.sin6_len = sizeof (serverAddrv6);
464 serverAddrv6.sin6_family = AF_INET6;
465 serverAddrv6.sin6_addr = in6addr_any;
466 serverAddrv6.sin6_port = htons (port);
467 addrlen = sizeof (serverAddrv6);
468 serverAddr = (struct sockaddr *) &serverAddrv6;
470 if (GNUNET_net_bind (desc, serverAddr, addrlen) < 0)
472 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
473 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
474 GNUNET_GE_IMMEDIATE, "bind");
475 GNUNET_GE_LOG (coreAPI->ectx,
476 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
478 _("Failed to bind to %s port %d.\n"),
479 MY_TRANSPORT_NAME, port);
480 if (0 != GNUNET_net_close (&desc))
481 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
482 GNUNET_GE_ERROR | GNUNET_GE_USER |
483 GNUNET_GE_ADMIN | GNUNET_GE_BULK,
485 return GNUNET_SYSERR;
487 selector = GNUNET_select_create ("udp", GNUNET_YES, coreAPI->ectx, load_monitor, desc, addrlen, 0, /* timeout */
488 &select_message_handler,
490 &select_accept_handler,
492 &select_close_handler,
494 16 /* max sockets */ );
495 if (selector == NULL)
496 return GNUNET_SYSERR;
498 desc = udp_create_socket ();
501 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
502 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
503 GNUNET_GE_BULK, "socket");
504 GNUNET_select_destroy (selector);
506 return GNUNET_SYSERR;
508 udp_sock = GNUNET_socket_create (coreAPI->ectx, load_monitor, desc);
509 GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL);
514 * The exported method. Makes the core api available via a global and
515 * returns the udp transport API.
517 GNUNET_TransportAPI *
518 inittransport_udp (GNUNET_CoreAPIForTransport * core)
520 unsigned long long mtu;
523 load_monitor = core->load_monitor;
524 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (UDPMessage) == 68);
525 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24);
527 if (-1 == GNUNET_GC_get_configuration_value_number (cfg,
532 GNUNET_P2P_MESSAGE_OVERHEAD
535 (GNUNET_MessageHeader) +
542 GNUNET_GE_LOG (coreAPI->ectx,
543 GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE,
544 _("MTU %llu for `%s' is probably too low!\n"), mtu, "UDP");
545 lock = GNUNET_mutex_create (GNUNET_NO);
547 GNUNET_GC_attach_change_listener (cfg, &reload_configuration, NULL))
549 GNUNET_mutex_destroy (lock);
553 if (GNUNET_GC_get_configuration_value_yesno (cfg, "UDP", "UPNP", GNUNET_YES)
556 upnp = coreAPI->service_request ("upnp");
558 GNUNET_GE_LOG (coreAPI->ectx,
559 GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE,
560 "The UPnP service could not be loaded. To disable UPnP, set the "
561 "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n",
564 stats = coreAPI->service_request ("stats");
568 = stats->create (gettext_noop ("# bytes received via UDP"));
569 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via UDP"));
571 = stats->create (gettext_noop ("# bytes dropped by UDP (outgoing)"));
573 = stats->create (gettext_noop ("# UDP connections (right now)"));
575 myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_UDP;
576 myAPI.mtu = mtu - sizeof (UDPMessage);
578 myAPI.hello_verify = &verify_hello;
579 myAPI.hello_create = &create_hello;
580 myAPI.connect = &udp_connect;
581 myAPI.send = &udp_send;
582 myAPI.associate = &udp_associate;
583 myAPI.disconnect = &udp_disconnect;
584 myAPI.server_start = &udp_transport_server_start;
585 myAPI.server_stop = &udp_transport_server_stop;
586 myAPI.hello_to_address = &hello_to_address;
587 myAPI.send_now_test = &udp_test_would_try;